summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-nvdimm3
-rw-r--r--Documentation/admin-guide/blockdev/zoned_loop.rst9
-rw-r--r--Makefile2
-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/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/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/include/asm/acpi.h19
-rw-r--r--arch/riscv/kernel/unaligned_access_speed.c19
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c215
-rw-r--r--arch/x86/coco/tdx/tdx.c10
-rw-r--r--arch/x86/events/intel/uncore.c1
-rw-r--r--arch/x86/events/intel/uncore_snbep.c13
-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/genhd.c1
-rw-r--r--drivers/accel/rocket/rocket_job.c32
-rw-r--r--drivers/acpi/apei/erst.c2
-rw-r--r--drivers/acpi/apei/ghes.c52
-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/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/crypto/allwinner/Kconfig16
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/Makefile1
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c57
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c159
-rw-r--r--drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h29
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/Makefile1
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c39
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c177
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h23
-rw-r--r--drivers/cxl/core/features.c8
-rw-r--r--drivers/cxl/core/pmem.c10
-rw-r--r--drivers/cxl/core/ras.c1
-rw-r--r--drivers/cxl/cxl.h3
-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.c112
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c24
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c10
-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.c9
-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/master.c9
-rw-r--r--drivers/i3c/master/adi-i3c-master.c15
-rw-r--r--drivers/i3c/master/renesas-i3c.c70
-rw-r--r--drivers/i3c/master/svc-i3c-master.c10
-rw-r--r--drivers/infiniband/core/ucma.c14
-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_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.c56
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c8
-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/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.c7
-rw-r--r--drivers/net/ethernet/ibm/emac/mal.c13
-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/ovpn/main.c19
-rw-r--r--drivers/net/ovpn/ovpnpriv.h4
-rw-r--r--drivers/net/ovpn/peer.c8
-rw-r--r--drivers/net/ovpn/tcp.c9
-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/ath11k/mac.c28
-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/mt76_connac_mcu.c16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c15
-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/mcu.c4
-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/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/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/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.c43
-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/intel/ishtp_eclite.c5
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c77
-rw-r--r--drivers/platform/x86/lenovo/think-lmi.c19
-rw-r--r--drivers/platform/x86/lenovo/wmi-helpers.c4
-rw-r--r--drivers/platform/x86/lenovo/ymc.c8
-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/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/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/tcpm/tcpci.c12
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c3
-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--fs/backing-file.c2
-rw-r--r--fs/btrfs/direct-io.c19
-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.c11
-rw-r--r--fs/ceph/dir.c10
-rw-r--r--fs/ceph/export.c26
-rw-r--r--fs/ceph/mds_client.c8
-rw-r--r--fs/ceph/mdsmap.c11
-rw-r--r--fs/ceph/xattr.c1
-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/fat/misc.c4
-rw-r--r--fs/fuse/dev.c3
-rw-r--r--fs/jbd2/checkpoint.c28
-rw-r--r--fs/lockd/clntproc.c3
-rw-r--r--fs/lockd/svcsubs.c55
-rw-r--r--fs/netfs/read_retry.c6
-rw-r--r--fs/netfs/write_retry.c6
-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.c22
-rw-r--r--fs/nfsd/flexfilelayoutxdr.c20
-rw-r--r--fs/nfsd/localio.c8
-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.c12
-rw-r--r--fs/nfsd/nfs4proc.c60
-rw-r--r--fs/nfsd/nfs4state.c249
-rw-r--r--fs/nfsd/nfs4xdr.c26
-rw-r--r--fs/nfsd/nfscache.c6
-rw-r--r--fs/nfsd/nfsctl.c129
-rw-r--r--fs/nfsd/nfsfh.c12
-rw-r--r--fs/nfsd/nfsproc.c7
-rw-r--r--fs/nfsd/nfssvc.c4
-rw-r--r--fs/nfsd/nfsxdr.c32
-rw-r--r--fs/nfsd/state.h3
-rw-r--r--fs/nfsd/trace.h18
-rw-r--r--fs/nfsd/vfs.c34
-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.c5
-rw-r--r--fs/nsfs.c2
-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.c2
-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/xattr.c5
-rw-r--r--fs/orangefs/devorangefs-req.c1
-rw-r--r--fs/orangefs/orangefs-debugfs.c1
-rw-r--r--fs/proc/task_mmu.c15
-rw-r--r--fs/smb/client/cifsfs.c15
-rw-r--r--fs/smb/client/dfs_cache.c33
-rw-r--r--fs/smb/client/file.c1
-rw-r--r--fs/smb/client/inode.c1
-rw-r--r--fs/smb/client/smb1ops.c2
-rw-r--r--fs/smb/client/smb2file.c5
-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/compaction.h3
-rw-r--r--include/linux/dm-io.h6
-rw-r--r--include/linux/hugetlb.h2
-rw-r--r--include/linux/list.h15
-rw-r--r--include/linux/mm_types.h2
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--include/linux/sunrpc/svc_rdma_pcl.h2
-rw-r--r--include/linux/swapops.h12
-rw-r--r--include/linux/usb/tcpci.h1
-rw-r--r--include/net/ip_tunnels.h41
-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--kernel/auditfilter.c6
-rw-r--r--kernel/bpf/bloom_filter.c19
-rw-r--r--kernel/bpf/stackmap.c3
-rw-r--r--kernel/bpf/verifier.c27
-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/cpufreq_schedutil.c15
-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.c173
-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/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.c4
-rw-r--r--mm/hugetlb.c8
-rw-r--r--mm/internal.h10
-rw-r--r--mm/kasan/quarantine.c7
-rw-r--r--mm/kmemleak.c51
-rw-r--r--mm/memcontrol-v1.c6
-rw-r--r--mm/memcontrol.c13
-rw-r--r--mm/memory.c49
-rw-r--r--mm/mempolicy.c21
-rw-r--r--mm/migrate.c2
-rw-r--r--mm/mm_init.c9
-rw-r--r--mm/page_alloc.c76
-rw-r--r--mm/page_table_check.c12
-rw-r--r--mm/pagewalk.c6
-rw-r--r--mm/rmap.c20
-rw-r--r--mm/swapfile.c6
-rw-r--r--mm/vmscan.c21
-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/mpls/af_mpls.c2
-rw-r--r--net/mptcp/syncookies.c1
-rw-r--r--net/openvswitch/conntrack.c8
-rw-r--r--net/openvswitch/datapath.c48
-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_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.c6
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c8
-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.c9
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_rw.c47
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c47
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c44
-rw-r--r--net/sunrpc/xprtsock.c12
-rw-r--r--net/vmw_vsock/virtio_transport.c2
-rw-r--r--rust/kernel/bug.rs6
-rw-r--r--rust/kernel/list.rs2
-rw-r--r--rust/kernel/time.rs17
-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/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/usb/6fire/comm.c9
-rw-r--r--sound/usb/bcd2000/bcd2000.c11
-rw-r--r--tools/include/linux/compiler.h2
-rwxr-xr-xtools/testing/selftests/mm/run_vmtests.sh2
503 files changed, 5434 insertions, 2529 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 64dcfde7450a..ae0eff616990 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 de74f98fca64..52950c3d861c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
VERSION = 6
PATCHLEVEL = 18
-SUBLEVEL = 49
+SUBLEVEL = 50
EXTRAVERSION =
NAME = Baby Opossum Posse
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 21833d1c867d..2e9870ee21c6 100644
--- a/arch/alpha/kernel/pci-sysfs.c
+++ b/arch/alpha/kernel/pci-sysfs.c
@@ -364,17 +364,17 @@ int pci_legacy_write(struct pci_bus *bus, loff_t port, u32 val, size_t size)
switch(size) {
case 1:
- outb(port, val);
+ outb(val, port);
return 1;
case 2:
if (port & 1)
return -EINVAL;
- outw(port, val);
+ outw(val, port);
return 2;
case 4:
if (port & 3)
return -EINVAL;
- outl(port, val);
+ outl(val, port);
return 4;
}
return -EINVAL;
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c
index 1f99b03effc2..a37707e05e34 100644
--- a/arch/alpha/kernel/sys_marvel.c
+++ b/arch/alpha/kernel/sys_marvel.c
@@ -263,6 +263,18 @@ init_io7_irqs(struct io7 *io7,
*/
printk(" Interrupts reported to CPU at PE %u\n", boot_cpuid);
+ /* Set up the lsi irqs. */
+ for (i = 0; i < 128; ++i) {
+ irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
+ irq_set_status_flags(base + i, IRQ_LEVEL);
+ }
+
+ /* Set up the msi irqs. */
+ for (i = 128; i < (128 + 512); ++i) {
+ irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
+ irq_set_status_flags(base + i, IRQ_LEVEL);
+ }
+
raw_spin_lock(&io7->irq_lock);
/* set up the error irqs */
@@ -272,12 +284,6 @@ init_io7_irqs(struct io7 *io7,
io7_redirect_irq(io7, &io7->csrs->STV_CTL.csr, boot_cpuid);
io7_redirect_irq(io7, &io7->csrs->HEI_CTL.csr, boot_cpuid);
- /* Set up the lsi irqs. */
- for (i = 0; i < 128; ++i) {
- irq_set_chip_and_handler(base + i, lsi_ops, handle_level_irq);
- irq_set_status_flags(i, IRQ_LEVEL);
- }
-
/* Disable the implemented irqs in hardware. */
for (i = 0; i < 0x60; ++i)
init_one_io7_lsi(io7, i, boot_cpuid);
@@ -285,13 +291,6 @@ init_io7_irqs(struct io7 *io7,
init_one_io7_lsi(io7, 0x74, boot_cpuid);
init_one_io7_lsi(io7, 0x75, boot_cpuid);
-
- /* Set up the msi irqs. */
- for (i = 128; i < (128 + 512); ++i) {
- irq_set_chip_and_handler(base + i, msi_ops, handle_level_irq);
- irq_set_status_flags(i, IRQ_LEVEL);
- }
-
for (i = 0; i < 16; ++i)
init_one_io7_msi(io7, i, boot_cpuid);
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index 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 e481254e3645..b4ef6a27e8ce 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -96,7 +96,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/sm6115-fxtec-pro1x.dts b/arch/arm64/boot/dts/qcom/sm6115-fxtec-pro1x.dts
index ad347ccd1975..fb5b9f10c8a7 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 aee3fafbe7fc..58a5fe65ae38 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 5de964d369b0..c224f76c1383 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -689,12 +689,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>;
@@ -721,19 +715,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 8e3858cf988c..27465cceca9a 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
@@ -132,6 +132,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 7e179862da6e..26c431de6d96 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";
};
@@ -323,12 +332,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 b3801f532b10..5138002d72f1 100644
--- a/arch/arm64/kernel/proton-pack.c
+++ b/arch/arm64/kernel/proton-pack.c
@@ -1022,6 +1022,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;
@@ -1035,6 +1040,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);
@@ -1200,6 +1207,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/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 eac84d687b53..c493692adc69 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 02474e27df9b..5eff89a6859e 100644
--- a/arch/powerpc/platforms/powermac/low_i2c.c
+++ b/arch/powerpc/platforms/powermac/low_i2c.c
@@ -1505,7 +1505,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 eec333dd2e59..4bcb6f222065 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/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h
index 6e13695120bc..a4b9e60f747c 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
@@ -71,6 +73,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 78fc74a41678..f111a30d480c 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>
@@ -375,18 +374,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
@@ -474,12 +464,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/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index fbe21b1db3ee..d866562cae38 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -111,6 +111,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;
@@ -119,13 +120,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);
@@ -168,12 +171,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",
@@ -187,17 +192,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 {
@@ -207,20 +212,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;
/*
@@ -228,28 +236,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;
@@ -257,12 +266,12 @@ static int cpum_cf_alloc_cpu(int cpu)
cpuhw = kzalloc(sizeof(*cpuhw), GFP_KERNEL);
if (cpuhw) {
p->cpucf = cpuhw;
- refcount_set(&cpuhw->refcnt, 1);
+ refcount_set(&cpuhw->refcnt, num);
} else {
rc = -ENOMEM;
}
} else {
- refcount_inc(&cpuhw->refcnt);
+ refcount_add(num, &cpuhw->refcnt);
}
if (rc) {
/*
@@ -270,10 +279,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;
}
@@ -285,39 +292,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 */
@@ -1091,53 +1129,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;
}
@@ -1184,7 +1236,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))
@@ -1226,11 +1278,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);
@@ -1386,6 +1451,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) {
@@ -1396,9 +1462,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;
}
@@ -1417,15 +1484,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 7b2833705d47..fcb9ec936d05 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -693,8 +693,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
@@ -702,18 +702,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/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index e228e564b15e..8301a589d9a6 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -67,6 +67,7 @@ int uncore_die_to_segment(int die)
return bus ? pci_domain_nr(bus) : -EINVAL;
}
+/* Note: This API can only be used when NUMA information is available. */
int uncore_device_to_die(struct pci_dev *dev)
{
int node = pcibus_to_node(dev->bus);
diff --git a/arch/x86/events/intel/uncore_snbep.c b/arch/x86/events/intel/uncore_snbep.c
index a338ee01bb24..0182785cad1f 100644
--- a/arch/x86/events/intel/uncore_snbep.c
+++ b/arch/x86/events/intel/uncore_snbep.c
@@ -1475,13 +1475,7 @@ static int snbep_pci2phy_map_init(int devid, int nodeid_loc, int idmap_loc, bool
}
map->pbus_to_dieid[bus] = die_id = uncore_device_to_die(ubox_dev);
-
raw_spin_unlock(&pci2phy_map_lock);
-
- if (WARN_ON_ONCE(die_id == -1)) {
- err = -EINVAL;
- break;
- }
}
}
@@ -6533,7 +6527,7 @@ static void spr_update_device_location(int type_id)
while ((dev = pci_get_device(PCI_VENDOR_ID_INTEL, device, dev)) != NULL) {
- die = uncore_device_to_die(dev);
+ die = uncore_pcibus_to_dieid(dev->bus);
if (die < 0)
continue;
@@ -6557,6 +6551,11 @@ static void spr_update_device_location(int type_id)
int spr_uncore_pci_init(void)
{
+ int ret = snbep_pci2phy_map_init(0x3250, SKX_CPUNODEID, SKX_GIDNIDMAP, true);
+
+ if (ret)
+ return ret;
+
/*
* The discovery table of UPI on some SPR variant is broken,
* which impacts the detection of both UPI and M3UPI uncore PMON.
diff --git a/arch/x86/include/asm/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 54368a43abf6..b1e6c47113aa 100644
--- a/arch/x86/include/asm/insn-eval.h
+++ b/arch/x86/include/asm/insn-eval.h
@@ -9,6 +9,7 @@
#include <linux/compiler.h>
#include <linux/bug.h>
#include <linux/err.h>
+#include <asm/insn.h>
#include <asm/ptrace.h>
#define INSN_CODE_SEG_ADDR_SZ(params) ((params >> 4) & 0xf)
@@ -44,4 +45,39 @@ enum insn_mmio_type {
enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
+/*
+ * Write @val into *@reg following the x86 rules for writes to
+ * general-purpose registers (Intel SDM Vol. 1, "General-Purpose
+ * Registers in 64-Bit Mode"): an 8- or 16-bit write leaves the rest of
+ * the register untouched, a 32-bit write zero-extends the result into
+ * the upper 32 bits, and a 64-bit write replaces the whole register.
+ *
+ * @bytes is the width of the write, not a property of the instruction:
+ * an instruction that, say, sign-extends a 32-bit immediate into a
+ * 64-bit register does a 64-bit write here.
+ *
+ * @reg need not be 8-byte aligned: KVM's instruction emulator offsets
+ * the pointer by one byte to address the high-byte registers (AH, CH,
+ * DH, BH). Use narrow stores for the sub-word cases so the access
+ * width matches @bytes and the adjacent bytes are left alone.
+ */
+static inline void insn_assign_reg(unsigned long *reg, u64 val, int bytes)
+{
+ switch (bytes) {
+ case 1:
+ *(u8 *)reg = (u8)val;
+ break;
+ case 2:
+ *(u16 *)reg = (u16)val;
+ break;
+ case 4:
+ /* A 32-bit write zero-extends into the upper 32 bits. */
+ *reg = (u32)val;
+ break;
+ case 8:
+ *reg = val;
+ break;
+ }
+}
+
#endif /* _ASM_X86_INSN_EVAL_H */
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index 4e3da5b497b8..9dbf3623e79e 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>
@@ -436,25 +437,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;
@@ -502,7 +484,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)
@@ -1756,7 +1738,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)
@@ -1989,7 +1971,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 c3798cab3b7d..88a1bbfa918f 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -1756,7 +1756,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
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/genhd.c b/block/genhd.c
index b00fa6baf4ff..28e5aea2418e 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 acd606160dc9..b374d7ee54b6 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)
@@ -550,6 +565,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,
@@ -575,8 +591,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/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 fc96a5e234f0..5b52455d86e0 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -581,7 +581,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;
@@ -736,7 +736,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,
@@ -749,7 +749,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;
@@ -787,10 +787,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;
}
@@ -798,11 +799,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");
@@ -818,7 +826,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,
@@ -838,7 +846,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;
@@ -856,10 +864,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;
}
@@ -867,11 +876,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/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 55299f28b123..8df501fa8def 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3534,17 +3534,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);
@@ -3579,13 +3575,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;
@@ -3629,13 +3623,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)) {
@@ -3661,6 +3655,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 13c2c48e587f..12e12e16047f 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -2161,6 +2161,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 b2dc2bd7e5ac..bdaaf833aa26 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -797,18 +797,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 26364e43aeb3..45088c163286 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -319,7 +319,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 b75016e0e654..fd438dcdfad6 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/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index cb1c321910e2..a62eae7276d6 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -1331,19 +1331,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 ea9df7dc2316..a869fd15bc89 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 */
@@ -608,6 +609,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 },
@@ -805,6 +810,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 },
@@ -4319,6 +4330,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 fff845ed44e3..0a54a7cf2bde 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -548,6 +548,7 @@ static int bcm_close(struct hci_uart *hu)
if (IS_ENABLED(CONFIG_PM) && bdev->irq_acquired) {
devm_free_irq(bdev->dev, bdev->irq, bdev);
device_init_wakeup(bdev->dev, false);
+ pm_runtime_dont_use_autosuspend(bdev->dev);
pm_runtime_disable(bdev->dev);
}
diff --git a/drivers/bluetooth/hci_bcm4377.c b/drivers/bluetooth/hci_bcm4377.c
index 45e6d84224ee..1d41401f3dba 100644
--- a/drivers/bluetooth/hci_bcm4377.c
+++ b/drivers/bluetooth/hci_bcm4377.c
@@ -2486,6 +2486,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 d0d4420c1a0f..9da2be1f3cc9 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -987,8 +987,10 @@ static void h5_btrtl_open(struct h5 *h5)
static void h5_btrtl_close(struct h5 *h5)
{
- if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags))
+ if (!test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
+ pm_runtime_dont_use_autosuspend(&h5->hu->serdev->dev);
pm_runtime_disable(&h5->hu->serdev->dev);
+ }
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
gpiod_set_value_cansleep(h5->enable_gpio, 0);
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 1d6e09508f1f..4d52b1d3d8fe 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -348,6 +348,7 @@ static int intel_set_power(struct hci_uart *hu, bool powered)
devm_free_irq(&idev->pdev->dev, idev->irq, idev);
device_wakeup_disable(&idev->pdev->dev);
+ pm_runtime_dont_use_autosuspend(&idev->pdev->dev);
pm_runtime_disable(&idev->pdev->dev);
}
}
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index c27413a371c2..55e0dceee54a 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 9d6d24f49938..a066e0105c77 100644
--- a/drivers/cdx/cdx.c
+++ b/drivers/cdx/cdx.c
@@ -745,7 +745,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;
@@ -754,8 +753,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 ee2bdc7ed0da..bc0564d87867 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 51f76d755f98..0247079db185 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -1393,6 +1393,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 d1740f18f718..2cf3b4ca6e1f 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/crypto/allwinner/Kconfig b/drivers/crypto/allwinner/Kconfig
index 06ea0e9fe6f2..d86ae005fbe2 100644
--- a/drivers/crypto/allwinner/Kconfig
+++ b/drivers/crypto/allwinner/Kconfig
@@ -70,14 +70,6 @@ config CRYPTO_DEV_SUN8I_CE_HASH
help
Say y to enable support for hash algorithms.
-config CRYPTO_DEV_SUN8I_CE_PRNG
- bool "Support for Allwinner Crypto Engine PRNG"
- depends on CRYPTO_DEV_SUN8I_CE
- select CRYPTO_RNG
- help
- Select this option if you want to provide kernel-side support for
- the Pseudo-Random Number Generator found in the Crypto Engine.
-
config CRYPTO_DEV_SUN8I_CE_TRNG
bool "Support for Allwinner Crypto Engine TRNG"
depends on CRYPTO_DEV_SUN8I_CE
@@ -113,14 +105,6 @@ config CRYPTO_DEV_SUN8I_SS_DEBUG
This will create /sys/kernel/debug/sun8i-ss/stats for displaying
the number of requests per flow and per algorithm.
-config CRYPTO_DEV_SUN8I_SS_PRNG
- bool "Support for Allwinner Security System PRNG"
- depends on CRYPTO_DEV_SUN8I_SS
- select CRYPTO_RNG
- help
- Select this option if you want to provide kernel-side support for
- the Pseudo-Random Number Generator found in the Security System.
-
config CRYPTO_DEV_SUN8I_SS_HASH
bool "Enable support for hash on sun8i-ss"
depends on CRYPTO_DEV_SUN8I_SS
diff --git a/drivers/crypto/allwinner/sun8i-ce/Makefile b/drivers/crypto/allwinner/sun8i-ce/Makefile
index 0842eb2d9408..ea708b427e2e 100644
--- a/drivers/crypto/allwinner/sun8i-ce/Makefile
+++ b/drivers/crypto/allwinner/sun8i-ce/Makefile
@@ -1,5 +1,4 @@
obj-$(CONFIG_CRYPTO_DEV_SUN8I_CE) += sun8i-ce.o
sun8i-ce-y += sun8i-ce-core.o sun8i-ce-cipher.o
sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_HASH) += sun8i-ce-hash.o
-sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG) += sun8i-ce-prng.o
sun8i-ce-$(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) += sun8i-ce-trng.o
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
index c16bb6ce6ee3..ef6f601c6259 100644
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c
@@ -12,7 +12,6 @@
#include <crypto/engine.h>
#include <crypto/internal/hash.h>
-#include <crypto/internal/rng.h>
#include <crypto/internal/skcipher.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -49,7 +48,6 @@ static const struct ce_variant ce_h3_variant = {
{ "mod", 50000000, 0 },
},
.esr = ESR_H3,
- .prng = CE_ALG_PRNG,
.trng = CE_ID_NOTSUPP,
};
@@ -66,7 +64,6 @@ static const struct ce_variant ce_h5_variant = {
{ "mod", 300000000, 0 },
},
.esr = ESR_H5,
- .prng = CE_ALG_PRNG,
.trng = CE_ID_NOTSUPP,
};
@@ -80,7 +77,6 @@ static const struct ce_variant ce_h6_variant = {
},
.cipher_t_dlen_in_bytes = true,
.hash_t_dlen_in_bits = true,
- .prng_t_dlen_in_bytes = true,
.trng_t_dlen_in_bytes = true,
.ce_clks = {
{ "bus", 0, 200000000 },
@@ -88,7 +84,6 @@ static const struct ce_variant ce_h6_variant = {
{ "ram", 0, 400000000 },
},
.esr = ESR_H6,
- .prng = CE_ALG_PRNG_V2,
.trng = CE_ALG_TRNG_V2,
};
@@ -102,7 +97,6 @@ static const struct ce_variant ce_h616_variant = {
},
.cipher_t_dlen_in_bytes = true,
.hash_t_dlen_in_bits = true,
- .prng_t_dlen_in_bytes = true,
.trng_t_dlen_in_bytes = true,
.needs_word_addresses = true,
.ce_clks = {
@@ -112,7 +106,6 @@ static const struct ce_variant ce_h616_variant = {
{ "trng", 0, 0 },
},
.esr = ESR_H6,
- .prng = CE_ALG_PRNG_V2,
.trng = CE_ALG_TRNG_V2,
};
@@ -129,7 +122,6 @@ static const struct ce_variant ce_a64_variant = {
{ "mod", 300000000, 0 },
},
.esr = ESR_A64,
- .prng = CE_ALG_PRNG,
.trng = CE_ID_NOTSUPP,
};
@@ -148,7 +140,6 @@ static const struct ce_variant ce_d1_variant = {
{ "trng", 0, 0 },
},
.esr = ESR_D1,
- .prng = CE_ALG_PRNG,
.trng = CE_ALG_TRNG,
};
@@ -165,7 +156,6 @@ static const struct ce_variant ce_r40_variant = {
{ "mod", 300000000, 0 },
},
.esr = ESR_R40,
- .prng = CE_ALG_PRNG,
.trng = CE_ID_NOTSUPP,
};
@@ -614,25 +604,6 @@ static struct sun8i_ce_alg_template ce_algs[] = {
},
},
#endif
-#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_PRNG
-{
- .type = CRYPTO_ALG_TYPE_RNG,
- .alg.rng = {
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "sun8i-ce-prng",
- .cra_priority = 300,
- .cra_ctxsize = sizeof(struct sun8i_ce_rng_tfm_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = sun8i_ce_prng_init,
- .cra_exit = sun8i_ce_prng_exit,
- },
- .generate = sun8i_ce_prng_generate,
- .seed = sun8i_ce_prng_seed,
- .seedsize = PRNG_SEED_SIZE,
- }
-},
-#endif
};
static int sun8i_ce_debugfs_show(struct seq_file *seq, void *v)
@@ -692,12 +663,6 @@ static int sun8i_ce_debugfs_show(struct seq_file *seq, void *v)
seq_printf(seq, "\tFallback due to SG numbers: %lu\n",
ce_algs[i].stat_fb_maxsg);
break;
- case CRYPTO_ALG_TYPE_RNG:
- seq_printf(seq, "%s %s reqs=%lu bytes=%lu\n",
- ce_algs[i].alg.rng.base.cra_driver_name,
- ce_algs[i].alg.rng.base.cra_name,
- ce_algs[i].stat_req, ce_algs[i].stat_bytes);
- break;
}
}
#if defined(CONFIG_CRYPTO_DEV_SUN8I_CE_TRNG) && \
@@ -925,23 +890,6 @@ static int sun8i_ce_register_algs(struct sun8i_ce_dev *ce)
return err;
}
break;
- case CRYPTO_ALG_TYPE_RNG:
- if (ce->variant->prng == CE_ID_NOTSUPP) {
- dev_info(ce->dev,
- "DEBUG: Algo of %s not supported\n",
- ce_algs[i].alg.rng.base.cra_name);
- ce_algs[i].ce = NULL;
- break;
- }
- dev_info(ce->dev, "Register %s\n",
- ce_algs[i].alg.rng.base.cra_name);
- err = crypto_register_rng(&ce_algs[i].alg.rng);
- if (err) {
- dev_err(ce->dev, "Fail to register %s\n",
- ce_algs[i].alg.rng.base.cra_name);
- ce_algs[i].ce = NULL;
- }
- break;
default:
ce_algs[i].ce = NULL;
dev_err(ce->dev, "ERROR: tried to register an unknown algo\n");
@@ -968,11 +916,6 @@ static void sun8i_ce_unregister_algs(struct sun8i_ce_dev *ce)
ce_algs[i].alg.hash.base.halg.base.cra_name);
crypto_engine_unregister_ahash(&ce_algs[i].alg.hash);
break;
- case CRYPTO_ALG_TYPE_RNG:
- dev_info(ce->dev, "Unregister %d %s\n", i,
- ce_algs[i].alg.rng.base.cra_name);
- crypto_unregister_rng(&ce_algs[i].alg.rng);
- break;
}
}
}
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c
deleted file mode 100644
index d0a1ac66738b..000000000000
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce-prng.c
+++ /dev/null
@@ -1,159 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * sun8i-ce-prng.c - hardware cryptographic offloader for
- * Allwinner H3/A64/H5/H2+/H6/R40 SoC
- *
- * Copyright (C) 2015-2020 Corentin Labbe <clabbe@baylibre.com>
- *
- * This file handle the PRNG
- *
- * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst
- */
-#include "sun8i-ce.h"
-#include <linux/dma-mapping.h>
-#include <linux/pm_runtime.h>
-#include <crypto/internal/rng.h>
-
-int sun8i_ce_prng_init(struct crypto_tfm *tfm)
-{
- struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
-
- memset(ctx, 0, sizeof(struct sun8i_ce_rng_tfm_ctx));
- return 0;
-}
-
-void sun8i_ce_prng_exit(struct crypto_tfm *tfm)
-{
- struct sun8i_ce_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
-
- kfree_sensitive(ctx->seed);
- ctx->seed = NULL;
- ctx->slen = 0;
-}
-
-int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed,
- unsigned int slen)
-{
- struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm);
-
- if (ctx->seed && ctx->slen != slen) {
- kfree_sensitive(ctx->seed);
- ctx->slen = 0;
- ctx->seed = NULL;
- }
- if (!ctx->seed)
- ctx->seed = kmalloc(slen, GFP_KERNEL | GFP_DMA);
- if (!ctx->seed)
- return -ENOMEM;
-
- memcpy(ctx->seed, seed, slen);
- ctx->slen = slen;
-
- return 0;
-}
-
-int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dst, unsigned int dlen)
-{
- struct sun8i_ce_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm);
- struct rng_alg *alg = crypto_rng_alg(tfm);
- struct sun8i_ce_alg_template *algt;
- struct sun8i_ce_dev *ce;
- dma_addr_t dma_iv, dma_dst;
- int err = 0;
- int flow = 3;
- unsigned int todo;
- struct sun8i_ce_flow *chan;
- struct ce_task *cet;
- u32 common, sym;
- void *d;
-
- algt = container_of(alg, struct sun8i_ce_alg_template, alg.rng);
- ce = algt->ce;
-
- if (ctx->slen == 0) {
- dev_err(ce->dev, "not seeded\n");
- return -EINVAL;
- }
-
- /* we want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE */
- todo = dlen + ctx->slen + PRNG_DATA_SIZE * 2;
- todo -= todo % PRNG_DATA_SIZE;
-
- d = kzalloc(todo, GFP_KERNEL | GFP_DMA);
- if (!d) {
- err = -ENOMEM;
- goto err_mem;
- }
-
- dev_dbg(ce->dev, "%s PRNG slen=%u dlen=%u todo=%u multi=%u\n", __func__,
- slen, dlen, todo, todo / PRNG_DATA_SIZE);
-
-#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
- algt->stat_req++;
- algt->stat_bytes += todo;
-#endif
-
- dma_iv = dma_map_single(ce->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE);
- if (dma_mapping_error(ce->dev, dma_iv)) {
- dev_err(ce->dev, "Cannot DMA MAP IV\n");
- err = -EFAULT;
- goto err_iv;
- }
-
- dma_dst = dma_map_single(ce->dev, d, todo, DMA_FROM_DEVICE);
- if (dma_mapping_error(ce->dev, dma_dst)) {
- dev_err(ce->dev, "Cannot DMA MAP DST\n");
- err = -EFAULT;
- goto err_dst;
- }
-
- err = pm_runtime_resume_and_get(ce->dev);
- if (err < 0)
- goto err_pm;
-
- mutex_lock(&ce->rnglock);
- chan = &ce->chanlist[flow];
-
- cet = &chan->tl[0];
- memset(cet, 0, sizeof(struct ce_task));
-
- cet->t_id = cpu_to_le32(flow);
- common = ce->variant->prng | CE_COMM_INT;
- cet->t_common_ctl = cpu_to_le32(common);
-
- /* recent CE (H6) need length in bytes, in word otherwise */
- if (ce->variant->prng_t_dlen_in_bytes)
- cet->t_dlen = cpu_to_le32(todo);
- else
- cet->t_dlen = cpu_to_le32(todo / 4);
-
- sym = PRNG_LD;
- cet->t_sym_ctl = cpu_to_le32(sym);
- cet->t_asym_ctl = 0;
-
- cet->t_key = desc_addr_val_le32(ce, dma_iv);
- cet->t_iv = desc_addr_val_le32(ce, dma_iv);
-
- cet->t_dst[0].addr = desc_addr_val_le32(ce, dma_dst);
- cet->t_dst[0].len = cpu_to_le32(todo / 4);
-
- err = sun8i_ce_run_task(ce, 3, "PRNG");
- mutex_unlock(&ce->rnglock);
-
- pm_runtime_put(ce->dev);
-
-err_pm:
- dma_unmap_single(ce->dev, dma_dst, todo, DMA_FROM_DEVICE);
-err_dst:
- dma_unmap_single(ce->dev, dma_iv, ctx->slen, DMA_TO_DEVICE);
-
- if (!err) {
- memcpy(dst, d, dlen);
- memcpy(ctx->seed, d + dlen, ctx->slen);
- }
-err_iv:
- kfree_sensitive(d);
-err_mem:
- return err;
-}
diff --git a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
index 71f5a0cd3d45..468d99bf5bf6 100644
--- a/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
+++ b/drivers/crypto/allwinner/sun8i-ce/sun8i-ce.h
@@ -15,7 +15,6 @@
#include <linux/hw_random.h>
#include <crypto/internal/hash.h>
#include <crypto/md5.h>
-#include <crypto/rng.h>
#include <crypto/sha1.h>
#include <crypto/sha2.h>
@@ -58,9 +57,7 @@
#define CE_ALG_SHA384 20
#define CE_ALG_SHA512 21
#define CE_ALG_TRNG 48
-#define CE_ALG_PRNG 49
#define CE_ALG_TRNG_V2 0x1c
-#define CE_ALG_PRNG_V2 0x1d
/* Used in ce_variant */
#define CE_ID_NOTSUPP 0xFF
@@ -96,10 +93,6 @@
#define ESR_H6 4
#define ESR_D1 5
-#define PRNG_DATA_SIZE (160 / 8)
-#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8)
-#define PRNG_LD BIT(17)
-
#define CE_DIE_ID_SHIFT 16
#define CE_DIE_ID_MASK 0x07
@@ -136,13 +129,10 @@ struct ce_clock {
* bytes or words
* @hash_t_dlen_in_bytes: Does the request size for hash is in
* bits or words
- * @prng_t_dlen_in_bytes: Does the request size for PRNG is in
- * bytes or words
* @trng_t_dlen_in_bytes: Does the request size for TRNG is in
* bytes or words
* @ce_clks: list of clocks needed by this variant
* @esr: The type of error register
- * @prng: The CE_ALG_XXX value for the PRNG
* @trng: The CE_ALG_XXX value for the TRNG
*/
struct ce_variant {
@@ -151,12 +141,10 @@ struct ce_variant {
u32 op_mode[CE_ID_OP_MAX];
bool cipher_t_dlen_in_bytes;
bool hash_t_dlen_in_bits;
- bool prng_t_dlen_in_bytes;
bool trng_t_dlen_in_bytes;
bool needs_word_addresses;
struct ce_clock ce_clks[CE_MAX_CLOCKS];
int esr;
- unsigned char prng;
unsigned char trng;
};
@@ -328,16 +316,6 @@ struct sun8i_ce_hash_reqctx {
};
/*
- * struct sun8i_ce_prng_ctx - context for PRNG TFM
- * @seed: The seed to use
- * @slen: The size of the seed
- */
-struct sun8i_ce_rng_tfm_ctx {
- void *seed;
- unsigned int slen;
-};
-
-/*
* struct sun8i_ce_alg_template - crypto_alg template
* @type: the CRYPTO_ALG_TYPE for this template
* @ce_algo_id: the CE_ID for this template
@@ -357,7 +335,6 @@ struct sun8i_ce_alg_template {
union {
struct skcipher_engine_alg skcipher;
struct ahash_engine_alg hash;
- struct rng_alg rng;
} alg;
unsigned long stat_req;
unsigned long stat_fb;
@@ -398,11 +375,5 @@ int sun8i_ce_hash_finup(struct ahash_request *areq);
int sun8i_ce_hash_digest(struct ahash_request *areq);
int sun8i_ce_hash_run(struct crypto_engine *engine, void *breq);
-int sun8i_ce_prng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dst, unsigned int dlen);
-int sun8i_ce_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
-void sun8i_ce_prng_exit(struct crypto_tfm *tfm);
-int sun8i_ce_prng_init(struct crypto_tfm *tfm);
-
int sun8i_ce_hwrng_register(struct sun8i_ce_dev *ce);
void sun8i_ce_hwrng_unregister(struct sun8i_ce_dev *ce);
diff --git a/drivers/crypto/allwinner/sun8i-ss/Makefile b/drivers/crypto/allwinner/sun8i-ss/Makefile
index aabfd893c817..2d6458a42e58 100644
--- a/drivers/crypto/allwinner/sun8i-ss/Makefile
+++ b/drivers/crypto/allwinner/sun8i-ss/Makefile
@@ -1,4 +1,3 @@
obj-$(CONFIG_CRYPTO_DEV_SUN8I_SS) += sun8i-ss.o
sun8i-ss-y += sun8i-ss-core.o sun8i-ss-cipher.o
-sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG) += sun8i-ss-prng.o
sun8i-ss-$(CONFIG_CRYPTO_DEV_SUN8I_SS_HASH) += sun8i-ss-hash.o
diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c
index f45685707e0d..119cc15509d1 100644
--- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c
+++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c
@@ -11,7 +11,6 @@
*/
#include <crypto/engine.h>
-#include <crypto/internal/rng.h>
#include <crypto/internal/skcipher.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -283,25 +282,6 @@ static struct sun8i_ss_alg_template ss_algs[] = {
.do_one_request = sun8i_ss_handle_cipher_request,
},
},
-#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_PRNG
-{
- .type = CRYPTO_ALG_TYPE_RNG,
- .alg.rng = {
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "sun8i-ss-prng",
- .cra_priority = 300,
- .cra_ctxsize = sizeof(struct sun8i_ss_rng_tfm_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = sun8i_ss_prng_init,
- .cra_exit = sun8i_ss_prng_exit,
- },
- .generate = sun8i_ss_prng_generate,
- .seed = sun8i_ss_prng_seed,
- .seedsize = PRNG_SEED_SIZE,
- }
-},
-#endif
#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_HASH
{ .type = CRYPTO_ALG_TYPE_AHASH,
.ss_algo_id = SS_ID_HASH_MD5,
@@ -501,12 +481,6 @@ static int sun8i_ss_debugfs_show(struct seq_file *seq, void *v)
seq_printf(seq, "\tFallback due to SG numbers: %lu\n",
ss_algs[i].stat_fb_sgnum);
break;
- case CRYPTO_ALG_TYPE_RNG:
- seq_printf(seq, "%s %s reqs=%lu tsize=%lu\n",
- ss_algs[i].alg.rng.base.cra_driver_name,
- ss_algs[i].alg.rng.base.cra_name,
- ss_algs[i].stat_req, ss_algs[i].stat_bytes);
- break;
case CRYPTO_ALG_TYPE_AHASH:
seq_printf(seq, "%s %s reqs=%lu fallback=%lu\n",
ss_algs[i].alg.hash.base.halg.base.cra_driver_name,
@@ -707,14 +681,6 @@ static int sun8i_ss_register_algs(struct sun8i_ss_dev *ss)
return err;
}
break;
- case CRYPTO_ALG_TYPE_RNG:
- err = crypto_register_rng(&ss_algs[i].alg.rng);
- if (err) {
- dev_err(ss->dev, "Fail to register %s\n",
- ss_algs[i].alg.rng.base.cra_name);
- ss_algs[i].ss = NULL;
- }
- break;
case CRYPTO_ALG_TYPE_AHASH:
id = ss_algs[i].ss_algo_id;
ss_method = ss->variant->alg_hash[id];
@@ -756,11 +722,6 @@ static void sun8i_ss_unregister_algs(struct sun8i_ss_dev *ss)
ss_algs[i].alg.skcipher.base.base.cra_name);
crypto_engine_unregister_skcipher(&ss_algs[i].alg.skcipher);
break;
- case CRYPTO_ALG_TYPE_RNG:
- dev_info(ss->dev, "Unregister %d %s\n", i,
- ss_algs[i].alg.rng.base.cra_name);
- crypto_unregister_rng(&ss_algs[i].alg.rng);
- break;
case CRYPTO_ALG_TYPE_AHASH:
dev_info(ss->dev, "Unregister %d %s\n", i,
ss_algs[i].alg.hash.base.halg.base.cra_name);
diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c
deleted file mode 100644
index a923cfc6553f..000000000000
--- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-prng.c
+++ /dev/null
@@ -1,177 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * sun8i-ss-prng.c - hardware cryptographic offloader for
- * Allwinner A80/A83T SoC
- *
- * Copyright (C) 2015-2020 Corentin Labbe <clabbe@baylibre.com>
- *
- * This file handle the PRNG found in the SS
- *
- * You could find a link for the datasheet in Documentation/arch/arm/sunxi.rst
- */
-#include "sun8i-ss.h"
-#include <linux/dma-mapping.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/pm_runtime.h>
-#include <crypto/internal/rng.h>
-
-int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed,
- unsigned int slen)
-{
- struct sun8i_ss_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm);
-
- if (ctx->seed && ctx->slen != slen) {
- kfree_sensitive(ctx->seed);
- ctx->slen = 0;
- ctx->seed = NULL;
- }
- if (!ctx->seed)
- ctx->seed = kmalloc(slen, GFP_KERNEL);
- if (!ctx->seed)
- return -ENOMEM;
-
- memcpy(ctx->seed, seed, slen);
- ctx->slen = slen;
-
- return 0;
-}
-
-int sun8i_ss_prng_init(struct crypto_tfm *tfm)
-{
- struct sun8i_ss_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
-
- memset(ctx, 0, sizeof(struct sun8i_ss_rng_tfm_ctx));
- return 0;
-}
-
-void sun8i_ss_prng_exit(struct crypto_tfm *tfm)
-{
- struct sun8i_ss_rng_tfm_ctx *ctx = crypto_tfm_ctx(tfm);
-
- kfree_sensitive(ctx->seed);
- ctx->seed = NULL;
- ctx->slen = 0;
-}
-
-int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dst, unsigned int dlen)
-{
- struct sun8i_ss_rng_tfm_ctx *ctx = crypto_rng_ctx(tfm);
- struct rng_alg *alg = crypto_rng_alg(tfm);
- struct sun8i_ss_alg_template *algt;
- unsigned int todo_with_padding;
- struct sun8i_ss_dev *ss;
- dma_addr_t dma_iv, dma_dst;
- unsigned int todo;
- int err = 0;
- int flow;
- void *d;
- u32 v;
-
- algt = container_of(alg, struct sun8i_ss_alg_template, alg.rng);
- ss = algt->ss;
-
- if (ctx->slen == 0) {
- dev_err(ss->dev, "The PRNG is not seeded\n");
- return -EINVAL;
- }
-
- /* The SS does not give an updated seed, so we need to get a new one.
- * So we will ask for an extra PRNG_SEED_SIZE data.
- * We want dlen + seedsize rounded up to a multiple of PRNG_DATA_SIZE
- */
- todo = dlen + PRNG_SEED_SIZE + PRNG_DATA_SIZE;
- todo -= todo % PRNG_DATA_SIZE;
-
- todo_with_padding = ALIGN(todo, dma_get_cache_alignment());
- if (todo_with_padding < todo || todo < dlen)
- return -EOVERFLOW;
-
- d = kzalloc(todo_with_padding, GFP_KERNEL);
- if (!d)
- return -ENOMEM;
-
- flow = sun8i_ss_get_engine_number(ss);
-
-#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG
- algt->stat_req++;
- algt->stat_bytes += todo;
-#endif
-
- v = SS_ALG_PRNG | SS_PRNG_CONTINUE | SS_START;
- if (flow)
- v |= SS_FLOW1;
- else
- v |= SS_FLOW0;
-
- dma_iv = dma_map_single(ss->dev, ctx->seed, ctx->slen, DMA_TO_DEVICE);
- if (dma_mapping_error(ss->dev, dma_iv)) {
- dev_err(ss->dev, "Cannot DMA MAP IV\n");
- err = -EFAULT;
- goto err_free;
- }
-
- dma_dst = dma_map_single(ss->dev, d, todo, DMA_FROM_DEVICE);
- if (dma_mapping_error(ss->dev, dma_dst)) {
- dev_err(ss->dev, "Cannot DMA MAP DST\n");
- err = -EFAULT;
- goto err_iv;
- }
-
- err = pm_runtime_resume_and_get(ss->dev);
- if (err < 0)
- goto err_pm;
- err = 0;
-
- mutex_lock(&ss->mlock);
- writel(dma_iv, ss->base + SS_IV_ADR_REG);
- /* the PRNG act badly (failing rngtest) without SS_KEY_ADR_REG set */
- writel(dma_iv, ss->base + SS_KEY_ADR_REG);
- writel(dma_dst, ss->base + SS_DST_ADR_REG);
- writel(todo / 4, ss->base + SS_LEN_ADR_REG);
-
- reinit_completion(&ss->flows[flow].complete);
- ss->flows[flow].status = 0;
- /* Be sure all data is written before enabling the task */
- wmb();
-
- writel(v, ss->base + SS_CTL_REG);
-
- wait_for_completion_interruptible_timeout(&ss->flows[flow].complete,
- msecs_to_jiffies(todo));
- if (ss->flows[flow].status == 0) {
- dev_err(ss->dev, "DMA timeout for PRNG (size=%u)\n", todo);
- err = -EFAULT;
- }
- /* Since cipher and hash use the linux/cryptoengine and that we have
- * a cryptoengine per flow, we are sure that they will issue only one
- * request per flow.
- * Since the cryptoengine wait for completion before submitting a new
- * one, the mlock could be left just after the final writel.
- * But cryptoengine cannot handle crypto_rng, so we need to be sure
- * nothing will use our flow.
- * The easiest way is to grab mlock until the hardware end our requests.
- * We could have used a per flow lock, but this would increase
- * complexity.
- * The drawback is that no request could be handled for the other flow.
- */
- mutex_unlock(&ss->mlock);
-
- pm_runtime_put(ss->dev);
-
-err_pm:
- dma_unmap_single(ss->dev, dma_dst, todo, DMA_FROM_DEVICE);
-err_iv:
- dma_unmap_single(ss->dev, dma_iv, ctx->slen, DMA_TO_DEVICE);
-
- if (!err) {
- memcpy(dst, d, dlen);
- /* Update seed */
- memcpy(ctx->seed, d + dlen, ctx->slen);
- }
-err_free:
- kfree_sensitive(d);
-
- return err;
-}
diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h
index ae66eb45fb24..9ec6c2ad5699 100644
--- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h
+++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss.h
@@ -8,7 +8,6 @@
#include <crypto/aes.h>
#include <crypto/des.h>
#include <crypto/engine.h>
-#include <crypto/rng.h>
#include <crypto/skcipher.h>
#include <linux/atomic.h>
#include <linux/debugfs.h>
@@ -27,7 +26,6 @@
#define SS_ALG_DES (1 << 2)
#define SS_ALG_3DES (2 << 2)
#define SS_ALG_MD5 (3 << 2)
-#define SS_ALG_PRNG (4 << 2)
#define SS_ALG_SHA1 (6 << 2)
#define SS_ALG_SHA224 (7 << 2)
#define SS_ALG_SHA256 (8 << 2)
@@ -68,8 +66,6 @@
#define SS_FLOW0 BIT(30)
#define SS_FLOW1 BIT(31)
-#define SS_PRNG_CONTINUE BIT(18)
-
#define MAX_SG 8
#define MAXFLOW 2
@@ -79,9 +75,6 @@
#define SS_DIE_ID_SHIFT 20
#define SS_DIE_ID_MASK 0x07
-#define PRNG_DATA_SIZE (160 / 8)
-#define PRNG_SEED_SIZE DIV_ROUND_UP(175, 8)
-
#define MAX_PAD_SIZE 4096
/*
@@ -214,16 +207,6 @@ struct sun8i_cipher_tfm_ctx {
};
/*
- * struct sun8i_ss_prng_ctx - context for PRNG TFM
- * @seed: The seed to use
- * @slen: The size of the seed
- */
-struct sun8i_ss_rng_tfm_ctx {
- void *seed;
- unsigned int slen;
-};
-
-/*
* struct sun8i_ss_hash_tfm_ctx - context for an ahash TFM
* @fallback_tfm: pointer to the fallback TFM
* @ss: pointer to the private data of driver handling this TFM
@@ -272,7 +255,6 @@ struct sun8i_ss_alg_template {
struct sun8i_ss_dev *ss;
union {
struct skcipher_engine_alg skcipher;
- struct rng_alg rng;
struct ahash_engine_alg hash;
} alg;
unsigned long stat_req;
@@ -298,11 +280,6 @@ int sun8i_ss_skencrypt(struct skcipher_request *areq);
int sun8i_ss_get_engine_number(struct sun8i_ss_dev *ss);
int sun8i_ss_run_task(struct sun8i_ss_dev *ss, struct sun8i_cipher_req_ctx *rctx, const char *name);
-int sun8i_ss_prng_generate(struct crypto_rng *tfm, const u8 *src,
- unsigned int slen, u8 *dst, unsigned int dlen);
-int sun8i_ss_prng_seed(struct crypto_rng *tfm, const u8 *seed, unsigned int slen);
-int sun8i_ss_prng_init(struct crypto_tfm *tfm);
-void sun8i_ss_prng_exit(struct crypto_tfm *tfm);
int sun8i_ss_hash_init_tfm(struct crypto_ahash *tfm);
void sun8i_ss_hash_exit_tfm(struct crypto_ahash *tfm);
diff --git a/drivers/cxl/core/features.c b/drivers/cxl/core/features.c
index c3cb2060bbea..7b0eeb0788d3 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/pmem.c b/drivers/cxl/core/pmem.c
index e3a8b8d81333..a62d84818f93 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 7056a79bff4d..f2d99865c6df 100644
--- a/drivers/cxl/core/ras.c
+++ b/drivers/cxl/core/ras.c
@@ -136,7 +136,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/cxl.h b/drivers/cxl/cxl.h
index a5b00c3b0444..1f00a07f9c33 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -563,7 +563,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,
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index c78d7d88744b..2c42ab75e56a 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1198,7 +1198,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 3432fd83b1e2..255ecfd67ca1 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 5af0bd33890c..e2a2307a41ee 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 */
@@ -264,7 +265,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++)
@@ -273,9 +274,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 fd5a6c6532a1..deac90c49d63 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7327,29 +7327,68 @@ finish:
return stream;
}
+/*
+ * 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
+ *
+ * A connector is considered connected when it has a sink that is not NULL.
+ * For connectors that support HPD (hotplug detection), the connection is
+ * handled in the HPD interrupt.
+ *
+ * Notes:
+ * 1. This interface is NOT called in context of HPD irq.
+ * 2. This interface *is called* in context of user-mode ioctl. Which
+ * makes it a bad place for *any* MST-related activity.
+ */
static enum drm_connector_status
amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
{
- bool connected;
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- /*
- * Notes:
- * 1. This interface is NOT called in context of HPD irq.
- * 2. This interface *is called* in context of user-mode ioctl. Which
- * makes it a bad place for *any* MST-related activity.
- */
+ update_subconnector_property(aconnector);
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED &&
- !aconnector->fake_enable)
- connected = (aconnector->dc_sink != NULL);
- else
- connected = (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL);
+ if (aconnector->base.force == DRM_FORCE_ON ||
+ aconnector->base.force == DRM_FORCE_ON_DIGITAL)
+ return connector_status_connected;
+ else if (aconnector->base.force == DRM_FORCE_OFF)
+ return connector_status_disconnected;
- update_subconnector_property(aconnector);
+ if (amdgpu_dm_hide_secondary_tile_from_userspace(connector))
+ return connector_status_disconnected;
- return (connected ? connector_status_connected :
+ return (aconnector->dc_sink ? connector_status_connected :
connector_status_disconnected);
}
@@ -8501,6 +8540,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;
@@ -8543,6 +8623,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_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index d0f770dd0a95..25923b00667a 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
@@ -56,8 +56,11 @@ static u32 edid_extract_panel_id(struct edid *edid)
(u32)EDID_PRODUCT_ID(edid);
}
-static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct dc_edid_caps *edid_caps)
+static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
+ struct dc_edid_caps *edid_caps)
{
+ struct amdgpu_dm_connector *aconnector = link->priv;
+ struct drm_device *dev = aconnector->base.dev;
uint32_t panel_id = edid_extract_panel_id(edid);
switch (panel_id) {
@@ -87,6 +90,22 @@ static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct
drm_dbg_driver(dev, "Disabling VSC on monitor with panel id %X\n", panel_id);
edid_caps->panel_patch.disable_colorimetry = true;
break;
+ /* Workaround for monitors that get corrupted by the PHY SSC reduction */
+ case drm_edid_encode_panel_id('D', 'E', 'L', 0x4147):
+ 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;
}
@@ -108,7 +127,6 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
{
struct amdgpu_dm_connector *aconnector = link->priv;
struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
struct edid *edid_buf = edid ? (struct edid *) edid->raw_edid : NULL;
struct cea_sad *sads;
int sad_count = -1;
@@ -138,7 +156,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
edid_caps->edid_hdmi = connector->display_info.is_hdmi;
- apply_edid_quirks(dev, edid_buf, edid_caps);
+ apply_edid_quirks(link, edid_buf, edid_caps);
sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads);
if (sad_count <= 0)
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
index 6518d5639d66..04bc014906f8 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
@@ -949,7 +949,10 @@ void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv)
{
uint32_t i;
- if (!dc_dmub_srv || !dc_dmub_srv->dmub) {
+ if (!dc_dmub_srv)
+ return;
+
+ if (!dc_dmub_srv->dmub) {
DC_LOG_ERROR("%s: invalid parameters.", __func__);
return;
}
@@ -1146,7 +1149,10 @@ void dc_dmub_srv_enable_dpia_trace(const struct dc *dc)
{
struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv;
- if (!dc_dmub_srv || !dc_dmub_srv->dmub) {
+ if (!dc_dmub_srv)
+ return;
+
+ if (!dc_dmub_srv->dmub) {
DC_LOG_ERROR("%s: invalid parameters.", __func__);
return;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index b5aa03a3e39c..ff06a1dae5c6 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -178,6 +178,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 f6cfb6ea4885..80e2bde9915f 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 cb1bd6dca835..924dab47849e 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;
@@ -823,6 +826,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;
@@ -858,13 +862,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);
@@ -997,6 +1008,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 ef3b5c77c38e..faebe95b4eb0 100644
--- a/drivers/hid/hid-mcp2221.c
+++ b/drivers/hid/hid-mcp2221.c
@@ -851,6 +851,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:
@@ -912,6 +915,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];
@@ -1035,6 +1042,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 d01176da8896..0d325d69e64b 100644
--- a/drivers/hid/hid-picolcd_debugfs.c
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -98,6 +98,15 @@ static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
ret = resp->raw_data[2];
if (ret > s)
ret = s;
+ /*
+ * raw_data[2] is a device-supplied length; also clamp it to
+ * what picolcd_raw_event() actually stored (raw_size), or a
+ * hostile device overruns the raw_data[] buffer.
+ */
+ if (ret > resp->raw_size - 3)
+ ret = resp->raw_size - 3;
+ if (ret < 0)
+ ret = 0;
if (copy_to_user(u, resp->raw_data+3, ret))
ret = -EFAULT;
else
diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c
index e413662f7508..e54b6a8a92ef 100644
--- a/drivers/hid/hid-roccat.c
+++ b/drivers/hid/hid-roccat.c
@@ -70,6 +70,15 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES];
/* protects modifications of devices array */
static DEFINE_MUTEX(devices_lock);
+static void roccat_free_device(struct roccat_device *device)
+{
+ int i;
+
+ for (i = 0; i < ROCCAT_CBUF_SIZE; i++)
+ kfree(device->cbuf[i].value);
+ kfree(device);
+}
+
static ssize_t roccat_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
@@ -226,7 +235,7 @@ static int roccat_release(struct inode *inode, struct file *file)
hid_hw_power(device->hid, PM_HINT_NORMAL);
hid_hw_close(device->hid);
} else {
- kfree(device);
+ roccat_free_device(device);
}
}
@@ -374,7 +383,7 @@ void roccat_disconnect(int minor)
hid_hw_close(device->hid);
wake_up_interruptible(&device->wait);
} else {
- kfree(device);
+ roccat_free_device(device);
}
}
EXPORT_SYMBOL_GPL(roccat_disconnect);
diff --git a/drivers/hid/hid-sensor-custom.c b/drivers/hid/hid-sensor-custom.c
index 957311e7b638..0b97d61df0c8 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 1b725c879650..faeb9c4536f5 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 b966e4044238..22db29add44e 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -2164,7 +2164,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 0156ab391778..c6ba6886072a 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -771,6 +771,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 f0830a56d556..1c96d1d85845 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
@@ -556,7 +556,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;
@@ -715,6 +722,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 ad52e402c28a..9df948f94d5a 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 a7066f3a0bb4..3690f2ffe5fc 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 3090479a2979..3c3e9fa93418 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 08c9091a1e35..72c80107c11a 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -831,7 +831,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");
@@ -869,9 +869,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/master.c b/drivers/i3c/master.c
index 588395e7a770..41452a27262d 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -2808,11 +2808,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 d329faf4b3f9..f0c2ddb2f4b2 100644
--- a/drivers/i3c/master/adi-i3c-master.c
+++ b/drivers/i3c/master/adi-i3c-master.c
@@ -967,17 +967,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,
@@ -989,6 +981,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 5b1bf5a0266c..b6c4dfc51fe5 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -428,6 +428,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;
@@ -878,10 +893,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;
}
@@ -891,6 +922,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);
@@ -1000,6 +1033,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) {
@@ -1040,6 +1078,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*/
@@ -1124,6 +1167,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) {
@@ -1170,6 +1218,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) {
@@ -1220,15 +1273,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);
}
@@ -1245,6 +1296,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) {
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index d33f4c765497..b6a697173e19 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -418,14 +418,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/ucma.c b/drivers/infiniband/core/ucma.c
index f86ece701db6..5d76879218ad 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_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 d892f55febe2..d03936d007b0 100644
--- a/drivers/infiniband/hw/cxgb4/device.c
+++ b/drivers/infiniband/hw/cxgb4/device.c
@@ -952,6 +952,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_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 6cc979b26151..cb99cb13089a 100644
--- a/drivers/interconnect/core.c
+++ b/drivers/interconnect/core.c
@@ -528,7 +528,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);
}
@@ -626,8 +626,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.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 7d18c6f9328a..daf8b87fdf08 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3837,6 +3837,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;
@@ -3864,6 +3878,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 */
@@ -3942,7 +3961,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)
@@ -4155,6 +4175,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;
@@ -4878,7 +4905,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);
@@ -4888,30 +4915,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)
@@ -4920,9 +4947,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/tegra241-cmdqv.c b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
index 2f0f9a2f74d9..cee489855311 100644
--- a/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
+++ b/drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
@@ -58,6 +58,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) \
@@ -1249,7 +1251,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);
@@ -1261,7 +1263,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/intel/dmar.c b/drivers/iommu/intel/dmar.c
index 6938800e9884..f4bdc5ffa9d9 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 8b38c65f403b..aec1a05d48d0 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -64,7 +64,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))
@@ -2956,10 +2956,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");
/*
@@ -2970,7 +2971,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 6b11081e690f..8e2df00e881c 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -1434,6 +1434,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)
{
@@ -1446,6 +1447,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 fd735aaae9e3..838563fc4431 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 59244c744eab..1547f3587a4e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -360,7 +360,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 5c7ead633edd..c40515bf5017 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 cb1d2248393a..164612195905 100644
--- a/drivers/iommu/iommufd/ioas.c
+++ b/drivers/iommu/iommufd/ioas.c
@@ -433,6 +433,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 8fd27f65d623..35c42ff4355a 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -183,6 +183,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;
@@ -1101,6 +1102,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;
@@ -1126,7 +1128,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;
@@ -1181,7 +1185,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;
@@ -1196,7 +1202,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:
@@ -1240,7 +1248,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;
@@ -1852,10 +1862,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)
@@ -1866,7 +1882,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;
@@ -1974,14 +1992,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);
@@ -1998,8 +2021,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;
@@ -2019,7 +2044,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;
}
@@ -2030,7 +2057,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 43a61ba021a5..d5e2fc9b0111 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -783,19 +783,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 b7cbe5874094..59e5d8aa42b7 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1282,11 +1282,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,
@@ -1314,7 +1314,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));
}
@@ -2197,7 +2197,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);
@@ -2223,7 +2223,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 883d1549cfc0..6f87e2c4e7da 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 cd602b39481d..46a815f20b09 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -561,7 +561,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;
@@ -1034,12 +1034,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);
}
@@ -1054,7 +1056,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;
}
@@ -1079,13 +1081,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);
}
}
}
@@ -1176,7 +1178,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;
}
@@ -1196,10 +1198,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);
}
}
@@ -1492,12 +1494,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);
}
@@ -1520,7 +1524,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);
}
@@ -1836,7 +1840,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;
@@ -2889,7 +2893,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;
@@ -2899,6 +2903,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);
}
@@ -3214,7 +3220,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 c37668790577..c6684e63ceb0 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 cec9a60227b6..7eecce8711ce 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 607436804a8b..40e21bec4792 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 c8a1f9b6385f..38b686a7a487 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 534bf07b794f..887157fde68d 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);
@@ -246,6 +250,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:
@@ -261,6 +272,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 2b77e121f89b..a3bb1f95afb9 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 87bcc1c515a6..f6da50b57c67 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 d9ec783a37dd..9b66863991d1 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 0e13d60bfdd1..0dc429eb2692 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 dbff4922e068..8019eeb4cc91 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 50a52ca50b34..65d169ffa753 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 40c0664fa034..2c754144e929 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -379,7 +379,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 af54e289bceb..34148aa4df79 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 0d60c211e7c0..5304e920dbcc 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1917,6 +1917,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 d6b174454b1a..1ae9a587a959 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3384,7 +3384,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 a3df3a33237e..654da27b1140 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 10a126e50c1c..406915afcb2f 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -1066,25 +1066,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 4782ff1114a9..33a32063050f 100644
--- a/drivers/mfd/cgbc-core.c
+++ b/drivers/mfd/cgbc-core.c
@@ -363,9 +363,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/sm501.c b/drivers/mfd/sm501.c
index c56f5005eb91..35bcb856595d 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 c628b3bbfd7a..cf7b74835aa3 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1154,10 +1154,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:
@@ -1200,6 +1206,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 2b9bd4462714..0df45c758beb 100644
--- a/drivers/net/dsa/realtek/rtl83xx.c
+++ b/drivers/net/dsa/realtek/rtl83xx.c
@@ -309,7 +309,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)
@@ -322,7 +322,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);
}
MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>");
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 8e6eec828d48..c4ea14f8fbe9 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -13474,10 +13474,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 29e52df2502e..186fc98a555d 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -499,11 +499,8 @@ 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
free_size = bnxt_tx_avail(bp, txr);
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index 7d70056e9008..a423496421e9 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/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c
index c09fb7694fac..01eb1c75f2da 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 7b48060c250b..330379c4f405 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -1039,6 +1039,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];
@@ -1133,6 +1134,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 e2d7ce1a85e8..cd0d65cd699a 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1757,7 +1757,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;
@@ -2896,11 +2896,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;
}
@@ -2963,6 +2965,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 8fe0d0e1a00f..bd54fd6bd380 100644
--- a/drivers/net/ipa/ipa_modem.c
+++ b/drivers/net/ipa/ipa_modem.c
@@ -269,13 +269,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 b72d0bfd7c2a..6b107e0f2cce 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -135,6 +135,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++;
@@ -146,13 +149,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;
@@ -187,8 +184,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);
@@ -237,21 +236,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/ovpn/main.c b/drivers/net/ovpn/main.c
index 0262926449f2..c18ed8690baf 100644
--- a/drivers/net/ovpn/main.c
+++ b/drivers/net/ovpn/main.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
+#include <linux/workqueue.h>
#include <net/gro_cells.h>
#include <net/ip.h>
#include <net/rtnetlink.h>
@@ -26,6 +27,9 @@
#include "tcp.h"
#include "udp.h"
+/* module-owned workqueue on which all ovpn-specific work is queued */
+struct workqueue_struct *ovpn_wq;
+
static void ovpn_priv_free(struct net_device *net)
{
struct ovpn_priv *ovpn = netdev_priv(net);
@@ -264,10 +268,16 @@ static int __init ovpn_init(void)
ovpn_tcp_init();
+ ovpn_wq = alloc_workqueue("ovpn", WQ_PERCPU, 0);
+ if (!ovpn_wq) {
+ pr_err("ovpn: cannot allocate workqueue\n");
+ return -ENOMEM;
+ }
+
err = rtnl_link_register(&ovpn_link_ops);
if (err) {
pr_err("ovpn: can't register rtnl link ops: %d\n", err);
- return err;
+ goto destroy_wq;
}
err = ovpn_nl_register();
@@ -280,6 +290,9 @@ static int __init ovpn_init(void)
unreg_rtnl:
rtnl_link_unregister(&ovpn_link_ops);
+destroy_wq:
+ destroy_workqueue(ovpn_wq);
+ ovpn_wq = NULL;
return err;
}
@@ -288,7 +301,11 @@ static __exit void ovpn_cleanup(void)
ovpn_nl_unregister();
rtnl_link_unregister(&ovpn_link_ops);
+ flush_workqueue(ovpn_wq);
rcu_barrier();
+
+ destroy_workqueue(ovpn_wq);
+ ovpn_wq = NULL;
}
module_init(ovpn_init);
diff --git a/drivers/net/ovpn/ovpnpriv.h b/drivers/net/ovpn/ovpnpriv.h
index 5898f6adada7..84499140e4bd 100644
--- a/drivers/net/ovpn/ovpnpriv.h
+++ b/drivers/net/ovpn/ovpnpriv.h
@@ -15,6 +15,10 @@
#include <uapi/linux/if_link.h>
#include <uapi/linux/ovpn.h>
+struct workqueue_struct;
+
+extern struct workqueue_struct *ovpn_wq;
+
/**
* struct ovpn_peer_collection - container of peers for MultiPeer mode
* @by_id: table of peers index by ID
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 412ef09abd5e..6b9f5f12fc5a 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -62,7 +62,7 @@ void ovpn_peer_keepalive_set(struct ovpn_peer *peer, u32 interval, u32 timeout)
/* now that interval and timeout have been changed, kick
* off the worker so that the next delay can be recomputed
*/
- mod_delayed_work(system_wq, &peer->ovpn->keepalive_work, 0);
+ mod_delayed_work(ovpn_wq, &peer->ovpn->keepalive_work, 0);
}
/**
@@ -1366,7 +1366,7 @@ static time64_t ovpn_peer_keepalive_work_single(struct ovpn_peer *peer,
peer->id);
if (WARN_ON(!ovpn_peer_hold(peer)))
return 0;
- if (!schedule_work(&peer->keepalive_work))
+ if (!queue_work(ovpn_wq, &peer->keepalive_work))
ovpn_peer_put(peer);
}
@@ -1458,8 +1458,8 @@ void ovpn_peer_keepalive_work(struct work_struct *work)
netdev_dbg(ovpn->dev,
"scheduling keepalive work: now=%llu next_run=%llu delta=%llu\n",
next_run, now, next_run - now);
- schedule_delayed_work(&ovpn->keepalive_work,
- (next_run - now) * HZ);
+ queue_delayed_work(ovpn_wq, &ovpn->keepalive_work,
+ (next_run - now) * HZ);
}
unlock_ovpn(ovpn, &release_list);
}
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 0af14055c39a..8fe8a8e750a4 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -151,7 +151,7 @@ err:
/* take reference for deferred peer deletion. should never fail */
if (WARN_ON(!ovpn_peer_hold(peer)))
goto err_nopeer;
- if (!schedule_work(&peer->tcp.defer_del_work))
+ if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work))
ovpn_peer_put(peer);
ovpn_dev_dstats_rx_dropped(peer->ovpn->dev);
err_nopeer:
@@ -284,13 +284,12 @@ static void ovpn_tcp_send_sock(struct ovpn_peer *peer, struct sock *sk)
* stream therefore we abort the connection
*/
ovpn_peer_hold(peer);
- if (!schedule_work(&peer->tcp.defer_del_work))
+ if (!queue_work(ovpn_wq, &peer->tcp.defer_del_work))
ovpn_peer_put(peer);
/* we bail out immediately and keep tx_in_progress set
* to true. This way we prevent more TX attempts
- * which would lead to more invocations of
- * schedule_work()
+ * which would lead to more invocations of queue_work()
*/
return;
}
@@ -487,7 +486,7 @@ static void ovpn_tcp_write_space(struct sock *sk)
rcu_read_lock();
sock = rcu_dereference_sk_user_data(sk);
if (likely(sock && sock->peer)) {
- schedule_work(&sock->tcp_tx_work);
+ queue_work(ovpn_wq, &sock->tcp_tx_work);
sock->peer->tcp.sk_cb.sk_write_space(sk);
}
rcu_read_unlock();
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 1b7fc17bf391..848b6ec9bd85 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -959,7 +959,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 bc84e2cc0502..59daf095cad6 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 ceaba08cd378..7bb9aa20fab9 100644
--- a/drivers/net/thunderbolt/main.c
+++ b/drivers/net/thunderbolt/main.c
@@ -629,6 +629,14 @@ static int tbnet_alloc_tx_buffers(struct tbnet *net)
return 0;
}
+static void tbnet_connect_failed(struct tbnet *net)
+{
+ /* Leave login_received set: only the peer can make it true again. */
+ mutex_lock(&net->connection_lock);
+ net->login_sent = false;
+ mutex_unlock(&net->connection_lock);
+}
+
static void tbnet_connected_work(struct work_struct *work)
{
struct tbnet *net = container_of(work, typeof(*net), connected_work);
@@ -650,6 +658,9 @@ static void tbnet_connected_work(struct work_struct *work)
ret = tb_xdomain_alloc_in_hopid(net->xd, net->remote_transmit_path);
if (ret != net->remote_transmit_path) {
netdev_err(net->dev, "failed to allocate Rx HopID\n");
+ if (ret >= 0)
+ tb_xdomain_release_in_hopid(net->xd, ret);
+ tbnet_connect_failed(net);
return;
}
@@ -694,6 +705,7 @@ 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 d27c3229465a..172273ae6575 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1105,11 +1105,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
@@ -1820,7 +1825,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:
@@ -1840,7 +1851,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 05acac10cd2b..c6d9ea259c0b 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/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index f142c17aa9aa..4794caa9f7ea 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1557,12 +1557,15 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
if (!beacons || !beacons->cnt) {
ath11k_warn(arvif->ar->ab,
"failed to get ema beacon templates from mac80211\n");
- return -EPERM;
+ ret = -EPERM;
+ goto free;
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else {
arvif->wpaie_present = tx_arvif->wpaie_present;
}
@@ -1589,11 +1592,11 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
}
}
- ieee80211_beacon_free_ema_list(beacons);
-
if (tx_arvif != arvif && !nontx_vif_params_set)
- return -EINVAL; /* Profile not found in the beacons */
+ ret = -EINVAL; /* Profile not found in the beacons */
+free:
+ ieee80211_beacon_free_ema_list(beacons);
return ret;
}
@@ -1622,19 +1625,22 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, bcn))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto free;
}
ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
- kfree_skb(bcn);
-
if (ret)
ath11k_warn(ab, "failed to submit beacon template command: %d\n",
ret);
+free:
+ kfree_skb(bcn);
return ret;
}
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index 88f0197fc041..e4f87018f164 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -753,6 +753,11 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel,
u8 *assoc_resp_ie = assoc_info + beacon_ie_len + assoc_req_len +
assoc_resp_ie_offset;
+ if (assoc_req_len < assoc_req_ie_offset)
+ assoc_req_len = assoc_req_ie_offset;
+ if (assoc_resp_len < assoc_resp_ie_offset)
+ assoc_resp_len = assoc_resp_ie_offset;
+
assoc_req_len -= assoc_req_ie_offset;
assoc_resp_len -= assoc_resp_ie_offset;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 936df04504ec..5beceea8f76b 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 2b4dbebc71c2..72dc0d8dc9b9 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 358de94eeb5e..b8267a5ebcfb 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 87a2e5163699..25936fb309fb 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/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 2aa7b711c774..46e337307c3d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -1295,8 +1295,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
wtbl_hdr);
ret = mt76_connac_mcu_sta_wed_update(dev, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true);
if (ret)
@@ -1309,8 +1311,10 @@ int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif,
mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx);
ret = mt76_connac_mcu_sta_wed_update(dev, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(dev, skb, cmd, true);
}
@@ -2766,12 +2770,16 @@ int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
return PTR_ERR(skb);
ret = mt76_connac_mcu_sta_key_tlv(sta_key_conf, skb, key, cmd);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
ret = mt76_connac_mcu_sta_wed_update(dev, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index f60e2eaea2af..1646b9ba2980 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -1773,8 +1773,10 @@ int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
}
out:
ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true);
@@ -2915,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 18f99d6570ad..f9980b5f4532 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1312,6 +1312,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 910713e62e14..8ef0d981adc9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1502,6 +1502,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);
@@ -2180,10 +2181,19 @@ static void mt7925_rfkill_poll(struct ieee80211_hw *hw)
wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0);
}
+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/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 647efa963db4..bc19e800b41c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1290,8 +1290,10 @@ int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
return PTR_ERR(skb);
ret = mt7925_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd, msta);
- if (ret)
+ if (ret) {
+ dev_kfree_skb(skb);
return ret;
+ }
return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index d6732f50784c..7972111d53a4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -41,6 +41,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);
@@ -454,6 +455,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 bf040f34e4b9..16ab139b6748 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
@@ -251,6 +251,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 da3231c9aa11..11e0a28113a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -148,6 +148,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 29f599a7ad01..e55bb232bdeb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -1279,6 +1279,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)
@@ -1287,7 +1311,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/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 7aa2da0cd63c..98174bb3cf2a 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 25e5fd48b8e2..f8bd9bfe74ba 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -5885,14 +5885,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);
@@ -5900,9 +5905,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)
@@ -7550,6 +7552,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 cc699efa9c79..a7824ba346e6 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/sw.c
@@ -147,8 +147,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");
@@ -162,10 +164,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 eae54f50485c..8bdac965d6ff 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -1799,7 +1799,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);
@@ -1831,6 +1831,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 9e24741b2cb1..611dade1b1eb 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/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index 2cee3c1729c3..aea63fab2ea3 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -1755,9 +1755,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) {
@@ -1797,15 +1804,13 @@ static void ntb_tx_copy_callback(void *data,
* "link down" or similar. Since no payload is being sent in these
* cases, there is nothing to add to the completion queue.
*/
- if (entry->len > 0) {
- qp->tx_bytes += entry->len;
-
- if (qp->tx_handler)
- qp->tx_handler(qp, qp->cb_data, entry->cb_data,
- entry->len);
- }
+ if (len > 0)
+ qp->tx_bytes += len;
ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry, &qp->tx_free_q);
+
+ if (len > 0 && qp->tx_handler)
+ qp->tx_handler(qp, qp->cb_data, cb_data, len);
}
static void ntb_memcpy_tx(struct ntb_queue_entry *entry, void __iomem *offset)
@@ -1930,15 +1935,6 @@ static int ntb_process_tx(struct ntb_transport_qp *qp,
return -EAGAIN;
}
- if (entry->len > qp->tx_max_frame - sizeof(struct ntb_payload_header)) {
- if (qp->tx_handler)
- qp->tx_handler(qp, qp->cb_data, NULL, -EIO);
-
- ntb_list_add(&qp->ntb_tx_free_q_lock, &entry->entry,
- &qp->tx_free_q);
- return 0;
- }
-
ntb_async_tx(qp, entry);
qp->tx_index++;
@@ -2310,9 +2306,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 04f4a049599a..584a9c27ce4b 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 24a5ca35aeb2..d7199fc97f18 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -5120,7 +5120,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 bf78faf1a4ff..101a3667559d 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2316,7 +2316,7 @@ nvme_fc_create_hw_io_queues(struct nvme_fc_ctrl *ctrl, u16 qsize)
return 0;
delete_queues:
- for (; i > 0; i--)
+ for (--i; i > 0; i--)
__nvme_fc_delete_hw_queue(ctrl, &ctrl->queues[i], i);
return ret;
}
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 489583393b84..0a46c60583d7 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2161,6 +2161,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 04c074ae8f7b..da826e96a4cc 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -117,6 +117,7 @@ struct nvme_tcp_request {
struct bio *curr_bio;
struct iov_iter iter;
+ u32 data_recvd;
/* send state */
size_t offset;
@@ -621,6 +622,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)
{
@@ -640,6 +664,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++;
@@ -650,6 +677,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);
@@ -660,7 +688,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);
@@ -754,6 +783,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",
@@ -962,6 +998,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) {
@@ -970,6 +1007,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++;
@@ -1018,6 +1057,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++;
}
@@ -2720,6 +2762,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 6620bf07b79b..25529235c1a6 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1891,7 +1891,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 225d887cd0a3..e3fe61ef862b 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -402,7 +402,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 28b157297487..fff5df771d5b 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -2120,6 +2120,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 3c2f68383010..dea3d443434c 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/msi/msi.c b/drivers/pci/msi/msi.c
index 34d664139f48..7e2332869306 100644
--- a/drivers/pci/msi/msi.c
+++ b/drivers/pci/msi/msi.c
@@ -860,6 +860,7 @@ void __pci_restore_msix_state(struct pci_dev *dev)
{
struct msi_desc *entry;
bool write_msg;
+ u16 cmd;
if (!dev->msix_enabled)
return;
@@ -869,6 +870,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) {
@@ -879,6 +888,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 edb9bc34c0c5..f3b7f4e410a4 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -723,7 +723,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;
@@ -804,7 +804,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;
@@ -906,12 +906,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;
}
/**
@@ -931,12 +949,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 85f1f0cf62d6..9f90abe3e88e 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -427,23 +427,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)
@@ -451,11 +460,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)
/*
@@ -470,7 +482,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[] = {
@@ -547,7 +560,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, \
@@ -938,7 +952,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;
@@ -1427,7 +1442,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 9348a0fb8084..005e5b455164 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -14,6 +14,8 @@
#include <linux/capability.h>
#include <linux/uaccess.h>
#include <linux/security.h>
+#include <linux/panic.h>
+#include <linux/sched.h>
#include <asm/byteorder.h>
#include "pci.h"
@@ -38,13 +40,16 @@ static ssize_t proc_bus_pci_read(struct file *file, char __user *buf,
* undefined locations (think of Intel PIIX4 as a typical example).
*/
- if (capable(CAP_SYS_ADMIN))
+ if (file_ns_capable(file, &init_user_ns, CAP_SYS_ADMIN))
size = dev->cfg_size;
else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
size = 128;
else
size = 64;
+ if (!nbytes)
+ return 0;
+
if (pos >= size)
return 0;
if (nbytes >= size)
@@ -121,6 +126,15 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
if (ret)
return ret;
+ if (!nbytes)
+ return 0;
+
+ if (resource_is_exclusive(&dev->driver_exclusive_resource, pos, nbytes)) {
+ pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %x",
+ current->comm, pos);
+ add_taint(TAINT_USER, LOCKDEP_STILL_OK);
+ }
+
if (pos >= size)
return 0;
if (nbytes >= size)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index f6001be94955..af58057665d9 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -2506,6 +2506,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,
@@ -5331,7 +5334,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);
}
}
@@ -6210,6 +6213,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 8e7b6e10e1f0..b4f602965c99 100644
--- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
+++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c
@@ -166,9 +166,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,
@@ -241,6 +241,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)
@@ -259,17 +260,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)
@@ -702,9 +697,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 4508a3147272..d4e49db6feed 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -1525,7 +1525,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 1205219515d6..302d037b90a1 100644
--- a/drivers/platform/chrome/cros_ec_sensorhub_ring.c
+++ b/drivers/platform/chrome/cros_ec_sensorhub_ring.c
@@ -464,6 +464,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) {
@@ -491,10 +506,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 3b9b9f30faa3..1a06425f596d 100644
--- a/drivers/platform/x86/amd/pmc/mp1_stb.c
+++ b/drivers/platform/x86/amd/pmc/mp1_stb.c
@@ -289,7 +289,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 +306,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 2cd77a44c7e7..000f876e27eb 100644
--- a/drivers/platform/x86/amd/pmc/pmc.c
+++ b/drivers/platform/x86/amd/pmc/pmc.c
@@ -870,13 +870,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 c2dd2de6bc20..fea97d6c3bf0 100644
--- a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c
+++ b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c
@@ -84,7 +84,6 @@ int set_attribute(const char *a_name, const char *a_value)
if (ret < 0)
goto out;
- print_hex_dump_bytes("set attribute data: ", DUMP_PREFIX_NONE, buffer, buffer_size);
ret = call_biosattributes_interface(wmi_priv.bios_attr_wdev,
buffer, buffer_size,
SETATTRIBUTE_METHOD_ID);
diff --git a/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c b/drivers/platform/x86/hp/hp-bioscfg/bioscfg.c
index 51e8977d3eb4..274bdecce685 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 63b1fda2be4e..5577f571d7c0 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 6a31f47ce3f5..db5513d86e00 100644
--- a/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
+++ b/drivers/platform/x86/hp/hp-bioscfg/order-list-attributes.c
@@ -146,7 +146,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:
@@ -233,6 +233,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:
@@ -262,7 +264,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;
@@ -299,10 +303,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];
@@ -310,7 +316,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 ec79d9d50377..3c7fbb1a87e2 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 7b885d25650c..4e3d1fe90310 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/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 b11d62c934b3..2e2e168d301c 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)
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)
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)
@@ -844,6 +885,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
@@ -876,7 +918,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) {
/*
@@ -994,6 +1036,9 @@ static int isst_if_set_perf_feature(void __user *argp)
if (power_domain_info->write_blocked)
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)
@@ -1260,6 +1305,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)
@@ -1305,6 +1356,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)
@@ -1341,6 +1395,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)
@@ -1369,6 +1429,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);
@@ -1436,6 +1498,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 540b472b1bf3..a56cca9d6598 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;
}
@@ -741,6 +740,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",
@@ -752,8 +753,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;
@@ -1452,6 +1457,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/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c
index f6fef6296251..910e024a5c82 100644
--- a/drivers/platform/x86/lenovo/wmi-helpers.c
+++ b/drivers/platform/x86/lenovo/wmi-helpers.c
@@ -45,7 +45,6 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
unsigned char *buf, size_t size, u32 *retval)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *ret_obj __free(kfree) = NULL;
struct acpi_buffer input = { size, buf };
acpi_status status;
@@ -54,8 +53,9 @@ int lwmi_dev_evaluate_int(struct wmi_device *wdev, u8 instance, u32 method_id,
if (ACPI_FAILURE(status))
return -EIO;
+ union acpi_object *ret_obj __free(kfree) = output.pointer;
+
if (retval) {
- ret_obj = output.pointer;
if (!ret_obj)
return -ENODATA;
diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c
index 470d53e3c9d2..c71d92f896ec 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/power/supply/bq24257_charger.c b/drivers/power/supply/bq24257_charger.c
index 766eecb35694..097b51271ba9 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 d3de4f8b80db..8cdd45ddd05b 100644
--- a/drivers/power/supply/bq256xx_charger.c
+++ b/drivers/power/supply/bq256xx_charger.c
@@ -897,6 +897,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,
@@ -1722,24 +1724,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) {
@@ -1747,6 +1737,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 f246ec871d5c..305318b21e5c 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 c3644018b6bb..70af1a4ce110 100644
--- a/drivers/power/supply/charger-manager.c
+++ b/drivers/power/supply/charger-manager.c
@@ -1018,6 +1018,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.
@@ -1040,15 +1063,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];
@@ -1584,13 +1598,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 */
@@ -1624,11 +1648,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;
}
@@ -1646,12 +1670,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 47d3f58aa15c..93f61da8d596 100644
--- a/drivers/power/supply/cros_usbpd-charger.c
+++ b/drivers/power/supply/cros_usbpd-charger.c
@@ -125,6 +125,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;
}
@@ -138,6 +143,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;
}
@@ -589,10 +599,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 4186fcd37512..e81bb393340c 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 f4748adec092..0d95ff5cc892 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/qcom_battmgr.c b/drivers/power/supply/qcom_battmgr.c
index ff77dba29a3e..7454ec0e9963 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>
@@ -1235,7 +1236,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);
}
}
@@ -1650,7 +1651,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);
@@ -1713,6 +1713,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 5130d2395e88..710d510f9809 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 fe94435340de..da49de82c082 100644
--- a/drivers/power/supply/ucs1002_power.c
+++ b/drivers/power/supply/ucs1002_power.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/device.h>
+#include <linux/devm-helpers.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -641,7 +642,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 83114695dbb5..8edc2880c609 100644
--- a/drivers/ptp/ptp_vmclock.c
+++ b/drivers/ptp/ptp_vmclock.c
@@ -371,6 +371,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 995cfeca972b..c3fa45b26c1b 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 254a77887f66..52ab2208675b 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 eb8908ea3bab..b3613802730c 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -35,6 +35,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) {
@@ -50,7 +51,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 7a982c60a8dd..17c885f4ed50 100644
--- a/drivers/rpmsg/qcom_glink_smem.c
+++ b/drivers/rpmsg/qcom_glink_smem.c
@@ -103,6 +103,13 @@ static void glink_smem_rx_peek(struct qcom_glink_pipe *np,
if (tail >= pipe->native.length)
tail -= pipe->native.length;
+ /*
+ * Order the availability (head) read in glink_smem_rx_avail()
+ * against the FIFO payload read below, so APPS never consumes
+ * stale data the remote has not yet published.
+ */
+ rmb();
+
len = min_t(size_t, count, pipe->native.length - tail);
if (len)
memcpy_fromio(data, pipe->fifo + tail, len);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 7765e40f7cea..e7845c8ccc9f 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1746,8 +1746,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 9dfa14eb09d2..8947fbd2797a 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1493,6 +1493,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);
@@ -1648,6 +1650,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;
}
@@ -1655,6 +1660,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;
}
@@ -1668,6 +1676,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;
@@ -1682,6 +1693,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;
@@ -1691,6 +1705,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;
}
@@ -1833,7 +1850,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;
@@ -1849,6 +1870,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;
}
@@ -1856,6 +1880,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;
}
@@ -1866,6 +1893,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;
}
@@ -5949,8 +5979,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 */
@@ -6815,6 +6848,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/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 3f7ba6d3987f..31e1d74143df 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1184,8 +1184,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 afa78c96ede8..96154d8c5f38 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 46f169346782..f5d7ba4cdd6e 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -1260,6 +1260,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");
@@ -1270,8 +1271,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) {
@@ -1292,10 +1293,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 5ec717f3afd5..eb0381c10d2b 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 90e2ea1e8afe..1f2b4c12eead 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>
@@ -2079,6 +2080,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)
{
@@ -2631,11 +2635,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);
@@ -2647,7 +2659,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 a6c2b8de3908..8282de6df011 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 ea8487d4bbb3..a2c735276909 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 311606097718..15e8ca3a616b 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -549,6 +549,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)
@@ -583,10 +584,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 de16b02d857e..0199d95ff803 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -2474,6 +2474,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);
@@ -2640,6 +2641,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 09d72bb4f855..02c5d9cfda45 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -1674,19 +1674,25 @@ static struct se_portal_group *usbg_make_tpg(struct se_wwn *wwn,
opts = container_of(tpg_instances[i].func_inst, struct f_tcm_opts,
func_inst);
- mutex_lock(&opts->dep_lock);
- if (!opts->ready)
- goto unlock_dep;
+ if (!READ_ONCE(opts->ready))
+ goto unlock_inst;
if (opts->has_dep) {
if (!try_module_get(opts->dependent))
- goto unlock_dep;
+ goto unlock_inst;
} else {
+ /*
+ * configfs_depend_item_unlocked() may acquire the configfs
+ * root inode lock when the target belongs to a different
+ * subsystem. Calling it under dep_lock would create a
+ * circular dependency:
+ * dep_lock -> configfs inode lock -> su_mutex -> dep_lock
+ */
ret = configfs_depend_item_unlocked(
wwn->wwn_group.cg_subsys,
&opts->func_inst.group.cg_item);
if (ret)
- goto unlock_dep;
+ goto unlock_inst;
}
tpg = kzalloc(sizeof(struct usbg_tpg), GFP_KERNEL);
@@ -1713,7 +1719,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;
@@ -1726,8 +1731,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);
@@ -2664,9 +2667,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 72601e15563c..ab77e6bf001a 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 ca8dbec65f73..24c673dcbbd7 100644
--- a/drivers/usb/gadget/function/u_audio.c
+++ b/drivers/usb/gadget/function/u_audio.c
@@ -1177,6 +1177,20 @@ static struct snd_kcontrol_new u_audio_controls[] = {
},
};
+static void u_audio_card_free(struct snd_card *card)
+{
+ struct snd_uac_chip *uac = card->private_data;
+
+ if (!uac)
+ return;
+
+ kfree(uac->p_prm.reqs);
+ kfree(uac->c_prm.reqs);
+ kfree(uac->p_prm.rbuf);
+ kfree(uac->c_prm.rbuf);
+ kfree(uac);
+}
+
int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
const char *card_name)
{
@@ -1258,6 +1272,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
goto fail;
uac->card = card;
+ card->private_data = uac;
+ card->private_free = u_audio_card_free;
/*
* Create first PCM device
@@ -1426,6 +1442,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);
@@ -1451,12 +1469,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 f24746e72877..dd9105a8ef78 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 42b94d858e37..005152798000 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 e266a47c4d48..1c405e5dc32c 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 d70341ae5a92..cc841c4e4762 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -373,6 +373,6 @@ struct fsl_otg_config {
#define FSL_OTG_NAME "fsl-usb2-otg"
-void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
-void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
-void fsl_otg_pulse_vbus(void);
+static void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer);
+static void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer);
+static void fsl_otg_pulse_vbus(void);
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index d09dd09cf1c3..1f4dab8d6264 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/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index 2a951c585e92..3be5d3f58c50 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -37,6 +37,7 @@ struct tcpci {
struct regmap *regmap;
unsigned int alert_mask;
+ unsigned int rx_type_mask;
bool controls_vbus;
@@ -485,6 +486,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;
@@ -746,6 +749,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);
@@ -770,10 +774,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 0d1062191951..85e2e7fcd02c 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -78,7 +78,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/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
index 1d75f27c6b80..ce1d4d5f4034 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
@@ -1151,14 +1151,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 0b8d23c12b77..d3459a56beef 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;
@@ -1078,7 +1078,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 83dd31fa1fab..0df3ac694f4e 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 b806da0538bc..8330d80fb394 100644
--- a/drivers/video/fbdev/uvesafb.c
+++ b/drivers/video/fbdev/uvesafb.c
@@ -1910,6 +1910,8 @@ static int uvesafb_init(void)
err = 0;
}
}
+ if (err)
+ cn_del_callback(&uvesafb_cn_id);
return err;
}
diff --git a/drivers/video/sticore.c b/drivers/video/sticore.c
index 88a1758616e0..91382628515b 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 5738cbce1a37..d98a1719d6b3 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/fs/backing-file.c b/fs/backing-file.c
index e049a627d78f..6f16e23f7046 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -34,7 +34,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 3091132190b6..5f2a99e68335 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/ioctl.c b/fs/btrfs/ioctl.c
index 2f1c5f5e2e72..b702f625c753 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -330,14 +330,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 80e5de6736e8..37f20f401b0e 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -4056,6 +4056,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.
*
@@ -4065,6 +4083,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;
@@ -4176,7 +4195,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);
@@ -4185,15 +4204,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);
@@ -4209,6 +4230,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 b6b477ff7b75..261f521dbc58 100644
--- a/fs/buffer.c
+++ b/fs/buffer.c
@@ -2199,6 +2199,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;
@@ -2221,6 +2222,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 ea31c892a1fb..faa3de95caff 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -1421,6 +1421,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,
@@ -1484,6 +1494,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 07e3236e0f97..94d59d58ea3a 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -2585,9 +2585,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)) {
@@ -2597,6 +2602,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;
@@ -2637,6 +2643,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc,
}
spin_lock(&ci->i_ceph_lock);
+ cf = next;
}
}
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c
index c30510bdae3a..e4577a80da58 100644
--- a/fs/ceph/dir.c
+++ b/fs/ceph/dir.c
@@ -1763,11 +1763,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;
@@ -1794,7 +1794,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)
@@ -1804,6 +1804,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 049b2d414fe9..7752564bd77a 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -4269,7 +4269,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] == '/') {
@@ -4286,7 +4288,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);
diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c
index 97ecf272a37d..c91c0e2e118b 100644
--- a/fs/ceph/mdsmap.c
+++ b/fs/ceph/mdsmap.c
@@ -3,6 +3,7 @@
#include <linux/bug.h>
#include <linux/err.h>
+#include <linux/overflow.h>
#include <linux/random.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -126,6 +127,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
u8 mdsmap_v;
u16 mdsmap_ev;
u32 target;
+ size_t export_targets_len;
m = kzalloc(sizeof(*m), GFP_NOFS);
if (!m)
@@ -224,8 +226,11 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
*p += namelen;
if (info_v >= 2) {
ceph_decode_32_safe(p, end, num_export_targets, bad);
+ export_targets_len = size_mul(num_export_targets,
+ sizeof(u32));
+ ceph_decode_need(p, end, export_targets_len, bad);
pexport_targets = *p;
- *p += num_export_targets * sizeof(u32);
+ *p += export_targets_len;
} else {
num_export_targets = 0;
}
@@ -264,6 +269,10 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p,
goto nomem;
for (j = 0; j < num_export_targets; j++) {
target = ceph_decode_32(&pexport_targets);
+ if (target >= CEPH_MAX_MDS) {
+ err = -EIO;
+ goto corrupt;
+ }
info->export_targets[j] = target;
}
} else {
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index caf0fe4d2b1b..cc73b0874ce4 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -850,6 +850,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/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c
index 69536cacdea8..70c204c995a7 100644
--- a/fs/ecryptfs/crypto.c
+++ b/fs/ecryptfs/crypto.c
@@ -1285,7 +1285,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 9e6ab0b41337..5be8c31653fa 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -585,7 +585,8 @@ int ecryptfs_generate_key_packet_set(char *dest_base,
size_t *len, size_t max);
int
ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
- unsigned char *src, struct dentry *ecryptfs_dentry);
+ unsigned char *src, size_t src_size,
+ struct dentry *ecryptfs_dentry);
int ecryptfs_truncate(struct dentry *dentry, loff_t new_length);
ssize_t
ecryptfs_getxattr_lower(struct dentry *lower_dentry, struct inode *lower_inode,
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 7f9f68c00ef6..970e37510394 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -937,6 +937,12 @@ ecryptfs_parse_tag_70_packet(char **filename, size_t *filename_size,
"rc = [%d]\n", __func__, rc);
goto out;
}
+ if (s->parsed_tag_70_packet_size < (ECRYPTFS_SIG_SIZE + 2)) {
+ ecryptfs_printk(KERN_WARNING, "Invalid packet size [%zd]\n",
+ s->parsed_tag_70_packet_size);
+ rc = -EINVAL;
+ goto out;
+ }
s->block_aligned_filename_size = (s->parsed_tag_70_packet_size
- ECRYPTFS_SIG_SIZE - 1);
if ((1 + s->packet_size_len + s->parsed_tag_70_packet_size)
@@ -1424,10 +1430,20 @@ parse_tag_3_packet(struct ecryptfs_crypt_stat *crypt_stat,
}
(*new_auth_tok)->session_key.encrypted_key_size =
(body_size - (ECRYPTFS_SALT_SIZE + 5));
+ /*
+ * Although encrypted_key_size is copied into the
+ * encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES] buffer here,
+ * it later bounds operations on a smaller buffer:
+ * decrypt_passphrase_encrypted_session_key() sets decrypted_key_size =
+ * encrypted_key_size and decrypts into
+ * decrypted_key[ECRYPTFS_MAX_KEY_BYTES], then memcpy's into
+ * crypt_stat->key[ECRYPTFS_MAX_KEY_BYTES]. Limit to
+ * ECRYPTFS_MAX_KEY_BYTES to protect those smaller buffers.
+ */
if ((*new_auth_tok)->session_key.encrypted_key_size
- > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
+ > ECRYPTFS_MAX_KEY_BYTES) {
printk(KERN_WARNING "Tag 3 packet contains key larger "
- "than ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES\n");
+ "than ECRYPTFS_MAX_KEY_BYTES\n");
rc = -EINVAL;
goto out_free;
}
@@ -1576,7 +1592,7 @@ parse_tag_11_packet(unsigned char *data, unsigned char *contents,
}
(*packet_size) += length_size;
(*tag_11_contents_size) = (body_size - 14);
- if (unlikely((*packet_size) + body_size + 1 > max_packet_size)) {
+ if (unlikely((*packet_size) + body_size > max_packet_size)) {
printk(KERN_ERR "Packet size exceeds max\n");
rc = -EINVAL;
goto out;
@@ -1743,6 +1759,7 @@ 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
@@ -1753,7 +1770,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;
@@ -1777,7 +1794,11 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
* added the our &auth_tok_list */
next_packet_is_auth_tok_packet = 1;
while (next_packet_is_auth_tok_packet) {
- size_t max_packet_size = ((PAGE_SIZE - 8) - i);
+ size_t max_packet_size;
+
+ if (i >= src_size)
+ break;
+ max_packet_size = src_size - i;
switch (src[i]) {
case ECRYPTFS_TAG_3_PACKET_TYPE:
@@ -1792,12 +1813,16 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto out_wipe_list;
}
i += packet_size;
+ if (i > src_size) {
+ rc = -EIO;
+ goto out_wipe_list;
+ }
rc = parse_tag_11_packet((unsigned char *)&src[i],
sig_tmp_space,
ECRYPTFS_SIG_SIZE,
&tag_11_contents_size,
&tag_11_packet_size,
- max_packet_size);
+ src_size - i);
if (rc) {
ecryptfs_printk(KERN_ERR, "No valid "
"(ecryptfs-specific) literal "
@@ -1809,6 +1834,10 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto out_wipe_list;
}
i += tag_11_packet_size;
+ if (i > src_size) {
+ rc = -EIO;
+ goto out_wipe_list;
+ }
if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) {
ecryptfs_printk(KERN_ERR, "Expected "
"signature of size [%d]; "
@@ -1836,6 +1865,10 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
goto out_wipe_list;
}
i += packet_size;
+ if (i > src_size) {
+ rc = -EIO;
+ goto out_wipe_list;
+ }
crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
break;
case ECRYPTFS_TAG_11_PACKET_TYPE:
diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c
index 6318f3500e5c..75c35b559b37 100644
--- a/fs/ecryptfs/messaging.c
+++ b/fs/ecryptfs/messaging.c
@@ -165,6 +165,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
mutex_unlock(&daemon->mux);
goto out;
}
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
list_for_each_entry_safe(msg_ctx, msg_ctx_tmp,
&daemon->msg_ctx_out_queue, daemon_out_list) {
list_del(&msg_ctx->daemon_out_list);
@@ -173,6 +174,7 @@ int ecryptfs_exorcise_daemon(struct ecryptfs_daemon *daemon)
"the out queue of a dying daemon\n", __func__);
ecryptfs_msg_ctx_alloc_to_free(msg_ctx);
}
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
hlist_del(&daemon->euid_chain);
mutex_unlock(&daemon->mux);
kfree_sensitive(daemon);
@@ -283,9 +285,16 @@ ecryptfs_send_message_locked(char *data, int data_len, u8 msg_type,
mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
rc = ecryptfs_send_miscdev(data, data_len, *msg_ctx, msg_type, 0,
daemon);
- if (rc)
+ if (rc) {
printk(KERN_ERR "%s: Error attempting to send message to "
"userspace daemon; rc = [%d]\n", __func__, rc);
+ mutex_lock(&ecryptfs_msg_ctx_lists_mux);
+ mutex_lock(&(*msg_ctx)->mux);
+ ecryptfs_msg_ctx_alloc_to_free(*msg_ctx);
+ mutex_unlock(&(*msg_ctx)->mux);
+ mutex_unlock(&ecryptfs_msg_ctx_lists_mux);
+ *msg_ctx = NULL;
+ }
out:
return rc;
}
diff --git a/fs/ecryptfs/miscdev.c b/fs/ecryptfs/miscdev.c
index 4e62c3cef70f..23baa55d390f 100644
--- a/fs/ecryptfs/miscdev.c
+++ b/fs/ecryptfs/miscdev.c
@@ -357,7 +357,7 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
u32 seq;
size_t packet_size, packet_size_length;
char *data;
- unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE];
+ unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE] = { };
ssize_t rc;
if (count == 0) {
@@ -373,7 +373,8 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
}
if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET],
- sizeof(packet_size_peek))) {
+ min_t(size_t, count - PKT_LEN_OFFSET,
+ sizeof(packet_size_peek)))) {
printk(KERN_WARNING "%s: Error while inspecting packet size\n",
__func__);
return -EFAULT;
diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c
index e7b7f426fecf..0e2108219a6f 100644
--- a/fs/ecryptfs/super.c
+++ b/fs/ecryptfs/super.c
@@ -153,6 +153,13 @@ static int ecryptfs_show_options(struct seq_file *m, struct dentry *root)
if (mount_crypt_stat->global_default_cipher_key_size)
seq_printf(m, ",ecryptfs_key_bytes=%zd",
mount_crypt_stat->global_default_cipher_key_size);
+ if (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) {
+ seq_printf(m, ",ecryptfs_fn_cipher=%s",
+ mount_crypt_stat->global_default_fn_cipher_name);
+ if (mount_crypt_stat->global_default_fn_cipher_key_bytes)
+ seq_printf(m, ",ecryptfs_fn_key_bytes=%zd",
+ mount_crypt_stat->global_default_fn_cipher_key_bytes);
+ }
if (mount_crypt_stat->flags & ECRYPTFS_PLAINTEXT_PASSTHROUGH_ENABLED)
seq_printf(m, ",ecryptfs_passthrough");
if (mount_crypt_stat->flags & ECRYPTFS_XATTR_METADATA_ENABLED)
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c
index 6de97565d5f7..ca5004774c0b 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/fat/misc.c b/fs/fat/misc.c
index 950da09f0961..5c45603bf77e 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 77c3982bca3d..e5559d28031b 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -580,7 +580,7 @@ static void request_wait_answer(struct fuse_req *req)
if (req->args->abort_on_kill) {
fuse_abort_conn(fc);
- return;
+ goto wait_for_finish;
}
if (test_bit(FR_URING, &req->flags))
@@ -591,6 +591,7 @@ static void request_wait_answer(struct fuse_req *req)
return;
}
+wait_for_finish:
/*
* Either request is already in userspace, or it was forced.
* Wait it out.
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index 6dbfb9cb07d7..d0a89ca1a4e4 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 cebcc283b7ce..8728b53cf2e8 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/svcsubs.c b/fs/lockd/svcsubs.c
index d11beea76ddb..91a4c22c86ff 100644
--- a/fs/lockd/svcsubs.c
+++ b/fs/lockd/svcsubs.c
@@ -301,12 +301,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);
}
/*
@@ -316,32 +314,41 @@ static int
nlm_traverse_files(void *data, nlm_host_match_fn_t match,
int (*is_failover_file)(void *data, struct nlm_file *file))
{
- struct hlist_node *next;
- struct nlm_file *file;
+ struct nlm_file *file, *next;
int i, ret = 0;
mutex_lock(&nlm_file_mutex);
for (i = 0; i < FILE_NRHASH; i++) {
- hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
- if (is_failover_file && !is_failover_file(data, file))
- continue;
+ file = hlist_entry_safe(nlm_files[i].first,
+ struct nlm_file, f_list);
+ if (file)
file->f_count++;
- mutex_unlock(&nlm_file_mutex);
-
- /* Traverse locks, blocks and shares of this file
- * and update file->f_locks count */
- if (nlm_inspect_file(data, file, match))
- ret = 1;
+ while (file) {
+ /*
+ * Pin the next neighbour before we drop the mutex
+ * for nlm_inspect_file(); a concurrent
+ * nlm_release_file() under the same mutex would
+ * otherwise be free to unlink and kfree it during
+ * the unlock window, leaving us to dereference a
+ * freed slab when we walked to next afterwards.
+ */
+ next = hlist_entry_safe(file->f_list.next,
+ struct nlm_file, f_list);
+ if (next)
+ next->f_count++;
+
+ if (!is_failover_file || is_failover_file(data, file)) {
+ mutex_unlock(&nlm_file_mutex);
+
+ if (nlm_inspect_file(data, file, match))
+ ret = 1;
+
+ mutex_lock(&nlm_file_mutex);
+ }
- mutex_lock(&nlm_file_mutex);
file->f_count--;
- /* No more references to this file. Let go of it. */
- if (list_empty(&file->f_blocks) && !file->f_locks
- && !file->f_shares && !file->f_count) {
- hlist_del(&file->f_list);
- nlm_close_files(file);
- kfree(file);
- }
+ nlm_file_release(file);
+ file = next;
}
}
mutex_unlock(&nlm_file_mutex);
diff --git a/fs/netfs/read_retry.c b/fs/netfs/read_retry.c
index 999177426141..f59a70f3a086 100644
--- a/fs/netfs/read_retry.c
+++ b/fs/netfs/read_retry.c
@@ -175,7 +175,9 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
list_for_each_entry_safe_from(subreq, tmp,
&stream->subrequests, rreq_link) {
trace_netfs_sreq(subreq, netfs_sreq_trace_superfluous);
+ spin_lock(&rreq->lock);
list_del(&subreq->rreq_link);
+ spin_unlock(&rreq->lock);
netfs_put_subrequest(subreq, netfs_sreq_trace_put_done);
if (subreq == to)
break;
@@ -203,8 +205,10 @@ static void netfs_retry_read_subrequests(struct netfs_io_request *rreq)
refcount_read(&subreq->ref),
netfs_sreq_trace_new);
+ spin_lock(&rreq->lock);
list_add(&subreq->rreq_link, &to->rreq_link);
- to = list_next_entry(to, rreq_link);
+ spin_unlock(&rreq->lock);
+ to = subreq;
trace_netfs_sreq(subreq, netfs_sreq_trace_retry);
stream->sreq_max_len = umin(len, rreq->rsize);
diff --git a/fs/netfs/write_retry.c b/fs/netfs/write_retry.c
index 29489a23a220..32735abfa03f 100644
--- a/fs/netfs/write_retry.c
+++ b/fs/netfs/write_retry.c
@@ -130,7 +130,9 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
list_for_each_entry_safe_from(subreq, tmp,
&stream->subrequests, rreq_link) {
trace_netfs_sreq(subreq, netfs_sreq_trace_discard);
+ spin_lock(&wreq->lock);
list_del(&subreq->rreq_link);
+ spin_unlock(&wreq->lock);
netfs_put_subrequest(subreq, netfs_sreq_trace_put_done);
if (subreq == to)
break;
@@ -153,8 +155,10 @@ static void netfs_retry_write_stream(struct netfs_io_request *wreq,
netfs_sreq_trace_new);
trace_netfs_sreq(subreq, netfs_sreq_trace_split);
+ spin_lock(&wreq->lock);
list_add(&subreq->rreq_link, &to->rreq_link);
- to = list_next_entry(to, rreq_link);
+ spin_unlock(&wreq->lock);
+ to = subreq;
trace_netfs_sreq(subreq, netfs_sreq_trace_retry);
stream->sreq_max_len = len;
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 495cace1e564..8f7ed5c1549e 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -1095,20 +1095,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,
@@ -1117,7 +1119,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);
@@ -1129,6 +1131,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 f84086437f53..57067589786f 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)
@@ -2631,6 +2631,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 a238b6725008..06df1edc8e4b 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -327,8 +327,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();
}
@@ -471,11 +474,20 @@ void nfsd_file_net_dispose(struct nfsd_net *nn)
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
+ if (!list_empty(&l->freeme)) {
+ /*
+ * 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);
}
}
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/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 b6d03e1ef5f7..fbf573c31719 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))
@@ -404,8 +430,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;
}
@@ -419,6 +450,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;
@@ -475,6 +510,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 e00b2aea8da2..441a55c25148 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;
@@ -1174,9 +1200,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;
@@ -1194,8 +1219,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();
@@ -1302,18 +1329,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;
}
@@ -1321,12 +1363,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);
}
@@ -1458,22 +1505,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:
/*
@@ -1505,12 +1565,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:
@@ -1522,11 +1586,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:
@@ -1648,7 +1714,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)
{
@@ -1700,6 +1774,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 62762e43f810..2121ef26e7a9 100644
--- a/fs/nfsd/nfs4layouts.c
+++ b/fs/nfsd/nfs4layouts.c
@@ -249,11 +249,17 @@ 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;
+ }
if (nfsd4_layout_setlease(ls)) {
nfs4_put_stid(stp);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 98086d217aa2..82f3d379d639 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1196,7 +1196,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 */
@@ -1805,6 +1805,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())
@@ -1820,13 +1821,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;
}
@@ -1985,16 +1987,14 @@ 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,
@@ -2014,11 +2014,12 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL);
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;
@@ -2124,7 +2125,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,
@@ -2138,12 +2138,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.
@@ -2152,10 +2154,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;
@@ -2929,9 +2932,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;
}
@@ -3317,7 +3333,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
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c9d9a7d46e7b..48715a55df8f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -944,7 +944,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;
@@ -954,19 +954,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,
@@ -977,13 +992,21 @@ struct nfs4_cpntf_state *nfs4_alloc_init_cpntf_state(struct nfsd_net *nn,
cps = kzalloc(sizeof(struct nfs4_cpntf_state), GFP_KERNEL);
if (!cps)
return NULL;
+ /* So a stale list_del_init() before linking is a no-op. */
+ INIT_LIST_HEAD(&cps->cp_list);
cps->cpntf_time = ktime_get_boottime_seconds();
- refcount_set(&cps->cp_stateid.cs_count, 1);
- if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID))
+ /*
+ * Fully initialize the entry before nfs4_init_cp_state() publishes it,
+ * since a concurrent OFFLOAD_CANCEL could then free it. Take an extra
+ * reference for the caller (dropped with nfs4_put_cpntf_state()).
+ */
+ memcpy(&cps->cp_p_stateid, &p_stid->sc_stateid, sizeof(stateid_t));
+ memcpy(&cps->cp_p_clid, &p_stid->sc_client->cl_clientid,
+ sizeof(clientid_t));
+ refcount_set(&cps->cp_stateid.cs_count, 2);
+ if (!nfs4_init_cp_state(nn, &cps->cp_stateid, NFS4_COPYNOTIFY_STID,
+ p_stid))
goto out_free;
- spin_lock(&nn->s2s_cp_lock);
- list_add(&cps->cp_list, &p_stid->sc_cp_list);
- spin_unlock(&nn->s2s_cp_lock);
return cps;
out_free:
kfree(cps);
@@ -1003,18 +1026,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);
}
@@ -2185,7 +2256,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)
@@ -2522,14 +2593,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);
@@ -3292,7 +3373,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,
@@ -4396,6 +4477,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)
@@ -4475,7 +4569,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);
@@ -5072,6 +5168,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;
@@ -5515,8 +5612,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. */
@@ -6738,30 +6837,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);
@@ -6911,16 +7016,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);
@@ -6955,7 +7066,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);
@@ -7074,12 +7185,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);
@@ -7342,16 +7453,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
@@ -7376,10 +7485,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);
@@ -7666,7 +7785,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;
}
@@ -7914,6 +8033,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();
@@ -8381,6 +8504,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;
@@ -8417,10 +8543,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) {
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 1e158881c1af..9f49e3c9e752 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -526,6 +526,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;
@@ -535,6 +537,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;
@@ -822,6 +826,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;
@@ -1969,6 +1977,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;
@@ -1998,6 +2007,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;
}
@@ -5984,9 +6005,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 ab13ee9c7fd8..ed1748a6d319 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 1da5c40d6722..b3281318cf77 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1431,7 +1431,7 @@ 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) {
case AF_INET: {
@@ -1447,7 +1447,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: {
@@ -1463,7 +1463,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;
}
}
@@ -1471,10 +1471,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;
}
/**
@@ -1502,10 +1506,20 @@ 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,
@@ -1513,7 +1527,7 @@ int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb,
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
@@ -1555,17 +1569,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;
+ }
}
}
@@ -1876,6 +1899,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
@@ -1893,6 +1970,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);
@@ -1919,16 +2005,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]);
@@ -1980,16 +2061,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]);
@@ -2250,11 +2326,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;
@@ -2299,8 +2376,8 @@ out_free_lockd:
out_free_pnfs:
nfsd4_exit_pnfs();
out_free_slabs:
- nfsd4_free_slabs();
nfsd_debugfs_exit();
+ nfsd4_free_slabs();
return retval;
}
@@ -2315,9 +2392,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 ed85dd43da18..3ab4b1b6e235 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -69,10 +69,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) {
@@ -279,15 +277,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 906a67257890..35f409b65552 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 fcb47f344e32..242b8c7b6e0f 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -835,7 +835,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;
@@ -845,7 +845,7 @@ nfsd_acl_init_request(struct svc_rqst *rqstp,
return rpc_prog_unavail;
ret->mismatch.hivers = NFSD_ACL_MINVERS;
for (i = NFSD_ACL_NRVERS - 1; i >= NFSD_ACL_MINVERS; i--) {
- if (nfsd_support_acl_version(rqstp->rq_vers) &&
+ if (nfsd_support_acl_version(i) &&
nfsd_vers(nn, i, NFSD_TEST)) {
ret->mismatch.hivers = i;
break;
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c
index fc262ceafca9..e7e309594f84 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 2eed2c5b3cd0..ea414f3adad5 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -383,6 +383,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 */
@@ -494,7 +495,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;
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index 6e2c8e2aab10..ba48d3963e9f 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -236,7 +236,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)
@@ -275,7 +275,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)
@@ -1688,9 +1688,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)
@@ -1702,8 +1703,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;
@@ -1729,9 +1728,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)
@@ -1742,8 +1742,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 ba882a4ea9cb..53b55ee4e00e 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -136,8 +136,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 */
@@ -418,21 +420,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)
@@ -559,12 +562,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;
diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c
index ccc1a7aa52d2..efa01f5b35df 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 dd0c8e560ef6..a47ee0ad57f7 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 2d8dc6b35b54..834ce02f2afb 100644
--- a/fs/nilfs2/direct.c
+++ b/fs/nilfs2/direct.c
@@ -144,7 +144,7 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
return ret;
}
-static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key)
+static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key, bool deform)
{
union nilfs_bmap_ptr_req req;
struct inode *dat;
@@ -234,7 +234,7 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap,
/* no need to allocate any resource for conversion */
/* delete */
- ret = bmap->b_ops->bop_delete(bmap, key);
+ ret = bmap->b_ops->bop_delete(bmap, key, true);
if (ret < 0)
return ret;
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index bfe884d624e7..cf57eabfed45 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -602,8 +602,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 39e60218df7c..eb2ba71d5c81 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -427,6 +427,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
@@ -437,7 +439,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 */
@@ -467,7 +469,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 5c30f4d8c7c1..c1ce663952c8 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -674,12 +674,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))
diff --git a/fs/nsfs.c b/fs/nsfs.c
index e2f9a725883c..4dee1e728569 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -566,7 +566,7 @@ static struct dentry *nsfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
return ERR_PTR(-EOPNOTSUPP);
}
- if (owning_ns && !ns_capable(owning_ns, CAP_SYS_ADMIN)) {
+ if (owning_ns && !may_see_all_namespaces()) {
ns->ops->put(ns);
return ERR_PTR(-EPERM);
}
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index e904c5da9192..3a458975181c 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -2652,6 +2652,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 a0503a334423..5437dc78209e 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.
*/
@@ -4280,6 +4302,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;
@@ -5072,6 +5099,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 724350925aff..798d76f479d0 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.
@@ -2115,6 +2123,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)
@@ -2122,10 +2131,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,
@@ -2263,48 +2273,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;
}
@@ -2349,12 +2424,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;
}
/*
@@ -2366,16 +2442,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 */
@@ -2394,6 +2477,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 2f61d39e4e50..5fffbed779da 100644
--- a/fs/ocfs2/cluster/nodemanager.c
+++ b/fs/ocfs2/cluster/nodemanager.c
@@ -776,6 +776,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 8c9c4825f984..c0fec28e62c8 100644
--- a/fs/ocfs2/dir.c
+++ b/fs/ocfs2/dir.c
@@ -1888,7 +1888,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 4145e06d2c08..4ab8c48582b1 100644
--- a/fs/ocfs2/dlm/dlmmaster.c
+++ b/fs/ocfs2/dlm/dlmmaster.c
@@ -3101,6 +3101,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 843ee02bd85f..2379225f0ee6 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 267b50e8e42e..54a030e6779d 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;
}
@@ -3355,10 +3382,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;
}
@@ -3841,10 +3867,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
@@ -4130,10 +4155,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;
}
@@ -4686,10 +4710,9 @@ loff_t ocfs2_reflink_remap_blocks(struct inode *s_inode,
}
out:
- if (ocfs2_dealloc_has_cluster(&dealloc)) {
+ if (ocfs2_dealloc_has_cluster(&dealloc))
ocfs2_schedule_truncate_log_flush(osb, 1);
- ocfs2_run_deallocs(osb, &dealloc);
- }
+ ocfs2_run_deallocs(osb, &dealloc);
return ret;
}
diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c
index fa49a768b0a1..cd9c8d4dd9f7 100644
--- a/fs/ocfs2/xattr.c
+++ b/fs/ocfs2/xattr.c
@@ -7187,10 +7187,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 79267b3419f2..4b7e6c52d4a3 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/proc/task_mmu.c b/fs/proc/task_mmu.c
index 6dc16295e307..5c3d966b70f2 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -1073,10 +1073,12 @@ static void smaps_pmd_entry(pmd_t *pmd, unsigned long addr,
bool present = false;
struct folio *folio;
+ if (pmd_none(*pmd))
+ return;
if (pmd_present(*pmd)) {
page = vm_normal_page_pmd(vma, addr, *pmd);
present = true;
- } else if (unlikely(thp_migration_supported() && is_swap_pmd(*pmd))) {
+ } else if (unlikely(thp_migration_supported())) {
swp_entry_t entry = pmd_to_swp_entry(*pmd);
if (is_pfn_swap_entry(entry))
@@ -2004,6 +2006,9 @@ static int pagemap_pmd_range_thp(pmd_t *pmdp, unsigned long addr,
if (vma->vm_flags & VM_SOFTDIRTY)
flags |= PM_SOFT_DIRTY;
+ if (pmd_none(pmd))
+ goto populate_pagemap;
+
if (pmd_present(pmd)) {
page = pmd_page(pmd);
@@ -2014,7 +2019,7 @@ static int pagemap_pmd_range_thp(pmd_t *pmdp, unsigned long addr,
flags |= PM_UFFD_WP;
if (pm->show_pfn)
frame = pmd_pfn(pmd) + idx;
- } else if (thp_migration_supported() && is_swap_pmd(pmd)) {
+ } else if (thp_migration_supported()) {
swp_entry_t entry = pmd_to_swp_entry(pmd);
unsigned long offset;
@@ -2041,6 +2046,7 @@ static int pagemap_pmd_range_thp(pmd_t *pmdp, unsigned long addr,
flags |= PM_FILE;
}
+populate_pagemap:
for (; addr != end; addr += PAGE_SIZE, idx++) {
u64 cur_flags = flags;
pagemap_entry_t pme;
@@ -2411,6 +2417,9 @@ static unsigned long pagemap_thp_category(struct pagemap_scan_private *p,
{
unsigned long categories = PAGE_IS_HUGE;
+ if (pmd_none(pmd))
+ return categories;
+
if (pmd_present(pmd)) {
struct page *page;
@@ -2428,7 +2437,7 @@ static unsigned long pagemap_thp_category(struct pagemap_scan_private *p,
categories |= PAGE_IS_PFNZERO;
if (pmd_soft_dirty(pmd))
categories |= PAGE_IS_SOFT_DIRTY;
- } else if (is_swap_pmd(pmd)) {
+ } else {
swp_entry_t swp;
categories |= PAGE_IS_SWAPPED;
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 9059c2efbcc0..5ad6a9b3bb56 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1364,8 +1364,19 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
*/
lock_two_nondirectories(target_inode, src_inode);
- if (len == 0)
- len = src_inode->i_size - off;
+ if (len == 0) {
+ loff_t src_size = i_size_read(src_inode);
+
+ if (off > src_size) {
+ rc = -EINVAL;
+ goto unlock;
+ }
+ len = src_size - off;
+ if (!len) {
+ rc = 0;
+ goto unlock;
+ }
+ }
cifs_dbg(FYI, "clone range\n");
diff --git a/fs/smb/client/dfs_cache.c b/fs/smb/client/dfs_cache.c
index f2ad0ccd08a7..9072b5e80122 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 a34457d5143e..2914b751970c 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -994,6 +994,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 c9e955d0d799..4ce3def36843 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -3080,6 +3080,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;
}
}
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index ca8f3dd7ff63..848e06c51009 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -727,7 +727,7 @@ static int cifs_query_path_info(const unsigned int xid,
ea->ea_value_length = cpu_to_le16(SMB2_WSL_XATTR_DEV_SIZE);
memcpy(&ea->ea_data[0], SMB2_WSL_XATTR_DEV, SMB2_WSL_XATTR_NAME_LEN + 1);
data->wsl.eas_len += ALIGN(sizeof(*ea) + SMB2_WSL_XATTR_NAME_LEN + 1 +
- SMB2_WSL_XATTR_MODE_SIZE, 4);
+ SMB2_WSL_XATTR_DEV_SIZE, 4);
rc = 0;
} else if (rc >= 0) {
/* It is an error if EA $LXDEV has wrong size. */
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
index 9bab3c57b3a4..8110f4e3f0a4 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/tracefs/event_inode.c b/fs/tracefs/event_inode.c
index ad182f9fbb48..a7b082a665bd 100644
--- a/fs/tracefs/event_inode.c
+++ b/fs/tracefs/event_inode.c
@@ -436,6 +436,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;
}
@@ -727,8 +729,6 @@ struct eventfs_inode *eventfs_create_dir(const char *name, struct eventfs_inode
ei->entries = entries;
ei->nr_entries = size;
ei->data = data;
- INIT_LIST_HEAD(&ei->children);
- INIT_LIST_HEAD(&ei->list);
mutex_lock(&eventfs_mutex);
if (!parent->is_freed)
@@ -801,9 +801,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 a79d73f28aa7..3a184ded12ad 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -532,7 +532,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/compaction.h b/include/linux/compaction.h
index 173d9c07a895..ff2d5fb29161 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -56,6 +56,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/hugetlb.h b/include/linux/hugetlb.h
index a3f6cdf8a2b6..d09604498fa4 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -1277,6 +1277,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)
{
diff --git a/include/linux/list.h b/include/linux/list.h
index 00ea8e5fb88b..0121e2f88f89 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/mm_types.h b/include/linux/mm_types.h
index 8b1045c51e0a..f9c552744b71 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -285,6 +285,8 @@ typedef struct {
unsigned long val;
} swp_entry_t;
+typedef swp_entry_t softleaf_t;
+
#if defined(CONFIG_MEMCG) || defined(CONFIG_SLAB_OBJ_EXT)
/* We have some extra room after the refcount in tail pages. */
#define NR_PAGES_IN_LARGE_FOLIO
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c13f759374be..7be5b78b2bff 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2145,6 +2145,7 @@ struct net_device {
unsigned long state;
unsigned int flags;
unsigned short hard_header_len;
+ enum netdev_stat_type pcpu_stat_type:8;
netdev_features_t features;
struct inet6_dev __rcu *ip6_ptr;
__cacheline_group_end(net_device_read_txrx);
@@ -2396,8 +2397,6 @@ struct net_device {
void *ml_priv;
enum netdev_ml_priv_type ml_priv_type;
- enum netdev_stat_type pcpu_stat_type:8;
-
#if IS_ENABLED(CONFIG_GARP)
struct garp_port __rcu *garp_port;
#endif
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/swapops.h b/include/linux/swapops.h
index 57aad87e46a6..1966b4890d46 100644
--- a/include/linux/swapops.h
+++ b/include/linux/swapops.h
@@ -120,6 +120,8 @@ static inline unsigned long swp_offset_pfn(swp_entry_t entry)
return swp_offset(entry) & SWP_PFN_MASK;
}
+#define softleaf_to_pfn swp_offset_pfn
+
/* check whether a pte points to a swap entry */
static inline int is_swap_pte(pte_t pte)
{
@@ -139,6 +141,8 @@ static inline swp_entry_t pte_to_swp_entry(pte_t pte)
return swp_entry(__swp_type(arch_entry), __swp_offset(arch_entry));
}
+#define softleaf_from_pte pte_to_swp_entry
+
/*
* Convert the arch-independent representation of a swp_entry_t into the
* arch-dependent pte representation.
@@ -572,7 +576,13 @@ static inline pmd_t swp_entry_to_pmd(swp_entry_t entry)
static inline int pmd_is_migration_entry(pmd_t pmd)
{
- return is_swap_pmd(pmd) && is_migration_entry(pmd_to_swp_entry(pmd));
+ swp_entry_t entry;
+
+ if (pmd_present(pmd))
+ return 0;
+
+ entry = pmd_to_swp_entry(pmd);
+ return is_migration_entry(entry);
}
#else /* CONFIG_ARCH_ENABLE_THP_MIGRATION */
static inline int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
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/net/ip_tunnels.h b/include/net/ip_tunnels.h
index 253ed3930f6e..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);
}
@@ -665,13 +672,29 @@ static inline int iptunnel_pull_offloads(struct sk_buff *skb)
static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len)
{
if (pkt_len > 0) {
- struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats);
-
- u64_stats_update_begin(&tstats->syncp);
- u64_stats_add(&tstats->tx_bytes, pkt_len);
- u64_stats_inc(&tstats->tx_packets);
- u64_stats_update_end(&tstats->syncp);
- put_cpu_ptr(tstats);
+ if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_DSTATS) {
+ struct pcpu_dstats *dstats = get_cpu_ptr(dev->dstats);
+
+ u64_stats_update_begin(&dstats->syncp);
+ u64_stats_add(&dstats->tx_bytes, pkt_len);
+ u64_stats_inc(&dstats->tx_packets);
+ u64_stats_update_end(&dstats->syncp);
+ put_cpu_ptr(dstats);
+ return;
+ }
+ if (dev->pcpu_stat_type == NETDEV_PCPU_STAT_TSTATS) {
+ struct pcpu_sw_netstats *tstats = get_cpu_ptr(dev->tstats);
+
+ u64_stats_update_begin(&tstats->syncp);
+ u64_stats_add(&tstats->tx_bytes, pkt_len);
+ u64_stats_inc(&tstats->tx_packets);
+ u64_stats_update_end(&tstats->syncp);
+ put_cpu_ptr(tstats);
+ return;
+ }
+ pr_err_once("iptunnel_xmit_stats pcpu_stat_type=%d\n",
+ dev->pcpu_stat_type);
+ WARN_ON_ONCE(1);
return;
}
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 4bbba29f7341..e2191b158ac4 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -2064,7 +2064,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 f030adc462ee..bb81293c6010 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -237,6 +237,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 cf02893ba911..e0b42e07eb08 100644
--- a/io_uring/query.c
+++ b/io_uring/query.c
@@ -36,6 +36,9 @@ static int io_handle_query_entry(struct io_ring_ctx *ctx,
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/kernel/auditfilter.c b/kernel/auditfilter.c
index f9de58df00fe..d01903b9f965 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1025,7 +1025,6 @@ static inline int audit_add_rule(struct audit_entry *entry)
int audit_del_rule(struct audit_entry *entry)
{
struct audit_entry *e;
- struct audit_tree *tree = entry->rule.tree;
struct list_head *list;
int ret = 0;
#ifdef CONFIG_AUDITSYSCALL
@@ -1073,9 +1072,6 @@ int audit_del_rule(struct audit_entry *entry)
out:
mutex_unlock(&audit_filter_mutex);
- if (tree)
- audit_put_tree(tree); /* that's the temporary one */
-
return ret;
}
@@ -1160,6 +1156,8 @@ int audit_rule_change(int type, int seq, void *data, size_t datasz)
}
if (err || type == AUDIT_DEL_RULE) {
+ if (type == AUDIT_DEL_RULE && entry->rule.tree)
+ audit_put_tree(entry->rule.tree);
if (entry->rule.exe)
audit_remove_mark(entry->rule.exe);
audit_free_rule(entry);
diff --git a/kernel/bpf/bloom_filter.c b/kernel/bpf/bloom_filter.c
index 35e1ddca74d2..3f55f28fd589 100644
--- a/kernel/bpf/bloom_filter.c
+++ b/kernel/bpf/bloom_filter.c
@@ -41,7 +41,7 @@ static long bloom_map_peek_elem(struct bpf_map *map, void *value)
for (i = 0; i < bloom->nr_hash_funcs; i++) {
h = hash(bloom, value, map->value_size, i);
- if (!test_bit(h, bloom->bitset))
+ if (!test_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h)))
return -ENOENT;
}
@@ -57,9 +57,13 @@ static long bloom_map_push_elem(struct bpf_map *map, void *value, u64 flags)
if (flags != BPF_ANY)
return -EINVAL;
+ /*
+ * On 32-bit architectures, hashes larger than INT_MAX would be
+ * treated as negative by set_bit().
+ */
for (i = 0; i < bloom->nr_hash_funcs; i++) {
h = hash(bloom, value, map->value_size, i);
- set_bit(h, bloom->bitset);
+ set_bit(h % BITS_PER_LONG, bloom->bitset + BIT_WORD(h));
}
return 0;
@@ -94,9 +98,10 @@ static int bloom_map_alloc_check(union bpf_attr *attr)
static struct bpf_map *bloom_map_alloc(union bpf_attr *attr)
{
- u32 bitset_bytes, bitset_mask, nr_hash_funcs, nr_bits;
+ u32 bitset_mask, nr_hash_funcs, nr_bits;
int numa_node = bpf_map_attr_numa_node(attr);
struct bpf_bloom_filter *bloom;
+ u64 bitset_bytes;
if (attr->key_size != 0 || attr->value_size == 0 ||
attr->max_entries == 0 ||
@@ -127,22 +132,16 @@ static struct bpf_map *bloom_map_alloc(union bpf_attr *attr)
if (check_mul_overflow(attr->max_entries, nr_hash_funcs, &nr_bits) ||
check_mul_overflow(nr_bits / 5, (u32)7, &nr_bits) ||
nr_bits > (1UL << 31)) {
- /* The bit array size is 2^32 bits but to avoid overflowing the
- * u32, we use U32_MAX, which will round up to the equivalent
- * number of bytes
- */
- bitset_bytes = BITS_TO_BYTES(U32_MAX);
bitset_mask = U32_MAX;
} else {
if (nr_bits <= BITS_PER_LONG)
nr_bits = BITS_PER_LONG;
else
nr_bits = roundup_pow_of_two(nr_bits);
- bitset_bytes = BITS_TO_BYTES(nr_bits);
bitset_mask = nr_bits - 1;
}
- bitset_bytes = roundup(bitset_bytes, sizeof(unsigned long));
+ bitset_bytes = BITS_TO_LONGS((u64)bitset_mask + 1) * sizeof(unsigned long);
bloom = bpf_map_area_alloc(sizeof(*bloom) + bitset_bytes, numa_node);
if (!bloom)
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index 2365541c81dd..13f4f696a7c0 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -460,6 +460,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 */
@@ -476,6 +477,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;
}
@@ -496,6 +498,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/bpf/verifier.c b/kernel/bpf/verifier.c
index 459ed144542c..dcecf8806856 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -611,6 +611,13 @@ static bool is_atomic_load_insn(const struct bpf_insn *insn)
insn->imm == BPF_LOAD_ACQ;
}
+static bool is_atomic_fetch_insn(const struct bpf_insn *insn)
+{
+ return BPF_CLASS(insn->code) == BPF_STX &&
+ BPF_MODE(insn->code) == BPF_ATOMIC &&
+ (insn->imm & BPF_FETCH);
+}
+
static int __get_spi(s32 off)
{
return (-off - 1) / BPF_REG_SIZE;
@@ -4323,10 +4330,24 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
* dreg still needs precision before this insn
*/
}
- } else if (class == BPF_LDX || is_atomic_load_insn(insn)) {
- if (!bt_is_reg_set(bt, dreg))
+ } else if (class == BPF_LDX ||
+ is_atomic_load_insn(insn) ||
+ is_atomic_fetch_insn(insn)) {
+ u32 load_reg = dreg;
+
+ /*
+ * Atomic fetch operation writes the old value into
+ * a register (sreg or r0) and if it was tracked for
+ * precision, propagate to the stack slot like we do
+ * in regular ldx.
+ */
+ if (is_atomic_fetch_insn(insn))
+ load_reg = insn->imm == BPF_CMPXCHG ?
+ BPF_REG_0 : sreg;
+
+ if (!bt_is_reg_set(bt, load_reg))
return 0;
- bt_clear_reg(bt, dreg);
+ bt_clear_reg(bt, load_reg);
/* scalars can only be spilled into stack w/o losing precision.
* Load from any other memory can be zero extended.
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 3745cd02c847..d34187b02d14 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1958,6 +1958,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
@@ -1985,6 +1986,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
@@ -2151,7 +2157,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.
*/
@@ -2165,6 +2171,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 8942884e21c4..10ae7aac16d7 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 6a7769749ee6..9dfc327394f1 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -715,29 +715,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",
@@ -748,14 +725,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 ec087827c85c..972cc11c9157 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/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 5d8fc382c4a1..374b65940d03 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/signal.c b/kernel/signal.c
index e1843b71efff..34ba75bd749a 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1183,6 +1183,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;
@@ -1196,6 +1197,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 889a8de3fc20..06184f304c6a 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -2693,10 +2693,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 d5ebb1d927ea..97a7bd5a8e3b 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -2491,6 +2491,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 9ea034b55158..5936389188bd 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -396,6 +396,17 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage)
return local_read(&bpage->page->commit);
}
+/**
+ * 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 */
@@ -595,11 +606,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;
@@ -639,7 +681,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->subbuf_size,
+ (unsigned int)rb_subbuf_capacity(buffer),
(unsigned int)is_signed_type(char));
return !trace_seq_has_overflowed(s);
@@ -1628,7 +1670,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;
@@ -2302,8 +2344,8 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer,
bpage->range = 1;
bpage->id = i + 1;
} 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;
}
@@ -2402,8 +2444,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
atomic_inc(&cpu_buffer->resize_disabled);
} 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;
}
@@ -2473,7 +2514,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, cpu_buffer->buffer->subbuf_order);
kfree(cpu_buffer);
}
@@ -2512,10 +2553,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;
@@ -2588,9 +2628,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,
rb_range_meta_init(buffer, nr_pages, scratch_size);
} 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;
}
@@ -2963,7 +3002,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.
*/
@@ -2985,12 +3024,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.
@@ -3001,6 +3034,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
@@ -3279,7 +3318,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;
}
@@ -3518,8 +3557,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;
@@ -3871,8 +3910,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);
@@ -4519,7 +4557,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);
}
@@ -4764,7 +4802,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)) {
@@ -5441,7 +5479,6 @@ static struct buffer_page *
rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer)
{
struct buffer_page *reader = NULL;
- unsigned long bsize = READ_ONCE(cpu_buffer->buffer->subbuf_size);
unsigned long overwrite;
unsigned long flags;
int nr_loops = 0;
@@ -5580,7 +5617,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);
@@ -6002,36 +6039,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(sizeof(*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);
@@ -6085,7 +6130,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);
@@ -6536,7 +6581,7 @@ ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu)
if (bpage->data) {
rb_init_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);
@@ -6655,15 +6700,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;
-
bpage = data_page->data;
if (!bpage)
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;
@@ -6778,7 +6823,7 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
/* If there is room at the end of the page to save the
* missed events, then record it there.
*/
- if (buffer->subbuf_size - commit >= sizeof(missed_events)) {
+ if (rb_page_capacity(reader) - commit >= sizeof(missed_events)) {
memcpy(&bpage->data[commit], &missed_events,
sizeof(missed_events));
local_add(RB_MISSED_STORED, &bpage->commit);
@@ -6790,8 +6835,8 @@ int ring_buffer_read_page(struct trace_buffer *buffer,
/*
* This page may be off to user land. Zero it out here.
*/
- if (commit < buffer->subbuf_size)
- memset(&bpage->data[commit], 0, buffer->subbuf_size - commit);
+ if (commit < rb_page_capacity(reader))
+ memset(&bpage->data[commit], 0, rb_page_capacity(reader) - commit);
return read;
}
@@ -6817,7 +6862,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);
@@ -6862,7 +6907,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;
@@ -6871,9 +6917,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;
@@ -6882,18 +6925,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) {
@@ -6909,8 +6955,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)
@@ -6973,8 +7019,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;
+ arch_spin_unlock(&cpu_buffer->lock);
rb_head_page_activate(cpu_buffer);
@@ -6996,7 +7044,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);
@@ -7074,7 +7121,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);
@@ -7436,7 +7483,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(&bpage->data[commit], &missed_events,
sizeof(missed_events));
local_add(RB_MISSED_STORED, &bpage->commit);
@@ -7468,7 +7515,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/trace.c b/kernel/trace/trace.c
index 914d6baa1bf9..e62772a76671 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -9699,6 +9699,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;
@@ -9738,6 +9740,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);
@@ -11172,7 +11175,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 c184d1a7387a..0f96eb95ad38 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -4729,6 +4729,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 cf2a4388845d..1799c22ff9f2 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -6613,8 +6613,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 8e839370f713..d4fe566991cb 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/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 6769a9d6f16a..7bb638df5db9 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1364,12 +1364,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;
@@ -1957,12 +1989,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;
/*
@@ -2748,9 +2780,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,
@@ -2765,54 +2796,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;
@@ -2839,8 +2840,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 95d948c8e86c..2264a36bc773 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 227aeffae8ee..8564a92d83f0 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2293,9 +2293,11 @@ static pmd_t move_soft_dirty_pmd(pmd_t pmd)
static pmd_t clear_uffd_wp_pmd(pmd_t pmd)
{
+ if (pmd_none(pmd))
+ return pmd;
if (pmd_present(pmd))
pmd = pmd_clear_uffd_wp(pmd);
- else if (is_swap_pmd(pmd))
+ else
pmd = pmd_swp_clear_uffd_wp(pmd);
return pmd;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5515870b5b15..e9f4cbf50a2b 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -5876,6 +5876,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));
@@ -5967,7 +5968,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.
*/
@@ -5975,7 +5979,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/internal.h b/mm/internal.h
index f5c0d927924d..ef42eee87c7b 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -908,7 +908,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 415b154fdf38..452f3714f337 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1690,6 +1690,42 @@ 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;
+
+ 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) {
+ scan_block(stack, stack + THREAD_SIZE, NULL);
+ put_task_stack(p);
+ }
+ put_task_struct(p);
+ }
+ cond_resched();
+ } while (pid && !scan_should_stop());
+}
+
+/*
* Scan data sections and all the referenced memory blocks allocated via the
* kernel's standard allocators. This function must be called with the
* scan_mutex held.
@@ -1780,19 +1816,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/memcontrol-v1.c b/mm/memcontrol-v1.c
index 6eed14bff742..74d3a4d78ec6 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -1139,13 +1139,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;
@@ -1982,7 +1982,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 752d98fd3921..898821793349 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -3809,9 +3809,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();
@@ -4028,6 +4030,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;
}
@@ -4045,9 +4048,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;
}
@@ -4056,9 +4061,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;
}
@@ -4378,6 +4385,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;
@@ -4429,6 +4439,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/memory.c b/mm/memory.c
index 6b4cdb8bd8db..72793ec63107 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -6320,34 +6320,39 @@ retry_pud:
if (pmd_none(*vmf.pmd) &&
thp_vma_allowable_order(vma, vm_flags, TVA_PAGEFAULT, PMD_ORDER)) {
ret = create_huge_pmd(&vmf);
- if (!(ret & VM_FAULT_FALLBACK))
+ if (ret & VM_FAULT_FALLBACK)
+ goto fallback;
+ else
return ret;
- } else {
- vmf.orig_pmd = pmdp_get_lockless(vmf.pmd);
+ }
- if (unlikely(is_swap_pmd(vmf.orig_pmd))) {
- VM_BUG_ON(thp_migration_supported() &&
- !pmd_is_migration_entry(vmf.orig_pmd));
- if (pmd_is_migration_entry(vmf.orig_pmd))
- pmd_migration_entry_wait(mm, vmf.pmd);
- return 0;
- }
- if (pmd_trans_huge(vmf.orig_pmd)) {
- if (pmd_protnone(vmf.orig_pmd) && vma_is_accessible(vma))
- return do_huge_pmd_numa_page(&vmf);
+ vmf.orig_pmd = pmdp_get_lockless(vmf.pmd);
+ if (pmd_none(vmf.orig_pmd))
+ goto fallback;
- if ((flags & (FAULT_FLAG_WRITE|FAULT_FLAG_UNSHARE)) &&
- !pmd_write(vmf.orig_pmd)) {
- ret = wp_huge_pmd(&vmf);
- if (!(ret & VM_FAULT_FALLBACK))
- return ret;
- } else {
- huge_pmd_set_accessed(&vmf);
- return 0;
- }
+ if (unlikely(!pmd_present(vmf.orig_pmd))) {
+ VM_BUG_ON(thp_migration_supported() &&
+ !pmd_is_migration_entry(vmf.orig_pmd));
+ if (pmd_is_migration_entry(vmf.orig_pmd))
+ pmd_migration_entry_wait(mm, vmf.pmd);
+ return 0;
+ }
+ if (pmd_trans_huge(vmf.orig_pmd)) {
+ if (pmd_protnone(vmf.orig_pmd) && vma_is_accessible(vma))
+ return do_huge_pmd_numa_page(&vmf);
+
+ if ((flags & (FAULT_FLAG_WRITE|FAULT_FLAG_UNSHARE)) &&
+ !pmd_write(vmf.orig_pmd)) {
+ ret = wp_huge_pmd(&vmf);
+ if (!(ret & VM_FAULT_FALLBACK))
+ return ret;
+ } else {
+ huge_pmd_set_accessed(&vmf);
+ return 0;
}
}
+fallback:
return handle_pte_fault(&vmf);
}
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index b4aa4f2cb8ec..d18cee845354 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -1989,24 +1989,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 e66d9cc67dda..d03b5d265e71 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1821,7 +1821,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/mm_init.c b/mm/mm_init.c
index d2d915984611..922493a35a28 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -2238,10 +2238,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);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 775e02b797a3..af37e0c2d6f8 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -763,14 +763,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*/
@@ -786,12 +786,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;
@@ -4132,18 +4132,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();
@@ -4167,7 +4216,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;
}
@@ -4404,9 +4453,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_table_check.c b/mm/page_table_check.c
index 0a5ab62c9c59..91c4dacc41fb 100644
--- a/mm/page_table_check.c
+++ b/mm/page_table_check.c
@@ -223,10 +223,14 @@ EXPORT_SYMBOL(__page_table_check_ptes_set);
static inline void page_table_check_pmd_flags(pmd_t pmd)
{
- if (pmd_present(pmd) && pmd_uffd_wp(pmd))
- WARN_ON_ONCE(pmd_write(pmd));
- else if (is_swap_pmd(pmd) && pmd_swp_uffd_wp(pmd))
- WARN_ON_ONCE(swap_cached_writable(pmd_to_swp_entry(pmd)));
+ if (pmd_present(pmd)) {
+ if (pmd_uffd_wp(pmd))
+ WARN_ON_ONCE(pmd_write(pmd));
+ } else if (pmd_swp_uffd_wp(pmd)) {
+ swp_entry_t entry = pmd_to_swp_entry(pmd);
+
+ WARN_ON_ONCE(swap_cached_writable(entry));
+ }
}
void __page_table_check_pmds_set(struct mm_struct *mm, pmd_t *pmdp, pmd_t pmd,
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index ca6c51598aed..47befc414adc 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -127,6 +127,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)
@@ -139,8 +140,6 @@ again:
continue;
}
- walk->action = ACTION_SUBTREE;
-
/*
* This implies that each ->pmd_entry() handler
* needs to know about pmd_trans_huge() pmds
@@ -197,6 +196,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)
@@ -209,8 +209,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 4c78c7109909..4348b35db465 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1983,20 +1983,26 @@ 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 {
- pfn = swp_offset_pfn(pte_to_swp_entry(pteval));
+ /*
+ * 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);
VM_WARN_ON_FOLIO(folio_test_hugetlb(folio), folio);
}
subpage = folio_page(folio, pfn - folio_pfn(folio));
- address = pvmw.address;
anon_exclusive = folio_test_anon(folio) &&
PageAnonExclusive(subpage);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 841cf1ab8330..cf33c3b2c31f 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1613,11 +1613,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;
}
@@ -3753,7 +3753,7 @@ static int __swap_duplicate(swp_entry_t entry, unsigned char usage, int nr)
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/vmscan.c b/mm/vmscan.c
index 95b1179a14e7..05027b320eda 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -201,6 +201,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. */
@@ -241,13 +248,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)
{
@@ -263,11 +263,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
/* for_each_managed_zone_pgdat - helper macro to iterate over all managed zones in a pgdat up to
@@ -5864,7 +5859,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 bb1f1e124dc0..3564b37f1fce 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -508,6 +508,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)
{
@@ -1052,7 +1057,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;
}
@@ -1279,9 +1284,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 12fb0e395412..18249dd96ef8 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1375,11 +1375,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;
@@ -1398,7 +1399,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 e630d6369a0c..eb91dc9fbe86 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -1298,7 +1298,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 a1d7e7985f76..f63f993226d5 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -4130,7 +4130,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 3c66262c1f4d..031ee997bb92 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -5665,10 +5665,11 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
hci_dev_lock(hdev);
- /* All controllers implicitly stop advertising in the event of a
- * connection, so ensure that the state bit is cleared.
+ /* Advertising stops when a connection is created. On a failed
+ * connection it keeps running, so leave the state bit alone.
*/
- hci_dev_clear_flag(hdev, HCI_LE_ADV);
+ if (!status)
+ hci_dev_clear_flag(hdev, HCI_LE_ADV);
/* Check for existing connection:
*
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 5c7cd2a38d51..27261f8e904e 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -5300,6 +5300,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);
@@ -5484,6 +5485,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 022978f4ffd6..03260e1e265c 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -2148,6 +2148,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 fe5ea8db2188..bdc9a7cd9405 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -2208,9 +2208,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)) {
@@ -2242,6 +2246,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 7108a3153a16..46b2c2ae7778 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>
@@ -5805,6 +5806,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)
@@ -5856,6 +5882,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 ff06ba2965cf..68bd45852fc3 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -519,6 +519,8 @@ static struct crush_map *crush_decode(void *pbyval, void *end)
ceph_decode_need(p, end, 4*sizeof(u32), bad);
b->id = ceph_decode_32(p);
+ if (b->id != -1 - i)
+ goto bad;
b->type = ceph_decode_16(p);
if (b->type == 0)
goto bad;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index e75baa72a22e..31c2c5a97a35 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1373,10 +1373,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 1948ef4a41aa..7a28900c52c6 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 50d0f5fe4e4c..e9da39010f8b 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 94e974c201f0..4c2447d770e4 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 ca9eaee4c2ef..f5b6e895b096 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -2168,6 +2168,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 fbc0df2daed3..15fb42ca5eed 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -371,23 +371,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));
@@ -488,12 +481,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;
@@ -502,8 +494,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) {
@@ -692,10 +682,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;
}
@@ -840,6 +834,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 58b473c157c8..e2463bcfc504 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -1136,13 +1136,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;
@@ -1170,8 +1168,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 168ec07e31cc..b59b2b119a1c 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -577,6 +577,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 757c37092be7..22c12045f2c3 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1228,19 +1228,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)
@@ -1517,8 +1506,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 5894f7dd4d4d..cce5ea6fe7a0 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -6017,7 +6017,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 c03df1682a63..f4d6370340d0 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1128,7 +1128,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 7f5a5aa0cfdc..bd5838e65c31 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/mpls/af_mpls.c b/net/mpls/af_mpls.c
index f9b8c4d0f049..608c43a4602d 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -198,6 +198,7 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
if (pskb_may_pull(skb, mpls_hdr_len + sizeof(struct iphdr))) {
const struct iphdr *v4hdr;
+ hdr = mpls_hdr(skb) + label_index;
v4hdr = (const struct iphdr *)(hdr + 1);
if (v4hdr->version == 4) {
hash = jhash_3words(ntohl(v4hdr->saddr),
@@ -208,6 +209,7 @@ static u32 mpls_multipath_hash(struct mpls_route *rt, struct sk_buff *skb)
sizeof(struct ipv6hdr))) {
const struct ipv6hdr *v6hdr;
+ hdr = mpls_hdr(skb) + label_index;
v6hdr = (const struct ipv6hdr *)(hdr + 1);
hash = __ipv6_addr_jhash(&v6hdr->saddr, hash);
hash = __ipv6_addr_jhash(&v6hdr->daddr, hash);
diff --git a/net/mptcp/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 f63c8ac74e4a..fb68aa17e479 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -2003,6 +2003,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;
@@ -2012,10 +2013,11 @@ 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)
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index b407644b652f..1c032d4a6665 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:
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 34573c9b8a5e..a753945f860c 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1259,7 +1259,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 9addcfad467f..8826dd60e59e 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 bc7b4a33b726..6057a7d9718b 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -3237,7 +3237,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 ea45467c1140..1425fd756d34 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 5df4838a6386..34fa809e9504 100644
--- a/net/smc/smc_core.c
+++ b/net/smc/smc_core.c
@@ -1134,13 +1134,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;
@@ -1195,14 +1195,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,
@@ -1254,11 +1256,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)
@@ -1511,12 +1512,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 28873ec06e3c..51937265203c 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(sizeof(*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 09b839725953..24524dfd5518 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 b04a21b8c511..4624817137be 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 bb3c3db2713b..6e40544fe2c0 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -2074,7 +2074,11 @@ gss_unwrap_resp_priv(struct rpc_task *task, struct rpc_cred *cred,
goto unwrap_failed;
opaque_len = be32_to_cpup(p++);
offset = (u8 *)(p) - (u8 *)head->iov_base;
- if (offset + opaque_len > rcv_buf->len)
+ if (offset > rcv_buf->len)
+ goto unwrap_failed;
+ if (opaque_len > rcv_buf->len - offset)
+ goto unwrap_failed;
+ if (opaque_len <= GSS_KRB5_TOK_HDR_LEN)
goto unwrap_failed;
maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset,
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index b3e1738ff6bf..71b406d00472 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -74,6 +74,8 @@ static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
int shifted = 0;
int this_shift;
+ if (!buf->len)
+ return;
shift %= buf->len;
while (shifted < shift) {
this_shift = min(shift - shifted, LOCAL_BUF_LEN);
@@ -86,6 +88,8 @@ static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
{
struct xdr_buf subbuf;
+ if (buf->len <= base)
+ return;
xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
_rotate_left(&subbuf, shift);
}
@@ -155,6 +159,9 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
dprintk("RPC: %s\n", __func__);
+ if (len - offset <= GSS_KRB5_TOK_HDR_LEN)
+ return GSS_S_DEFECTIVE_TOKEN;
+
ptr = buf->head[0].iov_base + offset;
if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
@@ -221,14 +228,16 @@ gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
* head buffer space rather than that actually occupied.
*/
movelen = min_t(unsigned int, buf->head[0].iov_len, len);
+ if (movelen < offset + GSS_KRB5_TOK_HDR_LEN + headskip)
+ return GSS_S_DEFECTIVE_TOKEN;
movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
- BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
- buf->head[0].iov_len);
memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
buf->len = len - (GSS_KRB5_TOK_HDR_LEN + headskip);
/* Trim off the trailing "extra count" and checksum blob */
+ if (ec + GSS_KRB5_TOK_HDR_LEN + tailskip > buf->len - offset)
+ return GSS_S_DEFECTIVE_TOKEN;
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
*align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c
index f549e4c05def..711857ac688a 100644
--- a/net/sunrpc/auth_gss/gss_rpc_upcall.c
+++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c
@@ -121,12 +121,6 @@ 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 f320c0a8e604..3837e0a33d49 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -230,6 +230,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;
@@ -280,9 +281,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 */
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index e2f0df8cdaa6..f7f885ab0a4b 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/cache.c b/net/sunrpc/cache.c
index 6f6e0d4928af..fda19596566f 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -423,10 +423,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 a142e20ce7e7..07de0efaf37d 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 bab6cab29405..04109278fda6 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -54,6 +54,7 @@ static __net_init int sunrpc_init_net(struct net *net)
INIT_LIST_HEAD(&sn->all_clients);
spin_lock_init(&sn->rpc_client_lock);
spin_lock_init(&sn->rpcb_clnt_lock);
+ mutex_init(&sn->gssp_lock);
return 0;
err_pipefs:
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 1f4bf5b290e4..1c7410488236 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
*/
@@ -541,12 +597,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;
}
/**
@@ -625,9 +687,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);
@@ -663,6 +723,15 @@ svc_release_buffer(struct svc_rqst *rqstp)
kfree(rqstp->rq_pages);
}
+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)
{
@@ -671,10 +740,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 *
@@ -691,7 +758,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 fb4dab207000..d3cd543ff80a 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 28c68b5f6823..cfc24e21d260 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 b63cfeaa2923..a1c61311714d 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 e7e4a39ca6c6..68a0aa627455 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -498,10 +498,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 310de7a80be5..b3700c1478ea 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -817,7 +817,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
@@ -834,9 +834,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;
@@ -887,9 +891,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;
@@ -907,6 +912,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)
@@ -914,7 +921,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);
}
@@ -939,8 +948,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);
}
/**
@@ -966,17 +979,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);
@@ -985,6 +1001,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;
@@ -1009,14 +1027,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);
@@ -1033,6 +1054,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);
@@ -1041,7 +1066,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 914cd263c2f1..082db5cea213 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -700,20 +700,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 = {
@@ -725,11 +726,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;
}
/**
@@ -785,7 +797,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
@@ -820,6 +832,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
*
@@ -835,6 +848,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;
@@ -849,7 +863,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,
@@ -1033,7 +1050,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;
}
@@ -1054,10 +1071,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 12857381e861..c8d994272f6f 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>
@@ -226,12 +227,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;
@@ -241,7 +246,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);
@@ -275,6 +280,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;
}
/**
@@ -298,8 +304,7 @@ 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);
@@ -576,13 +581,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;
}
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 6b64c6c49494..a4422cbd3254 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -2647,7 +2647,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 698cec8cbdab..8b56ff4bdf4d 100644
--- a/net/vmw_vsock/virtio_transport.c
+++ b/net/vmw_vsock/virtio_transport.c
@@ -848,10 +848,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/kernel/bug.rs b/rust/kernel/bug.rs
index 36aef43e5ebe..13ac693e7811 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,6 +48,7 @@ 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 {
@@ -73,6 +75,7 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, CONFIG_UML))]
macro_rules! warn_flags {
($flags:expr) => {
@@ -91,6 +94,7 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
+#[cfg(not(testlib))]
#[cfg(all(CONFIG_BUG, any(CONFIG_LOONGARCH, CONFIG_ARM)))]
macro_rules! warn_flags {
($flags:expr) => {
@@ -101,7 +105,7 @@ macro_rules! warn_flags {
#[macro_export]
#[doc(hidden)]
-#[cfg(not(CONFIG_BUG))]
+#[cfg(any(testlib, not(CONFIG_BUG)))]
macro_rules! warn_flags {
($flags:expr) => {};
}
diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs
index 7355bbac16a7..fb4ed721d1f6 100644
--- a/rust/kernel/list.rs
+++ b/rust/kernel/list.rs
@@ -233,7 +233,7 @@ pub use self::arc_field::{define_list_arc_field_getter, ListArcField};
/// 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/time.rs b/rust/kernel/time.rs
index c1e83aed2b46..0c944d89f0bc 100644
--- a/rust/kernel/time.rs
+++ b/rust/kernel/time.rs
@@ -421,22 +421,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/rust_is_available.sh b/scripts/rust_is_available.sh
index d2323de0692c..82be88d44c18 100755
--- a/scripts/rust_is_available.sh
+++ b/scripts/rust_is_available.sh
@@ -242,6 +242,20 @@ if [ "$bindgen_libclang_cversion" -ge 1900100 ] &&
fi
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 4fcc319dea84..a5fadef98132 100755
--- a/scripts/rust_is_available_test.py
+++ b/scripts/rust_is_available_test.py
@@ -54,7 +54,7 @@ else:
""")
@classmethod
- def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False):
+ def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False, libclang_concat_patched=False, libclang_22_patched=False):
if libclang_stderr is None:
libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})"
else:
@@ -70,6 +70,11 @@ else:
else:
libclang_concat_case = "pass"
+ 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):
@@ -78,6 +83,8 @@ elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv):
{version_0_66_case}
elif "rust_is_available_bindgen_libclang_concat.h" in " ".join(sys.argv):
{libclang_concat_case}
+elif "rust_is_available_bindgen_libclang_22.h" in " ".join(sys.argv):
+ {libclang_22_case}
else:
print({repr(version_stdout)})
""")
@@ -300,6 +307,27 @@ else:
bindgen = self.generate_bindgen(f"bindgen {bindgen_version}", libclang_stderr, libclang_concat_patched=True)
result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen, "CC": cc })
+ 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 b028e4c13b6f..d37f33f0eec8 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -184,13 +184,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 335f21930702..574be681f83e 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 c9bc9cc69475..f9519baa4e60 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/audit.h"
#include "include/cred.h"
@@ -88,6 +89,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 636acb66a4f6..ea6717da7cba 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -981,9 +981,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 e8a2bff2e5b6..80930957e84d 100644
--- a/security/landlock/errata/abi-1.h
+++ b/security/landlock/errata/abi-1.h
@@ -14,3 +14,26 @@
* actions.
*/
LANDLOCK_ERRATUM(3)
+
+/**
+ * DOC: erratum_4
+ *
+ * Erratum 4: Creation of whiteout objects
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This fix changes the access rights required for the creation of whiteout
+ * objects through :manpage:`mknod(2)`, :manpage:`renameat2(2)`, or
+ * :manpage:`link(2)`. Creating whiteout objects is now guarded by
+ * ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of ``LANDLOCK_ACCESS_FS_MAKE_CHAR``.
+ *
+ * Whiteout objects are used in OverlayFS to mark the absence of a file in an
+ * upper file system. Despite being created with ``S_IFCHR``, whiteout objects
+ * do not count as character devices.
+ *
+ * Impact:
+ *
+ * Sandboxed programs that create OverlayFS whiteouts (such as fuse-overlayfs)
+ * now require ``LANDLOCK_ACCESS_FS_MAKE_REG`` instead of
+ * ``LANDLOCK_ACCESS_FS_MAKE_CHAR``.
+ */
+LANDLOCK_ERRATUM(4)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 4f8dfeadbbe2..d741ed3e5568 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>
@@ -985,7 +986,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:
@@ -993,6 +995,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;
@@ -1009,6 +1014,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))
@@ -1098,6 +1110,7 @@ static bool collect_domain_accesses(
* @new_dentry: Destination file or directory.
* @removable: Sets to true if it is a rename operation.
* @exchange: Sets to true if it is a rename operation with RENAME_EXCHANGE.
+ * @whiteout: Sets to true if it is a rename operation with RENAME_WHITEOUT.
*
* Because of its unprivileged constraints, Landlock relies on file hierarchies
* (and not only inodes) to tie access rights to files. Being able to link or
@@ -1146,7 +1159,8 @@ static bool collect_domain_accesses(
static int current_check_refer_path(struct dentry *const old_dentry,
const struct path *const new_dir,
struct dentry *const new_dentry,
- const bool removable, const bool exchange)
+ const bool removable, const bool exchange,
+ const bool whiteout)
{
const struct landlock_cred_security *const subject =
landlock_get_applicable_subject(current_cred(), any_fs, NULL);
@@ -1166,18 +1180,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) {
/*
@@ -1527,7 +1548,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,
@@ -1538,7 +1559,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,
@@ -1551,7 +1573,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 7fd8a60b8be9..0d984fdf211a 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1969,7 +1969,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 3f8488716a08..d08027681c37 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1805,6 +1805,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 d3f9424088d4..e1550e02d264 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 fe50b48c10e7..017e478bd21f 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 b903a138fc2a..fc7aa7ceb5a5 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 e79997b34302..2e2e90d03787 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -7014,6 +7014,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),
@@ -7464,6 +7465,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),
@@ -7498,7 +7503,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x390d, "Lenovo Yoga Pro 7 14ASP10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x3911, "Lenovo Yoga Pro 7 14IAH10", 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),
@@ -7563,6 +7567,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/core/ext/stream.c b/sound/hda/core/ext/stream.c
index b4759198e51d..f6d80f95333b 100644
--- a/sound/hda/core/ext/stream.c
+++ b/sound/hda/core/ext/stream.c
@@ -211,8 +211,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 83066d08367e..7b37b2aa76a6 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1532,6 +1532,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)) {
@@ -1545,13 +1552,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 d9f145b634f3..df845260f570 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -749,6 +749,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, "Micro-Star International Co., Ltd."),
DMI_MATCH(DMI_PRODUCT_NAME, "Thin A15 B7VE"),
}
},
diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c
index 49629d4bb327..bcfb34db37d9 100644
--- a/sound/usb/6fire/comm.c
+++ b/sound/usb/6fire/comm.c
@@ -36,11 +36,14 @@ static void usb6fire_comm_receiver_handler(struct urb *urb)
struct midi_runtime *midi_rt = rt->chip->midi;
if (!urb->status) {
- if (rt->receiver_buffer[0] == 0x10) /* midi in event */
+ u8 len = rt->receiver_buffer[1];
+
+ if (rt->receiver_buffer[0] == 0x10 && /* midi in event */
+ len <= COMM_RECEIVER_BUFSIZE - 2 &&
+ urb->actual_length >= len + 2)
if (midi_rt)
midi_rt->in_received(midi_rt,
- rt->receiver_buffer + 2,
- rt->receiver_buffer[1]);
+ rt->receiver_buffer + 2, len);
}
if (!rt->chip->shutdown) {
diff --git a/sound/usb/bcd2000/bcd2000.c b/sound/usb/bcd2000/bcd2000.c
index 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/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/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 1edb2d785d93..0a6bc35190a1 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -370,7 +370,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