summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap2
-rw-r--r--Documentation/arch/arm64/silicon-errata.rst2
-rw-r--r--Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml18
-rw-r--r--Documentation/filesystems/netfs_library.rst26
-rw-r--r--MAINTAINERS85
-rw-r--r--Makefile2
-rw-r--r--arch/arc/Kconfig4
-rw-r--r--arch/arc/include/asm/atomic64-arcv2.h6
-rw-r--r--arch/arc/include/asm/cmpxchg.h4
-rw-r--r--arch/arc/kernel/setup.c1
-rw-r--r--arch/arc/kernel/smp.c1
-rw-r--r--arch/arm64/Kconfig30
-rw-r--r--arch/arm64/kernel/cpu_errata.c24
-rw-r--r--arch/arm64/kernel/cpufeature.c2
-rw-r--r--arch/arm64/kernel/signal.c34
-rw-r--r--arch/arm64/kernel/topology.c22
-rw-r--r--arch/arm64/kernel/vdso/Makefile2
-rw-r--r--arch/arm64/kvm/emulate-nested.c3
-rw-r--r--arch/arm64/kvm/hyp/include/nvhe/pkvm.h9
-rw-r--r--arch/arm64/kvm/hyp/nvhe/hyp-main.c7
-rw-r--r--arch/arm64/kvm/hyp/nvhe/pkvm.c25
-rw-r--r--arch/arm64/kvm/mmu.c47
-rw-r--r--arch/arm64/kvm/vgic/vgic-init.c12
-rw-r--r--arch/arm64/kvm/vgic/vgic-its.c12
-rw-r--r--arch/arm64/kvm/vgic/vgic-v2.c6
-rw-r--r--arch/arm64/kvm/vgic/vgic-v3.c6
-rw-r--r--arch/arm64/kvm/vgic/vgic.c21
-rw-r--r--arch/arm64/mm/mmu.c2
-rw-r--r--arch/arm64/net/bpf_jit_comp.c37
-rw-r--r--arch/arm64/tools/cpucaps2
-rw-r--r--arch/loongarch/net/bpf_jit.c60
-rw-r--r--arch/loongarch/vdso/Makefile1
-rw-r--r--arch/powerpc/kernel/vdso/Makefile1
-rw-r--r--arch/powerpc/kvm/book3s_hv.c2
-rw-r--r--arch/powerpc/net/bpf_jit_comp.c45
-rw-r--r--arch/powerpc/perf/imc-pmu.c23
-rw-r--r--arch/riscv/kernel/vdso/Makefile1
-rw-r--r--arch/riscv/kvm/aia_device.c2
-rw-r--r--arch/riscv/net/bpf_jit_comp64.c56
-rw-r--r--arch/s390/kernel/vdso/Makefile1
-rw-r--r--arch/s390/kvm/s390/s390.c5
-rw-r--r--arch/s390/net/bpf_jit_comp.c46
-rw-r--r--arch/x86/crypto/aesni-intel_glue.c2
-rw-r--r--arch/x86/entry/vdso/vdso64/Makefile2
-rw-r--r--arch/x86/include/uapi/asm/ptrace-abi.h4
-rw-r--r--arch/x86/kernel/sys_x86_64.c15
-rw-r--r--arch/x86/kvm/mmu/mmu.c6
-rw-r--r--arch/x86/kvm/svm/sev.c57
-rw-r--r--arch/x86/kvm/svm/svm.c43
-rw-r--r--arch/x86/kvm/svm/svm.h1
-rw-r--r--arch/x86/kvm/vmx/nested.c6
-rw-r--r--arch/x86/kvm/vmx/tdx.c5
-rw-r--r--arch/x86/mm/pgtable.c38
-rw-r--r--arch/x86/net/bpf_jit_comp.c26
-rw-r--r--arch/x86/um/asm/ptrace.h4
-rw-r--r--arch/x86/um/ptrace_32.c1
-rw-r--r--block/blk-cgroup.c5
-rw-r--r--block/blk-mq.c63
-rw-r--r--block/fops.c5
-rw-r--r--block/mq-deadline.c2
-rw-r--r--crypto/aes.c51
-rw-r--r--drivers/android/binder/node.rs20
-rw-r--r--drivers/android/binder/node/wrapper.rs34
-rw-r--r--drivers/android/binder/thread.rs2
-rw-r--r--drivers/block/drbd/drbd_nl_gen.c4
-rw-r--r--drivers/block/virtio_blk.c1
-rw-r--r--drivers/bluetooth/btintel.c15
-rw-r--r--drivers/bluetooth/btintel_pcie.c12
-rw-r--r--drivers/char/tpm/tpm-interface.c2
-rw-r--r--drivers/char/tpm/tpm2-cmd.c6
-rw-r--r--drivers/char/tpm/tpm2-sessions.c1
-rw-r--r--drivers/clk/spacemit/ccu-k3.c92
-rw-r--r--drivers/clk/ti/composite.c26
-rw-r--r--drivers/cpufreq/cpufreq.c4
-rw-r--r--drivers/cpufreq/intel_pstate.c10
-rw-r--r--drivers/edac/altera_edac.c67
-rw-r--r--drivers/edac/versalnet_edac.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c37
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vi.c1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c13
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c11
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c90
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c24
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c6
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c117
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c2
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power.c2
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c39
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_params.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_params.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.c4
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c17
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c4
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c6
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c30
-rw-r--r--drivers/hid/bpf/hid_bpf_dispatch.c2
-rw-r--r--drivers/hid/hid-ids.h3
-rw-r--r--drivers/hid/hid-logitech-dj.c48
-rw-r--r--drivers/hid/hid-logitech-hidpp.c51
-rw-r--r--drivers/hid/hid-multitouch.c2
-rw-r--r--drivers/hid/hid-universal-pidff.c1
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c6
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c3
-rw-r--r--drivers/i2c/busses/i2c-at91-master.c8
-rw-r--r--drivers/i2c/busses/i2c-xiic.c61
-rw-r--r--drivers/iio/accel/kionix-kx022a.c30
-rw-r--r--drivers/iio/accel/kxcjk-1013.c2
-rw-r--r--drivers/iio/accel/sca3000.c2
-rw-r--r--drivers/iio/adc/ad4030.c11
-rw-r--r--drivers/iio/adc/ad7173.c4
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c31
-rw-r--r--drivers/iio/adc/ade9000.c36
-rw-r--r--drivers/iio/adc/aspeed_adc.c4
-rw-r--r--drivers/iio/adc/axp288_adc.c8
-rw-r--r--drivers/iio/adc/max1363.c8
-rw-r--r--drivers/iio/adc/pac1934.c4
-rw-r--r--drivers/iio/adc/rohm-bd79124.c20
-rw-r--r--drivers/iio/adc/stm32-adc.c43
-rw-r--r--drivers/iio/adc/sun4i-gpadc-iio.c10
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c9
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c16
-rw-r--r--drivers/iio/cdc/ad7150.c8
-rw-r--r--drivers/iio/common/inv_sensors/inv_sensors_timestamp.c11
-rw-r--r--drivers/iio/dac/mcp47a1.c4
-rw-r--r--drivers/iio/dac/rohm-bd79703.c3
-rw-r--r--drivers/iio/frequency/adf4377.c4
-rw-r--r--drivers/iio/frequency/admv1013.c9
-rw-r--r--drivers/iio/gyro/adis16136.c2
-rw-r--r--drivers/iio/health/max30102.c12
-rw-r--r--drivers/iio/imu/adis16400.c2
-rw-r--r--drivers/iio/imu/adis16480.c4
-rw-r--r--drivers/iio/imu/inv_icm42607/inv_icm42607_core.c38
-rw-r--r--drivers/iio/industrialio-buffer.c7
-rw-r--r--drivers/iio/industrialio-trigger.c2
-rw-r--r--drivers/iio/light/gp2ap020a00f.c2
-rw-r--r--drivers/iio/light/rohm-bu27034.c62
-rw-r--r--drivers/iio/pressure/bmp280-core.c2
-rw-r--r--drivers/iio/pressure/rohm-bm1390.c2
-rw-r--r--drivers/iio/proximity/aw96103.c30
-rw-r--r--drivers/iio/proximity/isl29501.c2
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c4
-rw-r--r--drivers/iio/proximity/sx9324.c2
-rw-r--r--drivers/iio/proximity/vcnl3020.c24
-rw-r--r--drivers/iio/proximity/vl53l0x-i2c.c3
-rw-r--r--drivers/input/keyboard/atkbd.c8
-rw-r--r--drivers/input/mouse/synaptics.c7
-rw-r--r--drivers/input/serio/i8042-acpipnpio.h9
-rw-r--r--drivers/input/touchscreen/s6sy761.c14
-rw-r--r--drivers/interconnect/qcom/x1e80100.c485
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c42
-rw-r--r--drivers/mtd/devices/block2mtd.c2
-rw-r--r--drivers/mtd/devices/mtd_intel_dg.c3
-rw-r--r--drivers/mtd/mtdcore.c3
-rw-r--r--drivers/mtd/nand/ecc.c7
-rw-r--r--drivers/mtd/nand/raw/cadence-nand-controller.c6
-rw-r--r--drivers/mtd/nand/raw/vf610_nfc.c85
-rw-r--r--drivers/mtd/nand/spi/core.c53
-rw-r--r--drivers/mtd/spi-nor/core.c2
-rw-r--r--drivers/net/ethernet/broadcom/bcmsysport.c8
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c34
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.h2
-rw-r--r--drivers/net/ethernet/google/gve/gve_tx_dqo.c14
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs.c4
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c11
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c18
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h9
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c38
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c10
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.c1
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.h1
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c1
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c3
-rw-r--r--drivers/net/ethernet/microchip/vcap/vcap_api.c2
-rw-r--r--drivers/net/ethernet/realtek/r8169_main.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c83
-rw-r--r--drivers/net/pcs/pcs-rzn1-miic.c3
-rw-r--r--drivers/net/pfcp.c3
-rw-r--r--drivers/net/phy/aquantia/aquantia_main.c5
-rw-r--r--drivers/net/phy/qcom/at803x.c8
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/hif_usb.c19
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c7
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.c28
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.h1
-rw-r--r--drivers/net/wireless/intel/iwlegacy/3945-mac.c1
-rw-r--r--drivers/net/wireless/intersil/p54/fwio.c42
-rw-r--r--drivers/net/wireless/silabs/wfx/hif_tx.c4
-rw-r--r--drivers/net/wireless/st/cw1200/bh.c8
-rw-r--r--drivers/net/wireless/st/cw1200/txrx.c14
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c4
-rw-r--r--drivers/nvme/host/core.c63
-rw-r--r--drivers/nvme/host/fc.c2
-rw-r--r--drivers/nvme/host/multipath.c19
-rw-r--r--drivers/nvme/host/nvme.h9
-rw-r--r--drivers/nvme/host/pci.c2
-rw-r--r--drivers/nvme/host/sysfs.c4
-rw-r--r--drivers/nvme/host/tcp.c39
-rw-r--r--drivers/nvme/target/configfs.c9
-rw-r--r--drivers/nvme/target/core.c33
-rw-r--r--drivers/nvme/target/fabrics-cmd-auth.c2
-rw-r--r--drivers/nvme/target/nvmet.h2
-rw-r--r--drivers/nvme/target/pci-epf.c3
-rw-r--r--drivers/of/base.c10
-rw-r--r--drivers/of/irq.c2
-rw-r--r--drivers/of/overlay.c17
-rw-r--r--drivers/pci/pci.c15
-rw-r--r--drivers/rtc/dev.c2
-rw-r--r--drivers/rtc/rtc-ac100.c39
-rw-r--r--drivers/rtc/rtc-efi.c80
-rw-r--r--drivers/rtc/rtc-mpfs.c6
-rw-r--r--drivers/rtc/rtc-spear.c11
-rw-r--r--drivers/spi/spi-cs42l43.c97
-rw-r--r--drivers/staging/greybus/uart.c3
-rw-r--r--drivers/thunderbolt/ctl.c63
-rw-r--r--drivers/thunderbolt/domain.c26
-rw-r--r--drivers/thunderbolt/nhi.c31
-rw-r--r--drivers/thunderbolt/nhi.h21
-rw-r--r--drivers/thunderbolt/nhi_regs.h4
-rw-r--r--drivers/thunderbolt/pci.c23
-rw-r--r--drivers/thunderbolt/quirks.c3
-rw-r--r--drivers/thunderbolt/stream.c3
-rw-r--r--drivers/thunderbolt/switch.c9
-rw-r--r--drivers/thunderbolt/tb.c37
-rw-r--r--drivers/thunderbolt/test.c58
-rw-r--r--drivers/thunderbolt/tunnel.c73
-rw-r--r--drivers/thunderbolt/tunnel.h8
-rw-r--r--drivers/thunderbolt/xdomain.c5
-rw-r--r--drivers/tty/amiserial.c9
-rw-r--r--drivers/tty/n_gsm.c47
-rw-r--r--drivers/tty/serial/8250/8250_bcm7271.c2
-rw-r--r--drivers/tty/serial/8250/8250_omap.c15
-rw-r--r--drivers/tty/serial/8250/8250_port.c2
-rw-r--r--drivers/tty/serial/kgdboc.c9
-rw-r--r--drivers/tty/serial/ma35d1_serial.c6
-rw-r--r--drivers/tty/serial/max3100.c13
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c6
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c229
-rw-r--r--drivers/tty/serial/sc16is7xx.c53
-rw-r--r--drivers/tty/serial/serial-tegra.c18
-rw-r--r--drivers/tty/serial/serial_core.c76
-rw-r--r--drivers/tty/serial/vt8500_serial.c1
-rw-r--r--drivers/tty/tty_io.c54
-rw-r--r--drivers/tty/tty_port.c91
-rw-r--r--drivers/tty/vcc.c5
-rw-r--r--drivers/tty/vt/selection.c4
-rw-r--r--drivers/tty/vt/vc_screen.c23
-rw-r--r--drivers/tty/vt/vt.c5
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c4
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c5
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c4
-rw-r--r--drivers/usb/class/cdc-acm.c41
-rw-r--r--drivers/usb/class/cdc-acm.h1
-rw-r--r--drivers/usb/core/hub.c7
-rw-r--r--drivers/usb/core/message.c8
-rw-r--r--drivers/usb/dwc2/hcd.c3
-rw-r--r--drivers/usb/dwc3/core.h19
-rw-r--r--drivers/usb/dwc3/dwc3-am62.c1
-rw-r--r--drivers/usb/dwc3/ep0.c9
-rw-r--r--drivers/usb/dwc3/gadget.c108
-rw-r--r--drivers/usb/gadget/function/f_fs.c15
-rw-r--r--drivers/usb/gadget/function/f_midi2.c4
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c6
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/core.c4
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/dev.c2
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/hub.c5
-rw-r--r--drivers/usb/gadget/udc/aspeed-vhub/vhub.h1
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c4
-rw-r--r--drivers/usb/host/octeon-hcd.c54
-rw-r--r--drivers/usb/host/ohci-da8xx.c2
-rw-r--r--drivers/usb/host/ohci-s3c2410.c1
-rw-r--r--drivers/usb/host/ohci-spear.c1
-rw-r--r--drivers/usb/host/ohci-st.c1
-rw-r--r--drivers/usb/serial/bus.c6
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/generic.c10
-rw-r--r--drivers/usb/serial/option.c14
-rw-r--r--drivers/usb/serial/quatech2.c19
-rw-r--r--drivers/usb/serial/ssu100.c18
-rw-r--r--drivers/usb/serial/usb-serial.c129
-rw-r--r--drivers/usb/serial/xr_serial.c10
-rw-r--r--drivers/usb/storage/sierra_ms.c7
-rw-r--r--drivers/usb/typec/anx7411.c5
-rw-r--r--drivers/usb/typec/port-mapper.c21
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c6
-rw-r--r--drivers/usb/typec/tipd/core.c11
-rw-r--r--drivers/usb/typec/ucsi/displayport.c9
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c17
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c34
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c15
-rw-r--r--drivers/virt/nitro_enclaves/ne_misc_dev.c1
-rw-r--r--drivers/virt/vmgenid.c11
-rw-r--r--fs/netfs/buffered_read.c7
-rw-r--r--fs/netfs/buffered_write.c9
-rw-r--r--fs/netfs/direct_write.c9
-rw-r--r--fs/netfs/internal.h2
-rw-r--r--fs/netfs/misc.c99
-rw-r--r--fs/netfs/read_collect.c24
-rw-r--r--fs/smb/client/cifsfs.c41
-rw-r--r--fs/smb/client/cifsfs.h5
-rw-r--r--fs/smb/client/cifsglob.h10
-rw-r--r--fs/smb/client/file.c5
-rw-r--r--fs/smb/client/inode.c24
-rw-r--r--fs/smb/client/smb2ops.c130
-rw-r--r--fs/smb/client/smb2pdu.c10
-rw-r--r--include/linux/bpf.h45
-rw-r--r--include/linux/cpufreq.h3
-rw-r--r--include/linux/hrtimer_rearm.h2
-rw-r--r--include/linux/kvm_host.h1
-rw-r--r--include/linux/mtd/nand.h2
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--include/linux/netfilter_netdev.h2
-rw-r--r--include/linux/netfs.h2
-rw-r--r--include/linux/perf_event.h7
-rw-r--r--include/linux/rtnetlink.h7
-rw-r--r--include/linux/siphash.h8
-rw-r--r--include/linux/skbuff.h2
-rw-r--r--include/linux/soc/qcom/geni-se.h15
-rw-r--r--include/linux/thunderbolt.h3
-rw-r--r--include/linux/tty.h2
-rw-r--r--include/linux/tty_driver.h6
-rw-r--r--include/linux/tty_port.h22
-rw-r--r--include/linux/usb/serial.h1
-rw-r--r--include/net/bluetooth/hci_core.h5
-rw-r--r--include/net/gso.h8
-rw-r--r--include/net/netfilter/nf_flow_table.h2
-rw-r--r--include/net/sch_generic.h9
-rw-r--r--include/net/xdp.h5
-rw-r--r--include/sound/core.h9
-rw-r--r--include/uapi/linux/alloc_tag.h1
-rw-r--r--include/uapi/linux/random.h2
-rw-r--r--include/vdso/getrandom.h2
-rw-r--r--init/Kconfig5
-rw-r--r--io_uring/bpf_filter.c1
-rw-r--r--io_uring/cmd_net.c19
-rw-r--r--io_uring/io_uring.c16
-rw-r--r--io_uring/loop.c5
-rw-r--r--io_uring/tw.c3
-rw-r--r--io_uring/zcrx.c9
-rw-r--r--kernel/audit_tree.c24
-rw-r--r--kernel/bpf/core.c10
-rw-r--r--kernel/bpf/hashtab.c19
-rw-r--r--kernel/bpf/memalloc.c57
-rw-r--r--kernel/bpf/syscall.c19
-rw-r--r--kernel/bpf/trampoline.c85
-rw-r--r--kernel/bpf/verifier.c10
-rw-r--r--kernel/events/core.c315
-rw-r--r--kernel/events/ring_buffer.c7
-rw-r--r--kernel/futex/core.c10
-rw-r--r--kernel/irq/Kconfig12
-rw-r--r--kernel/irq/Makefile2
-rw-r--r--kernel/kprobes.c8
-rw-r--r--kernel/module/main.c8
-rw-r--r--kernel/sysctl.c2
-rw-r--r--kernel/time/jiffies.c2
-rw-r--r--kernel/trace/fprobe.c26
-rw-r--r--mm/damon/core.c2
-rw-r--r--mm/kasan/common.c13
-rw-r--r--mm/shmem.c7
-rw-r--r--mm/slub.c7
-rw-r--r--mm/vma.c4
-rw-r--r--net/bluetooth/hci_conn.c18
-rw-r--r--net/bluetooth/hci_core.c54
-rw-r--r--net/bluetooth/hci_debugfs.c2
-rw-r--r--net/bluetooth/hci_sync.c19
-rw-r--r--net/bluetooth/rfcomm/tty.c7
-rw-r--r--net/bluetooth/smp.c2
-rw-r--r--net/core/dev.c80
-rw-r--r--net/core/dev_ioctl.c3
-rw-r--r--net/core/gso.c1
-rw-r--r--net/core/page_pool.c2
-rw-r--r--net/core/skbuff.c8
-rw-r--r--net/core/sock.c4
-rw-r--r--net/ethtool/tsconfig.c24
-rw-r--r--net/ipv4/af_inet.c3
-rw-r--r--net/ipv4/tcp_input.c6
-rw-r--r--net/ipv6/addrconf.c2
-rw-r--r--net/ipv6/ip6_offload.c4
-rw-r--r--net/ipv6/seg6_iptunnel.c2
-rw-r--r--net/ipv6/seg6_local.c140
-rw-r--r--net/mac80211/agg-rx.c2
-rw-r--r--net/mac80211/chan.c50
-rw-r--r--net/mac80211/drop.h1
-rw-r--r--net/mac80211/fils_aead.c8
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c11
-rw-r--r--net/mac80211/mesh_pathtbl.c30
-rw-r--r--net/mac80211/mlme.c4
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c89
-rw-r--r--net/mac80211/rx.c6
-rw-r--r--net/mac80211/spectmgmt.c4
-rw-r--r--net/mac80211/sta_info.c6
-rw-r--r--net/mac80211/status.c70
-rw-r--r--net/mac80211/tx.c45
-rw-r--r--net/mctp/device.c6
-rw-r--r--net/netfilter/ipset/ip_set_bitmap_gen.h2
-rw-r--r--net/netfilter/ipvs/ip_vs_conn.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_lblc.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_lblcr.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c33
-rw-r--r--net/netfilter/nf_flow_table_core.c7
-rw-r--r--net/netfilter/nf_flow_table_offload.c14
-rw-r--r--net/netfilter/nf_flow_table_path.c3
-rw-r--r--net/netfilter/nf_nat_bpf.c3
-rw-r--r--net/netfilter/nf_nat_core.c5
-rw-r--r--net/netfilter/nft_flow_offload.c7
-rw-r--r--net/netfilter/nft_set_rbtree.c2
-rw-r--r--net/packet/af_packet.c3
-rw-r--r--net/sched/act_ct.c2
-rw-r--r--net/sched/act_skbedit.c5
-rw-r--r--net/sched/cls_api.c15
-rw-r--r--net/sched/sch_codel.c2
-rw-r--r--net/sched/sch_fq_codel.c4
-rw-r--r--net/sctp/socket.c2
-rw-r--r--net/tipc/crypto.c20
-rw-r--r--net/wireless/nl80211.c11
-rw-r--r--net/wireless/scan.c22
-rw-r--r--rust/kernel/net/netlink.rs9
-rw-r--r--sound/core/seq/seq_compat.c4
-rw-r--r--sound/hda/codecs/realtek/alc269.c62
-rw-r--r--sound/hda/codecs/realtek/realtek.c6
-rw-r--r--sound/hda/controllers/tegra.c3
-rw-r--r--sound/pci/ctxfi/cthw20k2.c2
-rw-r--r--sound/pci/ice1712/ice1712.c2
-rw-r--r--sound/soc/amd/acp-config.c14
-rw-r--r--sound/soc/amd/acp/Kconfig1
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c20
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c14
-rw-r--r--sound/soc/codecs/Kconfig4
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c3
-rw-r--r--sound/soc/codecs/max98363.c2
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c27
-rw-r--r--sound/soc/codecs/rt274.c4
-rw-r--r--sound/soc/codecs/rt286.c2
-rw-r--r--sound/soc/codecs/wm8903.c2
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c17
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c1
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c16
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h1
-rw-r--r--sound/soc/qcom/lpass-cpu.c4
-rw-r--r--sound/soc/sdca/sdca_class_function.c19
-rw-r--r--sound/soc/tegra/tegra186_asrc.c2
-rw-r--r--sound/usb/caiaq/device.c3
-rw-r--r--sound/usb/line6/playback.c7
-rw-r--r--sound/usb/misc/ua101.c9
-rw-r--r--sound/usb/mixer_maps.c4
-rw-r--r--sound/usb/quirks.c16
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_ma_ttrace.c60
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c34
-rw-r--r--tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c188
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_ma_ttrace.c50
-rw-r--r--tools/testing/selftests/bpf/progs/tramp_prog_detach.c56
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_align.c6
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c178
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_meta_access.c30
-rw-r--r--tools/testing/selftests/bpf/testing_helpers.c28
-rw-r--r--tools/testing/selftests/bpf/testing_helpers.h2
-rw-r--r--tools/testing/selftests/hid/hid_bpf.c31
-rw-r--r--tools/testing/selftests/kvm/Makefile.kvm2
-rw-r--r--tools/testing/selftests/kvm/arm64/hidden_features.c184
-rw-r--r--tools/testing/selftests/kvm/x86/nested_x2apic_test.c222
-rw-r--r--tools/testing/selftests/net/.gitignore1
-rw-r--r--tools/testing/selftests/net/Makefile1
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt22
-rw-r--r--tools/testing/selftests/net/udp_splice_checksum.c378
-rw-r--r--tools/testing/selftests/ublk/kublk.c2
-rw-r--r--virt/kvm/kvm_main.c35
487 files changed, 6852 insertions, 2848 deletions
diff --git a/.mailmap b/.mailmap
index 389b94a0124e..b8d0dc185714 100644
--- a/.mailmap
+++ b/.mailmap
@@ -286,6 +286,8 @@ Ed L. Cashin <ecashin@coraid.com>
Elliot Berman <quic_eberman@quicinc.com> <eberman@codeaurora.org>
Enric Balletbo i Serra <eballetbo@kernel.org> <enric.balletbo@collabora.com>
Enric Balletbo i Serra <eballetbo@kernel.org> <eballetbo@iseebcn.com>
+Eric Dumazet <edumazet@kernel.org> <edumazet@google.com>
+Eric Dumazet <edumazet@kernel.org> <eric.dumazet@gmail.com>
Erik Kaneda <erik.kaneda@intel.com> <erik.schmauss@intel.com>
Ethan Carter Edwards <ethan@ethancedwards.com> Ethan Edwards <ethancarteredwards@gmail.com>
Eugen Hristev <ehristev@kernel.org> <eugen.hristev@microchip.com>
diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index ac3248b9f2f3..99a1eb4b833d 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -174,6 +174,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A725 | #3456106 | ARM64_ERRATUM_3194386 |
+----------------+-----------------+-----------------+-----------------------------+
+| ARM | Cortex-A725 | #3821522 | ARM64_ERRATUM_3821522 |
++----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X1 | #1502854 | N/A |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X1 | #3324344 | ARM64_ERRATUM_3194386 |
diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml
index f0551d665916..0cce357cb01d 100644
--- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.yaml
@@ -12,19 +12,19 @@ maintainers:
properties:
compatible:
oneOf:
- - const: rockchip,saradc
- - const: rockchip,rk3066-tsadc
- - const: rockchip,rk3399-saradc
- - const: rockchip,rk3528-saradc
+ - enum:
+ - rockchip,saradc
+ - rockchip,rk3066-tsadc
+ - rockchip,rk3399-saradc
+ - rockchip,rk3528-saradc
+ - rockchip,rk3562-saradc
+ - rockchip,rk3588-saradc
+ - rockchip,rv1106-saradc
- items:
- const: rockchip,rk3506-saradc
- const: rockchip,rk3528-saradc
- - const: rockchip,rk3562-saradc
- - const: rockchip,rk3588-saradc
- items:
- - enum:
- - rockchip,rk3576-saradc
- - rockchip,rv1106-saradc
+ - const: rockchip,rk3576-saradc
- const: rockchip,rk3588-saradc
- items:
- enum:
diff --git a/Documentation/filesystems/netfs_library.rst b/Documentation/filesystems/netfs_library.rst
index ddd799df6ce3..a9de281db8ee 100644
--- a/Documentation/filesystems/netfs_library.rst
+++ b/Documentation/filesystems/netfs_library.rst
@@ -451,6 +451,32 @@ one.
The inode should be marked ``NETFS_ICTX_SINGLE_NO_UPLOAD`` if this API is to be
used. The writeback function requires the buffer to be of ITER_FOLIOQ type.
+Clearing Stale Post-EOF Pagecache
+---------------------------------
+
+When a file is extended, data left in the pagecache past the old EOF by a write
+through an mmap must not be exposed as file content. Netfslib clears this on
+its own write paths, and exports a helper so a filesystem can do the same from
+a resize path (truncate, setattr, fallocate and the like) that holds the
+inode's ``i_rwsem`` exclusively across the whole resize::
+
+ void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
+ uoff_t to);
+
+This zeroes any such data within the ``[from, to)`` hole to be made, where
+@from is the old EOF and @to is the new one, and the caller must have already
+updated ``i_size`` to @to before calling it. Only the folio straddling @from
+can hold data written past the EOF through an mmap, as pages wholly beyond the
+EOF can't be faulted in, so the zeroing is limited to that folio. The folio is
+zeroed rather than dropped so that a concurrent extending write can't lose
+data.
+
+This overlaps with ``pagecache_isize_extended()`` but can't reuse it: that
+helper is keyed on a sub-page block size and is a no-op when the block size is
+``>= PAGE_SIZE`` (as on network filesystems), and it doesn't wait for
+writeback. As both address the same problem, a change to one should probably
+be reflected in the other to keep them in sync.
+
High-Level VM API
==================
diff --git a/MAINTAINERS b/MAINTAINERS
index ec71820704fb..c548092c6a78 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5185,6 +5185,7 @@ L: netdev@vger.kernel.org
L: openwrt-devel@lists.openwrt.org (subscribers-only)
S: Supported
F: Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml
+F: Documentation/networking/dsa/b53.rst
F: drivers/net/dsa/b53/*
F: drivers/net/dsa/bcm_sf2*
F: include/linux/dsa/brcm.h
@@ -17273,31 +17274,6 @@ F: include/trace/events/ksm.h
F: mm/ksm.c
F: mm/mm_slot.h
-MEMORY MANAGEMENT - MEMORY POLICY AND MIGRATION
-M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@kernel.org>
-R: Zi Yan <ziy@nvidia.com>
-R: Matthew Brost <matthew.brost@intel.com>
-R: Joshua Hahn <joshua.hahnjy@gmail.com>
-R: Rakie Kim <rakie.kim@sk.com>
-R: Byungchul Park <byungchul@sk.com>
-R: Gregory Price <gourry@gourry.net>
-R: Ying Huang <ying.huang@linux.alibaba.com>
-R: Alistair Popple <apopple@nvidia.com>
-L: linux-mm@kvack.org
-S: Maintained
-W: http://www.linux-mm.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
-F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy
-F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave
-F: include/linux/mempolicy.h
-F: include/uapi/linux/mempolicy.h
-F: include/linux/migrate.h
-F: include/linux/migrate_mode.h
-F: mm/mempolicy.c
-F: mm/migrate.c
-F: mm/migrate_device.c
-
MEMORY MANAGEMENT - MGLRU (MULTI-GEN LRU)
M: Andrew Morton <akpm@linux-foundation.org>
R: Kairui Song <kasong@tencent.com>
@@ -17320,6 +17296,25 @@ F: include/linux/mmzone.h
F: mm/vmscan.c
F: mm/workingset.c
+MEMORY MANAGEMENT - MIGRATION
+M: Andrew Morton <akpm@linux-foundation.org>
+M: David Hildenbrand <david@kernel.org>
+R: Zi Yan <ziy@nvidia.com>
+R: Matthew Brost <matthew.brost@intel.com>
+R: Joshua Hahn <joshua.hahnjy@gmail.com>
+R: Byungchul Park <byungchul@sk.com>
+R: Gregory Price <gourry@gourry.net>
+R: Ying Huang <ying.huang@linux.alibaba.com>
+R: Alistair Popple <apopple@nvidia.com>
+L: linux-mm@kvack.org
+S: Maintained
+W: http://www.linux-mm.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F: include/linux/migrate.h
+F: include/linux/migrate_mode.h
+F: mm/migrate.c
+F: mm/migrate_device.c
+
MEMORY MANAGEMENT - MISC
M: Andrew Morton <akpm@linux-foundation.org>
M: David Hildenbrand <david@kernel.org>
@@ -17335,7 +17330,6 @@ W: http://www.linux-mm.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
F: Documentation/ABI/testing/sysfs-kernel-mm
F: Documentation/ABI/testing/sysfs-kernel-mm-cma
-F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers
F: Documentation/ABI/testing/sysfs-kernel-mm-numa
F: Documentation/admin-guide/mm/
F: Documentation/mm/
@@ -17343,7 +17337,6 @@ F: drivers/char/mem.c
F: include/linux/cma.h
F: include/linux/dmapool.h
F: include/linux/ioremap.h
-F: include/linux/memory-tiers.h
F: include/linux/page_idle.h
F: mm/backing-dev.c
F: mm/cma.c
@@ -17355,7 +17348,6 @@ F: mm/early_ioremap.c
F: mm/fadvise.c
F: mm/ioremap.c
F: mm/mapping_dirty_helpers.c
-F: mm/memory-tiers.c
F: mm/page_idle.c
F: mm/pgalloc-track.h
F: mm/process_vm_access.c
@@ -17372,6 +17364,27 @@ F: mm/numa.c
F: mm/numa_emulation.c
F: mm/numa_memblks.c
+MEMORY MANAGEMENT - NUMA PLACEMENT
+M: Andrew Morton <akpm@linux-foundation.org>
+M: David Hildenbrand <david@kernel.org>
+M: Gregory Price <gourry@gourry.net>
+R: Zi Yan <ziy@nvidia.com>
+R: Joshua Hahn <joshua.hahnjy@gmail.com>
+R: Rakie Kim <rakie.kim@sk.com>
+R: Ying Huang <ying.huang@linux.alibaba.com>
+L: linux-mm@kvack.org
+S: Maintained
+W: http://www.linux-mm.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
+F: Documentation/ABI/testing/sysfs-kernel-mm-memory-tiers
+F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy
+F: Documentation/ABI/testing/sysfs-kernel-mm-mempolicy-weighted-interleave
+F: include/linux/memory-tiers.h
+F: include/linux/mempolicy.h
+F: include/uapi/linux/mempolicy.h
+F: mm/memory-tiers.c
+F: mm/mempolicy.c
+
MEMORY MANAGEMENT - OOM KILLER
M: Michal Hocko <mhocko@suse.com>
R: David Rientjes <rientjes@google.com>
@@ -18949,7 +18962,7 @@ F: net/core/drop_monitor.c
NETWORKING DRIVERS
M: Andrew Lunn <andrew+netdev@lunn.ch>
M: "David S. Miller" <davem@davemloft.net>
-M: Eric Dumazet <edumazet@google.com>
+M: Eric Dumazet <edumazet@kernel.org>
M: Jakub Kicinski <kuba@kernel.org>
M: Paolo Abeni <pabeni@redhat.com>
L: netdev@vger.kernel.org
@@ -19066,7 +19079,7 @@ K: struct\s+phy_port|phy_port_
NETWORKING [GENERAL]
M: "David S. Miller" <davem@davemloft.net>
-M: Eric Dumazet <edumazet@google.com>
+M: Eric Dumazet <edumazet@kernel.org>
M: Jakub Kicinski <kuba@kernel.org>
M: Paolo Abeni <pabeni@redhat.com>
R: Simon Horman <horms@kernel.org>
@@ -19292,7 +19305,7 @@ F: net/ipv6/seg6*
F: tools/testing/selftests/net/srv6*
NETWORKING [TCP]
-M: Eric Dumazet <edumazet@google.com>
+M: Eric Dumazet <edumazet@kernel.org>
M: Neal Cardwell <ncardwell@google.com>
R: Kuniyuki Iwashima <kuniyu@google.com>
L: netdev@vger.kernel.org
@@ -19325,7 +19338,7 @@ F: net/tls/
F: tools/testing/selftests/net/tls.c
NETWORKING [SOCKETS]
-M: Eric Dumazet <edumazet@google.com>
+M: Eric Dumazet <edumazet@kernel.org>
M: Kuniyuki Iwashima <kuniyu@google.com>
M: Paolo Abeni <pabeni@redhat.com>
M: Willem de Bruijn <willemb@google.com>
@@ -20621,6 +20634,7 @@ ORACLE CLUSTER FILESYSTEM 2 (OCFS2)
M: Mark Fasheh <mark@fasheh.com>
M: Joel Becker <jlbec@evilplan.org>
M: Joseph Qi <joseph.qi@linux.alibaba.com>
+R: Heming Zhao <heming.zhao@suse.com>
L: ocfs2-devel@lists.linux.dev
S: Supported
W: http://ocfs2.wiki.kernel.org
@@ -24539,6 +24553,11 @@ R: Marc Murphy <marc.murphy@sancloud.com>
S: Supported
F: arch/arm/boot/dts/ti/omap/am335x-sancloud*
+SB1250-DUART BROADCOM SOC SERIAL DRIVER
+M: "Maciej W. Rozycki" <macro@orcam.me.uk>
+S: Maintained
+F: drivers/tty/serial/sb1250-duart.c
+
SBOM
M: Luis Augenstein <luis.augenstein@tngtech.com>
M: Maximilian Huber <maximilian.huber@tngtech.com>
@@ -25249,7 +25268,7 @@ R: David Rientjes <rientjes@google.com>
R: Roman Gushchin <roman.gushchin@linux.dev>
L: linux-mm@kvack.org
S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mm/slab.git
F: Documentation/admin-guide/mm/slab.rst
F: Documentation/mm/slab.rst
F: include/linux/mempool.h
diff --git a/Makefile b/Makefile
index 6b8b812f23b9..7c855e6fe544 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 7
PATCHLEVEL = 3
SUBLEVEL = 0
-EXTRAVERSION = -rc5
+EXTRAVERSION = -rc6
NAME = Baby Opossum Posse
# *DOCUMENTATION*
diff --git a/arch/arc/Kconfig b/arch/arc/Kconfig
index 2ed7186c81c5..80e61175bf52 100644
--- a/arch/arc/Kconfig
+++ b/arch/arc/Kconfig
@@ -331,13 +331,9 @@ config ARC_FPU_SAVE_RESTORE
for floating point exceptions and rounding modes. These are
preserved across task context switch when enabled.
-config ARC_CANT_LLSC
- def_bool n
-
config ARC_HAS_LLSC
bool "Insn: LLOCK/SCOND (efficient atomic ops)"
default y
- depends on !ARC_CANT_LLSC
if ISA_ARCV2
diff --git a/arch/arc/include/asm/atomic64-arcv2.h b/arch/arc/include/asm/atomic64-arcv2.h
index 73080a664369..1ca4a1047fb3 100644
--- a/arch/arc/include/asm/atomic64-arcv2.h
+++ b/arch/arc/include/asm/atomic64-arcv2.h
@@ -156,6 +156,12 @@ static inline u64 __arch_cmpxchg64_relaxed(volatile void *ptr, u64 old, u64 new)
}
#define arch_cmpxchg64_relaxed __arch_cmpxchg64_relaxed
+static inline s64 arch_atomic64_cmpxchg_relaxed(atomic64_t *v, s64 old, s64 new)
+{
+ return arch_cmpxchg64_relaxed(&v->counter, old, new);
+}
+#define arch_atomic64_cmpxchg_relaxed arch_atomic64_cmpxchg_relaxed
+
static inline s64 arch_atomic64_xchg(atomic64_t *ptr, s64 new)
{
s64 prev;
diff --git a/arch/arc/include/asm/cmpxchg.h b/arch/arc/include/asm/cmpxchg.h
index 76f43db0890f..66b3743c9400 100644
--- a/arch/arc/include/asm/cmpxchg.h
+++ b/arch/arc/include/asm/cmpxchg.h
@@ -46,7 +46,7 @@
__typeof__(*(ptr)) _n_ = (new); \
__typeof__(*(ptr)) _prev_; \
\
- switch(sizeof((_p_))) { \
+ switch (sizeof(*(_p_))) { \
case 1: \
_prev_ = (__typeof__(*(ptr)))cmpxchg_emu_u8((volatile u8 *__force)_p_, (uintptr_t)_o_, (uintptr_t)_n_); \
break; \
@@ -102,7 +102,7 @@
__typeof__(ptr) _p_ = (ptr); \
__typeof__(*(ptr)) _val_ = (val); \
\
- switch(sizeof(*(_p_))) { \
+ switch (sizeof(*(_p_))) { \
case 4: \
_val_ = __arch_xchg(_p_, _val_); \
break; \
diff --git a/arch/arc/kernel/setup.c b/arch/arc/kernel/setup.c
index 7b6a9beba9db..8bebd0e6fc2f 100644
--- a/arch/arc/kernel/setup.c
+++ b/arch/arc/kernel/setup.c
@@ -593,6 +593,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
cpu_id);
} else {
freq = clk_get_rate(cpu_clk);
+ clk_put(cpu_clk);
}
if (freq)
seq_printf(m, "CPU speed\t: %lu.%02lu Mhz\n",
diff --git a/arch/arc/kernel/smp.c b/arch/arc/kernel/smp.c
index 2d99dffed0ce..ee210a94ce45 100644
--- a/arch/arc/kernel/smp.c
+++ b/arch/arc/kernel/smp.c
@@ -12,7 +12,6 @@
#include <linux/spinlock.h>
#include <linux/sched/mm.h>
#include <linux/interrupt.h>
-#include <linux/profile.h>
#include <linux/mm.h>
#include <linux/cpu.h>
#include <linux/irq.h>
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b5a51b0ef944..cab741a695f5 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1040,9 +1040,17 @@ config ARM64_ERRATUM_1902691
If unsure, say Y.
+config ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT
+ bool
+ # Targeting errata affecting AMEVCNTR01, the AMU constant counter.
+ # On affected CPUs the counter may not increment at the expected rate,
+ # making it unreliable. As a result, AMU users should treat it as
+ # unavailable rather than relying on misleading counter values.
+
config ARM64_ERRATUM_2457168
bool "Cortex-A510: 2457168: workaround for AMEVCNTR01 incrementing incorrectly"
depends on ARM64_AMU_EXTN
+ select ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT
default y
help
This option adds the workaround for ARM Cortex-A510 erratum 2457168.
@@ -1073,6 +1081,28 @@ config ARM64_ERRATUM_2645198
If unsure, say Y.
+config ARM64_ERRATUM_3821522
+ bool "Cortex-A725: 3821522: workaround for possible CNT_CYCLES increment error due to WFE/WFI"
+ depends on ARM64_AMU_EXTN
+ select ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT
+ default y
+ help
+ This option adds the workaround for ARM Cortex-A725 erratum 3821522.
+
+ On affected A725 cores, the CNT_CYCLES event may incur a significant
+ increment error when entering and subsequently exiting WFx.
+ As a result, the CNT_CYCLES may diverge from the system counter
+ frequency at which it is expected to increment.
+ This renders the AMU counter AMEVCNTR01, that implements CNT_CYCLES,
+ being unreliable and unsuitable for use.
+
+ Since there is no hardware workaround, reads of the affected CNT_CYCLES
+ counter return 0 in the relevant paths. This causes users of the counter
+ to treat it as unavailable and is functionally equivalent to firmware
+ disabling the affected counter.
+
+ If unsure, say Y.
+
config ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD
bool
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 8ec47d89b45b..e0c09402540c 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -384,6 +384,20 @@ static const struct arm64_cpu_capabilities arm64_repeat_tlbi_list[] = {
};
#endif
+#ifdef CONFIG_ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT
+static const struct midr_range workaround_amu_constcnt_list[] = {
+#ifdef CONFIG_ARM64_ERRATUM_2457168
+ /* Cortex-A510 r0p0-r1p1 */
+ MIDR_RANGE(MIDR_CORTEX_A510, 0, 0, 1, 1),
+#endif
+#ifdef CONFIG_ARM64_ERRATUM_3821522
+ /* Cortex-A725 r0p0 - r0p2 */
+ MIDR_RANGE(MIDR_CORTEX_A725, 0, 0, 0, 2),
+#endif
+ {}
+};
+#endif /* CONFIG_ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT */
+
#ifdef CONFIG_CAVIUM_ERRATUM_23154
static const struct midr_range cavium_erratum_23154_cpus[] = {
MIDR_ALL_VERSIONS(MIDR_THUNDERX),
@@ -919,14 +933,12 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
ERRATA_MIDR_REV_RANGE(MIDR_CORTEX_A510, 0, 0, 2)
},
#endif
-#ifdef CONFIG_ARM64_ERRATUM_2457168
+#ifdef CONFIG_ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT
{
- .desc = "ARM erratum 2457168",
- .capability = ARM64_WORKAROUND_2457168,
+ .desc = "Broken AMU AMEVCNTR01 (const counter)",
+ .capability = ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT,
.type = ARM64_CPUCAP_WEAK_LOCAL_CPU_FEATURE,
-
- /* Cortex-A510 r0p0-r1p1 */
- CAP_MIDR_RANGE(MIDR_CORTEX_A510, 0, 0, 1, 1)
+ CAP_MIDR_RANGE_LIST(workaround_amu_constcnt_list)
},
#endif
#ifdef CONFIG_ARM64_ERRATUM_2038923
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 32102c3912fa..3ae3ad1a1cdf 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -2089,7 +2089,7 @@ static void cpu_amu_enable(struct arm64_cpu_capabilities const *cap)
cpumask_set_cpu(smp_processor_id(), &amu_cpus);
/* 0 reference values signal broken/disabled counters */
- if (!this_cpu_has_cap(ARM64_WORKAROUND_2457168))
+ if (!this_cpu_has_cap(ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT))
update_freq_counters_refs();
}
}
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 38e6fa204c17..6a06a4e352b1 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -433,6 +433,7 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
unsigned int vl, vq;
struct user_fpsimd_state fpsimd;
u16 user_vl, flags;
+ bool fpsimd_only;
bool sm;
if (user->sve_size < sizeof(*user->sve))
@@ -443,19 +444,33 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
if (err)
return err;
+ fpsimd_only = (user->sve_size == sizeof(*user->sve));
sm = flags & SVE_SIG_FLAG_SM;
+
if (sm) {
if (!system_supports_sme())
return -EINVAL;
+ /*
+ * Streaming SVE state is always preserved with an SVE payload.
+ * Only accept streaming state which has an SVE payload.
+ */
+ if (fpsimd_only)
+ return -EINVAL;
+
vl = task_get_sme_vl(current);
} else {
/*
- * A SME only system use SVE for streaming mode so can
- * have a SVE formatted context with a zero VL and no
- * payload data.
+ * Non-streaming SVE state may be preserved without an SVE
+ * payload, in which case all state is saved in the FPSIMD
+ * context.
+ *
+ * On SME-only systems, non-streaming (FPSIMD-only) state is
+ * always preserved without an SVE payload, and with VL==0. On
+ * such systems, only accept non-streaming state without an SVE
+ * payload.
*/
- if (!system_supports_sve() && !system_supports_sme())
+ if (!system_supports_sve() && !fpsimd_only)
return -EINVAL;
vl = task_get_sve_vl(current);
@@ -464,16 +479,7 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
if (user_vl != vl)
return -EINVAL;
- /*
- * Non-streaming SVE state may be preserved without an SVE payload, in
- * which case the SVE context only has a header with VL==0, and all
- * state can be restored from the FPSIMD context.
- *
- * Streaming SVE state is always preserved with an SVE payload. For
- * consistency and robustness, reject restoring streaming SVE state
- * without an SVE payload.
- */
- if (!sm && user->sve_size == sizeof(*user->sve))
+ if (fpsimd_only)
return restore_fpsimd_context(user);
vq = sve_vq_from_vl(vl);
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index d28438f8b83f..806373aac3db 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -186,7 +186,6 @@ int arch_freq_get_on_cpu(int cpu)
struct amu_cntr_sample *amu_sample;
unsigned int start_cpu = cpu;
unsigned long last_update;
- unsigned int freq = 0;
u64 scale;
if (!amu_fie_cpu_supported(cpu) || !arch_scale_freq_ref(cpu))
@@ -245,9 +244,8 @@ int arch_freq_get_on_cpu(int cpu)
* (see amu_scale_freq_tick for details)
*/
scale = arch_scale_freq_capacity(cpu);
- freq = scale * arch_scale_freq_ref(cpu);
- freq >>= SCHED_CAPACITY_SHIFT;
- return freq;
+
+ return (scale * arch_scale_freq_ref(cpu)) >> SCHED_CAPACITY_SHIFT;
}
static void amu_fie_setup(const struct cpumask *cpus)
@@ -397,12 +395,13 @@ static void cpu_read_corecnt(void *val)
static void cpu_read_constcnt(void *val)
{
/*
- * Return 0 if the current CPU is affected by erratum 2457168. A value
- * of 0 is also returned if the current CPU does not support AMUs or if
- * the counter is disabled. A return value of 0 at counter read is
- * properly handled as an error case by the users of the counter.
+ * Return 0 if the current CPU is affected by a HW erratum.
+ * A value of 0 is also returned if the current CPU does not
+ * support AMUs or if the counter is disabled. A return
+ * value of 0 at counter read is properly handled as an error
+ * case by the users of the counter.
*/
- *(u64 *)val = this_cpu_has_cap(ARM64_WORKAROUND_2457168) ?
+ *(u64 *)val = this_cpu_has_cap(ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT) ?
0UL : read_constcnt();
}
@@ -463,8 +462,9 @@ static void amu_read_core_const_ctrs(void *val)
/*
* cpu_read_constcnt() incurs slight latency due to the
- * ARM64_WORKAROUND_2457168 check. Read it first to minimize
- * the sampling skew between the const and core counters.
+ * ARM64_WORKAROUND_BROKEN_AMU_CONSTCNT check.
+ * Read it first to minimize the sampling skew between the const
+ * and core counters.
*/
cpu_read_constcnt(&ctrs->constcnt);
cpu_read_corecnt(&ctrs->corecnt);
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
index 7dec05dd33b7..f6619e1cb2ce 100644
--- a/arch/arm64/kernel/vdso/Makefile
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -41,7 +41,7 @@ CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \
$(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \
-Wmissing-prototypes -Wmissing-declarations
-CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables
+CC_FLAGS_ADD_VDSO := -O2 -mcmodel=tiny -fasynchronous-unwind-tables $(CONFIG_CC_OPT_INLINE_MEMSET)
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_REMOVE_VDSO)
CFLAGS_REMOVE_vgetrandom.o = $(CC_FLAGS_REMOVE_VDSO)
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 3806ff0920fe..f59d89d04eca 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -2386,9 +2386,6 @@ int __init populate_nv_trap_config(void)
print_nv_trap_error(fgt, "FGT bit is reserved", ret);
}
- if (!cpus_have_final_cap(ARM64_HAS_FGT))
- continue;
-
prev = xa_store(&sr_forward_xa, enc,
xa_mk_value(tc.val), GFP_KERNEL);
diff --git a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
index c904647d2f76..5c050f21066a 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/pkvm.h
@@ -13,6 +13,15 @@
#include <nvhe/spinlock.h>
/*
+ * HCR_EL2 bits EL2 takes from the host on each entry, per VM type. The rest
+ * are EL2's own and nothing the host sets there reaches the guest.
+ */
+#define PKVM_HCR_EL2_HOST_PVM (HCR_EL2_TWI | HCR_EL2_TWE | HCR_EL2_VSE)
+#define PKVM_HCR_EL2_HOST_NPVM (PKVM_HCR_EL2_HOST_PVM | HCR_EL2_VI | HCR_EL2_VF | \
+ HCR_EL2_TVM | HCR_EL2_TID2 | HCR_EL2_TID4 | \
+ HCR_EL2_TID5 | HCR_EL2_TTLBOS)
+
+/*
* Holds the relevant data for maintaining the vcpu state completely at hyp.
*/
struct pkvm_hyp_vcpu {
diff --git a/arch/arm64/kvm/hyp/nvhe/hyp-main.c b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
index 9a3b92e626ad..ac64a036b0a9 100644
--- a/arch/arm64/kvm/hyp/nvhe/hyp-main.c
+++ b/arch/arm64/kvm/hyp/nvhe/hyp-main.c
@@ -216,6 +216,7 @@ static void sync_debug_state(struct pkvm_hyp_vcpu *hyp_vcpu)
static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
{
struct kvm_vcpu *host_vcpu = hyp_vcpu->host_vcpu;
+ u64 host_hcr_mask = PKVM_HCR_EL2_HOST_PVM;
fpsimd_sve_flush();
flush_debug_state(hyp_vcpu);
@@ -228,6 +229,7 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
if (!pkvm_hyp_vcpu_is_protected(hyp_vcpu)) {
if (vcpu_get_flag(host_vcpu, PKVM_HOST_STATE_DIRTY))
flush_hyp_vcpu_state(hyp_vcpu);
+ host_hcr_mask = PKVM_HCR_EL2_HOST_NPVM;
} else {
hyp_vcpu->vcpu.arch.ctxt = host_vcpu->arch.ctxt;
}
@@ -241,9 +243,8 @@ static void flush_hyp_vcpu(struct pkvm_hyp_vcpu *hyp_vcpu)
* trap-control bit, so it must flow to the hyp vCPU alongside TWI/TWE
* for the vSError to be delivered. sync_hyp_vcpu() reflects it back.
*/
- hyp_vcpu->vcpu.arch.hcr_el2 &= ~(HCR_TWI | HCR_TWE | HCR_VSE);
- hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) &
- (HCR_TWI | HCR_TWE | HCR_VSE);
+ hyp_vcpu->vcpu.arch.hcr_el2 &= ~host_hcr_mask;
+ hyp_vcpu->vcpu.arch.hcr_el2 |= READ_ONCE(host_vcpu->arch.hcr_el2) & host_hcr_mask;
hyp_vcpu->vcpu.arch.iflags = host_vcpu->arch.iflags;
diff --git a/arch/arm64/kvm/hyp/nvhe/pkvm.c b/arch/arm64/kvm/hyp/nvhe/pkvm.c
index bb3e0dc0676e..6290c4b62659 100644
--- a/arch/arm64/kvm/hyp/nvhe/pkvm.c
+++ b/arch/arm64/kvm/hyp/nvhe/pkvm.c
@@ -30,6 +30,7 @@ unsigned int kvm_host_sve_max_vl;
*/
static DEFINE_PER_CPU(struct pkvm_hyp_vcpu *, loaded_hyp_vcpu);
+/* The PKVM_HCR_EL2_HOST_{PVM,NPVM} bits of this value come from the host on each entry. */
static void pkvm_vcpu_reset_hcr(struct kvm_vcpu *vcpu)
{
vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS;
@@ -47,18 +48,17 @@ static void pkvm_vcpu_reset_hcr(struct kvm_vcpu *vcpu)
if (cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
vcpu->arch.hcr_el2 |= HCR_FWB;
- if (cpus_have_final_cap(ARM64_HAS_EVT) &&
- !cpus_have_final_cap(ARM64_MISMATCHED_CACHE_TYPE) &&
- kvm_read_vm_id_reg(vcpu->kvm, SYS_CTR_EL0) == read_cpuid(CTR_EL0))
- vcpu->arch.hcr_el2 |= HCR_TID4;
- else
- vcpu->arch.hcr_el2 |= HCR_TID2;
+ /*
+ * Without AArch32 EL1, leave RW set and let the entry fail with an
+ * illegal exception return: the *32_EL2 registers EL2 would otherwise
+ * switch are UNDEFINED there.
+ */
+ if (vcpu_has_feature(vcpu, KVM_ARM_VCPU_EL1_32BIT) &&
+ cpus_have_final_cap(ARM64_HAS_32BIT_EL1))
+ vcpu->arch.hcr_el2 &= ~HCR_EL2_RW;
if (vcpu_has_ptrauth(vcpu))
vcpu->arch.hcr_el2 |= (HCR_API | HCR_APK);
-
- if (kvm_has_mte(vcpu->kvm))
- vcpu->arch.hcr_el2 |= HCR_ATA;
}
static void pvm_init_traps_hcr(struct kvm_vcpu *vcpu)
@@ -76,6 +76,13 @@ static void pvm_init_traps_hcr(struct kvm_vcpu *vcpu)
*/
val |= HCR_TACR | HCR_TIDCP | HCR_TID3 | HCR_TID1;
+ if (cpus_have_final_cap(ARM64_HAS_EVT) &&
+ !cpus_have_final_cap(ARM64_MISMATCHED_CACHE_TYPE) &&
+ kvm_read_vm_id_reg(kvm, SYS_CTR_EL0) == read_cpuid(CTR_EL0))
+ val |= HCR_EL2_TID4;
+ else
+ val |= HCR_EL2_TID2;
+
if (!kvm_has_feat(kvm, ID_AA64PFR0_EL1, RAS, IMP)) {
val |= HCR_TERR | HCR_TEA;
val &= ~(HCR_FIEN);
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 2d44cd6a5aed..bf6d6526639f 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1468,32 +1468,11 @@ transparent_hugepage_adjust(struct kvm *kvm, struct kvm_memory_slot *memslot,
return PAGE_SIZE;
}
-static int get_vma_page_shift(struct vm_area_struct *vma, unsigned long hva)
+static int get_vma_page_shift(struct vm_area_struct *vma)
{
- unsigned long pa;
-
- if (is_vm_hugetlb_page(vma) && !(vma->vm_flags & VM_PFNMAP))
+ if (is_vm_hugetlb_page(vma))
return huge_page_shift(hstate_vma(vma));
- if (!(vma->vm_flags & VM_PFNMAP))
- return PAGE_SHIFT;
-
- VM_BUG_ON(is_vm_hugetlb_page(vma));
-
- pa = (vma->vm_pgoff << PAGE_SHIFT) + (hva - vma->vm_start);
-
-#ifndef __PAGETABLE_PMD_FOLDED
- if ((hva & (PUD_SIZE - 1)) == (pa & (PUD_SIZE - 1)) &&
- ALIGN_DOWN(hva, PUD_SIZE) >= vma->vm_start &&
- ALIGN(hva, PUD_SIZE) <= vma->vm_end)
- return PUD_SHIFT;
-#endif
-
- if ((hva & (PMD_SIZE - 1)) == (pa & (PMD_SIZE - 1)) &&
- ALIGN_DOWN(hva, PMD_SIZE) >= vma->vm_start &&
- ALIGN(hva, PMD_SIZE) <= vma->vm_end)
- return PMD_SHIFT;
-
return PAGE_SHIFT;
}
@@ -1794,7 +1773,7 @@ static short kvm_s2_resolve_vma_size(const struct kvm_s2_fault_desc *s2fd,
vma_shift = PAGE_SHIFT;
} else {
s2vi->max_map_size = PUD_SIZE;
- vma_shift = get_vma_page_shift(vma, s2fd->hva);
+ vma_shift = get_vma_page_shift(vma);
}
switch (vma_shift) {
@@ -1952,16 +1931,6 @@ static int kvm_s2_fault_pin_pfn(const struct kvm_s2_fault_desc *s2fd,
return -EFAULT;
}
} else {
- /*
- * If the page was identified as device early by looking at
- * the VMA flags, vma_pagesize is already representing the
- * largest quantity we can map. If instead it was mapped
- * via __kvm_faultin_pfn(), vma_pagesize is set to PAGE_SIZE
- * and must not be upgraded.
- *
- * In both cases, we don't let transparent_hugepage_adjust()
- * change things at the last minute.
- */
s2vi->map_non_cacheable = true;
}
@@ -2051,10 +2020,10 @@ static int kvm_s2_fault_map(const struct kvm_s2_fault_desc *s2fd,
/*
* If we are not forced to use page mapping, check if we are
- * backed by a THP and thus use block mapping if possible.
+ * backed by a huge stage-1 mapping and thus use block mapping if
+ * possible.
*/
- if (mapping_size == PAGE_SIZE &&
- !(s2vi->max_map_size == PAGE_SIZE || s2vi->map_non_cacheable)) {
+ if (mapping_size == PAGE_SIZE && s2vi->max_map_size != PAGE_SIZE) {
if (perm_fault_granule > PAGE_SIZE) {
mapping_size = perm_fault_granule;
} else {
@@ -2135,10 +2104,6 @@ static int user_mem_abort(const struct kvm_s2_fault_desc *s2fd)
return ret;
}
- /*
- * Let's check if we will get back a huge page backed by hugetlbfs, or
- * get block mapping for device MMIO region.
- */
ret = kvm_s2_fault_pin_pfn(s2fd, &s2vi);
if (ret != 1)
return ret;
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 4012df6002ea..a58575df36e9 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -97,6 +97,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
/*
* - Acquiring the vCPU mutex for every *online* vCPU to prevent
* concurrent vCPU ioctls for vCPUs already visible to userspace.
+ * This also ensures KVM isn't in the middle of creating a vCPU,
+ * i.e. that there are no vCPUs that have been created but aren't
+ * yet fully online.
*/
ret = -EBUSY;
if (kvm_trylock_all_vcpus(kvm))
@@ -105,18 +108,11 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
/*
* - Taking the config_lock which protects VGIC data structures such
* as the per-vCPU arrays of private IRQs (SGIs, PPIs).
- */
- mutex_lock(&kvm->arch.config_lock);
-
- /*
- * - Bailing on the entire thing if a vCPU is in the middle of creation,
- * dropped the kvm->lock, but hasn't reached kvm_arch_vcpu_create().
*
* The whole combination of this guarantees that no vCPU can get into
* KVM with a VGIC configuration inconsistent with the VM's VGIC.
*/
- if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus))
- goto out_unlock;
+ mutex_lock(&kvm->arch.config_lock);
if (irqchip_in_kernel(kvm)) {
ret = -EEXIST;
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 0904ae850c35..c10a11373e10 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -319,12 +319,16 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
return ret;
}
-static int update_affinity(struct vgic_irq *irq, struct kvm_vcpu *vcpu)
+static int update_affinity(struct vgic_irq *irq,
+ struct kvm_vcpu *from_vcpu, struct kvm_vcpu *vcpu)
{
struct its_vlpi_map map;
int ret;
guard(raw_spinlock_irqsave)(&irq->irq_lock);
+ if (from_vcpu && irq->target_vcpu != from_vcpu)
+ return 0;
+
irq->target_vcpu = vcpu;
if (!irq->hw)
@@ -362,7 +366,7 @@ static void update_affinity_ite(struct kvm *kvm, struct its_ite *ite)
return;
vcpu = collection_to_vcpu(kvm, ite->collection);
- update_affinity(ite->irq, vcpu);
+ update_affinity(ite->irq, NULL, vcpu);
}
/*
@@ -856,7 +860,7 @@ static int vgic_its_cmd_handle_movi(struct kvm *kvm, struct vgic_its *its,
vgic_its_invalidate_cache(its);
- return update_affinity(ite->irq, vcpu);
+ return update_affinity(ite->irq, NULL, vcpu);
}
static bool __is_visible_gfn_locked(struct vgic_its *its, gpa_t gpa)
@@ -1383,7 +1387,7 @@ static int vgic_its_cmd_handle_movall(struct kvm *kvm, struct vgic_its *its,
if (!irq)
continue;
- update_affinity(irq, vcpu2);
+ update_affinity(irq, vcpu1, vcpu2);
vgic_put_irq(kvm, irq);
}
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index 7182f63fc938..70cc53ba810a 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -122,6 +122,10 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
for (int lr = 0; lr < vgic_cpu->vgic_v2.used_lrs; lr++)
vgic_v2_fold_lr(vcpu, cpuif->vgic_lr[lr]);
+ cpuif->used_lrs = 0;
+ if (!irq)
+ return;
+
/* See the GICv3 equivalent for the EOIcount handling rationale */
list_for_each_entry_continue(irq, &vgic_cpu->ap_list_head, ap_list) {
u32 lr;
@@ -144,8 +148,6 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
vgic_v2_fold_lr(vcpu, lr);
eoicount--;
}
-
- cpuif->used_lrs = 0;
}
void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 726e20a1da6e..346bacb3198f 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -155,6 +155,10 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
for (int lr = 0; lr < cpuif->used_lrs; lr++)
vgic_v3_fold_lr(vcpu, cpuif->vgic_lr[lr]);
+ cpuif->used_lrs = 0;
+ if (!irq)
+ return;
+
/*
* EOIMode=0: use EOIcount to emulate deactivation. We are
* guaranteed to deactivate in reverse order of the activation, so
@@ -188,8 +192,6 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
vgic_v3_fold_lr(vcpu, lr);
eoicount--;
}
-
- cpuif->used_lrs = 0;
}
void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val)
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index b25303d9919f..5cf5a1ef86cd 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -853,6 +853,17 @@ retry:
goto retry;
}
+ /*
+ * Fix the last_lr_irq refcount which was obtained while
+ * populating the LRs. This can also result in the LPI being
+ * deleted.
+ */
+ irq = *host_data_ptr(last_lr_irq);
+ if (irq) {
+ deleted_lpis |= vgic_put_irq_norelease(vcpu->kvm, irq);
+ *host_data_ptr(last_lr_irq) = NULL;
+ }
+
raw_spin_unlock(&vgic_cpu->ap_list_lock);
if (unlikely(deleted_lpis))
@@ -866,9 +877,6 @@ static void vgic_fold_state(struct kvm_vcpu *vcpu)
return;
}
- if (!*host_data_ptr(last_lr_irq))
- return;
-
if (kvm_vgic_global_state.type == VGIC_V2)
vgic_v2_fold_lr_state(vcpu);
else
@@ -1021,11 +1029,14 @@ static void vgic_flush_lr_state(struct kvm_vcpu *vcpu)
scoped_guard(raw_spinlock, &irq->irq_lock) {
if (likely(vgic_target_oracle(irq) == vcpu)) {
vgic_populate_lr(vcpu, irq, count++);
- *host_data_ptr(last_lr_irq) = irq;
+ if (count == kvm_vgic_global_state.nr_lr) {
+ vgic_get_irq_ref(irq);
+ *host_data_ptr(last_lr_irq) = irq;
+ }
}
}
- if (count == kvm_vgic_global_state.nr_lr)
+ if (*host_data_ptr(last_lr_irq))
break;
}
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 79d90226fd5d..d4384131e10d 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -2295,7 +2295,7 @@ pte_t modify_prot_start_ptes(struct vm_area_struct *vma, unsigned long addr,
* in cases where cpu is affected with errata #2645198.
*/
if (pte_accessible(vma->vm_mm, pte) && pte_user_exec(pte))
- __flush_tlb_range(vma, addr, nr * PAGE_SIZE,
+ __flush_tlb_range(vma, addr, addr + nr * PAGE_SIZE,
PAGE_SIZE, 3, TLBF_NOWALKCACHE);
}
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index c5f55d6161fe..c1279fabce47 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -2418,10 +2418,11 @@ bool bpf_jit_supports_subprog_tailcalls(void)
return true;
}
-static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node,
- int bargs_off, int retval_off, int run_ctx_off,
- bool save_ret)
+static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_image *im,
+ struct bpf_tramp_node *node, int bargs_off,
+ int retval_off, int run_ctx_off, bool save_ret)
{
+ void *skip;
__le32 *branch;
u64 enter_prog;
u64 exit_prog;
@@ -2431,6 +2432,10 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node,
enter_prog = (u64)bpf_trampoline_enter(p);
exit_prog = (u64)bpf_trampoline_exit(p);
+ /* nop, patched to skip this prog when it is detached */
+ skip = ctx->ro_image + ctx->idx;
+ emit(A64_NOP, ctx);
+
if (node->cookie == 0) {
/* if cookie is zero, one instruction is enough to store it */
emit(A64_STR64I(A64_ZR, A64_SP, run_ctx_off + cookie_off), ctx);
@@ -2483,11 +2488,13 @@ static void invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *node,
emit(A64_ADD_I(1, A64_R(2), A64_SP, run_ctx_off), ctx);
emit_call(exit_prog, ctx);
+
+ bpf_tramp_image_add_skip(im, p, skip, ctx->ro_image + ctx->idx);
}
-static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn,
- int bargs_off, int retval_off, int run_ctx_off,
- __le32 **branches)
+static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_image *im,
+ struct bpf_tramp_nodes *tn, int bargs_off,
+ int retval_off, int run_ctx_off, __le32 **branches)
{
int i;
@@ -2496,7 +2503,7 @@ static void invoke_bpf_mod_ret(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn,
*/
emit(A64_STR64I(A64_ZR, A64_SP, retval_off), ctx);
for (i = 0; i < tn->nr_nodes; i++) {
- invoke_bpf_prog(ctx, tn->nodes[i], bargs_off, retval_off,
+ invoke_bpf_prog(ctx, im, tn->nodes[i], bargs_off, retval_off,
run_ctx_off, true);
/* if (*(u64 *)(sp + retval_off) != 0)
* goto do_fexit;
@@ -2882,7 +2889,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
store_func_meta(ctx, meta, func_meta_off);
cookie_bargs_off--;
}
- invoke_bpf_prog(ctx, fentry->nodes[i], bargs_off,
+ invoke_bpf_prog(ctx, im, fentry->nodes[i], bargs_off,
retval_off, run_ctx_off,
flags & BPF_TRAMP_F_RET_FENTRY_RET);
}
@@ -2893,7 +2900,7 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
if (!branches)
return -ENOMEM;
- invoke_bpf_mod_ret(ctx, fmod_ret, bargs_off, retval_off,
+ invoke_bpf_mod_ret(ctx, im, fmod_ret, bargs_off, retval_off,
run_ctx_off, branches);
}
@@ -2906,9 +2913,6 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
emit(A64_RET(A64_R(10)), ctx);
/* store return value */
emit(A64_STR64I(A64_R(0), A64_SP, retval_off), ctx);
- /* reserve a nop for bpf_tramp_image_put */
- im->ip_after_call = ctx->ro_image + ctx->idx;
- emit(A64_NOP, ctx);
}
/* update the branches saved in invoke_bpf_mod_ret with cbnz */
@@ -2930,12 +2934,11 @@ static int prepare_trampoline(struct jit_ctx *ctx, struct bpf_tramp_image *im,
store_func_meta(ctx, meta, func_meta_off);
cookie_bargs_off--;
}
- invoke_bpf_prog(ctx, fexit->nodes[i], bargs_off, retval_off,
+ invoke_bpf_prog(ctx, im, fexit->nodes[i], bargs_off, retval_off,
run_ctx_off, false);
}
if (flags & BPF_TRAMP_F_CALL_ORIG) {
- im->ip_epilogue = ctx->ro_image + ctx->idx;
/* for the first pass, assume the worst case */
if (!ctx->image)
ctx->idx += 4;
@@ -2994,7 +2997,7 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
.image = NULL,
.idx = 0,
};
- struct bpf_tramp_image im;
+ struct bpf_tramp_image im = {};
struct arg_aux aaux;
int ret;
@@ -3281,6 +3284,10 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
* longer reachable, since bpf_tramp_image_put() function already
* uses percpu_ref and task-based rcu to do the sync, no need to call
* the sync version here, see bpf_tramp_image_put() for details.
+ *
+ * 3. when a detached prog is patched out of a trampoline, a CPU that
+ * still executes the old nop calls the prog before it went through
+ * a quiescent state, and the prog is freed after grace periods.
*/
ret = aarch64_insn_patch_text_nosync(ip, new_insn);
out:
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 2775ba3359cf..58b6c084f9f2 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -105,7 +105,6 @@ WORKAROUND_1902691
WORKAROUND_2038923
WORKAROUND_2064142
WORKAROUND_2077057
-WORKAROUND_2457168
WORKAROUND_2645198
WORKAROUND_2658417
WORKAROUND_4193714
@@ -132,3 +131,4 @@ WORKAROUND_REPEAT_TLBI_SYNC
WORKAROUND_SPECULATIVE_AT
WORKAROUND_SPECULATIVE_SSBS
WORKAROUND_SPECULATIVE_UNPRIV_LOAD
+WORKAROUND_BROKEN_AMU_CONSTCNT
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index 4da278900938..e78eb582c300 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -1578,6 +1578,24 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len)
return ret ? ERR_PTR(-EINVAL) : dst;
}
+int arch_bpf_trampoline_skip(void *nop, void *target)
+{
+ u32 old_insn = INSN_NOP;
+ u32 new_insn = larch_insn_gen_b((unsigned long)nop, (unsigned long)target);
+ int ret;
+
+ if (memcmp(nop, &old_insn, LOONGARCH_INSN_SIZE))
+ return -EFAULT;
+
+ cpus_read_lock();
+ mutex_lock(&text_mutex);
+ ret = larch_insn_text_copy(nop, &new_insn, LOONGARCH_INSN_SIZE);
+ mutex_unlock(&text_mutex);
+ cpus_read_unlock();
+
+ return ret;
+}
+
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
enum bpf_text_poke_type new_t, void *old_addr,
void *new_addr)
@@ -1696,13 +1714,18 @@ static void restore_stk_args(struct jit_ctx *ctx, int nr_stk_args, int args_off,
}
}
-static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *n,
- int args_off, int retval_off, int run_ctx_off, bool save_ret)
+static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_image *im,
+ struct bpf_tramp_node *n, int args_off, int retval_off,
+ int run_ctx_off, bool save_ret)
{
int ret;
u32 *branch;
struct bpf_prog *p = n->link->prog;
int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie);
+ void *skip = ctx->ro_image + ctx->idx;
+
+ /* nop, patched to a b over this prog when it is detached */
+ emit_insn(ctx, nop);
if (n->cookie)
emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1,
@@ -1755,13 +1778,17 @@ static int invoke_bpf_prog(struct jit_ctx *ctx, struct bpf_tramp_node *n,
/* arg3: &run_ctx */
emit_insn(ctx, addid, LOONGARCH_GPR_A2, LOONGARCH_GPR_FP, -run_ctx_off);
ret = emit_call(ctx, (const u64)bpf_trampoline_exit(p));
+ if (ret)
+ return ret;
- return ret;
+ bpf_tramp_image_add_skip(im, p, skip, ctx->ro_image + ctx->idx);
+ return 0;
}
-static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn,
- int args_off, int retval_off, int run_ctx_off,
- int func_meta_off, bool save_ret, u64 func_meta, int cookie_off)
+static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_image *im,
+ struct bpf_tramp_nodes *tn, int args_off, int retval_off,
+ int run_ctx_off, int func_meta_off, bool save_ret,
+ u64 func_meta, int cookie_off)
{
int i, cur_cookie = (cookie_off - args_off) / 8;
@@ -1774,7 +1801,8 @@ static int invoke_bpf(struct jit_ctx *ctx, struct bpf_tramp_nodes *tn,
emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, meta);
cur_cookie--;
}
- err = invoke_bpf_prog(ctx, tn->nodes[i], args_off, retval_off, run_ctx_off, save_ret);
+ err = invoke_bpf_prog(ctx, im, tn->nodes[i], args_off, retval_off,
+ run_ctx_off, save_ret);
if (err)
return err;
}
@@ -2017,7 +2045,7 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
}
if (fentry->nr_nodes) {
- ret = invoke_bpf(ctx, fentry, args_off, retval_off, run_ctx_off, func_meta_off,
+ ret = invoke_bpf(ctx, im, fentry, args_off, retval_off, run_ctx_off, func_meta_off,
flags & BPF_TRAMP_F_RET_FENTRY_RET, func_meta, cookie_off);
if (ret)
return ret;
@@ -2029,7 +2057,7 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
emit_insn(ctx, std, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_FP, -retval_off);
for (i = 0; i < fmod_ret->nr_nodes; i++) {
- ret = invoke_bpf_prog(ctx, fmod_ret->nodes[i],
+ ret = invoke_bpf_prog(ctx, im, fmod_ret->nodes[i],
args_off, retval_off, run_ctx_off, true);
if (ret)
goto out;
@@ -2051,10 +2079,6 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
goto out;
emit_insn(ctx, std, LOONGARCH_GPR_A0, LOONGARCH_GPR_FP, -retval_off);
emit_insn(ctx, std, regmap[BPF_REG_0], LOONGARCH_GPR_FP, -(retval_off - 8));
- im->ip_after_call = ctx->ro_image + ctx->idx;
- /* Reserve space for the move_imm + jirl instruction */
- for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++)
- emit_insn(ctx, nop);
}
for (i = 0; ctx->image && i < fmod_ret->nr_nodes; i++) {
@@ -2068,14 +2092,13 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
emit_store_stack_imm64(ctx, LOONGARCH_GPR_T1, -func_meta_off, func_meta);
if (fexit->nr_nodes) {
- ret = invoke_bpf(ctx, fexit, args_off, retval_off, run_ctx_off,
+ ret = invoke_bpf(ctx, im, fexit, args_off, retval_off, run_ctx_off,
func_meta_off, false, func_meta, cookie_off);
if (ret)
goto out;
}
if (flags & BPF_TRAMP_F_CALL_ORIG) {
- im->ip_epilogue = ctx->ro_image + ctx->idx;
move_addr(ctx, LOONGARCH_GPR_A0, (const u64)im);
ret = emit_call(ctx, (const u64)__bpf_tramp_exit);
if (ret)
@@ -2177,11 +2200,8 @@ int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *func_addr)
{
int ret;
- struct jit_ctx ctx;
- struct bpf_tramp_image im;
-
- ctx.image = NULL;
- ctx.idx = 0;
+ struct jit_ctx ctx = {};
+ struct bpf_tramp_image im = {};
ret = __arch_prepare_bpf_trampoline(&ctx, &im, m, tnodes, func_addr, flags);
diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile
index 9c9181bb4071..0b84892d084b 100644
--- a/arch/loongarch/vdso/Makefile
+++ b/arch/loongarch/vdso/Makefile
@@ -16,6 +16,7 @@ ccflags-vdso := \
$(filter -m64,$(KBUILD_CFLAGS)) \
$(filter -march=%,$(KBUILD_CFLAGS)) \
$(filter -m%-float,$(KBUILD_CFLAGS)) \
+ $(CONFIG_CC_OPT_INLINE_MEMSET) \
$(CLANG_FLAGS) \
-D__VDSO__
diff --git a/arch/powerpc/kernel/vdso/Makefile b/arch/powerpc/kernel/vdso/Makefile
index 368759f81708..0d1a49529885 100644
--- a/arch/powerpc/kernel/vdso/Makefile
+++ b/arch/powerpc/kernel/vdso/Makefile
@@ -43,6 +43,7 @@ ccflags-y := -fno-common -fno-builtin -DBUILD_VDSO
ccflags-y += $(DISABLE_LATENT_ENTROPY_PLUGIN)
ccflags-y += $(call cc-option, -fno-stack-protector)
ccflags-y += -DDISABLE_BRANCH_PROFILING
+ccflags-y += $(CONFIG_CC_OPT_INLINE_MEMSET)
ccflags-y += -ffreestanding -fasynchronous-unwind-tables
ccflags-remove-y := $(CC_FLAGS_FTRACE)
ldflags-y := -Wl,--hash-style=both -nostdlib -shared -z noexecstack $(CLANG_FLAGS)
diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c
index dbac3573b2c8..de05d721edc4 100644
--- a/arch/powerpc/kvm/book3s_hv.c
+++ b/arch/powerpc/kvm/book3s_hv.c
@@ -3058,7 +3058,6 @@ static int kvmppc_core_vcpu_create_hv(struct kvm_vcpu *vcpu)
init_waitqueue_head(&vcpu->arch.cpu_run);
- mutex_lock(&kvm->lock);
vcore = NULL;
err = -EINVAL;
if (cpu_has_feature(CPU_FTR_ARCH_300)) {
@@ -3091,7 +3090,6 @@ static int kvmppc_core_vcpu_create_hv(struct kvm_vcpu *vcpu)
mutex_unlock(&kvm->arch.mmu_setup_lock);
}
}
- mutex_unlock(&kvm->lock);
if (!vcore)
return err;
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 7b07b43575f1..7a612688e7a2 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -602,14 +602,18 @@ int arch_protect_bpf_trampoline(void *image, unsigned int size)
}
static int invoke_bpf_prog(u32 *image, u32 *ro_image, struct codegen_context *ctx,
- struct bpf_tramp_node *n, int regs_off, int retval_off,
- int run_ctx_off, bool save_ret)
+ struct bpf_tramp_image *im, struct bpf_tramp_node *n,
+ int regs_off, int retval_off, int run_ctx_off, bool save_ret)
{
struct bpf_prog *p = n->link->prog;
ppc_inst_t branch_insn;
- u32 jmp_idx;
+ u32 jmp_idx, skip_idx;
int ret = 0;
+ /* nop, patched to skip this prog when it is detached */
+ skip_idx = ctx->idx;
+ EMIT(PPC_RAW_NOP());
+
/* Save cookie */
if (IS_ENABLED(CONFIG_PPC64)) {
PPC_LI64(_R3, n->cookie);
@@ -679,13 +683,17 @@ static int invoke_bpf_prog(u32 *image, u32 *ro_image, struct codegen_context *ct
EMIT(PPC_RAW_ADDI(_R5, _R1, run_ctx_off));
ret = bpf_jit_emit_func_call_rel(image, ro_image, ctx,
(unsigned long)bpf_trampoline_exit(p));
+ if (ret)
+ return ret;
- return ret;
+ if (ro_image) /* image is NULL for dummy pass */
+ bpf_tramp_image_add_skip(im, p, &ro_image[skip_idx], &ro_image[ctx->idx]);
+ return 0;
}
static int invoke_bpf_mod_ret(u32 *image, u32 *ro_image, struct codegen_context *ctx,
- struct bpf_tramp_nodes *tn, int regs_off, int retval_off,
- int run_ctx_off, u32 *branches)
+ struct bpf_tramp_image *im, struct bpf_tramp_nodes *tn,
+ int regs_off, int retval_off, int run_ctx_off, u32 *branches)
{
int i;
@@ -696,8 +704,8 @@ static int invoke_bpf_mod_ret(u32 *image, u32 *ro_image, struct codegen_context
EMIT(PPC_RAW_LI(_R3, 0));
EMIT(PPC_RAW_STL(_R3, _R1, retval_off));
for (i = 0; i < tn->nr_nodes; i++) {
- if (invoke_bpf_prog(image, ro_image, ctx, tn->nodes[i], regs_off, retval_off,
- run_ctx_off, true))
+ if (invoke_bpf_prog(image, ro_image, ctx, im, tn->nodes[i], regs_off,
+ retval_off, run_ctx_off, true))
return -EINVAL;
/*
@@ -1043,8 +1051,8 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
cookie_ctx_off--;
}
- if (invoke_bpf_prog(image, ro_image, ctx, fentry->nodes[i], regs_off, retval_off,
- run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET))
+ if (invoke_bpf_prog(image, ro_image, ctx, im, fentry->nodes[i], regs_off,
+ retval_off, run_ctx_off, flags & BPF_TRAMP_F_RET_FENTRY_RET))
return -EINVAL;
}
@@ -1053,7 +1061,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
if (!branches)
return -ENOMEM;
- if (invoke_bpf_mod_ret(image, ro_image, ctx, fmod_ret, regs_off, retval_off,
+ if (invoke_bpf_mod_ret(image, ro_image, ctx, im, fmod_ret, regs_off, retval_off,
run_ctx_off, branches)) {
ret = -EINVAL;
goto cleanup;
@@ -1090,11 +1098,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
/* Restore updated tail_call_cnt */
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
bpf_trampoline_restore_tail_call_cnt(image, ctx, bpf_frame_size, r4_off);
-
- /* Reserve space to patch branch instruction to skip fexit progs */
- if (ro_image) /* image is NULL for dummy pass */
- im->ip_after_call = &((u32 *)ro_image)[ctx->idx];
- EMIT(PPC_RAW_NOP());
}
/* Update branches saved in invoke_bpf_mod_ret with address of do_fexit */
@@ -1123,16 +1126,14 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
cookie_ctx_off--;
}
- if (invoke_bpf_prog(image, ro_image, ctx, fexit->nodes[i], regs_off, retval_off,
- run_ctx_off, false)) {
+ if (invoke_bpf_prog(image, ro_image, ctx, im, fexit->nodes[i], regs_off,
+ retval_off, run_ctx_off, false)) {
ret = -EINVAL;
goto cleanup;
}
}
if (flags & BPF_TRAMP_F_CALL_ORIG) {
- if (ro_image) /* image is NULL for dummy pass */
- im->ip_epilogue = &((u32 *)ro_image)[ctx->idx];
PPC_LI_ADDR(_R3, im);
ret = bpf_jit_emit_func_call_rel(image, ro_image, ctx,
(unsigned long)__bpf_tramp_exit);
@@ -1192,7 +1193,7 @@ cleanup:
int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *func_addr)
{
- struct bpf_tramp_image im;
+ struct bpf_tramp_image im = {};
int ret;
ret = __arch_prepare_bpf_trampoline(&im, NULL, NULL, NULL, m, flags, tnodes, func_addr);
@@ -1320,7 +1321,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
/*
* If we are not poking at bpf prog entry, then we are simply patching in/out
- * an unconditional branch instruction at im->ip_after_call
+ * an unconditional branch instruction in a trampoline image
*/
if (offset) {
if (old_t == BPF_MOD_CALL || new_t == BPF_MOD_CALL) {
diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c
index f401181d8669..a9f8464997a2 100644
--- a/arch/powerpc/perf/imc-pmu.c
+++ b/arch/powerpc/perf/imc-pmu.c
@@ -1275,6 +1275,8 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem,
struct perf_event_header *header,
struct perf_event *event)
{
+ u16 misc = 0;
+
/* Sanity checks for a valid record */
if (be64_to_cpu(READ_ONCE(mem->tb1)) > *prev_tb)
*prev_tb = be64_to_cpu(READ_ONCE(mem->tb1));
@@ -1289,23 +1291,19 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem,
data->ip = be64_to_cpu(READ_ONCE(mem->ip));
data->period = event->hw.last_period;
- header->type = PERF_RECORD_SAMPLE;
- header->size = sizeof(*header) + event->header_size;
- header->misc = 0;
-
if (cpu_has_feature(CPU_FTR_ARCH_31)) {
switch (IMC_TRACE_RECORD_VAL_HVPR(be64_to_cpu(READ_ONCE(mem->val)))) {
case 0:/* when MSR HV and PR not set in the trace-record */
- header->misc |= PERF_RECORD_MISC_GUEST_KERNEL;
+ misc |= PERF_RECORD_MISC_GUEST_KERNEL;
break;
case 1: /* MSR HV is 0 and PR is 1 */
- header->misc |= PERF_RECORD_MISC_GUEST_USER;
+ misc |= PERF_RECORD_MISC_GUEST_USER;
break;
case 2: /* MSR HV is 1 and PR is 0 */
- header->misc |= PERF_RECORD_MISC_KERNEL;
+ misc |= PERF_RECORD_MISC_KERNEL;
break;
case 3: /* MSR HV is 1 and PR is 1 */
- header->misc |= PERF_RECORD_MISC_USER;
+ misc |= PERF_RECORD_MISC_USER;
break;
default:
pr_info("IMC: Unable to set the flag based on MSR bits\n");
@@ -1313,11 +1311,14 @@ static int trace_imc_prepare_sample(struct trace_imc_data *mem,
}
} else {
if (is_kernel_addr(data->ip))
- header->misc |= PERF_RECORD_MISC_KERNEL;
+ misc |= PERF_RECORD_MISC_KERNEL;
else
- header->misc |= PERF_RECORD_MISC_USER;
+ misc |= PERF_RECORD_MISC_USER;
}
- perf_event_header__init_id(header, data, event);
+ perf_event_header__init(header, data,
+ PERF_RECORD_SAMPLE, misc,
+ sizeof(*header) + event->header_size,
+ event);
return 0;
}
diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile
index 8dbf2532a573..c023046a3fd7 100644
--- a/arch/riscv/kernel/vdso/Makefile
+++ b/arch/riscv/kernel/vdso/Makefile
@@ -36,6 +36,7 @@ endif
ccflags-y := -fno-stack-protector
ccflags-y += -DDISABLE_BRANCH_PROFILING
ccflags-y += -fno-builtin
+ccflags-y += $(CONFIG_CC_OPT_INLINE_MEMSET)
ccflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH)
ccflags-y += $(CFI_FULL)
asflags-y += $(KBUILD_BASE_ISA)$(CFI_MARCH)
diff --git a/arch/riscv/kvm/aia_device.c b/arch/riscv/kvm/aia_device.c
index efc7c0bcfba9..97833a04268a 100644
--- a/arch/riscv/kvm/aia_device.c
+++ b/arch/riscv/kvm/aia_device.c
@@ -237,7 +237,7 @@ static int aia_init(struct kvm *kvm)
return -EBUSY;
/* We might be in the middle of creating a VCPU? */
- if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus))
+ if (kvm_is_vcpu_creation_in_progress(kvm))
return -EBUSY;
/* Number of sources should be less than or equals number of IDs */
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 151031e97a24..01fe66774f02 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -822,6 +822,24 @@ static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
return emit_jump_and_link(is_call ? RV_REG_T0 : RV_REG_ZERO, rvoff, false, &ctx);
}
+int arch_bpf_trampoline_skip(void *nop, void *target)
+{
+ u32 old_insn = rv_nop();
+ u32 new_insn = rv_jal(RV_REG_ZERO, (target - nop) >> 1);
+ int ret;
+
+ if (memcmp(nop, &old_insn, sizeof(old_insn)))
+ return -EFAULT;
+
+ cpus_read_lock();
+ mutex_lock(&text_mutex);
+ ret = patch_text(nop, &new_insn, sizeof(new_insn));
+ mutex_unlock(&text_mutex);
+ cpus_read_unlock();
+
+ return ret;
+}
+
int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
enum bpf_text_poke_type new_t, void *old_addr,
void *new_addr)
@@ -904,12 +922,17 @@ static void emit_store_stack_imm64(u8 reg, int stack_off, u64 imm64,
emit_sd(RV_REG_FP, stack_off, reg, ctx);
}
-static int invoke_bpf_prog(struct bpf_tramp_node *node, int args_off, int retval_off,
- int run_ctx_off, bool save_ret, struct rv_jit_context *ctx)
+static int invoke_bpf_prog(struct bpf_tramp_image *im, struct bpf_tramp_node *node,
+ int args_off, int retval_off, int run_ctx_off, bool save_ret,
+ struct rv_jit_context *ctx)
{
int ret, branch_off;
struct bpf_prog *p = node->link->prog;
int cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie);
+ void *skip = ctx->ro_insns + ctx->ninsns;
+
+ /* nop, patched to a jal over this prog when it is detached */
+ emit(rv_nop(), ctx);
if (node->cookie)
emit_store_stack_imm64(RV_REG_T1, -run_ctx_off + cookie_off, node->cookie, ctx);
@@ -962,13 +985,17 @@ static int invoke_bpf_prog(struct bpf_tramp_node *node, int args_off, int retval
/* arg3: &run_ctx */
emit_addi(RV_REG_A2, RV_REG_FP, -run_ctx_off, ctx);
ret = emit_call((const u64)bpf_trampoline_exit(p), true, ctx);
+ if (ret)
+ return ret;
- return ret;
+ bpf_tramp_image_add_skip(im, p, skip, ctx->ro_insns + ctx->ninsns);
+ return 0;
}
-static int invoke_bpf(struct bpf_tramp_nodes *tn, int args_off, int retval_off,
- int run_ctx_off, int func_meta_off, bool save_ret, u64 func_meta,
- int cookie_off, struct rv_jit_context *ctx)
+static int invoke_bpf(struct bpf_tramp_image *im, struct bpf_tramp_nodes *tn,
+ int args_off, int retval_off, int run_ctx_off, int func_meta_off,
+ bool save_ret, u64 func_meta, int cookie_off,
+ struct rv_jit_context *ctx)
{
int i, cur_cookie = (cookie_off - args_off) / 8;
@@ -981,8 +1008,8 @@ static int invoke_bpf(struct bpf_tramp_nodes *tn, int args_off, int retval_off,
emit_store_stack_imm64(RV_REG_T1, -func_meta_off, meta, ctx);
cur_cookie--;
}
- err = invoke_bpf_prog(tn->nodes[i], args_off, retval_off, run_ctx_off,
- save_ret, ctx);
+ err = invoke_bpf_prog(im, tn->nodes[i], args_off, retval_off,
+ run_ctx_off, save_ret, ctx);
if (err)
return err;
}
@@ -1170,7 +1197,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
}
if (fentry->nr_nodes) {
- ret = invoke_bpf(fentry, args_off, retval_off, run_ctx_off, func_meta_off,
+ ret = invoke_bpf(im, fentry, args_off, retval_off, run_ctx_off, func_meta_off,
flags & BPF_TRAMP_F_RET_FENTRY_RET, func_meta, cookie_off, ctx);
if (ret)
return ret;
@@ -1184,7 +1211,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
/* cleanup to avoid garbage return value confusion */
emit_sd(RV_REG_FP, -retval_off, RV_REG_ZERO, ctx);
for (i = 0; i < fmod_ret->nr_nodes; i++) {
- ret = invoke_bpf_prog(fmod_ret->nodes[i], args_off, retval_off,
+ ret = invoke_bpf_prog(im, fmod_ret->nodes[i], args_off, retval_off,
run_ctx_off, true, ctx);
if (ret)
goto out;
@@ -1211,10 +1238,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
emit_sd(RV_REG_FP, -tcc_off, RV_REG_TCC, ctx);
emit_sd(RV_REG_FP, -retval_off, RV_REG_A0, ctx);
emit_sd(RV_REG_FP, -(retval_off - 8), regmap[BPF_REG_0], ctx);
- im->ip_after_call = ctx->ro_insns + ctx->ninsns;
- /* 2 nops reserved for auipc+jalr pair */
- emit(rv_nop(), ctx);
- emit(rv_nop(), ctx);
}
/* update branches saved in invoke_bpf_mod_ret with bnez */
@@ -1230,14 +1253,13 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
emit_store_stack_imm64(RV_REG_T1, -func_meta_off, func_meta, ctx);
if (fexit->nr_nodes) {
- ret = invoke_bpf(fexit, args_off, retval_off, run_ctx_off, func_meta_off,
+ ret = invoke_bpf(im, fexit, args_off, retval_off, run_ctx_off, func_meta_off,
false, func_meta, cookie_off, ctx);
if (ret)
goto out;
}
if (flags & BPF_TRAMP_F_CALL_ORIG) {
- im->ip_epilogue = ctx->ro_insns + ctx->ninsns;
emit_imm(RV_REG_A0, ctx->insns ? (const s64)im : RV_MAX_COUNT_IMM, ctx);
ret = emit_call((const u64)__bpf_tramp_exit, true, ctx);
if (ret)
@@ -1299,7 +1321,7 @@ out:
int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *func_addr)
{
- struct bpf_tramp_image im;
+ struct bpf_tramp_image im = {};
struct rv_jit_context ctx;
int ret;
diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
index 35c834b895ec..54bcf2984ca1 100644
--- a/arch/s390/kernel/vdso/Makefile
+++ b/arch/s390/kernel/vdso/Makefile
@@ -29,6 +29,7 @@ KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO))
KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO))
KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables
KBUILD_CFLAGS_VDSO += -fno-stack-protector $(DISABLE_KSTACK_ERASE)
+KBUILD_CFLAGS_VDSO += $(CONFIG_CC_OPT_INLINE_MEMSET)
ldflags-y := -shared -soname=linux-vdso.so.1 \
--hash-style=both --build-id=sha1 \
$(call ld-option, --eh-frame-hdr) -T
diff --git a/arch/s390/kvm/s390/s390.c b/arch/s390/kvm/s390/s390.c
index efd77b9cd36c..e4912db67adc 100644
--- a/arch/s390/kvm/s390/s390.c
+++ b/arch/s390/kvm/s390/s390.c
@@ -3579,12 +3579,11 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
{
- mutex_lock(&vcpu->kvm->lock);
preempt_disable();
vcpu->arch.sie_block->epoch = vcpu->kvm->arch.epoch;
vcpu->arch.sie_block->epdx = vcpu->kvm->arch.epdx;
preempt_enable();
- mutex_unlock(&vcpu->kvm->lock);
+
if (!kvm_is_ucontrol(vcpu->kvm)) {
vcpu->arch.gmap = vcpu->kvm->arch.gmap;
sca_add_vcpu(vcpu);
@@ -3757,13 +3756,11 @@ static int kvm_s390_vcpu_setup(struct kvm_vcpu *vcpu)
kvm_s390_vcpu_pci_setup(vcpu);
- mutex_lock(&vcpu->kvm->lock);
if (kvm_s390_pv_is_protected(vcpu->kvm)) {
rc = kvm_s390_pv_create_cpu(vcpu, &uvrc, &uvrrc);
if (rc)
kvm_s390_vcpu_unsetup_cmma(vcpu);
}
- mutex_unlock(&vcpu->kvm->lock);
return rc;
}
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index c4b47070bb59..1b2566012b63 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -2566,6 +2566,8 @@ struct bpf_tramp_jit {
int r14_off; /* Offset of saved %r14, has to be at the
* bottom */
int do_fexit; /* do_fexit: label */
+ int skip[BPF_MAX_TRAMP_LINKS]; /* skip: labels after each prog */
+ int nr_progs;
};
static void load_imm64(struct bpf_jit *jit, int dst_reg, u64 val)
@@ -2584,6 +2586,7 @@ static void emit_store_stack_imm64(struct bpf_jit *jit, int tmp_reg, int stack_o
}
static int invoke_bpf_prog(struct bpf_tramp_jit *tjit,
+ struct bpf_tramp_image *im,
const struct btf_func_model *m,
struct bpf_tramp_node *node, bool save_ret)
{
@@ -2591,8 +2594,20 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit,
int cookie_off = tjit->run_ctx_off +
offsetof(struct bpf_tramp_run_ctx, bpf_cookie);
struct bpf_prog *p = node->link->prog;
+ void *skip = jit->prg_buf + jit->prg;
+ int idx = tjit->nr_progs++;
int patch;
+ if (idx >= ARRAY_SIZE(tjit->skip))
+ return -E2BIG;
+
+ /*
+ * nop, patched to skip this prog when it is detached
+ */
+
+ /* brcl 0,skip */
+ EMIT6_PCREL_RILC(0xc0040000, 0, tjit->skip[idx]);
+
/*
* run_ctx.cookie = node->cookie;
*/
@@ -2652,10 +2667,15 @@ static int invoke_bpf_prog(struct bpf_tramp_jit *tjit,
/* brasl %r14,__bpf_prog_exit */
EMIT6_PCREL_RILB_PTR(0xc0050000, REG_14, bpf_trampoline_exit(p));
+ /* skip: */
+ tjit->skip[idx] = jit->prg;
+ bpf_tramp_image_add_skip(im, p, skip, jit->prg_buf + jit->prg);
+
return 0;
}
static int invoke_bpf(struct bpf_tramp_jit *tjit,
+ struct bpf_tramp_image *im,
const struct btf_func_model *m,
struct bpf_tramp_nodes *tn, bool save_ret,
u64 func_meta, int cookie_off)
@@ -2670,7 +2690,7 @@ static int invoke_bpf(struct bpf_tramp_jit *tjit,
emit_store_stack_imm64(jit, REG_0, tjit->func_meta_off, meta);
cur_cookie--;
}
- if (invoke_bpf_prog(tjit, m, tn->nodes[i], save_ret))
+ if (invoke_bpf_prog(tjit, im, m, tn->nodes[i], save_ret))
return -EINVAL;
}
@@ -2712,6 +2732,11 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
u64 func_meta;
int i, j;
+ /* The skip labels are taken from the previous pass. */
+ tjit->nr_progs = 0;
+ if (im)
+ im->nr_skips = 0;
+
/* Support as many stack arguments as "mvc" instruction can handle. */
nr_reg_args = min_t(int, m->nr_args, MAX_NR_REG_ARGS);
nr_stack_args = m->nr_args - nr_reg_args;
@@ -2875,7 +2900,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
emit_store_stack_imm64(jit, REG_0, tjit->retval_off, 0);
}
- if (invoke_bpf(tjit, m, fentry, flags & BPF_TRAMP_F_RET_FENTRY_RET,
+ if (invoke_bpf(tjit, im, m, fentry, flags & BPF_TRAMP_F_RET_FENTRY_RET,
func_meta, cookie_off))
return -EINVAL;
@@ -2889,7 +2914,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
0xf000 | tjit->retval_off);
for (i = 0; i < fmod_ret->nr_nodes; i++) {
- if (invoke_bpf_prog(tjit, m, fmod_ret->nodes[i], true))
+ if (invoke_bpf_prog(tjit, im, m, fmod_ret->nodes[i], true))
return -EINVAL;
/*
@@ -2943,15 +2968,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
/* mvc tccnt_off(%r15),tail_call_cnt(4,%r15) */
_EMIT6(0xd203f000 | tjit->tccnt_off,
0xf000 | offsetof(struct prog_frame, tail_call_cnt));
-
- im->ip_after_call = jit->prg_buf + jit->prg;
-
- /*
- * The following nop will be patched by bpf_tramp_image_put().
- */
-
- /* brcl 0,im->ip_epilogue */
- EMIT6_PCREL_RILC(0xc0040000, 0, (u64)im->ip_epilogue);
}
/* Set the "is_return" flag for fsession. */
@@ -2962,12 +2978,10 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
/* do_fexit: */
tjit->do_fexit = jit->prg;
- if (invoke_bpf(tjit, m, fexit, false, func_meta, cookie_off))
+ if (invoke_bpf(tjit, im, m, fexit, false, func_meta, cookie_off))
return -EINVAL;
if (flags & BPF_TRAMP_F_CALL_ORIG) {
- im->ip_epilogue = jit->prg_buf + jit->prg;
-
/*
* __bpf_tramp_exit(im);
*/
@@ -3016,7 +3030,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *orig_call)
{
- struct bpf_tramp_image im;
+ struct bpf_tramp_image im = {};
struct bpf_tramp_jit tjit;
int ret;
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index f522fff9231e..1903ed8bbe3b 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -1293,6 +1293,7 @@ static void gcm_process_assoc(const struct aes_gcm_key *key, u8 ghash_acc[16],
unsigned int len;
const u8 *src = walk.addr;
+ assoclen -= orig_len_this_step;
if (unlikely(pos)) {
len = min(len_this_step, 16 - pos);
memcpy(&buf[pos], src, len);
@@ -1320,7 +1321,6 @@ next:
kernel_fpu_end();
kernel_fpu_begin();
}
- assoclen -= orig_len_this_step;
}
if (unlikely(pos))
aes_gcm_aad_update(key, ghash_acc, buf, pos, flags);
diff --git a/arch/x86/entry/vdso/vdso64/Makefile b/arch/x86/entry/vdso/vdso64/Makefile
index 7c0790065b5e..c2353a065279 100644
--- a/arch/x86/entry/vdso/vdso64/Makefile
+++ b/arch/x86/entry/vdso/vdso64/Makefile
@@ -14,7 +14,7 @@ vobjs-$(CONFIG_X86_SGX) += vsgx.o
vobjs-$(CONFIG_FUTEX_ROBUST_UNLOCK) += vfutex.o
# Compilation flags
-flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small
+flags-y := -DBUILD_VDSO64 -m64 -mcmodel=small $(CONFIG_CC_OPT_INLINE_MEMSET)
# The location of this include matters!
include $(src)/../common/Makefile.include
diff --git a/arch/x86/include/uapi/asm/ptrace-abi.h b/arch/x86/include/uapi/asm/ptrace-abi.h
index 5823584dea13..3656955c6faa 100644
--- a/arch/x86/include/uapi/asm/ptrace-abi.h
+++ b/arch/x86/include/uapi/asm/ptrace-abi.h
@@ -2,6 +2,7 @@
#ifndef _ASM_X86_PTRACE_ABI_H
#define _ASM_X86_PTRACE_ABI_H
+#if defined(__ASSEMBLER__) || defined(__FRAME_OFFSETS)
#ifdef __i386__
#define EBX 0
@@ -25,7 +26,6 @@
#else /* __i386__ */
-#if defined(__ASSEMBLER__) || defined(__FRAME_OFFSETS)
/*
* C ABI says these regs are callee-preserved. They aren't saved on kernel entry
* unless syscall needs a complete, fully filled "struct pt_regs".
@@ -57,12 +57,12 @@
#define EFLAGS 144
#define RSP 152
#define SS 160
-#endif /* __ASSEMBLER__ */
/* top of stack page */
#define FRAME_SIZE 168
#endif /* !__i386__ */
+#endif /* defined(__ASSEMBLER__) || defined(__FRAME_OFFSETS) */
/* Arbitrarily choose the same ptrace numbers as used by the Sparc code. */
#define PTRACE_GETREGS 12
diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
index 776ae6fa7f2d..4c078827f26a 100644
--- a/arch/x86/kernel/sys_x86_64.c
+++ b/arch/x86/kernel/sys_x86_64.c
@@ -50,9 +50,16 @@ static unsigned long get_align_mask(struct file *filp)
* value before calling vm_unmapped_area() or ORed directly to the
* address.
*/
-static unsigned long get_align_bits(void)
+static unsigned long get_align_bits(struct file *filp)
{
- return va_align.bits & get_align_mask(NULL);
+ /*
+ * va_align.bits is smaller than the huge page size and will
+ * lead to misaligned huge pages. Ignore it for huge mappings.
+ */
+ if (is_file_hugepages(filp))
+ return 0;
+
+ return va_align.bits & get_align_mask(filp);
}
static int __init control_va_addr_alignment(char *str)
@@ -157,7 +164,7 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len,
}
if (filp) {
info.align_mask = get_align_mask(filp);
- info.align_offset += get_align_bits();
+ info.align_offset += get_align_bits(filp);
}
return vm_unmapped_area(&info);
@@ -222,7 +229,7 @@ get_unmapped_area:
if (filp) {
info.align_mask = get_align_mask(filp);
- info.align_offset += get_align_bits();
+ info.align_offset += get_align_bits(filp);
}
addr = vm_unmapped_area(&info);
if (!(addr & ~PAGE_MASK))
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 8e62476e477b..a0d608e3fceb 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -2512,6 +2512,12 @@ static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterato
iterator->addr = addr;
iterator->shadow_addr = root;
+
+ if (WARN_ON_ONCE(!VALID_PAGE(root)) || kvm_mmu_is_dummy_root(root)) {
+ iterator->level = 0;
+ return;
+ }
+
iterator->level = vcpu->arch.mmu->root_role.level;
if (iterator->level >= PT64_ROOT_4LEVEL &&
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 63eb2155a774..0c1ebb16cec6 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -488,6 +488,20 @@ static void snp_guest_req_cleanup(struct kvm *kvm)
sev->guest_resp_buf = NULL;
}
+static int sev_alloc_have_run_cpus(struct kvm_sev_info *sev)
+{
+ if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void sev_free_have_run_cpus(struct kvm_sev_info *sev)
+{
+ free_cpumask_var(sev->have_run_cpus);
+ memset(&sev->have_run_cpus, 0, sizeof(sev->have_run_cpus));
+}
+
static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
struct kvm_sev_init *data,
unsigned long vm_type)
@@ -545,10 +559,9 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
if (ret)
goto e_free_asid;
- if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
- ret = -ENOMEM;
+ ret = sev_alloc_have_run_cpus(sev);
+ if (ret)
goto e_free_asid;
- }
/* This needs to happen after SEV/SNP firmware initialization. */
if (snp_active) {
@@ -566,7 +579,7 @@ static int __sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp,
return 0;
e_free:
- free_cpumask_var(sev->have_run_cpus);
+ sev_free_have_run_cpus(sev);
e_free_asid:
argp->error = init_args.error;
sev_asid_free(sev);
@@ -1125,9 +1138,6 @@ static int sev_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
if (!sev_es_guest(kvm))
return -ENOTTY;
- if (kvm_is_vcpu_creation_in_progress(kvm))
- return -EBUSY;
-
ret = kvm_lock_all_vcpus(kvm);
if (ret)
return ret;
@@ -2035,6 +2045,13 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm)
struct kvm_sev_info *mirror;
unsigned long i;
+ /*
+ * Do cache maintenance on the source VM *before* clearing "SEV active",
+ * as memory reclaim flows won't trigger cache maintenance on the VM
+ * once it's no longer an SEV VM.
+ */
+ sev_writeback_caches(src_kvm);
+
dst->active = true;
dst->asid = src->asid;
dst->handle = src->handle;
@@ -2048,12 +2065,6 @@ static void sev_migrate_from(struct kvm *dst_kvm, struct kvm *src_kvm)
src->pages_locked = 0;
src->es_active = false;
- /*
- * Do cache maintenance on the source VM as it is no longer an SEV VM,
- * i.e. memory reclaim flows won't trigger cache maintenance on the VM.
- */
- sev_writeback_caches(src_kvm);
-
list_cut_before(&dst->regions_list, &src->regions_list, &src->regions_list);
mutex_lock(&sev_mirror_lock);
@@ -2121,10 +2132,6 @@ static int sev_check_source_vcpus(struct kvm *dst, struct kvm *src)
struct kvm_vcpu *src_vcpu;
unsigned long i;
- if (kvm_is_vcpu_creation_in_progress(src) ||
- kvm_is_vcpu_creation_in_progress(dst))
- return -EBUSY;
-
if (!sev_es_guest(src))
return 0;
@@ -2198,10 +2205,9 @@ int sev_vm_move_enc_context_from(struct kvm *kvm, unsigned int source_fd)
* does not, i.e. KVM could skip flushes if memory is reclaimed from
* the old VM but not the new VM.
*/
- if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
- ret = -ENOMEM;
+ ret = sev_alloc_have_run_cpus(dst_sev);
+ if (ret)
goto out_source_vcpu;
- }
sev_migrate_from(kvm, source_kvm);
kvm_vm_dead(source_kvm);
@@ -2520,9 +2526,6 @@ static int snp_launch_update_vmsa(struct kvm *kvm, struct kvm_sev_cmd *argp)
unsigned long i;
int ret;
- if (kvm_is_vcpu_creation_in_progress(kvm))
- return -EBUSY;
-
ret = kvm_lock_all_vcpus(kvm);
if (ret)
return ret;
@@ -2898,10 +2901,9 @@ int sev_vm_copy_enc_context_from(struct kvm *kvm, unsigned int source_fd)
}
mirror_sev = to_kvm_sev_info(kvm);
- if (!zalloc_cpumask_var(&mirror_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
- ret = -ENOMEM;
+ ret = sev_alloc_have_run_cpus(mirror_sev);
+ if (ret)
goto e_unlock;
- }
/*
* The mirror kvm holds an enc_context_owner ref so its asid can't
@@ -2994,7 +2996,7 @@ void sev_vm_destroy(struct kvm *kvm)
* Free the mask even if the VM is not *currently* an SEV VM, as it may
* have been an SEV VM prior to intra-host migration.
*/
- free_cpumask_var(sev->have_run_cpus);
+ sev_free_have_run_cpus(sev);
if (!sev_guest(kvm))
return;
@@ -3630,7 +3632,6 @@ int pre_sev_run(struct vcpu_svm *svm, int cpu)
sd->sev_vmcbs[asid] = svm->vmcb;
svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ASID;
- vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
return 0;
}
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 7d59d301e1e5..fe0cba4731b2 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -673,16 +673,7 @@ static void clr_dr_intercepts(struct vcpu_svm *svm)
static bool msr_write_intercepted(struct vcpu_svm *svm, u32 msr)
{
- /*
- * For non-nested case:
- * If the L01 MSR bitmap does not intercept the MSR, then we need to
- * save it.
- *
- * For nested case:
- * If the L02 MSR bitmap does not intercept the MSR, then we need to
- * save it.
- */
- void *msrpm = is_guest_mode(&svm->vcpu) ? svm->nested.msrpm : svm->msrpm;
+ void *msrpm = __va(__sme_clr(svm->vmcb->control.msrpm_base_pa));
return svm_test_msr_bitmap_write(msrpm, msr);
}
@@ -1902,8 +1893,7 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *sd)
if (sd->next_asid > sd->max_asid) {
++sd->asid_generation;
sd->next_asid = sd->min_asid;
- svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
- vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
+ sd->flush_all_asids = true;
}
svm->current_vmcb->asid_generation = sd->asid_generation;
@@ -4528,6 +4518,9 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
svm->vmcb->control.asid = svm->asid;
vmcb_mark_dirty(svm->vmcb, VMCB_ASID);
}
+ if (this_cpu_ptr(&svm_data)->flush_all_asids)
+ svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
+
svm->vmcb->save.cr2 = vcpu->arch.cr2;
if (guest_cpu_cap_has(vcpu, X86_FEATURE_ERAPS) &&
@@ -4618,16 +4611,23 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
vcpu->arch.nested_run_pending = 0;
}
- svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
+ if (!svm_is_vmrun_failure(svm->vmcb->control.exit_code)) {
+ this_cpu_ptr(&svm_data)->flush_all_asids = false;
+ svm->vmcb->control.tlb_ctl = TLB_CONTROL_DO_NOTHING;
- /*
- * Unconditionally mask off the CLEAR_RAP bit, the AND is just as cheap
- * as the TEST+Jcc to avoid it.
- */
- if (cpu_feature_enabled(X86_FEATURE_ERAPS))
- svm->vmcb->control.erap_ctl &= ~ERAP_CONTROL_CLEAR_RAP;
+ /*
+ * Unconditionally mask off the CLEAR_RAP bit, the AND is just
+ * as cheap as the TEST+Jcc to avoid it.
+ */
+ if (cpu_feature_enabled(X86_FEATURE_ERAPS))
+ svm->vmcb->control.erap_ctl &= ~ERAP_CONTROL_CLEAR_RAP;
- vmcb_mark_all_clean(svm->vmcb);
+ vmcb_mark_all_clean(svm->vmcb);
+
+ if (!msr_write_intercepted(svm, MSR_AMD64_PERF_CNTR_GLOBAL_CTL))
+ rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL,
+ vcpu_to_pmu(vcpu)->global_ctrl);
+ }
/* if exit due to PF check for async PF */
if (svm->vmcb->control.exit_code == SVM_EXIT_EXCP_BASE + PF_VECTOR)
@@ -4636,9 +4636,6 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu, u64 run_flags)
kvm_clear_available_registers(vcpu, SVM_REGS_LAZY_LOAD_SET);
- if (!msr_write_intercepted(svm, MSR_AMD64_PERF_CNTR_GLOBAL_CTL))
- rdmsrq(MSR_AMD64_PERF_CNTR_GLOBAL_CTL, vcpu_to_pmu(vcpu)->global_ctrl);
-
trace_kvm_exit(vcpu, KVM_ISA_SVM);
svm_complete_interrupts(vcpu);
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index e958943b8162..84f19026d3e8 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -376,6 +376,7 @@ struct svm_cpu_data {
u32 next_asid;
u32 min_asid;
+ bool flush_all_asids;
bool bp_spec_reduce_set;
struct vmcb *save_area;
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 40c1a5f6fa8a..b25216862740 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -1754,6 +1754,9 @@ static void copy_vmcs12_to_shadow(struct vcpu_vmx *vmx)
static void copy_enlightened_to_vmcs12(struct vcpu_vmx *vmx, u32 hv_clean_fields)
{
#ifdef CONFIG_KVM_HYPERV
+ const u64 runtime_controls = HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_GRP1 |
+ HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_GRP2 |
+ HV_VMX_ENLIGHTENED_CLEAN_FIELD_CONTROL_PROC;
struct vmcs12 *vmcs12 = vmx->nested.cached_vmcs12;
struct hv_enlightened_vmcs *evmcs = nested_vmx_evmcs(vmx);
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(&vmx->vcpu);
@@ -1762,6 +1765,9 @@ static void copy_enlightened_to_vmcs12(struct vcpu_vmx *vmx, u32 hv_clean_fields
vmcs12->tpr_threshold = evmcs->tpr_threshold;
vmcs12->guest_rip = evmcs->guest_rip;
+ if ((hv_clean_fields & runtime_controls) != runtime_controls)
+ vmx->nested.force_msr_bitmap_recalc = true;
+
if (unlikely(!(hv_clean_fields &
HV_VMX_ENLIGHTENED_CLEAN_FIELD_ENLIGHTENMENTSCONTROL))) {
hv_vcpu->nested.pa_page_gpa = evmcs->partition_assist_page;
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 6c842e9191a5..00850a3a77d6 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -2728,11 +2728,6 @@ static tdx_vm_state_guard_t tdx_acquire_vm_state_locks(struct kvm *kvm)
mutex_lock(&kvm->lock);
- if (kvm->created_vcpus != atomic_read(&kvm->online_vcpus)) {
- r = -EBUSY;
- goto out_err;
- }
-
r = kvm_lock_all_vcpus(kvm);
if (r)
goto out_err;
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index cb03f5a2b243..4a105f283cfb 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -705,47 +705,37 @@ int pmd_clear_huge(pmd_t *pmd)
}
#ifdef CONFIG_X86_64
-/**
- * pud_free_pmd_page - Clear PUD entry and free PMD page
- * @pud: Pointer to a PUD
- * @addr: Virtual address associated with PUD
- *
- * Context: The PUD range has been unmapped and TLB purged.
- * Return: 1 if clearing the entry succeeded. 0 otherwise.
- *
- * NOTE: Callers must allow a single page allocation.
+/*
+ * Given a PUD poitner, detach and free the pointed-to
+ * PMD page and any PTE page children. The entire range
+ * under the PUD must not have any valid translations
+ * and the TLB must have already been flushed.
*/
int pud_free_pmd_page(pud_t *pud, unsigned long addr)
{
- pmd_t *pmd, *pmd_sv;
struct ptdesc *pt;
+ pmd_t *pmd;
int i;
pmd = pud_pgtable(*pud);
- pmd_sv = (pmd_t *)__get_free_page(GFP_KERNEL);
- if (!pmd_sv)
- return 0;
-
- for (i = 0; i < PTRS_PER_PMD; i++) {
- pmd_sv[i] = pmd[i];
- if (!pmd_none(pmd[i]))
- pmd_clear(&pmd[i]);
- }
+ /* Detach the PMD page: */
pud_clear(pud);
- /* INVLPG to clear all paging-structure caches */
+ /*
+ * PMD and all its descendents are unreachable
+ * via normal page walks. Make them unreachable
+ * in cached mid-level walks too:
+ */
flush_tlb_kernel_range(addr, addr + PAGE_SIZE-1);
for (i = 0; i < PTRS_PER_PMD; i++) {
- if (!pmd_none(pmd_sv[i])) {
- pt = page_ptdesc(pmd_page(pmd_sv[i]));
+ if (!pmd_none(pmd[i])) {
+ pt = page_ptdesc(pmd_page(pmd[i]));
pagetable_dtor_free(pt);
}
}
- free_page((unsigned long)pmd_sv);
-
pmd_free(&init_mm, pmd);
return 1;
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 2853e87797a7..6bca87457e87 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3217,16 +3217,21 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog,
}
static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
+ struct bpf_tramp_image *im,
struct bpf_tramp_node *node, int stack_size,
int run_ctx_off, bool save_ret,
void *image, void *rw_image)
{
u8 *prog = *pprog;
- u8 *jmp_insn;
+ u8 *jmp_insn, *skip;
int ctx_cookie_off = offsetof(struct bpf_tramp_run_ctx, bpf_cookie);
struct bpf_prog *p = node->link->prog;
u64 cookie = node->cookie;
+ /* nop, patched to skip this prog when it is detached */
+ skip = image + (prog - (u8 *)rw_image);
+ emit_nops(&prog, X86_PATCH_SIZE);
+
/* mov rdi, cookie */
emit_mov_imm64(&prog, BPF_REG_1, (long) cookie >> 32, (u32) (long) cookie);
@@ -3301,6 +3306,8 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
if (emit_rsb_call(&prog, bpf_trampoline_exit(p), image + (prog - (u8 *)rw_image)))
return -EINVAL;
+ bpf_tramp_image_add_skip(im, p, skip, image + (prog - (u8 *)rw_image));
+
*pprog = prog;
return 0;
}
@@ -3332,6 +3339,7 @@ static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond)
}
static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
+ struct bpf_tramp_image *im,
struct bpf_tramp_nodes *tl, int stack_size,
int run_ctx_off, int func_meta_off, bool save_ret,
void *image, void *rw_image, u64 func_meta,
@@ -3346,7 +3354,7 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
func_meta | (cur_cookie << BPF_TRAMP_COOKIE_INDEX_SHIFT));
cur_cookie--;
}
- if (invoke_bpf_prog(m, &prog, tl->nodes[i], stack_size,
+ if (invoke_bpf_prog(m, &prog, im, tl->nodes[i], stack_size,
run_ctx_off, save_ret, image, rw_image))
return -EINVAL;
}
@@ -3355,6 +3363,7 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,
}
static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog,
+ struct bpf_tramp_image *im,
struct bpf_tramp_nodes *tl, int stack_size,
int run_ctx_off, u8 **branches,
void *image, void *rw_image)
@@ -3368,7 +3377,7 @@ static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog,
emit_mov_imm32(&prog, false, BPF_REG_0, 0);
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
for (i = 0; i < tl->nr_nodes; i++) {
- if (invoke_bpf_prog(m, &prog, tl->nodes[i], stack_size, run_ctx_off, true,
+ if (invoke_bpf_prog(m, &prog, im, tl->nodes[i], stack_size, run_ctx_off, true,
image, rw_image))
return -EINVAL;
@@ -3640,7 +3649,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
}
if (fentry->nr_nodes) {
- if (invoke_bpf(m, &prog, fentry, regs_off, run_ctx_off, func_meta_off,
+ if (invoke_bpf(m, &prog, im, fentry, regs_off, run_ctx_off, func_meta_off,
flags & BPF_TRAMP_F_RET_FENTRY_RET, image, rw_image,
func_meta, cookie_off))
return -EINVAL;
@@ -3652,7 +3661,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
if (!branches)
return -ENOMEM;
- if (invoke_bpf_mod_ret(m, &prog, fmod_ret, regs_off,
+ if (invoke_bpf_mod_ret(m, &prog, im, fmod_ret, regs_off,
run_ctx_off, branches, image, rw_image)) {
ret = -EINVAL;
goto cleanup;
@@ -3682,8 +3691,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
}
/* remember return value in a stack for bpf prog to access */
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8);
- im->ip_after_call = image + (prog - (u8 *)rw_image);
- emit_nops(&prog, X86_PATCH_SIZE);
}
if (fmod_ret->nr_nodes) {
@@ -3708,7 +3715,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
emit_store_stack_imm64(&prog, BPF_REG_0, -func_meta_off, func_meta);
if (fexit->nr_nodes) {
- if (invoke_bpf(m, &prog, fexit, regs_off, run_ctx_off, func_meta_off,
+ if (invoke_bpf(m, &prog, im, fexit, regs_off, run_ctx_off, func_meta_off,
false, image, rw_image, func_meta, cookie_off)) {
ret = -EINVAL;
goto cleanup;
@@ -3723,7 +3730,6 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
* restored to R0.
*/
if (flags & BPF_TRAMP_F_CALL_ORIG) {
- im->ip_epilogue = image + (prog - (u8 *)rw_image);
/* arg1: mov rdi, im */
emit_mov_imm64(&prog, BPF_REG_1, (long) im >> 32, (u32) (long) im);
if (emit_rsb_call(&prog, __bpf_tramp_exit, image + (prog - (u8 *)rw_image))) {
@@ -3811,7 +3817,7 @@ out:
int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *func_addr)
{
- struct bpf_tramp_image im;
+ struct bpf_tramp_image im = {};
void *image;
int ret;
diff --git a/arch/x86/um/asm/ptrace.h b/arch/x86/um/asm/ptrace.h
index 2641d28d115c..439c4151f6b7 100644
--- a/arch/x86/um/asm/ptrace.h
+++ b/arch/x86/um/asm/ptrace.h
@@ -13,9 +13,7 @@ enum {
};
#include <linux/compiler.h>
-#ifndef CONFIG_X86_32
-#define __FRAME_OFFSETS /* Needed to get the R* macros */
-#endif
+#define __FRAME_OFFSETS /* Needed to get the register macros */
#include <asm/ptrace-generic.h>
#define user_mode(r) UPT_IS_USER(&(r)->regs)
diff --git a/arch/x86/um/ptrace_32.c b/arch/x86/um/ptrace_32.c
index 3af3cb821524..9e9155b0e918 100644
--- a/arch/x86/um/ptrace_32.c
+++ b/arch/x86/um/ptrace_32.c
@@ -7,6 +7,7 @@
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/regset.h>
+#define __FRAME_OFFSETS
#include <asm/ptrace-abi.h>
#include <registers.h>
#include <skas.h>
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 2b5c29434e42..b56db1cc6778 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -2091,6 +2091,7 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,
struct request_queue *q = bio->bi_bdev->bd_queue;
struct blkcg *blkcg = css_to_blkcg(css);
struct blkcg_gq *blkg;
+ unsigned long flags;
rcu_read_lock();
blkg = blkg_lookup(blkcg, q);
@@ -2105,11 +2106,11 @@ static inline struct blkcg_gq *blkg_tryget_closest(struct bio *bio,
* Fast path failed, we're probably issuing IO in this cgroup the first
* time, hold lock to create new blkg.
*/
- spin_lock_irq(&q->queue_lock);
+ spin_lock_irqsave(&q->queue_lock, flags);
blkg = blkg_lookup_create(blkcg, bio->bi_bdev->bd_disk);
if (blkg)
blkg = blkg_lookup_tryget(blkg);
- spin_unlock_irq(&q->queue_lock);
+ spin_unlock_irqrestore(&q->queue_lock, flags);
return blkg;
}
diff --git a/block/blk-mq.c b/block/blk-mq.c
index a26a11c73ee3..025a799f35b9 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -447,16 +447,6 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data,
WRITE_ONCE(rq->deadline, 0);
req_ref_set(rq, 1);
- if (rq->rq_flags & RQF_USE_SCHED) {
- struct elevator_queue *e = data->q->elevator;
-
- INIT_HLIST_NODE(&rq->hash);
- RB_CLEAR_NODE(&rq->rb_node);
-
- if (e->type->ops.prepare_request)
- e->type->ops.prepare_request(rq);
- }
-
return rq;
}
@@ -498,6 +488,12 @@ __blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data)
return rq_list_pop(data->cached_rqs);
}
+static bool blk_op_bypass_sched(blk_opf_t opf)
+{
+ return (opf & REQ_OP_MASK) == REQ_OP_FLUSH ||
+ blk_op_is_passthrough(opf);
+}
+
static void blk_mq_limit_depth(struct blk_mq_alloc_data *data)
{
struct elevator_mq_ops *ops;
@@ -518,12 +514,10 @@ static void blk_mq_limit_depth(struct blk_mq_alloc_data *data)
* Flush/passthrough requests are special and go directly to the
* dispatch list, they are not subject to the async_depth limit.
*/
- if ((data->cmd_flags & REQ_OP_MASK) == REQ_OP_FLUSH ||
- blk_op_is_passthrough(data->cmd_flags))
+ if (blk_op_bypass_sched(data->cmd_flags))
return;
WARN_ON_ONCE(data->flags & BLK_MQ_REQ_RESERVED);
- data->rq_flags |= RQF_USE_SCHED;
/*
* By default, sync requests have no limit, and async requests are
@@ -534,6 +528,29 @@ static void blk_mq_limit_depth(struct blk_mq_alloc_data *data)
ops->limit_depth(data->cmd_flags, data);
}
+/*
+ * Finish initializing a request once it has been claimed for an operation.
+ * Cached requests are allocated before that operation is known.
+ */
+static void blk_mq_rq_late_init(struct request *rq, u64 alloc_time_ns)
+{
+ struct elevator_queue *e;
+
+ blk_mq_rq_time_init(rq, alloc_time_ns);
+
+ if (!(rq->rq_flags & RQF_SCHED_TAGS) || (rq->rq_flags & RQF_RESV) ||
+ blk_op_bypass_sched(rq->cmd_flags))
+ return;
+
+ rq->rq_flags |= RQF_USE_SCHED;
+ INIT_HLIST_NODE(&rq->hash);
+ RB_CLEAR_NODE(&rq->rb_node);
+
+ e = rq->q->elevator;
+ if (e->type->ops.prepare_request)
+ e->type->ops.prepare_request(rq);
+}
+
static struct request *__blk_mq_alloc_requests(struct blk_mq_alloc_data *data)
{
struct request_queue *q = data->q;
@@ -562,7 +579,7 @@ retry:
if (data->nr_tags > 1) {
rq = __blk_mq_alloc_requests_batch(data);
if (rq) {
- blk_mq_rq_time_init(rq, alloc_time_ns);
+ blk_mq_rq_late_init(rq, alloc_time_ns);
return rq;
}
data->nr_tags = 1;
@@ -590,7 +607,7 @@ retry:
if (!(data->rq_flags & RQF_SCHED_TAGS))
blk_mq_inc_active_requests(data->hctx);
rq = blk_mq_rq_ctx_init(data, blk_mq_tags_from_data(data), tag);
- blk_mq_rq_time_init(rq, alloc_time_ns);
+ blk_mq_rq_late_init(rq, alloc_time_ns);
return rq;
}
@@ -637,8 +654,6 @@ static struct request *blk_mq_alloc_cached_request(struct request_queue *q,
if (plug->nr_ios == 1)
return NULL;
rq = blk_mq_rq_cache_fill(q, plug, opf, flags);
- if (!rq)
- return NULL;
} else {
rq = rq_list_peek(&plug->cached_rqs);
if (!rq || rq->q != q)
@@ -646,15 +661,13 @@ static struct request *blk_mq_alloc_cached_request(struct request_queue *q,
if (blk_mq_get_hctx_type(opf) != rq->mq_hctx->type)
return NULL;
- if (op_is_flush(rq->cmd_flags) != op_is_flush(opf))
- return NULL;
rq_list_pop(&plug->cached_rqs);
- blk_mq_rq_time_init(rq, blk_time_get_ns());
+ rq->cmd_flags = opf;
+ INIT_LIST_HEAD(&rq->queuelist);
+ blk_mq_rq_late_init(rq, blk_time_get_ns());
}
- rq->cmd_flags = opf;
- INIT_LIST_HEAD(&rq->queuelist);
return rq;
}
@@ -766,7 +779,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q,
if (!(data.rq_flags & RQF_SCHED_TAGS))
blk_mq_inc_active_requests(data.hctx);
rq = blk_mq_rq_ctx_init(&data, blk_mq_tags_from_data(&data), tag);
- blk_mq_rq_time_init(rq, alloc_time_ns);
+ blk_mq_rq_late_init(rq, alloc_time_ns);
rq->__data_len = 0;
rq->phys_gap_bit = 0;
rq->__sector = (sector_t) -1;
@@ -3060,8 +3073,6 @@ static struct request *blk_mq_get_cached_request(struct blk_plug *plug,
if (type != rq->mq_hctx->type &&
(type != HCTX_TYPE_READ || rq->mq_hctx->type != HCTX_TYPE_DEFAULT))
return NULL;
- if (op_is_flush(rq->cmd_flags) != op_is_flush(opf))
- return NULL;
rq_list_pop(&plug->cached_rqs);
return rq;
}
@@ -3163,9 +3174,9 @@ void blk_mq_submit_bio(struct bio *bio)
new_request:
if (rq) {
rq_qos_throttle(rq->q, bio);
- blk_mq_rq_time_init(rq, blk_time_get_ns());
rq->cmd_flags = bio->bi_opf;
INIT_LIST_HEAD(&rq->queuelist);
+ blk_mq_rq_late_init(rq, blk_time_get_ns());
} else {
rq = blk_mq_get_new_requests(q, plug, bio);
if (unlikely(!rq)) {
diff --git a/block/fops.c b/block/fops.c
index 2ce7c6c4714e..c915ec0126d7 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -402,6 +402,11 @@ static ssize_t blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
if (blkdev_dio_invalid(bdev, iocb, iter))
return -EINVAL;
+ /* HIPRI needs private as bio; HAS_METADATA keeps it as uio_meta */
+ if ((iocb->ki_flags & IOCB_HIPRI) &&
+ (iocb->ki_flags & IOCB_HAS_METADATA))
+ return -EOPNOTSUPP;
+
if (iov_iter_rw(iter) == WRITE) {
u16 max_write_streams = bdev_max_write_streams(bdev);
diff --git a/block/mq-deadline.c b/block/mq-deadline.c
index 5f643c0ce2a8..e5db1ee097c3 100644
--- a/block/mq-deadline.c
+++ b/block/mq-deadline.c
@@ -685,7 +685,7 @@ static void dd_insert_requests(struct blk_mq_hw_ctx *hctx,
blk_mq_free_requests(&free);
}
-/* Callback from inside blk_mq_rq_ctx_init(). */
+/* Callback from inside blk_mq_rq_late_init(). */
static void dd_prepare_request(struct request *rq)
{
rq->elv.priv[0] = NULL;
diff --git a/crypto/aes.c b/crypto/aes.c
index 94791f481e98..51eee78396ea 100644
--- a/crypto/aes.c
+++ b/crypto/aes.c
@@ -637,7 +637,18 @@ static struct skcipher_alg skcipher_algs[] = {
.decrypt = crypto_aes_cbc_decrypt,
},
#endif
-#if IS_ENABLED(CONFIG_CRYPTO_CTS)
+ /*
+ * Don't register library-based "cts(cbc(aes))" on architectures where
+ * it might block a "better" implementation from being instantiated via
+ * the "cts" template. These exclusions are temporary and will go away
+ * as the arch-optimized AES code is migrated into the library.
+ */
+#if IS_ENABLED(CONFIG_CRYPTO_CTS) && \
+ !(IS_ENABLED(CONFIG_ARM) || \
+ IS_ENABLED(CONFIG_ARM64) || \
+ IS_ENABLED(CONFIG_PPC) || \
+ IS_ENABLED(CONFIG_S390) || \
+ IS_ENABLED(CONFIG_SPARC))
{
.base.cra_name = "cts(cbc(aes))",
.base.cra_driver_name = "cts-cbc-aes-lib",
@@ -687,7 +698,13 @@ static struct skcipher_alg skcipher_algs[] = {
.decrypt = crypto_aes_xctr_crypt,
},
#endif
-#if IS_ENABLED(CONFIG_CRYPTO_XTS)
+ /*
+ * Don't register library-based "xts(aes)" on architectures where it
+ * might block a "better" implementation from being instantiated via the
+ * "xts" template. This exclusion is temporary and will go away when
+ * the library AES-XTS is optimized for SPARC.
+ */
+#if IS_ENABLED(CONFIG_CRYPTO_XTS) && !IS_ENABLED(CONFIG_SPARC)
{
.base.cra_name = "xts(aes)",
.base.cra_driver_name = "xts-aes-lib",
@@ -980,7 +997,20 @@ static __maybe_unused int crypto_aes_ccm_decrypt(struct aead_request *req)
}
static struct aead_alg aead_algs[] = {
-#if IS_ENABLED(CONFIG_CRYPTO_GCM)
+ /*
+ * Don't register library-based "gcm(aes)" and "rfc4106(gcm(aes))" on
+ * architectures where they might block a "better" implementation from
+ * being instantiated via the "gcm" and "rfc4106" templates. These
+ * exclusions are temporary and will go away as the arch-optimized AES
+ * code is migrated into the library.
+ */
+#if IS_ENABLED(CONFIG_CRYPTO_GCM) && \
+ !(IS_ENABLED(CONFIG_ARM) || \
+ IS_ENABLED(CONFIG_ARM64) || \
+ IS_ENABLED(CONFIG_PPC) || \
+ IS_ENABLED(CONFIG_RISCV) || \
+ IS_ENABLED(CONFIG_S390) || \
+ IS_ENABLED(CONFIG_SPARC))
{
.base.cra_name = "gcm(aes)",
.base.cra_driver_name = "gcm-aes-lib",
@@ -1012,7 +1042,20 @@ static struct aead_alg aead_algs[] = {
.chunksize = AES_BLOCK_SIZE,
},
#endif /* CONFIG_CRYPTO_GCM */
-#if IS_ENABLED(CONFIG_CRYPTO_CCM)
+ /*
+ * Don't register library-based "ccm(aes)" on architectures where it
+ * might block a "better" implementation from being instantiated via the
+ * "ccm" template. These exclusions are temporary and will go away as
+ * the arch-optimized AES code is migrated into the library.
+ */
+#if IS_ENABLED(CONFIG_CRYPTO_CCM) && \
+ !(IS_ENABLED(CONFIG_ARM) || \
+ IS_ENABLED(CONFIG_ARM64) || \
+ IS_ENABLED(CONFIG_PPC) || \
+ IS_ENABLED(CONFIG_RISCV) || \
+ IS_ENABLED(CONFIG_S390) || \
+ IS_ENABLED(CONFIG_SPARC) || \
+ IS_ENABLED(CONFIG_X86))
{
.base.cra_name = "ccm(aes)",
.base.cra_driver_name = "ccm-aes-lib",
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index b71b423c56bd..b16ea0ff9ed9 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -51,9 +51,9 @@ pub(crate) struct CouldNotDeliverCriticalIncrement;
/// about to drop the weak reference, then the strong increment could be processed after the
/// other thread has already exited, which would be too late.
///
-/// Note that trying to create a `ListArc` to the node can succeed even if `has_normal_push` is
+/// Note that trying to create a `ListArc` to the node can succeed even if `has_pushed_node` is
/// set. This is because another thread might just have popped the node from a todo list, but not
-/// yet called `do_work`. However, if `has_normal_push` is false, then creating a `ListArc` should
+/// yet called `do_work`. However, if `has_pushed_node` is false, then creating a `ListArc` should
/// always succeed.
///
/// Like the other fields in `NodeInner`, the delivery state is protected by the process lock.
@@ -734,7 +734,21 @@ impl DeliverToRead for Node {
self.do_work_locked(writer, owner_inner)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ let _drop_outside_lock;
+ let mut owner_inner = self.owner.inner.lock();
+
+ // We only do something on BINDER_THREAD_EXIT, not process exit.
+ if owner_inner.is_dead {
+ return;
+ }
+
+ // If BINDER_THREAD_EXIT is invoked on a thread with a pending node refcount update, we
+ // should move ourselves to ensure the refcount update is still delivered.
+ if let Some(node) = ListArc::try_from_arc_borrow(self.as_arc_borrow()) {
+ _drop_outside_lock = owner_inner.push_work(&self.owner, node);
+ }
+ }
fn should_sync_wakeup(&self) -> bool {
false
diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs
index 02085beb6fab..edfbccf6e6da 100644
--- a/drivers/android/binder/node/wrapper.rs
+++ b/drivers/android/binder/node/wrapper.rs
@@ -55,7 +55,39 @@ impl DeliverToRead for NodeWrapper {
node.do_work_locked(writer, owner_inner)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ let _drop_outside_lock;
+ let node = &self.node;
+ let mut owner_inner = node.owner.inner.lock();
+
+ // We only do something on BINDER_THREAD_EXIT, not process exit.
+ if owner_inner.is_dead {
+ return;
+ }
+
+ // We transfer the responsibility of the node refcount update to the scheduled Node because
+ // NodeWrapper has no way to re-create the ListArc.
+ let inner = node.inner.access_mut(&mut owner_inner);
+
+ let ds = &mut inner.delivery_state;
+ assert!(ds.has_pushed_wrapper);
+ assert!(ds.has_strong_zero2one);
+ ds.has_pushed_wrapper = false;
+
+ // We are changing the state to one where the Node is the strong zero2one update instead of
+ // the wrapper.
+ ds.has_weak_zero2one = false;
+
+ if !ds.has_pushed_node {
+ if let Some(node2) = ListArc::try_from_arc_borrow(node.as_arc_borrow()) {
+ ds.has_pushed_node = true;
+ _drop_outside_lock = owner_inner.push_work(&node.owner, node2);
+ } else {
+ // This can't actually happen.
+ ds.has_strong_zero2one = false;
+ }
+ }
+ }
fn should_sync_wakeup(&self) -> bool {
false
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index 4df006b98ebf..e5ae3b0e56c5 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -1728,7 +1728,7 @@ impl Thread {
self.unwind_transaction_stack();
// Cancel all pending work items.
- while let Ok(Some(work)) = self.get_work_local(false) {
+ while let Some(work) = self.pop_work_even_if_deferred() {
work.into_arc().cancel();
}
}
diff --git a/drivers/block/drbd/drbd_nl_gen.c b/drivers/block/drbd/drbd_nl_gen.c
index 9753dc789bde..a3b24aa58297 100644
--- a/drivers/block/drbd/drbd_nl_gen.c
+++ b/drivers/block/drbd/drbd_nl_gen.c
@@ -638,10 +638,6 @@ const struct genl_split_ops drbd_nl_ops[32] = {
},
};
-static const struct genl_multicast_group drbd_nl_mcgrps[] = {
- [DRBD_NLGRP_EVENTS] = { "events", },
-};
-
static int __drbd_cfg_context_from_attrs(struct drbd_cfg_context *s,
struct nlattr ***ret_nested_attribute_table,
struct genl_info *info)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 32bf3ba07a9d..c8bfbf8435ac 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -743,6 +743,7 @@ static int virtblk_read_zoned_limits(struct virtio_blk *vblk,
dev_warn(&vdev->dev, "zero write granularity reported\n");
return -ENODEV;
}
+ lim->zone_write_granularity = wg;
lim->physical_block_size = wg;
lim->io_min = wg;
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 909a265fd906..c69a5d226e88 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -289,7 +289,8 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code)
goto unlock;
}
- bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1));
+ bt_dev_err(hdev, "Exception info %.*s", (int)(skb->len - 1),
+ (char *)(skb->data + 1));
kfree_skb(skb);
@@ -408,8 +409,16 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
/* DDC file contains one or more DDC structure which has
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
*/
- while (fw->size > fw_ptr - fw->data) {
- u8 cmd_plen = fw_ptr[0] + sizeof(u8);
+ while (fw->size > (size_t)(fw_ptr - fw->data)) {
+ size_t remaining = fw->size - (fw_ptr - fw->data);
+ unsigned int cmd_plen = fw_ptr[0] + 1U;
+
+ if (cmd_plen < 3 || cmd_plen > U8_MAX || cmd_plen > remaining) {
+ bt_dev_err(hdev, "Malformed DDC record (plen=%u, remaining=%zu)",
+ cmd_plen, remaining);
+ release_firmware(fw);
+ return -EINVAL;
+ }
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
HCI_INIT_TIMEOUT);
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 2819e001797b..f333b01f7465 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1197,7 +1197,7 @@ static int btintel_pcie_recv_frame(struct btintel_pcie_data *data,
{
int ret;
u8 pkt_type;
- u16 plen;
+ u32 plen;
u32 pcie_pkt_type;
void *pdata;
struct hci_dev *hdev = data->hdev;
@@ -2219,6 +2219,16 @@ static int btintel_pcie_send_frame(struct hci_dev *hdev,
if (test_bit(BTINTEL_PCIE_RECOVERY_IN_PROGRESS, &data->flags))
return -ENODEV;
+ /* Account for the 4-byte PCIe type header prepended before the
+ * DMA copy. Written as a subtraction to avoid wrap-around on
+ * attacker-controlled skb->len.
+ */
+ if (skb->len > BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN) {
+ bt_dev_err(hdev, "Packet too large: %u > %u", skb->len,
+ BTINTEL_PCIE_BUFFER_SIZE - BTINTEL_PCIE_HCI_TYPE_LEN);
+ return -EMSGSIZE;
+ }
+
/* Due to the fw limitation, the type header of the packet should be
* 4 bytes unlike 1 byte for UART. In UART, the firmware can read
* the first byte to get the packet type and redirect the rest of data
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index f745a098908b..1ccdbde98b69 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -268,7 +268,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf,
int err;
ssize_t len;
- len = tpm_transmit(chip, buf->data, PAGE_SIZE);
+ len = tpm_transmit(chip, buf->data, buf->capacity);
if (len < 0)
return len;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 48cec39995fe..ae22295df798 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -281,8 +281,10 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
}
tpm_buf_append_u16(buf, num_bytes);
err = tpm_buf_fill_hmac_session(chip, buf);
- if (err)
+ if (err) {
+ tpm2_end_auth_session(chip);
return err;
+ }
err = tpm_transmit_cmd(chip, buf,
offsetof(struct tpm2_get_random_out,
@@ -305,7 +307,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
out = (struct tpm2_get_random_out *)&buf->data[offset];
recd = min_t(u32, be16_to_cpu(out->size), num_bytes);
if (tpm_buf_length(buf) <
- TPM_HEADER_SIZE +
+ offset +
offsetof(struct tpm2_get_random_out, buffer) +
recd) {
tpm2_end_auth_session(chip);
diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c
index cf8f1fd6790b..ca1e2bf424e1 100644
--- a/drivers/char/tpm/tpm2-sessions.c
+++ b/drivers/char/tpm/tpm2-sessions.c
@@ -975,6 +975,7 @@ static int tpm2_load_null(struct tpm_chip *chip, u32 *null_key)
/* Deduce from the name change TPM interference: */
dev_err(&chip->dev, "null key integrity check failed\n");
tpm2_flush_context(chip, tmp_null_key);
+ rc = -ENODEV;
err:
if (rc) {
diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c
index 92b930d5ff30..39df15c8a3e6 100644
--- a/drivers/clk/spacemit/ccu-k3.c
+++ b/drivers/clk/spacemit/ccu-k3.c
@@ -30,14 +30,87 @@ static const struct ccu_pll_rate_tbl pll2_rate_tbl[] = {
};
static const struct ccu_pll_rate_tbl pll3_rate_tbl[] = {
+ CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080),
+ CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181),
+ CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181),
+ CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282),
+ CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282),
+ CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282),
+ CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383),
+ CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383),
+ CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383),
+ CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484),
+ CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484),
+ CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484),
+ CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585),
+ CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585),
+ CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585),
+ CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686),
+ CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686),
+ CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686),
+ CCU_PLLA_RATE(2050000000UL, 0x0b2a2d55, 0x00005500, 0xa0558686),
+ CCU_PLLA_RATE(2100000000UL, 0x0b2b3000, 0x00000000, 0xa0558787),
+ CCU_PLLA_RATE(2150000000UL, 0x0b2c32aa, 0x0000ab00, 0xa0558787),
CCU_PLLA_RATE(2200000000UL, 0x0b2d3555, 0x00005500, 0xa0558787),
+ CCU_PLLA_RATE(2250000000UL, 0x0b2e3800, 0x00000000, 0xa0558888),
+ CCU_PLLA_RATE(2300000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0558888),
+ CCU_PLLA_RATE(2350000000UL, 0x0b303d55, 0x00005500, 0xa0558888),
+ CCU_PLLA_RATE(2400000000UL, 0x0b320000, 0x00000000, 0xa0558989),
};
static const struct ccu_pll_rate_tbl pll4_rate_tbl[] = {
+ CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080),
+ CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181),
+ CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181),
+ CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282),
+ CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282),
+ CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282),
+ CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383),
+ CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383),
+ CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383),
+ CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484),
+ CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484),
+ CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484),
+ CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585),
+ CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585),
+ CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585),
+ CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686),
+ CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686),
+ CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686),
+ CCU_PLLA_RATE(2050000000UL, 0x0b2a2d55, 0x00005500, 0xa0558686),
+ CCU_PLLA_RATE(2100000000UL, 0x0b2b3000, 0x00000000, 0xa0558787),
+ CCU_PLLA_RATE(2150000000UL, 0x0b2c32aa, 0x0000ab00, 0xa0558787),
CCU_PLLA_RATE(2200000000UL, 0x0b2d3555, 0x00005500, 0xa0558787),
+ CCU_PLLA_RATE(2250000000UL, 0x0b2e3800, 0x00000000, 0xa0558888),
+ CCU_PLLA_RATE(2300000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0558888),
+ CCU_PLLA_RATE(2350000000UL, 0x0b303d55, 0x00005500, 0xa0558888),
+ CCU_PLLA_RATE(2400000000UL, 0x0b320000, 0x00000000, 0xa0558989),
};
static const struct ccu_pll_rate_tbl pll5_rate_tbl[] = {
+ CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080),
+ CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181),
+ CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181),
+ CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282),
+ CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282),
+ CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282),
+ CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383),
+ CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383),
+ CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383),
+ CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484),
+ CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484),
+ CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484),
+ CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585),
+ CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585),
+ CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585),
+ CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686),
+ CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686),
CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686),
};
@@ -50,6 +123,25 @@ static const struct ccu_pll_rate_tbl pll7_rate_tbl[] = {
};
static const struct ccu_pll_rate_tbl pll8_rate_tbl[] = {
+ CCU_PLLA_RATE(1050000000UL, 0x0b2b3000, 0x00000000, 0xa0458080),
+ CCU_PLLA_RATE(1100000000UL, 0x0b2d3555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1150000000UL, 0x0b2f3aaa, 0x0000ab00, 0xa0458181),
+ CCU_PLLA_RATE(1200000000UL, 0x0b320000, 0x00000000, 0xa0458181),
+ CCU_PLLA_RATE(1250000000UL, 0x0b340555, 0x00005500, 0xa0458181),
+ CCU_PLLA_RATE(1300000000UL, 0x0b360aaa, 0x0000ab00, 0xa0458282),
+ CCU_PLLA_RATE(1350000000UL, 0x0b381000, 0x00000000, 0xa0458282),
+ CCU_PLLA_RATE(1400000000UL, 0x0b3a1555, 0x00005500, 0xa0458282),
+ CCU_PLLA_RATE(1450000000UL, 0x0b3c1aaa, 0x0000ab00, 0xa0458383),
+ CCU_PLLA_RATE(1500000000UL, 0x0b3e2000, 0x00000000, 0xa0458383),
+ CCU_PLLA_RATE(1550000000UL, 0x0b402555, 0x00005500, 0xa0458383),
+ CCU_PLLA_RATE(1600000000UL, 0x0b422aaa, 0x0000ab00, 0xa0458484),
+ CCU_PLLA_RATE(1650000000UL, 0x0b443000, 0x00000000, 0xa0458484),
+ CCU_PLLA_RATE(1700000000UL, 0x0b463555, 0x00005500, 0xa0458484),
+ CCU_PLLA_RATE(1750000000UL, 0x0b483aaa, 0x0000ab00, 0xa0458585),
+ CCU_PLLA_RATE(1800000000UL, 0x0b4b0000, 0x00000000, 0xa0458585),
+ CCU_PLLA_RATE(1850000000UL, 0x0b4d0555, 0x00005500, 0xa0458585),
+ CCU_PLLA_RATE(1900000000UL, 0x0b4f0aaa, 0x0000ab00, 0xa0458686),
+ CCU_PLLA_RATE(1950000000UL, 0x0b511000, 0x00000000, 0xa0458686),
CCU_PLLA_RATE(2000000000UL, 0x0b292aaa, 0x0000ab00, 0xa0558686),
};
diff --git a/drivers/clk/ti/composite.c b/drivers/clk/ti/composite.c
index 83c3592cd179..c379bbdae25a 100644
--- a/drivers/clk/ti/composite.c
+++ b/drivers/clk/ti/composite.c
@@ -52,7 +52,7 @@ static const struct clk_ops ti_composite_gate_ops = {
struct component_clk {
int num_parents;
- struct clk_parent_data *parent_data;
+ const char **parent_names;
struct device_node *node;
int type;
struct clk_hw *hw;
@@ -116,7 +116,7 @@ static void __init _register_composite(void *user,
struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw);
struct component_clk *comp;
int num_parents = 0;
- struct clk_parent_data *parent_data = NULL;
+ const char **parent_names = NULL;
const char *name;
int i;
int ret;
@@ -155,7 +155,7 @@ static void __init _register_composite(void *user,
continue;
if (comp->num_parents) {
num_parents = comp->num_parents;
- parent_data = comp->parent_data;
+ parent_names = comp->parent_names;
break;
}
}
@@ -166,8 +166,8 @@ static void __init _register_composite(void *user,
}
name = ti_dt_clk_name(node);
- clk = clk_register_composite_pdata(NULL, name,
- parent_data, num_parents,
+ clk = clk_register_composite(NULL, name,
+ parent_names, num_parents,
_get_hw(cclk, CLK_COMPONENT_TYPE_MUX),
&ti_clk_mux_ops,
_get_hw(cclk, CLK_COMPONENT_TYPE_DIVIDER),
@@ -190,7 +190,7 @@ cleanup:
if (!cclk->comp_clks[i])
continue;
list_del(&cclk->comp_clks[i]->link);
- kfree(cclk->comp_clks[i]->parent_data);
+ kfree(cclk->comp_clks[i]->parent_names);
kfree(cclk->comp_clks[i]);
}
@@ -237,9 +237,8 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw,
int type)
{
unsigned int num_parents;
- struct clk_parent_data *parent_data;
+ const char **parent_names;
struct component_clk *clk;
- unsigned int i;
num_parents = of_clk_get_parent_count(node);
@@ -248,21 +247,20 @@ int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw,
return -EINVAL;
}
- parent_data = kzalloc_objs(*parent_data, num_parents);
- if (!parent_data)
+ parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL);
+ if (!parent_names)
return -ENOMEM;
- for (i = 0; i < num_parents; i++)
- parent_data[i].index = i;
+ of_clk_parent_fill(node, parent_names, num_parents);
clk = kzalloc_obj(*clk);
if (!clk) {
- kfree(parent_data);
+ kfree(parent_names);
return -ENOMEM;
}
clk->num_parents = num_parents;
- clk->parent_data = parent_data;
+ clk->parent_names = parent_names;
clk->hw = hw;
clk->node = node;
clk->type = type;
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 96515880b4ac..54dde8419bdc 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -2590,8 +2590,8 @@ static void cpufreq_update_pressure(struct cpufreq_policy *policy)
cpu = cpumask_first(policy->related_cpus);
max_freq = arch_scale_freq_ref(cpu);
- if (!max_freq)
- max_freq = policy->cpuinfo.max_freq;
+ if (!max_freq && cpufreq_driver->scale_freq_ref)
+ max_freq = cpufreq_driver->scale_freq_ref(policy);
capped_freq = policy->max;
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index ceb340f7a110..17158cb25dd8 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -1135,6 +1135,14 @@ static bool hybrid_clear_max_perf_cpu(void)
return ret;
}
+static unsigned int intel_pstate_scale_freq_ref(struct cpufreq_policy *policy)
+{
+ if (READ_ONCE(all_cpu_data[policy->cpu]->capacity_perf))
+ return policy->cpuinfo.max_freq;
+
+ return 0;
+}
+
static void intel_pstate_update_freq_limits(struct cpudata *cpu)
{
int scaling = cpu->pstate.scaling;
@@ -3088,6 +3096,7 @@ static struct cpufreq_driver intel_pstate = {
.offline = intel_pstate_cpu_offline,
.online = intel_pstate_cpu_online,
.update_limits = intel_pstate_update_limits,
+ .scale_freq_ref = intel_pstate_scale_freq_ref,
.name = "intel_pstate",
};
@@ -3411,6 +3420,7 @@ static struct cpufreq_driver intel_cpufreq = {
.suspend = intel_cpufreq_suspend,
.resume = intel_pstate_resume,
.update_limits = intel_pstate_update_limits,
+ .scale_freq_ref = intel_pstate_scale_freq_ref,
.name = "intel_cpufreq",
};
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 68846f583eee..bb95dab847b3 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -453,15 +453,6 @@ free:
return res;
}
-static void altr_sdram_remove(struct platform_device *pdev)
-{
- struct mem_ctl_info *mci = platform_get_drvdata(pdev);
-
- edac_mc_del_mc(&pdev->dev);
- edac_mc_free(mci);
- platform_set_drvdata(pdev, NULL);
-}
-
/*
* If you want to suspend, need to disable EDAC by removing it
* from the device tree or defconfig.
@@ -481,13 +472,13 @@ static const struct dev_pm_ops altr_sdram_pm_ops = {
static struct platform_driver altr_sdram_edac_driver = {
.probe = altr_sdram_probe,
- .remove = altr_sdram_remove,
.driver = {
.name = "altr_sdram_edac",
#ifdef CONFIG_PM
.pm = &altr_sdram_pm_ops,
#endif
.of_match_table = altr_sdram_ctrl_of_match,
+ .suppress_bind_attrs = true,
},
};
@@ -517,6 +508,7 @@ static struct platform_driver altr_edac_driver = {
.driver = {
.name = "socfpga_ecc_manager",
.of_match_table = altr_edac_of_match,
+ .suppress_bind_attrs = true,
},
};
module_platform_driver(altr_edac_driver);
@@ -803,22 +795,12 @@ fail:
return res;
}
-static void altr_edac_device_remove(struct platform_device *pdev)
-{
- struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
- struct altr_edac_device_dev *drvdata = dci->pvt_info;
-
- debugfs_remove_recursive(drvdata->debugfs_dir);
- edac_device_del_device(&pdev->dev);
- edac_device_free_ctl_info(dci);
-}
-
static struct platform_driver altr_edac_device_driver = {
.probe = altr_edac_device_probe,
- .remove = altr_edac_device_remove,
.driver = {
.name = "altr_edac_device",
.of_match_table = altr_edac_device_of_match,
+ .suppress_bind_attrs = true,
},
};
module_platform_driver(altr_edac_device_driver);
@@ -944,7 +926,7 @@ static int __maybe_unused altr_init_memory_port(void __iomem *ioaddr, int port)
return ret;
}
-static __init int __maybe_unused
+static int __maybe_unused
altr_init_a10_ecc_block(struct device_node *np, u32 irq_mask,
u32 ecc_ctrl_en_mask, bool dual_port)
{
@@ -1019,7 +1001,7 @@ out:
static int validate_parent_available(struct device_node *np);
static const struct of_device_id altr_edac_a10_device_of_match[];
-static int __init __maybe_unused altr_init_a10_ecc_device_type(char *compat)
+static int __maybe_unused altr_init_a10_ecc_device_type(char *compat)
{
int irq;
struct device_node *child, *np;
@@ -1348,7 +1330,7 @@ static const struct edac_device_prv_data a10_l2ecc_data = {
#ifdef CONFIG_EDAC_ALTERA_ETHERNET
-static int __init socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev)
+static int socfpga_init_ethernet_ecc(struct altr_edac_device_dev *dev)
{
int ret;
@@ -1378,7 +1360,7 @@ static const struct edac_device_prv_data a10_enetecc_data = {
#ifdef CONFIG_EDAC_ALTERA_NAND
-static int __init socfpga_init_nand_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_nand_ecc(struct altr_edac_device_dev *device)
{
int ret;
@@ -1408,7 +1390,7 @@ static const struct edac_device_prv_data a10_nandecc_data = {
#ifdef CONFIG_EDAC_ALTERA_DMA
-static int __init socfpga_init_dma_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_dma_ecc(struct altr_edac_device_dev *device)
{
int ret;
@@ -1438,7 +1420,7 @@ static const struct edac_device_prv_data a10_dmaecc_data = {
#ifdef CONFIG_EDAC_ALTERA_USB
-static int __init socfpga_init_usb_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_usb_ecc(struct altr_edac_device_dev *device)
{
int ret;
@@ -1468,7 +1450,7 @@ static const struct edac_device_prv_data a10_usbecc_data = {
#ifdef CONFIG_EDAC_ALTERA_QSPI
-static int __init socfpga_init_qspi_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_qspi_ecc(struct altr_edac_device_dev *device)
{
int ret;
@@ -1524,6 +1506,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
dci = edac_device_alloc_ctl_info(sizeof(*altdev), ecc_name, 1,
ecc_name, 1, 0, edac_idx);
if (!dci) {
+ of_node_put(np);
edac_printk(KERN_ERR, EDAC_DEVICE,
"%s: Unable to allocate PortB EDAC device\n",
ecc_name);
@@ -1534,8 +1517,11 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
altdev = dci->pvt_info;
*altdev = *device;
- if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL))
+ if (!devres_open_group(device->edac->dev, altr_portb_setup, GFP_KERNEL)) {
+ edac_device_free_ctl_info(dci);
+ of_node_put(np);
return -ENOMEM;
+ }
/* Update PortB specific values */
altdev->edac_dev_name = ecc_name;
@@ -1600,6 +1586,8 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
rc = -ENOMEM;
goto err_release_group_1;
}
+ of_node_put(np);
+
altr_create_edacdev_dbgfs(dci, prv);
list_add(&altdev->next, &altdev->edac->a10_ecc_devices);
@@ -1609,14 +1597,20 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
return 0;
err_release_group_1:
- edac_device_free_ctl_info(dci);
+ /*
+ * Release the devres group first so the managed IRQs are
+ * unregistered before dci (which contains the IRQ handler's
+ * data via dci->pvt_info) is freed, avoiding a use-after-free.
+ */
devres_release_group(device->edac->dev, altr_portb_setup);
+ edac_device_free_ctl_info(dci);
+ of_node_put(np);
edac_printk(KERN_ERR, EDAC_DEVICE,
"%s:Error setting up EDAC device: %d\n", ecc_name, rc);
return rc;
}
-static int __init socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device)
+static int socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device)
{
int rc = -ENODEV;
struct device_node *child;
@@ -1638,7 +1632,7 @@ static int __init socfpga_init_sdmmc_ecc(struct altr_edac_device_dev *device)
goto exit;
/* Setup portB */
- return altr_portb_setup(device);
+ rc = altr_portb_setup(device);
exit:
of_node_put(child);
@@ -2013,9 +2007,17 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
return 0;
err_release_group1:
+ /*
+ * Release the devres group first so the managed IRQs are
+ * unregistered before dci (which contains the IRQ handler's
+ * data via dci->pvt_info) is freed, avoiding a use-after-free.
+ */
+ devres_release_group(edac->dev, NULL);
edac_device_free_ctl_info(dci);
+ goto err_print;
err_release_group:
devres_release_group(edac->dev, NULL);
+err_print:
edac_printk(KERN_ERR, EDAC_DEVICE,
"%s:Error setting up EDAC device: %d\n", ecc_name, rc);
@@ -2214,6 +2216,7 @@ static struct platform_driver altr_edac_a10_driver = {
.driver = {
.name = "socfpga_a10_ecc_manager",
.of_match_table = altr_edac_a10_of_match,
+ .suppress_bind_attrs = true,
},
};
module_platform_driver(altr_edac_a10_driver);
diff --git a/drivers/edac/versalnet_edac.c b/drivers/edac/versalnet_edac.c
index 9e65c4b1d99d..eae9fea60db1 100644
--- a/drivers/edac/versalnet_edac.c
+++ b/drivers/edac/versalnet_edac.c
@@ -953,6 +953,7 @@ static void mc_remove(struct platform_device *pdev)
remove_versalnet(priv);
rproc_shutdown(priv->mcdi->r5_rproc);
cdx_mcdi_finish(priv->mcdi);
+ rproc_put(priv->mcdi->r5_rproc);
kfree(priv->mcdi);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
index 9014678d75ab..be4d00ac96b3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -318,7 +318,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
r = device_for_each_child(adev->acp.parent, &adev->acp.acp_genpd->gpd,
acp_genpd_add_device);
if (r)
- goto failure;
+ goto failure_remove_mfd;
break;
}
default:
@@ -443,7 +443,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
r = device_for_each_child(adev->acp.parent, &adev->acp.acp_genpd->gpd,
acp_genpd_add_device);
if (r)
- goto failure;
+ goto failure_remove_mfd;
}
/* Assert Soft reset of ACP */
@@ -461,7 +461,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
if (--count == 0) {
dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
r = -ETIMEDOUT;
- goto failure;
+ goto failure_remove_mfd;
}
udelay(100);
}
@@ -479,7 +479,7 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
if (--count == 0) {
dev_err(&adev->pdev->dev, "Failed to reset ACP\n");
r = -ETIMEDOUT;
- goto failure;
+ goto failure_remove_mfd;
}
udelay(100);
}
@@ -489,6 +489,11 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
cgs_write_register(adev->acp.cgs_device, mmACP_SOFT_RESET, val);
return 0;
+failure_remove_mfd:
+ device_for_each_child(adev->acp.parent, NULL,
+ acp_genpd_remove_device);
+ mfd_remove_devices(adev->acp.parent);
+
failure:
kfree(adev->acp.i2s_pdata);
kfree(adev->acp.acp_res);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index 62f5c5cbd7f9..7ab2ad2c5ba9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -1325,6 +1325,8 @@ static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
ip_hw_instance->kobj.kset = &ip_hw_id->hw_id_kset;
res = kobject_add(&ip_hw_instance->kobj, NULL,
"%d", ip_hw_instance->num_instance);
+ if (res)
+ kobject_put(&ip_hw_instance->kobj);
next_ip:
if (reg_base_64)
ip_offset += struct_size(ip, base_address_64,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 5c33c19fd9bc..d9d794a7db96 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2664,6 +2664,14 @@ static int amdgpu_pmops_suspend_noirq(struct device *dev)
struct amdgpu_device *adev = drm_to_adev(drm_dev);
int r;
+ /*
+ * A GPU parked by vga_switcheroo has no power and no PCIe link, so the
+ * ASIC reset below would fail and abort the whole noirq suspend phase.
+ * Bail out like amdgpu_device_prepare/suspend/resume() already do.
+ */
+ if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ return 0;
+
if (amdgpu_acpi_should_gpu_reset(adev)) {
amdgpu_device_lock_reset_domain(adev->reset_domain);
r = amdgpu_asic_reset(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
index 3043ad041bb4..3017ddcefbe2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
@@ -724,8 +724,14 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring,
bool is_guilty_fence;
bool is_guilty_context;
- last_seq = amdgpu_fence_read(ring) & ring->fence_drv.num_fences_mask;
- seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask;
+ last_seq = amdgpu_fence_read(ring);
+ seq = ring->fence_drv.sync_seq;
+
+ /* Hardware completion can precede the interrupt which signals fences. */
+ if (last_seq == seq) {
+ amdgpu_fence_process(ring);
+ return;
+ }
/* If there is nothing to reemit, return early and set an error on the fence
* if applicable. If all of the fences are siganlled, this will be a nop.
@@ -733,7 +739,9 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring,
* we are skipping it on purpose.
*/
if (!ring->ring_backup_entries_to_copy) {
- amdgpu_fence_driver_force_completion(ring, &guilty_fence->base);
+ if (guilty_fence)
+ amdgpu_fence_driver_force_completion(ring,
+ &guilty_fence->base);
return;
}
ring->reemit = true;
@@ -741,9 +749,8 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring,
spin_lock_irqsave(&ring->fence_drv.lock, flags);
do {
last_seq++;
- last_seq &= ring->fence_drv.num_fences_mask;
- ptr = &ring->fence_drv.fences[last_seq];
+ ptr = &ring->fence_drv.fences[last_seq & ring->fence_drv.num_fences_mask];
rcu_read_lock();
unprocessed = rcu_dereference(*ptr);
@@ -800,26 +807,24 @@ void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring,
struct amdgpu_fence *fence;
u32 seq, last_seq;
- last_seq = amdgpu_fence_read(ring) & ring->fence_drv.num_fences_mask;
- seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask;
+ last_seq = amdgpu_fence_read(ring);
+ seq = ring->fence_drv.sync_seq;
ring->ring_backup_entries_to_copy = 0;
- /* if we've already seen this fence, return early.
- * ring->ring_backup_entries_to_copy is set to 0 so
- * the reemit helper will return early as well to
- * avoid getting stuck in a reemit loop.
+ /*
+ * Avoid replay loops for a repeated guilty fence. NULL identifies a
+ * collateral ring, whose pending commands still need backup.
*/
- if (ring->guilty_fence == guilty_fence) {
+ if (guilty_fence && ring->guilty_fence == guilty_fence) {
ring->guilty_fence = NULL;
return;
}
ring->guilty_fence = guilty_fence;
- do {
+ while (last_seq != seq) {
last_seq++;
- last_seq &= ring->fence_drv.num_fences_mask;
- ptr = &ring->fence_drv.fences[last_seq];
+ ptr = &ring->fence_drv.fences[last_seq & ring->fence_drv.num_fences_mask];
rcu_read_lock();
unprocessed = rcu_dereference(*ptr);
@@ -829,7 +834,7 @@ void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring,
amdgpu_ring_backup_unprocessed_command(ring, fence);
}
rcu_read_unlock();
- } while (last_seq != seq);
+ }
}
struct amdgpu_fence *
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 016957cac1f2..3838e464703c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -2342,7 +2342,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
void amdgpu_ttm_enable_buffer_funcs(struct amdgpu_device *adev)
{
struct ttm_resource_manager *man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM);
- u32 num_clear_entities, num_move_entities;
+ u32 num_clear_entities, num_move_entities, sdma_ip_version;
int r, i, j;
if (!adev->mman.initialized || amdgpu_in_reset(adev) ||
@@ -2367,6 +2367,12 @@ void amdgpu_ttm_enable_buffer_funcs(struct amdgpu_device *adev)
num_clear_entities = MIN(adev->mman.num_buffer_funcs_scheds, TTM_NUM_MOVE_FENCES);
num_move_entities = MIN(adev->mman.num_buffer_funcs_scheds, TTM_NUM_MOVE_FENCES);
+ /* TODO: workaround for DCC corruption when moving BOs from multiple queues at
+ * the same time: use a single queue until the root cause is identified and fixed.
+ */
+ sdma_ip_version = amdgpu_ip_version(adev, SDMA0_HWIP, 0);
+ if (sdma_ip_version == IP_VERSION(7, 0, 0) || sdma_ip_version == IP_VERSION(7, 0, 1))
+ num_move_entities = 1;
adev->mman.clear_entities = kzalloc_objs(struct amdgpu_ttm_buffer_entity,
num_clear_entities);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index 0ff5a80aa918..7a006b22e4be 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -3808,6 +3808,7 @@ static int gfx_v11_0_cp_gfx_resume(struct amdgpu_device *adev)
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = REG_SET_FIELD(0, CP_RB0_CNTL, RB_BUFSZ, rb_bufsz);
tmp = REG_SET_FIELD(tmp, CP_RB0_CNTL, RB_BLKSZ, rb_bufsz - 2);
+ tmp = REG_SET_FIELD(tmp, CP_RB0_CNTL, KMD_QUEUE, 1);
WREG32_SOC15(GC, 0, regCP_RB0_CNTL, tmp);
/* Initialize the ring buffer's write pointers */
@@ -4216,6 +4217,7 @@ static int gfx_v11_0_gfx_mqd_init(struct amdgpu_device *adev, void *m,
tmp = regCP_GFX_HQD_CNTL_DEFAULT;
tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, RB_BUFSZ, rb_bufsz);
tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, RB_BLKSZ, rb_bufsz - 2);
+ tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, KMD_QUEUE, prop->kernel_queue);
#ifdef __BIG_ENDIAN
tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, BUF_SWAP, 1);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index e2a81a55c63b..d69c43f1e194 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -2767,6 +2767,7 @@ static int gfx_v12_0_cp_gfx_resume(struct amdgpu_device *adev)
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = REG_SET_FIELD(0, CP_RB0_CNTL, RB_BUFSZ, rb_bufsz);
tmp = REG_SET_FIELD(tmp, CP_RB0_CNTL, RB_BLKSZ, rb_bufsz - 2);
+ tmp = REG_SET_FIELD(tmp, CP_RB0_CNTL, KMD_QUEUE, 1);
WREG32_SOC15(GC, 0, regCP_RB0_CNTL, tmp);
/* Initialize the ring buffer's write pointers */
@@ -3059,6 +3060,7 @@ static int gfx_v12_0_gfx_mqd_init(struct amdgpu_device *adev, void *m,
tmp = regCP_GFX_HQD_CNTL_DEFAULT;
tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, RB_BUFSZ, rb_bufsz);
tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, RB_BLKSZ, rb_bufsz - 2);
+ tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, KMD_QUEUE, prop->kernel_queue);
#ifdef __BIG_ENDIAN
tmp = REG_SET_FIELD(tmp, CP_GFX_HQD_CNTL, BUF_SWAP, 1);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
index 563abcf718b7..493ccfa26655 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
@@ -322,6 +322,14 @@ static const u32 verde_rlc_save_restore_register_list[] =
0x00000000
};
+static void gfx_v6_0_free_microcode(struct amdgpu_device *adev)
+{
+ amdgpu_ucode_release(&adev->gfx.pfp_fw);
+ amdgpu_ucode_release(&adev->gfx.me_fw);
+ amdgpu_ucode_release(&adev->gfx.ce_fw);
+ amdgpu_ucode_release(&adev->gfx.rlc_fw);
+}
+
static int gfx_v6_0_init_microcode(struct amdgpu_device *adev)
{
const char *chip_name;
@@ -3249,6 +3257,7 @@ static int gfx_v6_0_sw_fini(struct amdgpu_ip_block *ip_block)
amdgpu_ring_fini(&adev->gfx.compute_ring[i]);
amdgpu_gfx_rlc_fini(adev);
+ gfx_v6_0_free_microcode(adev);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c
index b05465dc30b4..ba5b1a17af61 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c
@@ -327,7 +327,7 @@ static void gmc_v12_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid,
return;
}
- gmc_v12_0_flush_vm_hub(adev, vmid, vmhub, 0);
+ gmc_v12_0_flush_vm_hub(adev, vmid, vmhub, flush_type);
return;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c
index 5fe43f7eab29..5d0d316fa94e 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_1.c
@@ -389,7 +389,7 @@ static void gmc_v12_1_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid,
return;
}
- gmc_v12_1_flush_vm_hub(adev, vmid, vmhub, 0);
+ gmc_v12_1_flush_vm_hub(adev, vmid, vmhub, flush_type);
return;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
index 18366e16ef3f..1e4a3dcde1a2 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c
@@ -1339,10 +1339,6 @@ static int sdma_v7_1_sw_init(struct amdgpu_ip_block *ip_block)
else
DRM_ERROR("Failed to allocated memory for SDMA IP Dump\n");
-#ifdef CONFIG_DRM_AMDGPU_NAVI3X_USERQ
- adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs;
-#endif
-
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c
index 352c27103305..e926d09128e0 100644
--- a/drivers/gpu/drm/amd/amdgpu/vi.c
+++ b/drivers/gpu/drm/amd/amdgpu/vi.c
@@ -1397,6 +1397,7 @@ struct vi_reset_quirk {
static const struct vi_reset_quirk vi_reset_quirks[] = {
{ 0x67ef, PCI_VENDOR_ID_APPLE, 0x0190, 0xe3 }, /* Radeon Pro 555X */
{ 0x67ef, PCI_VENDOR_ID_APPLE, 0x018f, 0xc2 }, /* Radeon Pro 560X */
+ { 0x67ef, PCI_VENDOR_ID_APPLE, 0x017a, 0xc7 }, /* Radeon Pro 555 */
};
static bool vi_need_reset_on_init(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index fa4054d51f60..d7f2ff5c8f02 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -1752,12 +1752,15 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
}
/*
- * If prange with always mapped flag, update mapping on GPUs with
- * ACCESS attribute
+ * If prange with always mapped flag is not mapped or migrated
+ * yet, update mapping on GPUs with ACCESS attribute. Don't add
+ * them otherwise, ACCESS GPUs may be on different XGMI hives,
+ * that would force hmm_range_fault to migrate VRAM back to ram.
*/
- if (prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)
- bitmap_or(ctx->bitmap, ctx->bitmap, prange->bitmap_access,
- MAX_GPU_INSTANCE);
+ if (bitmap_empty(ctx->bitmap, MAX_GPU_INSTANCE) &&
+ prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)
+ bitmap_copy(ctx->bitmap, prange->bitmap_access,
+ MAX_GPU_INSTANCE);
} else {
bitmap_or(ctx->bitmap, prange->bitmap_access,
prange->bitmap_aip, MAX_GPU_INSTANCE);
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 91fdf3de7202..1b42c4606002 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -426,6 +426,8 @@ static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm)
init_data[i].use_custom_backlight_caps = false;
init_data[i].custom_backlight_caps_config_no = 0;
init_data[i].use_linear_backlight_curve = false;
+ if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
+ init_data[i].use_linear_backlight_curve = true;
init_data[i].def_varibright_enable = 0;
init_data[i].varibright_level = 0;
/*
@@ -1582,9 +1584,14 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
dc_allow_idle_optimizations(adev->dm.dc, false);
dm->cached_dc_state = dc_state_create_copy(dm->dc->current_state);
+ if (!dm->cached_dc_state) {
+ drm_err(adev_to_drm(adev),
+ "Failed to allocate cached DC state during suspend\n");
+ mutex_unlock(&dm->dc_lock);
+ return -ENOMEM;
+ }
- if (dm->cached_dc_state)
- dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, false);
+ dm_gpureset_toggle_interrupts(adev, dm->cached_dc_state, false);
res = amdgpu_dm_commit_zero_streams(dm->dc);
if (res != DC_OK) {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
index b9e90ea449ca..703d0617df6d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -39,6 +39,7 @@
#include "modules/inc/mod_power.h"
#include <linux/backlight.h>
+#include <linux/minmax.h>
#include <linux/power_supply.h>
#include <drm/drm_edid.h>
#include <drm/drm_utils.h>
@@ -118,6 +119,68 @@ static inline u32 scale_fw_to_input(int min, int max, u64 input)
return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
}
+static u32 backlight_signal_to_luminance(const struct amdgpu_dm_backlight_caps *caps,
+ u32 signal)
+{
+ u32 lower_signal, upper_signal;
+ u32 lower_lum, upper_lum;
+ int i;
+
+ if (!caps->data_points)
+ return DIV_ROUND_CLOSEST(signal * 100, AMDGPU_MAX_BL_LEVEL);
+
+ if (signal < caps->luminance_data[0].input_signal) {
+ lower_signal = 0;
+ lower_lum = 0;
+ upper_signal = caps->luminance_data[0].input_signal;
+ upper_lum = caps->luminance_data[0].luminance;
+ } else if (signal >=
+ caps->luminance_data[caps->data_points - 1].input_signal) {
+ lower_signal = caps->luminance_data[caps->data_points - 1].input_signal;
+ lower_lum = caps->luminance_data[caps->data_points - 1].luminance;
+ upper_signal = caps->max_input_signal;
+ upper_lum = 100;
+ } else {
+ for (i = 1;
+ i < caps->data_points &&
+ signal > caps->luminance_data[i].input_signal;
+ i++)
+ ;
+
+ lower_signal = caps->luminance_data[i - 1].input_signal;
+ upper_signal = caps->luminance_data[i].input_signal;
+ lower_lum = caps->luminance_data[i - 1].luminance;
+ upper_lum = caps->luminance_data[i].luminance;
+ }
+
+ if (upper_signal == lower_signal)
+ return upper_lum;
+
+ return lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
+ (signal - lower_signal),
+ upper_signal - lower_signal);
+}
+
+static u32 convert_brightness_to_millipercent(const struct amdgpu_dm_backlight_caps *caps,
+ u32 user_brightness)
+{
+ unsigned int min, max;
+ u32 signal, luminance;
+
+ if (!get_brightness_range(caps, &min, &max))
+ return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000,
+ MAX_BACKLIGHT_LEVEL);
+
+ user_brightness = clamp(user_brightness, min, max);
+ if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
+ return DIV_ROUND_CLOSEST_ULL((u64)user_brightness * 100000, max);
+
+ signal = scale_input_to_fw(max, user_brightness);
+ luminance = backlight_signal_to_luminance(caps, signal);
+
+ return min_t(u32, luminance * 1000, 100000);
+}
+
STATIC_IFN_KUNIT
void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
unsigned int min, unsigned int max,
@@ -260,10 +323,10 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
struct amdgpu_dm_backlight_caps *caps;
struct dc_link *link;
u32 brightness = 0;
+ u32 millipercent = 0;
bool rc = false, reallow_idle = false;
struct drm_connector *connector;
struct dc_stream_state *stream;
- unsigned int min, max;
list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
@@ -286,12 +349,21 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
/* update scratch register */
if (bl_idx == 0)
amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
- brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
+
link = (struct dc_link *)dm->backlight_link[bl_idx];
+ stream = dm_find_stream_with_link(dm, link);
+ if (!stream)
+ return;
- /* Apply brightness quirk */
- if (caps->brightness_mask)
- brightness |= caps->brightness_mask;
+ if (caps->aux_support) {
+ brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
+ /* Apply brightness quirk */
+ if (caps->brightness_mask)
+ brightness |= caps->brightness_mask;
+ } else {
+ millipercent = convert_brightness_to_millipercent(caps,
+ user_brightness);
+ }
if (trace_amdgpu_dm_brightness_enabled()) {
trace_amdgpu_dm_brightness(__builtin_return_address(0),
@@ -301,10 +373,6 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
power_supply_is_system_supplied() > 0);
}
- stream = dm_find_stream_with_link(dm, link);
- if (!stream)
- return;
-
mutex_lock(&dm->dc_lock);
if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
dc_allow_idle_optimizations(dm->dc, false);
@@ -316,10 +384,8 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
} else {
/* power module uses millipercent */
- get_brightness_range(caps, &min, &max);
- brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
rc = mod_power_set_backlight_percent(dm->power_module, stream,
- brightness, 0, false);
+ millipercent, 0, false);
}
/*
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index 154e1f35dcb1..d386bb1475bb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -732,6 +732,24 @@ get_aspect_ratio(const struct drm_display_mode *mode_in)
}
EXPORT_IF_KUNIT(get_aspect_ratio);
+/*
+ * CTA-861 5.1: RGB video formats default to limited range except 640x480 (VIC 1).
+ * With Broadcast RGB left at Automatic follow that default, as i915 does.
+ */
+static bool rgb_output_is_limited_range(const struct dc_crtc_timing *dc_crtc_timing,
+ const struct drm_connector_state *connector_state)
+{
+ switch (connector_state->hdmi.broadcast_rgb) {
+ case DRM_HDMI_BROADCAST_RGB_FULL:
+ return false;
+ case DRM_HDMI_BROADCAST_RGB_LIMITED:
+ return true;
+ default:
+ return connector_state->connector->display_info.is_hdmi &&
+ dc_crtc_timing->vic > 1;
+ }
+}
+
enum dc_color_space
amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
const struct drm_connector_state *connector_state)
@@ -757,7 +775,7 @@ amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
case DRM_MODE_COLORIMETRY_BT2020_RGB:
case DRM_MODE_COLORIMETRY_BT2020_YCC:
if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
- if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
+ if (rgb_output_is_limited_range(dc_crtc_timing, connector_state))
color_space = COLOR_SPACE_2020_RGB_LIMITEDRANGE;
else
color_space = COLOR_SPACE_2020_RGB_FULLRANGE;
@@ -769,7 +787,7 @@ amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
default:
if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
color_space = COLOR_SPACE_SRGB;
- if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
+ if (rgb_output_is_limited_range(dc_crtc_timing, connector_state))
color_space = COLOR_SPACE_SRGB_LIMITED;
/*
* 27030khz is the separation point between HDTV and SDTV
@@ -2256,8 +2274,8 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector,
* below gate which of these entries are actually attempted.
*/
static const enum dc_pixel_encoding encoding_order[] = {
- PIXEL_ENCODING_YCBCR444,
PIXEL_ENCODING_RGB,
+ PIXEL_ENCODING_YCBCR444,
PIXEL_ENCODING_YCBCR422,
PIXEL_ENCODING_YCBCR420,
};
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 045a7f88b754..7261317d4c0f 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -1231,7 +1231,8 @@ static bool get_conv_frl_bw(struct amdgpu_dm_connector *aconnector,
unsigned int max_sink_bw_in_kbps = 0;
unsigned int dsc_max_sink_bw_in_kbps = 0;
- if (aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support &&
+ if (aconnector->dc_sink &&
+ aconnector->dc_link->dc->caps.dp_hdmi21_pcon_support &&
aconnector->mst_downstream_port_caps.bytes.byte0.bits.DWN_STRM_PORTX_TYPE == DOWN_STREAM_DETAILED_HDMI) {
max_conv_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(
aconnector->dc_link->dc,
@@ -1996,6 +1997,9 @@ enum dc_status dm_dp_mst_is_port_support_mode(
struct dc_dsc_config_options dsc_options = {0};
uint32_t stream_kbps;
+ if (!aconnector->dc_sink)
+ return DC_FAIL_BANDWIDTH_VALIDATE;
+
/* DSC unnecessary case
* Check if timing could be supported within end-to-end BW
*/
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
index 212a7536e65b..59ce682f491b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c
@@ -409,7 +409,9 @@ static void dm_test_output_color_space_default_rgb_full(struct kunit *test)
{
struct dc_crtc_timing timing = {};
struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ state.connector = connector;
timing.pixel_encoding = PIXEL_ENCODING_RGB;
state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
@@ -559,7 +561,9 @@ static void dm_test_output_color_space_bt2020_rgb(struct kunit *test)
{
struct dc_crtc_timing timing = {};
struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ state.connector = connector;
timing.pixel_encoding = PIXEL_ENCODING_RGB;
state.colorspace = DRM_MODE_COLORIMETRY_BT2020_RGB;
@@ -585,6 +589,110 @@ static void dm_test_output_color_space_bt2020_rgb_limited(struct kunit *test)
}
/**
+ * dm_test_output_color_space_default_rgb_auto_hdmi_cta - Automatic follows the CTA-861 default
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_rgb_auto_hdmi_cta(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+
+ connector->display_info.is_hdmi = true;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ timing.vic = 32;
+ state.connector = connector;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_SRGB_LIMITED);
+}
+
+/**
+ * dm_test_output_color_space_default_rgb_auto_hdmi_vga - 640x480 (VIC 1) stays full range
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_rgb_auto_hdmi_vga(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+
+ connector->display_info.is_hdmi = true;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ timing.vic = 1;
+ state.connector = connector;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_SRGB);
+}
+
+/**
+ * dm_test_output_color_space_default_rgb_auto_dp_cta - Automatic on a non-HDMI sink is full range
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_rgb_auto_dp_cta(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ timing.vic = 32;
+ state.connector = connector;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_SRGB);
+}
+
+/**
+ * dm_test_output_color_space_default_rgb_full_hdmi_cta - Full overrides the CTA-861 default
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_default_rgb_full_hdmi_cta(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+
+ connector->display_info.is_hdmi = true;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ timing.vic = 32;
+ state.connector = connector;
+ state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_FULL;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_SRGB);
+}
+
+/**
+ * dm_test_output_color_space_bt2020_rgb_auto_hdmi_cta - BT.2020 RGB follows the same default
+ * @test: The KUnit test context
+ */
+static void dm_test_output_color_space_bt2020_rgb_auto_hdmi_cta(struct kunit *test)
+{
+ struct dc_crtc_timing timing = {};
+ struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+
+ connector->display_info.is_hdmi = true;
+ timing.pixel_encoding = PIXEL_ENCODING_RGB;
+ timing.vic = 32;
+ state.connector = connector;
+ state.colorspace = DRM_MODE_COLORIMETRY_BT2020_RGB;
+ state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO;
+
+ KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state),
+ (int)COLOR_SPACE_2020_RGB_LIMITEDRANGE);
+}
+
+/**
* dm_test_output_color_space_bt2020_ycc - Test Output color space bt2020 ycc
* @test: The KUnit test context
*/
@@ -647,7 +755,9 @@ static void dm_test_output_color_space_bt2020_ycc_rgb_encoding(struct kunit *tes
{
struct dc_crtc_timing timing = {};
struct drm_connector_state state = {};
+ struct drm_connector *connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
+ state.connector = connector;
timing.pixel_encoding = PIXEL_ENCODING_RGB;
state.colorspace = DRM_MODE_COLORIMETRY_BT2020_YCC;
@@ -3425,6 +3535,7 @@ static struct dm_test_fill_ctx *dm_test_fill_ctx_alloc(struct kunit *test)
ctx->conn_state = drmm_kzalloc(ctx->drm, sizeof(*ctx->conn_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->conn_state);
+ ctx->conn_state->connector = &ctx->aconnector->base;
ctx->stream = kunit_kzalloc(test, sizeof(*ctx->stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->stream);
ctx->mode = kunit_kzalloc(test, sizeof(*ctx->mode), GFP_KERNEL);
@@ -3818,6 +3929,7 @@ static struct dm_test_stream_ctx *dm_test_stream_ctx_alloc(struct kunit *test)
ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state);
+ ctx->dm_state->base.connector = &ctx->aconnector->base;
ctx->dm_state->scaling = RMX_OFF;
ctx->mode = kunit_kzalloc(test, sizeof(*ctx->mode), GFP_KERNEL);
@@ -5458,6 +5570,11 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
KUNIT_CASE(dm_test_output_color_space_oprgb),
KUNIT_CASE(dm_test_output_color_space_bt2020_rgb),
KUNIT_CASE(dm_test_output_color_space_bt2020_rgb_limited),
+ KUNIT_CASE(dm_test_output_color_space_default_rgb_auto_hdmi_cta),
+ KUNIT_CASE(dm_test_output_color_space_default_rgb_auto_hdmi_vga),
+ KUNIT_CASE(dm_test_output_color_space_default_rgb_auto_dp_cta),
+ KUNIT_CASE(dm_test_output_color_space_default_rgb_full_hdmi_cta),
+ KUNIT_CASE(dm_test_output_color_space_bt2020_rgb_auto_hdmi_cta),
KUNIT_CASE(dm_test_output_color_space_bt2020_ycc),
KUNIT_CASE(dm_test_output_color_space_default_ycbcr709_y_only),
KUNIT_CASE(dm_test_output_color_space_default_ycbcr601),
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 705c500ad711..871063f809dc 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -1905,6 +1905,7 @@ struct dc_scratch_space {
struct ddc_service *ddc;
enum dp_panel_mode panel_mode;
+ bool panel_mode_initialized;
bool aux_mode;
/* Private to DC core */
diff --git a/drivers/gpu/drm/amd/display/dc/dml/Makefile b/drivers/gpu/drm/amd/display/dc/dml/Makefile
index 91465ac05c97..eb4ce5b1ecfb 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml/Makefile
@@ -29,7 +29,7 @@ dml_ccflags := $(CC_FLAGS_FPU)
dml_rcflags := $(CC_FLAGS_NO_FPU)
ifneq ($(CONFIG_FRAME_WARN),0)
- ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)$(CONFIG_UBSAN)),y)
+ ifneq ($(filter y,$(CONFIG_KASAN) $(CONFIG_KCSAN) $(CONFIG_UBSAN)),)
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
frame_warn_limit := 4096
else
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
index 39ee2d1999dd..289bac08c82c 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
@@ -28,7 +28,7 @@ dml2_ccflags := $(CC_FLAGS_FPU)
dml2_rcflags := $(CC_FLAGS_NO_FPU)
ifneq ($(CONFIG_FRAME_WARN),0)
- ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)$(CONFIG_UBSAN)),y)
+ ifneq ($(filter y,$(CONFIG_KASAN) $(CONFIG_KCSAN) $(CONFIG_UBSAN)),)
ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
frame_warn_limit := 4096
else
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
index 16951a9550f2..21dd91fc8010 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
@@ -93,6 +93,7 @@ void dp_set_panel_mode(struct dc_link *link, enum dp_panel_mode panel_mode)
}
link->panel_mode = panel_mode;
+ link->panel_mode_initialized = true;
DC_LOG_DETECTION_DP_CAPS("%d eDP panel mode supported: %d, enabled: %d\n",
link->link_index,
link->dpcd_caps.panel_mode_edp,
@@ -1297,7 +1298,8 @@ void edp_set_panel_assr(struct dc_link *link, struct pipe_ctx *pipe_ctx,
result = cp_psp->funcs.enable_assr(cp_psp->handle, link);
- if (!result && link->panel_mode != DP_PANEL_MODE_EDP)
+ if (!result && link->panel_mode_initialized &&
+ link->panel_mode != DP_PANEL_MODE_EDP)
*panel_mode = DP_PANEL_MODE_DEFAULT;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c
index d2d56a1c4b8b..65c7dae3ff66 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c
@@ -448,6 +448,12 @@ static const struct dce_abm_mask abm_mask = {
ABM_MASK_SH_LIST_DCE110(_MASK)
};
+static const struct dc_debug_options debug_defaults = { 0 };
+
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
#define CTX ctx
#define REG(reg) mm ## reg
@@ -937,6 +943,8 @@ static bool dce60_construct(
dc->caps.max_cursor_size = 64;
dc->caps.dual_link_dvi = true;
dc->caps.extended_aux_timeout_support = false;
+ dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
@@ -1134,6 +1142,8 @@ static bool dce61_construct(
dc->caps.i2c_speed_in_khz = 40;
dc->caps.max_cursor_size = 64;
dc->caps.is_apu = true;
+ dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
@@ -1331,7 +1341,8 @@ static bool dce64_construct(
dc->caps.max_downscale_ratio = 200;
dc->caps.i2c_speed_in_khz = 40;
dc->caps.max_cursor_size = 64;
- dc->caps.is_apu = true;
+ dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c
index 26820d35478a..2f4de697ec4b 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c
@@ -1148,6 +1148,8 @@ static bool dce81_construct(
dc->caps.max_cursor_size = 128;
dc->caps.min_horizontal_blanking_period = 80;
dc->caps.is_apu = true;
+ dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c
index 2f9690e65ca9..d900be8cb5fc 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power.c
+++ b/drivers/gpu/drm/amd/display/modules/power/power.c
@@ -328,6 +328,7 @@ bool mod_power_add_stream(struct mod_power *mod_power,
core_power->map[core_power->num_entities].psr_enabled = 0;
core_power->map[core_power->num_entities].psr_events = psr_event_vsync;
core_power->map[core_power->num_entities].psr_power_opt = 0;
+ core_power->map[core_power->num_entities].replay_events = replay_event_vsync;
core_power->num_entities++;
return true;
}
@@ -387,6 +388,7 @@ bool mod_power_remove_stream(struct mod_power *mod_power,
core_power->map[i].psr_enabled = core_power->map[i + 1].psr_enabled;
core_power->map[i].psr_events = core_power->map[i + 1].psr_events;
core_power->map[i].psr_power_opt = core_power->map[i + 1].psr_power_opt;
+ core_power->map[i].replay_events = core_power->map[i + 1].replay_events;
memcpy(core_power->map[i].psr_context, core_power->map[i + 1].psr_context, sizeof(struct mod_power_psr_context));
memset(core_power->map[i + 1].psr_context, 0, sizeof(struct mod_power_psr_context));
diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
index 012227c70600..d26be73637e3 100644
--- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
+++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
@@ -5263,7 +5263,8 @@ static int si_init_smc_table(struct amdgpu_device *adev)
break;
}
- if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+ if ((adev->flags & AMD_IS_MOBILITY) &&
+ (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC))
table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) {
@@ -7237,6 +7238,19 @@ static void si_parse_pplib_non_clock_info(struct amdgpu_device *adev,
adev->pm.dpm.uvd_ps = rps;
}
+static void si_update_limits(struct amdgpu_clock_and_voltage_limits *limits,
+ struct rv7xx_pl *pl)
+{
+ if (pl->sclk > limits->sclk)
+ limits->sclk = pl->sclk;
+ if (pl->mclk > limits->mclk)
+ limits->mclk = pl->mclk;
+ if (pl->vddc > limits->vddc)
+ limits->vddc = pl->vddc;
+ if (pl->vddci > limits->vddci)
+ limits->vddci = pl->vddci;
+}
+
static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
struct amdgpu_ps *rps, int index,
union pplib_clock_info *clock_info)
@@ -7245,7 +7259,6 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
struct si_power_info *si_pi = si_get_pi(adev);
struct si_ps *ps = si_get_ps(rps);
- struct amdgpu_clock_and_voltage_limits *limits;
u16 leakage_voltage;
struct rv7xx_pl *pl = &ps->performance_levels[index];
int ret;
@@ -7310,26 +7323,14 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
* VBIOS can contain conflicting values between:
* - the maximum allowed clocks and voltages on AC or DC
* - the clocks and voltages in power states on AC or DC
+ *
+ * Assume that the AC limits are the maximum of all power states,
+ * and the DC limits are the maximum of battery power states.
*/
+ si_update_limits(&adev->pm.dpm.dyn_state.max_clock_voltage_on_ac, pl);
if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
- ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE)
- limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
- else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
- limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
- else
- limits = NULL;
-
- if (limits) {
- if (pl->sclk > limits->sclk)
- limits->sclk = pl->sclk;
- if (pl->mclk > limits->mclk)
- limits->mclk = pl->mclk;
- if (pl->vddc > limits->vddc)
- limits->vddc = pl->vddc;
- if (pl->vddci > limits->vddci)
- limits->vddci = pl->vddci;
- }
+ si_update_limits(&adev->pm.dpm.dyn_state.max_clock_voltage_on_dc, pl);
}
union pplib_power_state {
diff --git a/drivers/gpu/drm/i915/display/intel_display_params.c b/drivers/gpu/drm/i915/display/intel_display_params.c
index 2aed110c5b09..ca0ef466bb10 100644
--- a/drivers/gpu/drm/i915/display/intel_display_params.c
+++ b/drivers/gpu/drm/i915/display/intel_display_params.c
@@ -120,6 +120,10 @@ intel_display_param_named_unsafe(enable_psr, int, 0400,
"(0=disabled, 1=enable up to PSR1, 2=enable up to PSR2) "
"Default: -1 (use per-chip default)");
+intel_display_param_named_unsafe(enable_dc_balance, bool, 0400,
+ "Enable VRR DC balance (0=disabled, 1=enabled). "
+ "Default: 0 (disabled)");
+
intel_display_param_named_unsafe(enable_panel_replay, int, 0400,
"Enable Panel Replay (0=disabled, 1=enabled). Default: -1 (use per-chip default)");
diff --git a/drivers/gpu/drm/i915/display/intel_display_params.h b/drivers/gpu/drm/i915/display/intel_display_params.h
index b95ecf728daa..16759e94c025 100644
--- a/drivers/gpu/drm/i915/display/intel_display_params.h
+++ b/drivers/gpu/drm/i915/display/intel_display_params.h
@@ -46,6 +46,7 @@ struct drm_printer;
param(bool, enable_dp_mst, true, 0600) \
param(int, enable_fbc, -1, 0600) \
param(int, enable_psr, -1, 0600) \
+ param(bool, enable_dc_balance, false, 0600) \
param(int, enable_panel_replay, -1, 0600) \
param(bool, psr_safest_params, false, 0400) \
param(bool, enable_psr2_sel_fetch, true, 0400) \
diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c
index 51e4f3309b8b..cfc0cfbff017 100644
--- a/drivers/gpu/drm/i915/display/intel_vrr.c
+++ b/drivers/gpu/drm/i915/display/intel_vrr.c
@@ -386,10 +386,12 @@ static bool intel_vrr_dc_balance_possible(const struct intel_crtc_state *crtc_st
static void
intel_vrr_dc_balance_compute_config(struct intel_crtc_state *crtc_state)
{
+ struct intel_display *display = to_intel_display(crtc_state);
int guardband_usec, adjustment_usec;
struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
- if (!intel_vrr_dc_balance_possible(crtc_state) || !crtc_state->vrr.enable)
+ if (!intel_vrr_dc_balance_possible(crtc_state) ||
+ !crtc_state->vrr.enable || !display->params.enable_dc_balance)
return;
crtc_state->vrr.dc_balance.vmax = crtc_state->vrr.vmax;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index c86a3f54f35b..dc8e3e917dd7 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -638,8 +638,10 @@ static int mtk_drm_bind(struct device *dev)
private->mtk_drm_bound = true;
private->dev = dev;
- if (!mtk_drm_get_all_drm_priv(dev))
+ if (!mtk_drm_get_all_drm_priv(dev)) {
+ put_device(private->mutex_dev);
return 0;
+ }
drm = drm_dev_alloc(&mtk_drm_driver, dev);
if (IS_ERR(drm)) {
@@ -1060,7 +1062,7 @@ static int mtk_drm_probe(struct platform_device *pdev)
struct mtk_mmsys_driver_data *mtk_drm_data;
struct device_node *node;
struct component_match *match = NULL;
- struct platform_device *ovl_adaptor;
+ struct platform_device *ovl_adaptor = NULL;
int ret;
int i;
@@ -1112,6 +1114,9 @@ static int mtk_drm_probe(struct platform_device *pdev)
PLATFORM_DEVID_AUTO,
(void *)private->mmsys_dev,
sizeof(*private->mmsys_dev));
+ if (IS_ERR(ovl_adaptor))
+ return PTR_ERR(ovl_adaptor);
+
private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR].dev = &ovl_adaptor->dev;
mtk_ddp_comp_init(dev, NULL, &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR],
DDP_COMPONENT_DRM_OVL_ADAPTOR);
@@ -1205,6 +1210,8 @@ static int mtk_drm_probe(struct platform_device *pdev)
err_pm:
pm_runtime_disable(dev);
err_node:
+ if (ovl_adaptor)
+ platform_device_unregister(ovl_adaptor);
of_node_put(private->mutex_node);
for (i = 0; i < DDP_COMPONENT_DRM_ID_MAX; i++)
of_node_put(private->comp_node[i]);
@@ -1214,9 +1221,15 @@ err_node:
static void mtk_drm_remove(struct platform_device *pdev)
{
struct mtk_drm_private *private = platform_get_drvdata(pdev);
+ struct device *ovl_adaptor_dev;
int i;
+ ovl_adaptor_dev =
+ private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR].dev;
+
component_master_del(&pdev->dev, &mtk_drm_ops);
+ if (ovl_adaptor_dev)
+ platform_device_unregister(to_platform_device(ovl_adaptor_dev));
pm_runtime_disable(&pdev->dev);
of_node_put(private->mutex_node);
for (i = 0; i < DDP_COMPONENT_DRM_ID_MAX; i++)
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
index 31e81a6de6d8..1ec973d3f52c 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
@@ -376,8 +376,10 @@ static int mtk_hdmi_ddc_v2_probe(struct platform_device *pdev)
pm_runtime_get_sync(dev);
ret = devm_i2c_add_adapter(dev, &ddc->adap);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
return dev_err_probe(dev, ret, "Cannot add DDC I2C adapter\n");
+ }
platform_set_drvdata(pdev, ddc);
return 0;
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
index 521b35c7e14d..bf3ebf5492d1 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
@@ -173,7 +173,7 @@
#define TPI_AUDIO_LOOKUP_EN BIT(2)
/* Video downsampling configuration */
-#define VID_DOWNSAMPLE_CONFIG 0x8d0
+#define VID_DOWNSAMPLE_CONFIG 0x8f0
#define C444_C422_CONFIG_ENABLE BIT(0)
#define C422_C420_CONFIG_ENABLE BIT(4)
#define C422_C420_CONFIG_BYPASS BIT(5)
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 8595f4c6e2e5..1da6bf1c17ff 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -49,6 +49,7 @@
static bool igp_read_bios_from_vram(struct radeon_device *rdev)
{
uint8_t __iomem *bios;
+ uint8_t val1, val2;
resource_size_t vram_base;
resource_size_t size = 256 * 1024; /* ??? */
@@ -63,7 +64,10 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev)
return false;
}
- if (size == 0 || bios[0] != 0x55 || bios[1] != 0xaa) {
+ val1 = readb(&bios[0]);
+ val2 = readb(&bios[1]);
+
+ if (size == 0 || val1 != 0x55 || val2 != 0xaa) {
iounmap(bios);
return false;
}
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c
index 35e6b53e18ce..31abcee55211 100644
--- a/drivers/gpu/drm/xe/xe_pci_sriov.c
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.c
@@ -82,16 +82,33 @@ static void pf_engine_activity_stats(struct xe_device *xe, unsigned int num_vfs,
}
}
-static int resize_vf_vram_bar(struct xe_device *xe, int num_vfs)
+static int pf_resize_vf_vram_bar(struct xe_device *xe, int num_vfs)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+ char buf[10];
u32 sizes;
+ int size;
+ int err;
+
+ if (!IS_DGFX(xe))
+ return 0;
sizes = pci_iov_vf_bar_get_sizes(pdev, VF_LMEM_BAR, num_vfs);
if (!sizes)
return 0;
- return pci_iov_vf_bar_set_size(pdev, VF_LMEM_BAR, __fls(sizes));
+ size = __fls(sizes);
+ string_get_size(pci_rebar_size_to_bytes(size), 1, STRING_UNITS_2, buf, sizeof(buf));
+
+ err = pci_iov_vf_bar_set_size(pdev, VF_LMEM_BAR, size);
+ if (err) {
+ xe_sriov_notice(xe, "Failed to resize VF BAR %u to %s (%pe)\n",
+ VF_LMEM_BAR, buf, ERR_PTR(err));
+ return err;
+ }
+
+ xe_sriov_dbg(xe, "VF LMEM BAR resized to %s\n", buf);
+ return 0;
}
static int pf_prepare_vfs_enabling(struct xe_device *xe)
@@ -142,11 +159,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
if (err < 0)
goto failed;
- if (IS_DGFX(xe)) {
- err = resize_vf_vram_bar(xe, num_vfs);
- if (err)
- xe_sriov_info(xe, "Failed to set VF LMEM BAR size: %d\n", err);
- }
+ pf_resize_vf_vram_bar(xe, num_vfs);
err = pci_enable_sriov(pdev, num_vfs);
if (err < 0)
@@ -164,6 +177,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
return num_vfs;
failed:
+ pf_resize_vf_vram_bar(xe, xe->sriov.pf.device_total_vfs);
xe_sriov_pf_unprovision_vfs(xe, num_vfs);
xe_pm_runtime_put(xe);
pf_finish_vfs_enabling(xe);
@@ -191,6 +205,8 @@ static int pf_disable_vfs(struct xe_device *xe)
pci_disable_sriov(pdev);
+ pf_resize_vf_vram_bar(xe, xe->sriov.pf.device_total_vfs);
+
xe_sriov_pf_reprovision_default(xe);
pf_reset_vfs(xe, num_vfs);
diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c
index d46779b63660..6b467c74601c 100644
--- a/drivers/hid/bpf/hid_bpf_dispatch.c
+++ b/drivers/hid/bpf/hid_bpf_dispatch.c
@@ -453,7 +453,7 @@ hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz,
(u64)(long)ctx,
true); /* prevent infinite recursions */
- if (ret > size)
+ if (ret > (ssize_t)size)
ret = size;
if (ret > 0)
memcpy(buf, dma_data, ret);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 9a473f72c8cb..cb7f16d75fce 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1510,6 +1510,9 @@
#define USB_DEVICE_ID_ASUS_MD_5110 0x5110
#define USB_DEVICE_ID_TURBOX_TOUCHSCREEN_MOSART 0x7100
+#define USB_VENDOR_ID_TURTLE_BEACH 0x10f5
+#define USB_DEVICE_ID_TB_VELOCITYONE_RACE 0x7077
+
#define USB_VENDOR_ID_TWINHAN 0x6253
#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 1d619d2345e1..390d106d5dc1 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -121,7 +121,6 @@ enum recvr_type {
recvr_type_27mhz,
recvr_type_bluetooth,
recvr_type_dinovo,
- recvr_type_bolt,
};
struct dj_report {
@@ -1157,10 +1156,6 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
workitem.reports_supported |= STD_KEYBOARD;
break;
- case 0x10:
- device_type = "Bolt";
- logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
- break;
}
/* custom receiver device (eg. powerplay) */
@@ -1751,24 +1746,6 @@ static int logi_dj_hidpp_event(struct hid_device *hdev,
dj_dev = djrcv_dev->paired_dj_devices[device_index];
/*
- * Bolt receivers send explicit unpair notifications as HID++ events;
- * queue device removal when we receive one.
- */
- if (djrcv_dev->type == recvr_type_bolt &&
- hidpp_report->report_id == REPORT_ID_HIDPP_SHORT &&
- hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_UNPAIRED) {
- struct dj_workitem workitem = {
- .device_index = device_index,
- .type = WORKITEM_TYPE_UNPAIRED,
- };
-
- kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
- schedule_work(&djrcv_dev->work);
- spin_unlock_irqrestore(&djrcv_dev->lock, flags);
- return false;
- }
-
- /*
* With 27 MHz receivers, we do not get an explicit unpair event,
* remove the old device if the user has paired a *different* device.
*/
@@ -1907,9 +1884,6 @@ static int logi_dj_probe(struct hid_device *hdev,
* treat these as logitech-dj interfaces then this causes input events
* reported through this extra interface to not be reported correctly.
* To avoid this, we treat these as generic-hid devices.
- *
- * Bolt receivers only use LOGITECH_DJ_INTERFACE_NUMBER for receiver
- * reporting. Treat all other Bolt interfaces as generic-hid devices.
*/
switch (id->driver_data) {
case recvr_type_dj: no_dj_interfaces = 3; break;
@@ -1923,20 +1897,10 @@ static int logi_dj_probe(struct hid_device *hdev,
}
if (hid_is_usb(hdev)) {
intf = to_usb_interface(hdev->dev.parent);
- if (intf) {
- bool generic_hid_interface;
-
- if (id->driver_data == recvr_type_bolt)
- generic_hid_interface =
- intf->altsetting->desc.bInterfaceNumber !=
- LOGITECH_DJ_INTERFACE_NUMBER;
- else
- generic_hid_interface =
- intf->altsetting->desc.bInterfaceNumber >= no_dj_interfaces;
- if (generic_hid_interface) {
- hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
- return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- }
+ if (intf && intf->altsetting->desc.bInterfaceNumber >=
+ no_dj_interfaces) {
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
}
@@ -2139,10 +2103,6 @@ static const struct hid_device_id logi_dj_receivers[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3),
.driver_data = recvr_type_gaming_hidpp_ls_1_3},
- { /* Logitech Bolt receiver (0xc548) */
- HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
- USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER),
- .driver_data = recvr_type_bolt},
{ /* Logitech lightspeed receiver (0xc54d) */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_4),
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 493763a12518..9646380febac 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -988,8 +988,7 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
}
/* the device might not be connected */
- if (ret == HIDPP_ERROR_CONNECT_FAIL ||
- ret == HIDPP_ERROR_RESOURCE_ERROR ||
+ if (ret == HIDPP_ERROR_RESOURCE_ERROR ||
ret == HIDPP_ERROR_UNKNOWN_DEVICE)
return -EIO;
@@ -4405,50 +4404,8 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
return ret;
}
-static bool hidpp_is_bolt_child(struct hid_device *hdev)
-{
- struct device *parent = hdev->dev.parent;
- struct hid_device *receiver_hdev;
-
- if (!parent)
- return false;
-
- receiver_hdev = to_hid_device(parent);
- return receiver_hdev->vendor == USB_VENDOR_ID_LOGITECH &&
- receiver_hdev->product == USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER;
-}
-
-static int hidpp_bolt_init(struct hidpp_device *hidpp)
-{
- struct hid_device *hdev = hidpp->hid_dev;
- char *name;
- int ret;
-
- ret = hidpp_serial_init(hidpp);
- if (ret)
- return ret;
-
- name = hidpp_get_device_name(hidpp);
- if (!name)
- return -EIO;
-
- snprintf(hdev->name, sizeof(hdev->name), "%s", name);
- dbg_hid("HID++ Bolt: Got name: %s\n", name);
-
- kfree(name);
- return 0;
-}
-
-static int hidpp_receiver_init(struct hidpp_device *hidpp)
-{
- if (hidpp_is_bolt_child(hidpp->hid_dev))
- return hidpp_bolt_init(hidpp);
-
- return hidpp_unifying_init(hidpp);
-}
-
/* Get name + serial for USB and Bluetooth HID++ devices */
-static void hidpp_non_receiver_init(struct hidpp_device *hidpp)
+static void hidpp_non_unifying_init(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
char *name;
@@ -4800,9 +4757,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* Get name + serial, store in hdev->name + hdev->uniq */
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
- hidpp_receiver_init(hidpp);
+ hidpp_unifying_init(hidpp);
else
- hidpp_non_receiver_init(hidpp);
+ hidpp_non_unifying_init(hidpp);
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index ab4ac261fe76..4e19a0c4d238 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -2249,7 +2249,7 @@ static void mt_remove(struct hid_device *hdev)
{
struct mt_device *td = hid_get_drvdata(hdev);
- timer_delete_sync(&td->release_timer);
+ timer_shutdown_sync(&td->release_timer);
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c
index 60180467a0bf..f1854cfe42d0 100644
--- a/drivers/hid/hid-universal-pidff.c
+++ b/drivers/hid/hid-universal-pidff.c
@@ -187,6 +187,7 @@ static const struct hid_device_id universal_pidff_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_FORTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_LA_PRIMA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_TONY_KANAAN) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TURTLE_BEACH, USB_DEVICE_ID_TB_VELOCITYONE_RACE)},
{}
};
MODULE_DEVICE_TABLE(hid, universal_pidff_devices);
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 0d2ad7bc3648..2537288b5026 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -606,7 +606,8 @@ static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev)
*/
max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_input_len), SZ_4K);
- qcdev->input_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL);
+ qcdev->input_buf = devm_kzalloc(qcdev->dev,
+ HIDI2C_PACKET_LEN(max_report_len), GFP_KERNEL);
if (!qcdev->input_buf)
return -ENOMEM;
@@ -616,7 +617,8 @@ static int quicki2c_alloc_report_buf(struct quicki2c_device *qcdev)
max_report_len = max(le16_to_cpu(qcdev->dev_desc.max_output_len),
max_report_len);
- qcdev->report_buf = devm_kzalloc(qcdev->dev, max_report_len, GFP_KERNEL);
+ qcdev->report_buf = devm_kzalloc(qcdev->dev,
+ HIDI2C_PACKET_LEN(max_report_len), GFP_KERNEL);
if (!qcdev->report_buf)
return -ENOMEM;
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 89226f5ce45e..ad676fde211b 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
@@ -552,7 +552,8 @@ static int quickspi_alloc_report_buf(struct quickspi_device *qsdev)
max_input_len = max(le16_to_cpu(qsdev->dev_desc.rep_desc_len),
le16_to_cpu(qsdev->dev_desc.max_input_len));
- qsdev->input_buf = devm_kzalloc(qsdev->dev, max_input_len, GFP_KERNEL);
+ qsdev->input_buf = devm_kzalloc(qsdev->dev,
+ HIDSPI_INPUT_BODY_SIZE(max_input_len), GFP_KERNEL);
if (!qsdev->input_buf)
return -ENOMEM;
diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c
index 68238cc8aee0..e0c3de5e8203 100644
--- a/drivers/i2c/busses/i2c-at91-master.c
+++ b/drivers/i2c/busses/i2c-at91-master.c
@@ -921,8 +921,14 @@ int at91_twi_probe_master(struct platform_device *pdev,
at91_calc_twi_clock(dev);
rc = at91_init_twi_recovery_info(pdev, dev);
- if (rc == -EPROBE_DEFER)
+ if (rc == -EPROBE_DEFER) {
+ /*
+ * The DMA channels were already set up above, release them
+ * before deferring the probe so they are not leaked.
+ */
+ at91_twi_dma_release(dev);
return rc;
+ }
dev->adapter.algo = &at91_twi_algorithm;
dev->adapter.quirks = &at91_twi_quirks;
diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c
index 3e7735e1dae0..5e397a7e63f6 100644
--- a/drivers/i2c/busses/i2c-xiic.c
+++ b/drivers/i2c/busses/i2c-xiic.c
@@ -73,6 +73,9 @@ enum i2c_scl_freq {
* @prev_msg_tx: Previous message is Tx
* @quirks: To hold platform specific bug info
* @smbus_block_read: Flag to handle block read
+ * @smbus_actual_len: Valid byte count (length + payload + optional PEC) of a
+ * padded SMBus block read. msg->len is trimmed to this on completion.
+ * Zero when no trimming is needed.
* @input_clk: Input clock to I2C controller
* @i2c_clk: I2C SCL frequency
* @atomic: Mode of transfer
@@ -98,6 +101,7 @@ struct xiic_i2c {
bool prev_msg_tx;
u32 quirks;
bool smbus_block_read;
+ unsigned int smbus_actual_len;
unsigned long input_clk;
unsigned int i2c_clk;
bool atomic;
@@ -539,30 +543,38 @@ static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c)
/* Check if received length is valid */
if (rxmsg_len <= I2C_SMBUS_BLOCK_MAX) {
+ unsigned int pec_len = i2c->rx_msg->len - 1;
+
/* Set Receive fifo depth */
- if (rxmsg_len > IIC_RX_FIFO_DEPTH) {
+ if (rxmsg_len + pec_len > IIC_RX_FIFO_DEPTH) {
/*
- * When Rx msg len greater than or equal to Rx fifo capacity
- * Receive fifo depth should set to Rx fifo capacity minus 1
+ * Trailing bytes (payload plus any PEC) exceed Rx FIFO
+ * capacity, so drain in chunks. This also keeps the
+ * else branch below from pushing rfd_set past the
+ * 4-bit XIIC_RFD_REG_OFFSET field.
*/
rfd_set = IIC_RX_FIFO_DEPTH - 1;
- i2c->rx_msg->len = rxmsg_len + 1;
- } else if ((rxmsg_len == 1) ||
- (rxmsg_len == 0)) {
+ i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
+ } else if (1 + rxmsg_len + pec_len < SMBUS_BLOCK_READ_MIN_LEN) {
/*
- * Minimum of 3 bytes required to exit cleanly. 1 byte
- * already received, Second byte is being received. Have
- * to set NACK in read_rx before receiving the last byte
+ * The HW needs SMBUS_BLOCK_READ_MIN_LEN bytes on the
+ * bus to exit cleanly: by the time the ISR reads the
+ * length byte the second byte is already being clocked
+ * in, too late to NACK. Pad the drain target and record
+ * the real length, trimmed back on completion so the
+ * PEC check sees the right byte.
*/
rfd_set = 0;
i2c->rx_msg->len = SMBUS_BLOCK_READ_MIN_LEN;
+ i2c->smbus_actual_len = 1 + rxmsg_len + pec_len;
} else {
/*
- * When Rx msg len less than Rx fifo capacity
- * Receive fifo depth should set to Rx msg len minus 2
+ * All trailing bytes fit in the Rx FIFO. Defer RX_FULL
+ * until every one of them is buffered, so the drain
+ * takes xiic_read_rx()'s bytes_rem == 0 path.
*/
- rfd_set = rxmsg_len - 2;
- i2c->rx_msg->len = rxmsg_len + 1;
+ rfd_set = rxmsg_len + pec_len - 1;
+ i2c->rx_msg->len = rxmsg_len + 1 + pec_len;
}
xiic_setreg8(i2c, XIIC_RFD_REG_OFFSET, rfd_set);
@@ -575,6 +587,16 @@ static void xiic_smbus_block_read_setup(struct xiic_i2c *i2c)
dev_err(i2c->adap.dev.parent, "smbus_block_read Invalid msg length\n");
}
+/*
+ * Undo the setup-time padding of a short SMBus block read before rx_msg is
+ * cleared, so the PEC check sees the right byte.
+ */
+static void xiic_smbus_trim_len(struct xiic_i2c *i2c)
+{
+ if (i2c->rx_msg && i2c->smbus_actual_len)
+ i2c->rx_msg->len = i2c->smbus_actual_len;
+}
+
static void xiic_read_rx(struct xiic_i2c *i2c)
{
u8 bytes_in_fifo, cr = 0, bytes_to_read = 0;
@@ -797,6 +819,8 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
xiic_read_rx(i2c);
if (xiic_rx_space(i2c) == 0) {
+ xiic_smbus_trim_len(i2c);
+
/* this is the last part of the message */
i2c->rx_msg = NULL;
@@ -865,8 +889,11 @@ static irqreturn_t xiic_process(int irq, void *dev_id)
if (i2c->tx_msg && i2c->smbus_block_read) {
i2c->smbus_block_read = false;
- /* Set requested message len=1 to indicate STATE_DONE */
- i2c->tx_msg->len = 1;
+ /*
+ * Drive xiic_tx_space() to 0 to signal STATE_DONE
+ * without truncating the rx_msg length.
+ */
+ i2c->tx_pos = i2c->tx_msg->len;
}
if (!i2c->tx_msg)
@@ -938,6 +965,7 @@ static void xiic_recv_atomic(struct xiic_i2c *i2c)
return;
}
+ xiic_smbus_trim_len(i2c);
i2c->rx_msg = NULL;
xiic_irq_clr_en(i2c, XIIC_INTR_TX_ERROR_MASK);
@@ -955,6 +983,9 @@ static void xiic_start_recv(struct xiic_i2c *i2c)
u8 cr = 0, rfd_set = 0;
struct i2c_msg *msg = i2c->rx_msg = i2c->tx_msg;
+ /* A stale value from an aborted block read would truncate this msg. */
+ i2c->smbus_actual_len = 0;
+
if (!i2c->atomic)
dev_dbg(i2c->adap.dev.parent, "%s entry, ISR: 0x%x, CR: 0x%x\n",
__func__, xiic_getreg32(i2c, XIIC_IISR_OFFSET),
diff --git a/drivers/iio/accel/kionix-kx022a.c b/drivers/iio/accel/kionix-kx022a.c
index a3d80e4e7f1e..add89512af79 100644
--- a/drivers/iio/accel/kionix-kx022a.c
+++ b/drivers/iio/accel/kionix-kx022a.c
@@ -649,7 +649,7 @@ static int kx022a_read_raw(struct iio_dev *idev,
if (ret)
return ret;
- if ((regval & KX022A_MASK_ODR) >
+ if ((regval & KX022A_MASK_ODR) >=
ARRAY_SIZE(kx022a_accel_samp_freq_table)) {
dev_err(data->dev, "Invalid ODR\n");
return -EINVAL;
@@ -981,26 +981,44 @@ static int kx022a_fifo_enable(struct kx022a_data *data)
guard(mutex)(&data->mutex);
ret = __kx022a_turn_on_off(data, false);
if (ret)
- return ret;
+ goto err_free_out;
/* Update watermark to HW */
ret = kx022a_fifo_set_wmi(data);
if (ret)
- return ret;
+ goto err_turn_on_out;
/* Enable buffer */
ret = regmap_set_bits(data->regmap, data->chip_info->buf_cntl2,
KX022A_MASK_BUF_EN);
if (ret)
- return ret;
+ goto err_turn_on_out;
data->state |= KX022A_STATE_FIFO;
ret = regmap_set_bits(data->regmap, data->ien_reg,
KX022A_MASK_WMI);
if (ret)
- return ret;
+ goto err_buf_disable_out;
- return __kx022a_turn_on_off(data, true);
+ ret = __kx022a_turn_on_off(data, true);
+ if (ret)
+ goto err_wmi_clear_out;
+
+ return ret;
+
+err_wmi_clear_out:
+ regmap_clear_bits(data->regmap, data->ien_reg,
+ KX022A_MASK_WMI);
+err_buf_disable_out:
+ regmap_clear_bits(data->regmap, data->chip_info->buf_cntl2,
+ KX022A_MASK_BUF_EN);
+ data->state &= ~KX022A_STATE_FIFO;
+err_turn_on_out:
+ __kx022a_turn_on_off(data, true);
+err_free_out:
+ kfree(data->fifo_buffer);
+
+ return ret;
}
static int kx022a_buffer_postenable(struct iio_dev *idev)
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index c6b030f64be1..b3d753bd9476 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1030,7 +1030,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret;
- if (state && data->ev_enable_state)
+ if (state == data->ev_enable_state)
return 0;
mutex_lock(&data->mutex);
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index 0d5ed244a663..acd0890c78a5 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -638,7 +638,7 @@ static int sca3000_write_raw_samp_freq(struct sca3000_state *st, int val)
if (val == base_freq / 2)
ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_2;
- if (val == base_freq / 4)
+ else if (val == base_freq / 4)
ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_4;
else if (val != base_freq)
return -EINVAL;
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index e97400a1aa48..877b06007396 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -746,14 +746,21 @@ static int ad4030_set_chan_calibbias(struct iio_dev *indio_dev,
static int ad4030_set_avg_frame_len(struct iio_dev *dev, int avg_val)
{
struct ad4030_state *st = iio_priv(dev);
- unsigned int avg_log2 = ilog2(avg_val);
unsigned int last_avg_idx = ARRAY_SIZE(ad4030_average_modes) - 1;
+ unsigned int avg_log2;
int freq_hz;
int ret;
- if (avg_val < 0 || avg_val > ad4030_average_modes[last_avg_idx])
+ /* Reject unsupported modes */
+ if (avg_val > ad4030_average_modes[last_avg_idx])
+ return -EINVAL;
+
+ /* Avoid invalid values for logarithm since it's undefined */
+ if (avg_val < 1)
return -EINVAL;
+ avg_log2 = ilog2(avg_val);
+
if (st->offload_trigger) {
/*
* The sample averaging and sampling frequency configurations
diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c
index 5dd82ada79bf..24904be5090a 100644
--- a/drivers/iio/adc/ad7173.c
+++ b/drivers/iio/adc/ad7173.c
@@ -775,9 +775,9 @@ static int ad7173_load_config(struct ad7173_state *st,
return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) |
- FIELD_PREP(AD7173_FILTER_ENHFILT_MASK,
- post_filter_enable) |
FIELD_PREP(AD7173_FILTER_ENHFILTEN,
+ post_filter_enable) |
+ FIELD_PREP(AD7173_FILTER_ENHFILT_MASK,
post_filter_select) |
FIELD_PREP(AD7173_FILTER_ORDER, 0) |
FIELD_PREP(AD7173_FILTER_ODR_MASK,
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index 1b410291da53..119ddc5a13e2 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -498,7 +498,6 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type;
struct spi_transfer *xfer = sigma_delta->sample_xfer;
unsigned int i, slot, channel;
- u8 *samples_buf;
int ret;
if (sigma_delta->num_slots == 1) {
@@ -530,7 +529,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
xfer[1].bits_per_word = scan_type->realbits;
xfer[1].len = spi_bpw_to_bytes(scan_type->realbits);
} else {
- unsigned int samples_buf_size, scan_size;
+ unsigned int scan_size;
if (sigma_delta->active_slots > 1) {
ret = ad_sigma_delta_append_status(sigma_delta, true);
@@ -538,17 +537,6 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
return ret;
}
- samples_buf_size =
- ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits),
- sizeof(s64));
- samples_buf_size += sizeof(s64);
- samples_buf = devm_krealloc(&sigma_delta->spi->dev,
- sigma_delta->samples_buf,
- samples_buf_size, GFP_KERNEL);
- if (!samples_buf)
- return -ENOMEM;
-
- sigma_delta->samples_buf = samples_buf;
scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift);
/* For 24-bit data, there is an extra byte of padding. */
xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0];
@@ -855,6 +843,23 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi
indio_dev->setup_ops = &ad_sd_buffer_setup_ops;
} else {
+ const struct iio_scan_type *scan_type =
+ &indio_dev->channels[0].scan_type;
+ unsigned int samples_buf_size;
+
+ /*
+ * Worst-case size: all sequencer slots can be active, capped
+ * at num_slots by ad_sd_validate_scan_mask().
+ */
+ samples_buf_size =
+ ALIGN(sigma_delta->num_slots *
+ BITS_TO_BYTES(scan_type->storagebits),
+ sizeof(s64));
+ samples_buf_size += sizeof(s64);
+ sigma_delta->samples_buf = devm_kzalloc(dev, samples_buf_size, GFP_KERNEL);
+ if (!sigma_delta->samples_buf)
+ return -ENOMEM;
+
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad_sd_trigger_handler,
diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c
index fd1362a98052..c4450f038898 100644
--- a/drivers/iio/adc/ade9000.c
+++ b/drivers/iio/adc/ade9000.c
@@ -219,7 +219,7 @@
#define ADE9000_ST1_ERROR2_BIT BIT(30)
#define ADE9000_ST1_ERROR3_BIT BIT(31)
#define ADE9000_ST1_CROSSING_FIRST 6
-#define ADE9000_ST1_CROSSING_DEPTH 25
+#define ADE9000_ST1_CROSSING_DEPTH 26
#define ADE9000_WFB_TRG_DIP_BIT BIT(0)
#define ADE9000_WFB_TRG_SWELL_BIT BIT(1)
@@ -458,7 +458,7 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
BIT(IIO_CHAN_INFO_CALIBSCALE), \
.event_spec = ade9000_current_events, \
.num_event_specs = ARRAY_SIZE(ade9000_current_events), \
- .scan_index = num, \
+ .scan_index = num * 2, \
.indexed = 1, \
.scan_type = { \
.sign = 's', \
@@ -478,7 +478,7 @@ static const struct iio_chan_spec_ext_info ade9000_ext_info[] = {
BIT(IIO_CHAN_INFO_FREQUENCY), \
.event_spec = ade9000_voltage_events, \
.num_event_specs = ARRAY_SIZE(ade9000_voltage_events), \
- .scan_index = num + 1, /* interleave with current channels */ \
+ .scan_index = num * 2 + 1, /* interleave with current channels */ \
.indexed = 1, \
.scan_type = { \
.sign = 's', \
@@ -1715,8 +1715,9 @@ static int ade9000_setup_clkout(struct device *dev, struct ade9000_state *st)
return 0;
/* CLKOUT passes through CLKIN with divider of 1 */
- clkout_hw = devm_clk_hw_register_divider(dev, "clkout", __clk_get_name(st->clkin),
- CLK_SET_RATE_PARENT, NULL, 0, 1, 0, NULL);
+ clkout_hw = devm_clk_hw_register_fixed_factor(dev, "clkout",
+ __clk_get_name(st->clkin),
+ CLK_SET_RATE_PARENT, 1, 1);
if (IS_ERR(clkout_hw))
return dev_err_probe(dev, PTR_ERR(clkout_hw), "Failed to register clkout");
@@ -1777,18 +1778,6 @@ static int ade9000_probe(struct spi_device *spi)
if (ret)
return ret;
- ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev);
- if (ret)
- return ret;
-
- ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev);
- if (ret)
- return ret;
-
- ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev);
- if (ret)
- return ret;
-
/* External CMOS clock input (optional - crystal can be used instead) */
st->clkin = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(st->clkin))
@@ -1808,6 +1797,19 @@ static int ade9000_probe(struct spi_device *spi)
return dev_err_probe(&spi->dev, ret,
"Failed to get and enable vdd regulator\n");
+ /* Request IRQs only after powering the chip; the handlers do SPI access. */
+ ret = ade9000_request_irq(dev, "irq0", ade9000_irq0_thread, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ade9000_request_irq(dev, "irq1", ade9000_irq1_thread, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ade9000_request_irq(dev, "dready", ade9000_dready_thread, indio_dev);
+ if (ret)
+ return ret;
+
indio_dev->channels = st->info->channels;
indio_dev->num_channels = st->info->num_channels;
diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 6169ce28261e..452c30cf04f2 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -585,7 +585,9 @@ static int aspeed_adc_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(data->rst),
"invalid or missing reset controller device tree entry");
- reset_control_deassert(data->rst);
+ ret = reset_control_deassert(data->rst);
+ if (ret)
+ return ret;
ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst);
if (ret)
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index c8283279c477..c98863d612ad 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -207,6 +207,14 @@ static const struct dmi_system_id axp288_adc_ts_bias_override[] = {
},
.driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
},
+ {
+ /* Haier HV103H */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Haier"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HV103H"),
+ },
+ .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
+ },
{ }
};
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index 65a2d92bb112..629c53af6472 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -404,6 +404,14 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
data = rxbuf[0];
}
+
+ /*
+ * Differential channels are bipolar and the device returns the sample
+ * in two's complement, so sign-extend it from the resolution bit.
+ */
+ if (chan->differential)
+ data = sign_extend32(data, st->chip_info->bits - 1);
+
*val = data;
return 0;
diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c
index 2d934452eb11..5e901ab25c8e 100644
--- a/drivers/iio/adc/pac1934.c
+++ b/drivers/iio/adc/pac1934.c
@@ -1111,6 +1111,10 @@ static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
devm_kmemdup(dev, rez->package.elements[i].string.pointer,
(size_t)rez->package.elements[i].string.length + 1,
GFP_KERNEL);
+ if (!info->labels[idx]) {
+ ACPI_FREE(rez);
+ return -ENOMEM;
+ }
info->labels[idx][rez->package.elements[i].string.length] = '\0';
info->shunts[idx] = rez->package.elements[i + 1].integer.value * 1000;
info->active_channels[idx] = (info->shunts[idx] != 0);
diff --git a/drivers/iio/adc/rohm-bd79124.c b/drivers/iio/adc/rohm-bd79124.c
index ed5427288961..8091a138cca8 100644
--- a/drivers/iio/adc/rohm-bd79124.c
+++ b/drivers/iio/adc/rohm-bd79124.c
@@ -200,7 +200,7 @@ static int bd79124gpo_set_multiple(struct gpio_chip *gc, unsigned long *mask,
if (ret)
return ret;
- if (all_gpos ^ *mask) {
+ if (*mask & ~all_gpos) {
dev_dbg(data->dev, "Invalid mux config. Can't set value.\n");
return -EINVAL;
@@ -381,6 +381,9 @@ static int bd79124_start_measurement(struct bd79124_data *data, int chan)
/* See if already started */
ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &val);
+ if (ret)
+ return ret;
+
if (val & BIT(chan))
return 0;
@@ -420,11 +423,16 @@ static int bd79124_stop_measurement(struct bd79124_data *data, int chan)
/* See if already stopped */
ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &enabled_chans);
+ if (ret)
+ return ret;
+
if (!(enabled_chans & BIT(chan)))
return 0;
ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG,
BD79124_MSK_SEQ_START);
+ if (ret)
+ return ret;
/* Clear the channel from the measured channels */
enabled_chans &= ~BIT(chan);
@@ -524,7 +532,7 @@ static int bd79124_enable_event(struct bd79124_data *data,
return ret;
if (dir == IIO_EV_DIR_RISING) {
- limit = &data->alarm_f_limit[channel];
+ limit = &data->alarm_r_limit[channel];
reg = BD79124_GET_HIGH_LIMIT_REG(channel);
} else {
limit = &data->alarm_f_limit[channel];
@@ -919,13 +927,13 @@ static int bd79124_chan_init(struct bd79124_data *data, int channel)
{
int ret;
- ret = regmap_write(data->map, BD79124_GET_HIGH_LIMIT_REG(channel),
- BD79124_HIGH_LIMIT_MAX);
+ ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel),
+ BD79124_HIGH_LIMIT_MAX);
if (ret)
return ret;
- return regmap_write(data->map, BD79124_GET_LOW_LIMIT_REG(channel),
- BD79124_LOW_LIMIT_MIN);
+ return bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel),
+ BD79124_LOW_LIMIT_MIN);
}
static int bd79124_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels)
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 3cde89c06ca3..600f975f1d7b 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -1608,11 +1608,16 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
ret = stm32_adc_single_conv(indio_dev, chan, val);
else
ret = -EINVAL;
+ iio_device_release_direct(indio_dev);
+ if (ret < 0)
+ return ret;
- if (mask == IIO_CHAN_INFO_PROCESSED)
+ if (mask == IIO_CHAN_INFO_PROCESSED) {
+ if (*val == 0)
+ return -EINVAL;
*val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val;
+ }
- iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
@@ -2263,33 +2268,37 @@ static int stm32_adc_populate_int_ch(struct iio_dev *indio_dev, const char *ch_n
for (i = 0; i < STM32_ADC_INT_CH_NB; i++) {
if (!strncmp(stm32_adc_ic[i].name, ch_name, STM32_ADC_CH_SZ)) {
+ bool na;
+
/* Check internal channel availability */
switch (i) {
case STM32_ADC_INT_CH_VDDCORE:
- if (!adc->cfg->regs->or_vddcore.reg)
- dev_warn(&indio_dev->dev,
- "%s channel not available\n", ch_name);
+ na = !adc->cfg->regs->or_vddcore.reg;
break;
case STM32_ADC_INT_CH_VDDCPU:
- if (!adc->cfg->regs->or_vddcpu.reg)
- dev_warn(&indio_dev->dev,
- "%s channel not available\n", ch_name);
+ na = !adc->cfg->regs->or_vddcpu.reg;
break;
case STM32_ADC_INT_CH_VDDQ_DDR:
- if (!adc->cfg->regs->or_vddq_ddr.reg)
- dev_warn(&indio_dev->dev,
- "%s channel not available\n", ch_name);
+ na = !adc->cfg->regs->or_vddq_ddr.reg;
break;
case STM32_ADC_INT_CH_VREFINT:
- if (!adc->cfg->regs->ccr_vref.reg)
- dev_warn(&indio_dev->dev,
- "%s channel not available\n", ch_name);
+ na = !adc->cfg->regs->ccr_vref.reg;
break;
case STM32_ADC_INT_CH_VBAT:
- if (!adc->cfg->regs->ccr_vbat.reg)
- dev_warn(&indio_dev->dev,
- "%s channel not available\n", ch_name);
+ na = !adc->cfg->regs->ccr_vbat.reg;
break;
+ default:
+ return -EINVAL;
+ }
+
+ if (na) {
+ /*
+ * Channel label matches an internal STM32 ADC channel.
+ * Warn about it, as there's normally no restriction on the
+ * name but that's not among available internal channels.
+ */
+ dev_warn(&indio_dev->dev, "no %s internal channel\n", ch_name);
+ return 0;
}
if (stm32_adc_ic[i].idx != STM32_ADC_INT_CH_VREFINT) {
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 203459ca9907..ac5ba76b979e 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -638,10 +638,10 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
* thermal because no thermal DT node is found.
*/
if (IS_ERR(info->tzd) && PTR_ERR(info->tzd) != -ENODEV) {
- dev_err(&pdev->dev,
- "could not register thermal sensor: %ld\n",
- PTR_ERR(info->tzd));
- return PTR_ERR(info->tzd);
+ ret = dev_err_probe(&pdev->dev,
+ PTR_ERR(info->tzd),
+ "could not register thermal sensor\n");
+ goto err_map;
}
}
@@ -657,7 +657,6 @@ err_map:
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
iio_map_array_unregister(indio_dev);
- pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return ret;
@@ -668,7 +667,6 @@ static void sun4i_gpadc_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
- pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!IS_ENABLED(CONFIG_THERMAL_OF))
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index cab66bb8cc1c..66202b3a3abb 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1401,13 +1401,14 @@ static int xadc_probe(struct platform_device *pdev)
}
if (irq > 0) {
- ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler,
- 0, dev_name(dev), indio_dev);
+ /* Clear out any work queued by the interrupt */
+ ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
+ &xadc->zynq_unmask_work);
if (ret)
return ret;
- ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work,
- &xadc->zynq_unmask_work);
+ ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler,
+ 0, dev_name(dev), indio_dev);
if (ret)
return ret;
}
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index a38922d13d25..b9f8b5b3aa6b 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -101,10 +101,13 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
if (block->sg_table) {
unsigned long flags;
- sgl = block->sg_table->sgl;
- nents = sg_nents_for_len(sgl, block->bytes_used);
- if (nents < 0)
- return nents;
+ /*
+ * Only the first sgt->nents entries carry a valid
+ * sg_dma_address()/sg_dma_len() pair as mapping the table may
+ * have coalesced entries, in which case nents is smaller than
+ * orig_nents.
+ */
+ nents = block->sg_table->nents;
vecs = kmalloc_objs(*vecs, nents, GFP_ATOMIC);
if (!vecs)
@@ -112,7 +115,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
len_total = block->bytes_used;
- for (i = 0; i < nents; i++) {
+ sgl = block->sg_table->sgl;
+ for (i = 0; i < nents && len_total; i++) {
vecs[i].addr = sg_dma_address(sgl);
vecs[i].len = min(sg_dma_len(sgl), len_total);
len_total -= vecs[i].len;
@@ -120,6 +124,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
sgl = sg_next(sgl);
}
+ nents = i;
+
if (block->cyclic)
flags = DMA_PREP_REPEAT;
else
diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c
index 2f35c6d2f9ce..b36ac4e2dae8 100644
--- a/drivers/iio/cdc/ad7150.c
+++ b/drivers/iio/cdc/ad7150.c
@@ -636,11 +636,13 @@ static const struct i2c_device_id ad7150_id[] = {
MODULE_DEVICE_TABLE(i2c, ad7150_id);
static const struct of_device_id ad7150_of_match[] = {
- { "adi,ad7150" },
- { "adi,ad7151" },
- { "adi,ad7156" },
+ { .compatible = "adi,ad7150" },
+ { .compatible = "adi,ad7151" },
+ { .compatible = "adi,ad7156" },
{ }
};
+MODULE_DEVICE_TABLE(of, ad7150_of_match);
+
static struct i2c_driver ad7150_driver = {
.driver = {
.name = "ad7150",
diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
index 88a82d1370c5..8c9e81a77e68 100644
--- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
+++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c
@@ -167,11 +167,16 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
valid = inv_validate_period(ts, period);
}
- /* if interrupt interval is valid, sync with interrupt timestamp */
+ /*
+ * If interrupt interval is valid, sync with interrupt timestamp.
+ * Otherwise, use estimated value while ensuring interrupt timestamp
+ * remains the maximum possible value.
+ */
+ period = inv_align_timestamp_it(ts, sample_nb);
if (valid)
- ts->period = inv_align_timestamp_it(ts, sample_nb);
+ ts->period = period;
else
- ts->period = ts->mult * ts->chip_period.val;
+ ts->period = min(ts->mult * ts->chip_period.val, period);
}
EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, "IIO_INV_SENSORS_TIMESTAMP");
diff --git a/drivers/iio/dac/mcp47a1.c b/drivers/iio/dac/mcp47a1.c
index 0bf994aa0e4f..3ed306f3060f 100644
--- a/drivers/iio/dac/mcp47a1.c
+++ b/drivers/iio/dac/mcp47a1.c
@@ -26,7 +26,7 @@ struct mcp47a1_data {
int vref_mV;
};
-static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS - 1 };
+static const int mcp47a1_raw_avail[] = { 0, 1, MCP47A1_MAX_STEPS };
static const struct iio_chan_spec mcp47a1_channel = {
.type = IIO_VOLTAGE,
@@ -46,7 +46,7 @@ static int mcp47a1_write(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (!in_range(val, 0, MCP47A1_MAX_STEPS))
+ if (!in_range(val, 0, MCP47A1_MAX_STEPS + 1))
return -EINVAL;
return i2c_smbus_write_byte_data(data->client, MCP47A1_CMD_CODE,
diff --git a/drivers/iio/dac/rohm-bd79703.c b/drivers/iio/dac/rohm-bd79703.c
index 6e2e7787f8a4..127ccc2c4a22 100644
--- a/drivers/iio/dac/rohm-bd79703.c
+++ b/drivers/iio/dac/rohm-bd79703.c
@@ -73,6 +73,9 @@ static int bd79703_write_raw(struct iio_dev *idev,
{
struct bd79703_data *data = iio_priv(idev);
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
if (val < 0 || val >= 1 << BD79703_DAC_BITS)
return -EINVAL;
diff --git a/drivers/iio/frequency/adf4377.c b/drivers/iio/frequency/adf4377.c
index d1e5bb062474..52266f83877a 100644
--- a/drivers/iio/frequency/adf4377.c
+++ b/drivers/iio/frequency/adf4377.c
@@ -988,10 +988,10 @@ static const struct clk_ops adf4377_clk_ops = {
static int adf4377_clk_register(struct adf4377_state *st)
{
+ struct clk_parent_data parent_data = { };
struct spi_device *spi = st->spi;
struct device *dev = &spi->dev;
- struct clk_init_data init;
- struct clk_parent_data parent_data;
+ struct clk_init_data init = { };
int ret;
if (!device_property_present(dev, "#clock-cells"))
diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c
index c9baaab574e3..078ea3e5aa03 100644
--- a/drivers/iio/frequency/admv1013.c
+++ b/drivers/iio/frequency/admv1013.c
@@ -195,7 +195,7 @@ static int admv1013_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_CALIBBIAS:
- switch (chan->channel) {
+ switch (chan->channel2) {
case IIO_MOD_I:
addr = ADMV1013_REG_OFFSET_ADJUST_I;
break;
@@ -601,11 +601,14 @@ static int admv1013_probe(struct spi_device *spi)
"failed to get the LO input clock\n");
st->nb.notifier_call = admv1013_freq_change;
- ret = devm_clk_notifier_register(dev, st->clkin, &st->nb);
+
+ ret = devm_mutex_init(dev, &st->lock);
if (ret)
return ret;
- mutex_init(&st->lock);
+ ret = devm_clk_notifier_register(dev, st->clkin, &st->nb);
+ if (ret)
+ return ret;
ret = admv1013_init(st, vcm_uv);
if (ret)
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
index ff335f81e9ae..b687588ada03 100644
--- a/drivers/iio/gyro/adis16136.c
+++ b/drivers/iio/gyro/adis16136.c
@@ -145,7 +145,7 @@ static int adis16136_debugfs_init(struct iio_dev *indio_dev)
struct adis16136 *adis16136 = iio_priv(indio_dev);
struct dentry *d = iio_get_debugfs_dentry(indio_dev);
- debugfs_create_file_unsafe("serial_number", 0400,
+ debugfs_create_file("serial_number", 0400,
d, adis16136, &adis16136_serial_fops);
debugfs_create_file_unsafe("product_id", 0400,
d, adis16136, &adis16136_product_id_fops);
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index c37316c86f14..aee96167f01e 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -290,9 +290,15 @@ static irqreturn_t max30102_interrupt_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct max30102_data *data = iio_priv(indio_dev);
- unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask,
- iio_get_masklength(indio_dev));
- int ret, cnt = 0;
+ unsigned int measurements;
+ int ret, cnt;
+
+ cnt = max30102_fifo_count(data);
+ if (cnt <= 0)
+ return IRQ_HANDLED;
+
+ measurements = bitmap_weight(indio_dev->active_scan_mask,
+ iio_get_masklength(indio_dev));
mutex_lock(&data->lock);
diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c
index d6c0a544ee9b..79f2974d3777 100644
--- a/drivers/iio/imu/adis16400.c
+++ b/drivers/iio/imu/adis16400.c
@@ -280,7 +280,7 @@ static void adis16400_debugfs_init(struct iio_dev *indio_dev)
return;
if (st->variant->flags & ADIS16400_HAS_SERIAL_NUMBER)
- debugfs_create_file_unsafe("serial_number", 0400,
+ debugfs_create_file("serial_number", 0400,
d, st, &adis16400_serial_number_fops);
if (st->variant->flags & ADIS16400_HAS_PROD_ID)
debugfs_create_file_unsafe("product_id", 0400,
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index 5187566c1876..d51786874e74 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -309,9 +309,9 @@ static void adis16480_debugfs_init(struct iio_dev *indio_dev)
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return;
- debugfs_create_file_unsafe("firmware_revision", 0400,
+ debugfs_create_file("firmware_revision", 0400,
d, adis16480, &adis16480_firmware_revision_fops);
- debugfs_create_file_unsafe("firmware_date", 0400,
+ debugfs_create_file("firmware_date", 0400,
d, adis16480, &adis16480_firmware_date_fops);
debugfs_create_file_unsafe("serial_number", 0400,
d, adis16480, &adis16480_serial_number_fops);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 190e998f7b8e..f4ef75da22c7 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -537,9 +537,8 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
return 0;
}
-static void inv_icm42607_sensors_off(void *_data)
+static int inv_icm42607_sensors_off(struct inv_icm42607_state *st)
{
- struct inv_icm42607_state *st = _data;
const struct device *dev = regmap_get_device(st->map);
int ret;
@@ -552,6 +551,13 @@ static void inv_icm42607_sensors_off(void *_data)
st->conf.accel.mode);
if (ret)
dev_err(dev, "Unable to turn off sensors\n");
+
+ return ret;
+}
+
+static void inv_icm42607_sensors_off_action(void *data)
+{
+ inv_icm42607_sensors_off(data);
}
static void inv_icm42607_disable_vddio_reg(void *_data)
@@ -619,7 +625,7 @@ int inv_icm42607_core_probe(struct regmap *regmap,
* Ensure if sensors get turned on at some point, they're turned off
* as part of teardown.
*/
- ret = devm_add_action_or_reset(dev, inv_icm42607_sensors_off, st);
+ ret = devm_add_action_or_reset(dev, inv_icm42607_sensors_off_action, st);
if (ret)
return ret;
@@ -658,9 +664,8 @@ static int inv_icm42607_suspend(struct device *dev)
return 0;
}
-static int inv_icm42607_resume(struct device *dev)
+static int inv_icm42607_resume_core(struct inv_icm42607_state *st)
{
- struct inv_icm42607_state *st = dev_get_drvdata(dev);
int ret;
ret = inv_icm42607_enable_vddio_reg(st);
@@ -669,9 +674,25 @@ static int inv_icm42607_resume(struct device *dev)
/* Sync the regcache again after regulator shutdown. */
regcache_mark_dirty(st->map);
- ret = regcache_sync(st->map);
- if (ret)
+
+ return regcache_sync(st->map);
+}
+
+static int inv_icm42607_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = inv_icm42607_resume_core(st);
+ if (ret) {
+ int rc;
+
+ rc = pm_runtime_force_resume(dev);
+ if (rc)
+ dev_warn(dev, "Failed to restore runtime PM state: %d\n", rc);
+
return ret;
+ }
return pm_runtime_force_resume(dev);
}
@@ -688,8 +709,7 @@ static int inv_icm42607_runtime_suspend(struct device *dev)
* however the tradeoff is that an unused sensor won't be
* turned off until the entire chip is no longer in use.
*/
- inv_icm42607_sensors_off(st);
- return 0;
+ return inv_icm42607_sensors_off(st);
}
EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = {
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 2c9ec93dff47..4294101810fd 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1377,6 +1377,10 @@ EXPORT_SYMBOL_GPL(iio_update_buffers);
void iio_disable_all_buffers(struct iio_dev *indio_dev)
{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+
+ guard(mutex)(&iio_dev_opaque->mlock);
+
iio_disable_buffers(indio_dev);
iio_buffer_deactivate_all(indio_dev);
}
@@ -2462,7 +2466,8 @@ int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev,
bb = devm_krealloc(&indio_dev->dev,
iio_dev_opaque->bounce_buffer,
- indio_dev->scan_bytes, GFP_KERNEL);
+ indio_dev->scan_bytes,
+ GFP_KERNEL | __GFP_ZERO);
if (!bb)
return -ENOMEM;
iio_dev_opaque->bounce_buffer = bb;
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index 17781c12bc85..76926fee0f84 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -509,6 +509,8 @@ static void iio_trig_release(struct device *device)
struct iio_trigger *trig = to_iio_trigger(device);
int i;
+ cancel_work_sync(&trig->reenable_work);
+
if (trig->subirq_base) {
for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) {
irq_modify_status(trig->subirq_base + i,
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c
index 63591b7ecb89..b435dc5e8706 100644
--- a/drivers/iio/light/gp2ap020a00f.c
+++ b/drivers/iio/light/gp2ap020a00f.c
@@ -1497,6 +1497,7 @@ error_trigger_unregister:
iio_trigger_unregister(data->trig);
error_free_irq:
free_irq(client->irq, indio_dev);
+ irq_work_sync(&data->work);
error_uninit_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_regulator_disable:
@@ -1519,6 +1520,7 @@ static void gp2ap020a00f_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_trigger_unregister(data->trig);
free_irq(client->irq, indio_dev);
+ irq_work_sync(&data->work);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(data->vled_reg);
}
diff --git a/drivers/iio/light/rohm-bu27034.c b/drivers/iio/light/rohm-bu27034.c
index 28d111ac8c0a..92fca33387bb 100644
--- a/drivers/iio/light/rohm-bu27034.c
+++ b/drivers/iio/light/rohm-bu27034.c
@@ -137,6 +137,7 @@ static const struct iio_gain_sel_pair bu27034_gains[] = {
#define BU27034_MEAS_MODE_200MS 2
#define BU27034_MEAS_MODE_400MS 4
+#define BU27034_INT_TIME_US_MIN (55 * USEC_PER_MSEC)
static const struct iio_itime_sel_mul bu27034_itimes[] = {
GAIN_SCALE_ITIME_US(400000, BU27034_MEAS_MODE_400MS, 8),
GAIN_SCALE_ITIME_US(200000, BU27034_MEAS_MODE_200MS, 4),
@@ -296,7 +297,7 @@ static int bu27034_get_gain(struct bu27034_data *data, int chan, int *gain)
return 0;
}
-static int bu27034_get_int_time(struct bu27034_data *data)
+static int bu27034_get_int_time(struct bu27034_data *data, int *itime)
{
int ret, sel;
@@ -304,24 +305,30 @@ static int bu27034_get_int_time(struct bu27034_data *data)
if (ret)
return ret;
- return iio_gts_find_int_time_by_sel(&data->gts,
- sel & BU27034_MASK_MEAS_MODE);
+ ret = iio_gts_find_int_time_by_sel(&data->gts,
+ sel & BU27034_MASK_MEAS_MODE);
+ if (ret < 0)
+ return ret;
+
+ *itime = ret;
+
+ return 0;
}
static int _bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
int *val2)
{
- int gain, ret;
+ int gain, itime, ret;
ret = bu27034_get_gain(data, channel, &gain);
if (ret)
return ret;
- ret = bu27034_get_int_time(data);
- if (ret < 0)
+ ret = bu27034_get_int_time(data, &itime);
+ if (ret)
return ret;
- return iio_gts_get_scale(&data->gts, gain, ret, val, val2);
+ return iio_gts_get_scale(&data->gts, gain, itime, val, val2);
}
static int bu27034_get_scale(struct bu27034_data *data, int channel, int *val,
@@ -397,12 +404,10 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
int ret, int_time_old, i;
guard(mutex)(&data->mutex);
- ret = bu27034_get_int_time(data);
- if (ret < 0)
+ ret = bu27034_get_int_time(data, &int_time_old);
+ if (ret)
return ret;
- int_time_old = ret;
-
if (!iio_gts_valid_time(&data->gts, time_us)) {
dev_err(data->dev, "Unsupported integration time %u\n",
time_us);
@@ -415,7 +420,7 @@ static int bu27034_try_set_int_time(struct bu27034_data *data, int time_us)
for (i = 0; i < numg; i++) {
ret = bu27034_get_gain(data, gains[i].chan, &gains[i].old_gain);
if (ret)
- return 0;
+ return ret;
ret = iio_gts_find_new_gain_by_old_gain_time(&data->gts,
gains[i].old_gain,
@@ -841,7 +846,7 @@ static int bu27034_meas_set(struct bu27034_data *data, bool en)
static int bu27034_get_single_result(struct bu27034_data *data, int chan,
int *val)
{
- int ret;
+ int ret, itime;
if (chan < BU27034_CHAN_DATA0 || chan > BU27034_CHAN_DATA1)
return -EINVAL;
@@ -850,11 +855,11 @@ static int bu27034_get_single_result(struct bu27034_data *data, int chan,
if (ret)
return ret;
- ret = bu27034_get_int_time(data);
- if (ret < 0)
+ ret = bu27034_get_int_time(data, &itime);
+ if (ret)
return ret;
- msleep(ret / 1000);
+ msleep(itime / 1000);
return bu27034_read_result(data, chan, val);
}
@@ -904,12 +909,10 @@ static int bu27034_calc_mlux(struct bu27034_data *data, __le16 *res, int *val)
if (ret)
return ret;
- ret = bu27034_get_int_time(data);
- if (ret < 0)
+ ret = bu27034_get_int_time(data, &meastime);
+ if (ret)
return ret;
- meastime = ret;
-
d1_d0_ratio_scaled = (unsigned int)ch1 * (unsigned int)gain0 * 100;
helper64 = (u64)ch1 * (u64)gain0 * 100LLU;
@@ -970,9 +973,9 @@ static int bu27034_read_raw(struct iio_dev *idev,
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
- *val2 = bu27034_get_int_time(data);
- if (*val2 < 0)
- return *val2;
+ ret = bu27034_get_int_time(data, val2);
+ if (ret)
+ return ret;
return IIO_VAL_INT_PLUS_MICRO;
@@ -1157,11 +1160,20 @@ static int bu27034_buffer_thread(void *arg)
{
struct iio_dev *idev = arg;
struct bu27034_data *data;
- int wait_ms;
+ int wait_ms, ret;
data = iio_priv(idev);
- wait_ms = bu27034_get_int_time(data);
+ /*
+ * If reading the integration time fails, default to the minimum so we
+ * don't lose samples. This may waste CPU cycles, but as a hardening
+ * against theoretical, once-in-a-blue-moon error, this should be Ok.
+ */
+ wait_ms = BU27034_INT_TIME_US_MIN;
+ ret = bu27034_get_int_time(data, &wait_ms);
+ if (ret)
+ dev_warn(data->dev, "Failed to get integration time\n");
+
wait_ms /= 1000;
wait_ms -= BU27034_MEAS_WAIT_PREMATURE_MS;
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index 1066792b9087..3940b8e61a06 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -839,7 +839,7 @@ static int bmp280_write_sampling_frequency(struct bmp280_data *data,
int val, int val2)
{
const int (*avail)[2] = data->chip_info->sampling_freq_avail;
- const int n = data->chip_info->num_sampling_freq_avail;
+ const int n = data->chip_info->num_sampling_freq_avail / 2;
int ret, prev;
int i;
diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c
index 2a87b113823b..7a6a16cffb66 100644
--- a/drivers/iio/pressure/rohm-bm1390.c
+++ b/drivers/iio/pressure/rohm-bm1390.c
@@ -289,7 +289,7 @@ static int bm1390_read_data(struct bm1390_data *data,
if (warn)
dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn);
- return 0;
+ return ret;
}
static int bm1390_read_raw(struct iio_dev *idev,
diff --git a/drivers/iio/proximity/aw96103.c b/drivers/iio/proximity/aw96103.c
index b1f36a51d56f..e3ab55170b6c 100644
--- a/drivers/iio/proximity/aw96103.c
+++ b/drivers/iio/proximity/aw96103.c
@@ -24,6 +24,7 @@
#define AW96103_BIN_VALID_DATA_OFFSET 64
#define AW96103_BIN_DATA_LEN_OFFSET 16
#define AW96103_BIN_DATA_REG_NUM_SIZE 4
+#define AW96103_BIN_REG_SIZE 6
#define AW96103_BIN_CHIP_TYPE_SIZE 8
#define AW96103_BIN_CHIP_TYPE_OFFSET 24
@@ -229,14 +230,27 @@ static const struct aw_chip_info aw_chip_info_tbl[] = {
},
};
-static void aw96103_parsing_bin_file(struct aw_bin *bin)
+static int aw96103_parsing_bin_file(struct aw_bin *bin)
{
+ u32 data_len;
+
+ if (bin->len < AW96103_BIN_VALID_DATA_OFFSET)
+ return -EINVAL;
+
+ data_len = get_unaligned_le32(bin->data + AW96103_BIN_DATA_LEN_OFFSET);
+ if (data_len < AW96103_BIN_DATA_REG_NUM_SIZE)
+ return -EINVAL;
+
+ bin->valid_data_len = data_len - AW96103_BIN_DATA_REG_NUM_SIZE;
+ if (bin->valid_data_len > bin->len - AW96103_BIN_VALID_DATA_OFFSET ||
+ bin->valid_data_len % AW96103_BIN_REG_SIZE)
+ return -EINVAL;
+
bin->valid_data_addr = AW96103_BIN_VALID_DATA_OFFSET;
- bin->valid_data_len =
- *(unsigned int *)(bin->data + AW96103_BIN_DATA_LEN_OFFSET) -
- AW96103_BIN_DATA_REG_NUM_SIZE;
memcpy(bin->chip_type, bin->data + AW96103_BIN_CHIP_TYPE_OFFSET,
AW96103_BIN_CHIP_TYPE_SIZE);
+
+ return 0;
}
static const struct regmap_config aw96103_regmap_confg = {
@@ -500,7 +514,7 @@ static int aw96103_bin_valid_loaded(struct aw96103 *aw96103,
int ret;
for (i = 0; i < aw_bin_data_s->valid_data_len;
- i += 6, start_addr += 6) {
+ i += AW96103_BIN_REG_SIZE, start_addr += AW96103_BIN_REG_SIZE) {
reg_addr = get_unaligned_le16(aw_bin_data_s->data + start_addr);
reg_data = get_unaligned_le32(aw_bin_data_s->data +
start_addr + 2);
@@ -550,6 +564,8 @@ static int aw96103_para_loaded(struct aw96103 *aw96103)
static int aw96103_cfg_all_loaded(const struct firmware *cont,
struct aw96103 *aw96103)
{
+ int ret;
+
if (!cont)
return -EINVAL;
@@ -561,7 +577,9 @@ static int aw96103_cfg_all_loaded(const struct firmware *cont,
aw_bin->len = cont->size;
memcpy(aw_bin->data, cont->data, cont->size);
release_firmware(cont);
- aw96103_parsing_bin_file(aw_bin);
+ ret = aw96103_parsing_bin_file(aw_bin);
+ if (ret)
+ return ret;
return aw96103_bin_valid_loaded(aw96103, aw_bin);
}
diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c
index 95fb7238f678..a98a9975323c 100644
--- a/drivers/iio/proximity/isl29501.c
+++ b/drivers/iio/proximity/isl29501.c
@@ -226,7 +226,7 @@ err:
return ret;
}
-static u32 isl29501_register_write(struct isl29501_private *isl29501,
+static int isl29501_register_write(struct isl29501_private *isl29501,
enum isl29501_register_name name,
u32 value)
{
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
index 400477b4c740..8a9ee21f2bf6 100644
--- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -294,12 +294,14 @@ static int lidar_probe(struct i2c_client *client)
ret = pm_runtime_set_active(&client->dev);
if (ret)
- goto error_unreg_buffer;
+ goto error_unreg_dev;
pm_runtime_enable(&client->dev);
pm_runtime_idle(&client->dev);
return 0;
+error_unreg_dev:
+ iio_device_unregister(indio_dev);
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c
index 13b4ef2896e3..255a36976ac4 100644
--- a/drivers/iio/proximity/sx9324.c
+++ b/drivers/iio/proximity/sx9324.c
@@ -222,7 +222,7 @@ static const struct iio_chan_spec_ext_info sx9324_channel_ext_info[] = {
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
- .realbits = 12, \
+ .realbits = 16, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c
index 6b0d7a9038d9..768870127ff8 100644
--- a/drivers/iio/proximity/vcnl3020.c
+++ b/drivers/iio/proximity/vcnl3020.c
@@ -584,16 +584,26 @@ static irqreturn_t vcnl3020_handle_irq_thread(int irq, void *p)
return IRQ_HANDLED;
}
- if (!(isr & VCNL_ICR_THRES_EN))
+ if (!(isr & (VCNL_INT_TH_HI | VCNL_INT_TH_LOW)))
return IRQ_NONE;
- iio_push_event(indio_dev,
- IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
- IIO_EV_TYPE_THRESH,
- IIO_EV_DIR_RISING),
- iio_get_time_ns(indio_dev));
+ if (isr & VCNL_INT_TH_HI) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+ }
+
+ if (isr & VCNL_INT_TH_LOW) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ iio_get_time_ns(indio_dev));
+ }
- rc = regmap_write(data->regmap, VCNL_ISR, isr & VCNL_ICR_THRES_EN);
+ rc = regmap_write(data->regmap, VCNL_ISR, isr & (VCNL_INT_TH_HI | VCNL_INT_TH_LOW));
if (rc)
dev_err(data->dev, "Error (%d) writing in reg (0x%x)\n",
rc, VCNL_ISR);
diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c
index 9fe14ceb8be7..173e69753df7 100644
--- a/drivers/iio/proximity/vl53l0x-i2c.c
+++ b/drivers/iio/proximity/vl53l0x-i2c.c
@@ -219,7 +219,10 @@ static int vl53l0x_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
ret = vl53l0x_read_proximity(data, chan, val);
+ iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 93650c98206d..b5ee5697a136 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -1939,6 +1939,14 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
.callback = atkbd_deactivate_fixup,
},
{
+ /* Lenovo IdeaPad Slim 3 15IWC11 (83RR) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83RR"),
+ },
+ .callback = atkbd_deactivate_fixup,
+ },
+ {
/* Xiaomi Book Pro 14 (TM2424) */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a8ce89d41faa..1ed9261bff2b 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -192,6 +192,7 @@ static const char * const smbus_pnp_ids[] = {
"LEN2054", /* E480 */
"LEN2055", /* E580 */
"LEN2058", /* E490 */
+ "LEN205b", /* T490 */
"LEN2068", /* T14 Gen 1 */
"SYN1221", /* TUXEDO InfinityBook Pro 14 v5 */
"SYN3003", /* HP EliteBook 850 G1 */
@@ -1839,8 +1840,10 @@ static int synaptics_setup_intertouch(struct psmouse *psmouse,
return -ENXIO;
}
- /* Disable intertouch on known-broken board revisions */
- if (info->board_id == 2722) {
+ /* Disable intertouch on known-broken T440p board revisions */
+ if (info->board_id == 2722 &&
+ psmouse_matches_pnp_id(psmouse,
+ (const char * const []){"LEN0036", NULL})) {
psmouse_info(psmouse,
"Disabling intertouch for board id %u\n",
info->board_id);
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index 54eb3687000a..1b1c5d4ac01c 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -672,6 +672,15 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
.driver_data = (void *)(SERIO_QUIRK_NOAUX)
},
{
+ /* Fujitsu LIFEBOOK U7410 */
+ /* Mux mode also disturbs the EC keyboard, causing spurious ACKs */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU CLIENT COMPUTING LIMITED"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U7410"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
/* Gigabyte M912 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
diff --git a/drivers/input/touchscreen/s6sy761.c b/drivers/input/touchscreen/s6sy761.c
index 0f24a9b73063..899c179fb2d3 100644
--- a/drivers/input/touchscreen/s6sy761.c
+++ b/drivers/input/touchscreen/s6sy761.c
@@ -500,10 +500,22 @@ static int s6sy761_suspend(struct device *dev)
static int s6sy761_resume(struct device *dev)
{
struct s6sy761_data *sdata = dev_get_drvdata(dev);
+ int err;
+
+ err = s6sy761_power_on(sdata);
+ if (err)
+ return err;
enable_irq(sdata->client->irq);
- return s6sy761_power_on(sdata);
+ guard(mutex)(&sdata->input->mutex);
+
+ if (!input_device_enabled(sdata->input))
+ return 0;
+
+ err = i2c_smbus_write_byte(sdata->client, S6SY761_SENSE_ON);
+
+ return err;
}
static const struct dev_pm_ops s6sy761_pm_ops = {
diff --git a/drivers/interconnect/qcom/x1e80100.c b/drivers/interconnect/qcom/x1e80100.c
index 8075e0ff2059..2ba2823c7860 100644
--- a/drivers/interconnect/qcom/x1e80100.c
+++ b/drivers/interconnect/qcom/x1e80100.c
@@ -173,13 +173,6 @@ static struct qcom_icc_node qhm_qspi = {
.name = "qhm_qspi",
.channels = 1,
.buswidth = 4,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xb000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -188,13 +181,6 @@ static struct qcom_icc_node qhm_qup1 = {
.name = "qhm_qup1",
.channels = 1,
.buswidth = 4,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xc000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -203,13 +189,6 @@ static struct qcom_icc_node xm_sdc4 = {
.name = "xm_sdc4",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xd000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -218,13 +197,6 @@ static struct qcom_icc_node xm_ufs_mem = {
.name = "xm_ufs_mem",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xe000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a1noc_snoc },
};
@@ -233,13 +205,6 @@ static struct qcom_icc_node qhm_qup0 = {
.name = "qhm_qup0",
.channels = 1,
.buswidth = 4,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x16000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -248,13 +213,6 @@ static struct qcom_icc_node qhm_qup2 = {
.name = "qhm_qup2",
.channels = 1,
.buswidth = 4,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x11000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -263,13 +221,6 @@ static struct qcom_icc_node qxm_crypto = {
.name = "qxm_crypto",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x12000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -286,13 +237,6 @@ static struct qcom_icc_node xm_qdss_etr_0 = {
.name = "xm_qdss_etr_0",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x13000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -301,13 +245,6 @@ static struct qcom_icc_node xm_qdss_etr_1 = {
.name = "xm_qdss_etr_1",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x14000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -316,13 +253,6 @@ static struct qcom_icc_node xm_sdc2 = {
.name = "xm_sdc2",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x15000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_a2noc_snoc },
};
@@ -407,13 +337,6 @@ static struct qcom_icc_node alm_gpu_tcu = {
.name = "alm_gpu_tcu",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x271000 },
- .prio = 1,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -422,13 +345,6 @@ static struct qcom_icc_node alm_pcie_tcu = {
.name = "alm_pcie_tcu",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x27d000 },
- .prio = 3,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -437,13 +353,6 @@ static struct qcom_icc_node alm_sys_tcu = {
.name = "alm_sys_tcu",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x273000 },
- .prio = 6,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -461,13 +370,6 @@ static struct qcom_icc_node qnm_gpu = {
.name = "qnm_gpu",
.channels = 4,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 4,
- .port_offsets = { 0x51000, 0x58000, 0xd1000, 0xd8000 },
- .prio = 0,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -476,13 +378,6 @@ static struct qcom_icc_node qnm_lpass = {
.name = "qnm_lpass",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x275000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
&qns_pcie },
@@ -492,13 +387,6 @@ static struct qcom_icc_node qnm_mnoc_hf = {
.name = "qnm_mnoc_hf",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x53000, 0xd3000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -507,13 +395,6 @@ static struct qcom_icc_node qnm_mnoc_sf = {
.name = "qnm_mnoc_sf",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x55000, 0xd5000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -522,13 +403,6 @@ static struct qcom_icc_node qnm_nsp_noc = {
.name = "qnm_nsp_noc",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x57000, 0xd7000 },
- .prio = 0,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
&qns_pcie },
@@ -538,13 +412,6 @@ static struct qcom_icc_node qnm_pcie = {
.name = "qnm_pcie",
.channels = 1,
.buswidth = 64,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x277000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 2,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc },
};
@@ -553,13 +420,6 @@ static struct qcom_icc_node qnm_snoc_sf = {
.name = "qnm_snoc_sf",
.channels = 1,
.buswidth = 64,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x27b000 },
- .prio = 2,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 3,
.link_nodes = { &qns_gem_noc_cnoc, &qns_llcc,
&qns_pcie },
@@ -569,13 +429,6 @@ static struct qcom_icc_node xm_gic = {
.name = "xm_gic",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x27f000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_llcc },
};
@@ -616,13 +469,6 @@ static struct qcom_icc_node qnm_av1_enc = {
.name = "qnm_av1_enc",
.channels = 1,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x2f000 },
- .prio = 4,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -631,13 +477,6 @@ static struct qcom_icc_node qnm_camnoc_hf = {
.name = "qnm_camnoc_hf",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x28000, 0x29000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -646,13 +485,6 @@ static struct qcom_icc_node qnm_camnoc_icp = {
.name = "qnm_camnoc_icp",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x2a000 },
- .prio = 4,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -661,13 +493,6 @@ static struct qcom_icc_node qnm_camnoc_sf = {
.name = "qnm_camnoc_sf",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x2b000, 0x2c000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -676,13 +501,6 @@ static struct qcom_icc_node qnm_eva = {
.name = "qnm_eva",
.channels = 1,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x33000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -691,13 +509,6 @@ static struct qcom_icc_node qnm_mdp = {
.name = "qnm_mdp",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x2d000, 0x2e000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_hf },
};
@@ -706,13 +517,6 @@ static struct qcom_icc_node qnm_video = {
.name = "qnm_video",
.channels = 2,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 2,
- .port_offsets = { 0x30000, 0x31000 },
- .prio = 0,
- .urg_fwd = 1,
- .prio_fwd_disable = 0,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -721,13 +525,6 @@ static struct qcom_icc_node qnm_video_cv_cpu = {
.name = "qnm_video_cv_cpu",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x32000 },
- .prio = 4,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -736,13 +533,6 @@ static struct qcom_icc_node qnm_video_v_cpu = {
.name = "qnm_video_v_cpu",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x34000 },
- .prio = 4,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_mem_noc_sf },
};
@@ -783,13 +573,6 @@ static struct qcom_icc_node xm_pcie_3 = {
.name = "xm_pcie_3",
.channels = 1,
.buswidth = 64,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x7000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_north_gem_noc },
};
@@ -798,13 +581,6 @@ static struct qcom_icc_node xm_pcie_4 = {
.name = "xm_pcie_4",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x8000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_north_gem_noc },
};
@@ -813,13 +589,6 @@ static struct qcom_icc_node xm_pcie_5 = {
.name = "xm_pcie_5",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x9000 },
- .prio = 3,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_north_gem_noc },
};
@@ -828,13 +597,6 @@ static struct qcom_icc_node xm_pcie_0 = {
.name = "xm_pcie_0",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x9000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -843,13 +605,6 @@ static struct qcom_icc_node xm_pcie_1 = {
.name = "xm_pcie_1",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xa000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -858,13 +613,6 @@ static struct qcom_icc_node xm_pcie_2 = {
.name = "xm_pcie_2",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xb000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -873,13 +621,6 @@ static struct qcom_icc_node xm_pcie_6a = {
.name = "xm_pcie_6a",
.channels = 1,
.buswidth = 32,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xc000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -888,13 +629,6 @@ static struct qcom_icc_node xm_pcie_6b = {
.name = "xm_pcie_6b",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xd000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_pcie_south_gem_noc },
};
@@ -919,13 +653,6 @@ static struct qcom_icc_node qnm_gic = {
.name = "qnm_gic",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x1c000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_gemnoc_sf },
};
@@ -958,13 +685,6 @@ static struct qcom_icc_node xm_usb2_0 = {
.name = "xm_usb2_0",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x6000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_north_snoc },
};
@@ -973,13 +693,6 @@ static struct qcom_icc_node xm_usb3_mp = {
.name = "xm_usb3_mp",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0x7000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_north_snoc },
};
@@ -988,13 +701,6 @@ static struct qcom_icc_node xm_usb3_0 = {
.name = "xm_usb3_0",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xa000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1003,13 +709,6 @@ static struct qcom_icc_node xm_usb3_1 = {
.name = "xm_usb3_1",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xb000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1018,13 +717,6 @@ static struct qcom_icc_node xm_usb3_2 = {
.name = "xm_usb3_2",
.channels = 1,
.buswidth = 8,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xc000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1033,13 +725,6 @@ static struct qcom_icc_node xm_usb4_0 = {
.name = "xm_usb4_0",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xd000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1048,13 +733,6 @@ static struct qcom_icc_node xm_usb4_1 = {
.name = "xm_usb4_1",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xe000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1063,13 +741,6 @@ static struct qcom_icc_node xm_usb4_2 = {
.name = "xm_usb4_2",
.channels = 1,
.buswidth = 16,
- .qosbox = &(const struct qcom_icc_qosbox) {
- .num_ports = 1,
- .port_offsets = { 0xf000 },
- .prio = 2,
- .urg_fwd = 0,
- .prio_fwd_disable = 1,
- },
.num_links = 1,
.link_nodes = { &qns_aggre_usb_south_snoc },
};
@@ -1795,21 +1466,11 @@ static struct qcom_icc_node * const aggre1_noc_nodes[] = {
[SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc,
};
-static const struct regmap_config x1e80100_aggre1_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x14400,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_aggre1_noc = {
- .config = &x1e80100_aggre1_noc_regmap_config,
.nodes = aggre1_noc_nodes,
.num_nodes = ARRAY_SIZE(aggre1_noc_nodes),
.bcms = aggre1_noc_bcms,
.num_bcms = ARRAY_SIZE(aggre1_noc_bcms),
- .qos_requires_clocks = true,
};
static struct qcom_icc_bcm * const aggre2_noc_bcms[] = {
@@ -1827,16 +1488,7 @@ static struct qcom_icc_node * const aggre2_noc_nodes[] = {
[SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc,
};
-static const struct regmap_config x1e80100_aggre2_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x1c400,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_aggre2_noc = {
- .config = &x1e80100_aggre2_noc_regmap_config,
.nodes = aggre2_noc_nodes,
.num_nodes = ARRAY_SIZE(aggre2_noc_nodes),
.bcms = aggre2_noc_bcms,
@@ -1921,16 +1573,7 @@ static struct qcom_icc_node * const cnoc_cfg_nodes[] = {
[SLAVE_TCU] = &xs_sys_tcu_cfg,
};
-static const struct regmap_config x1e80100_cnoc_cfg_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x6600,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_cnoc_cfg = {
- .config = &x1e80100_cnoc_cfg_regmap_config,
.nodes = cnoc_cfg_nodes,
.num_nodes = ARRAY_SIZE(cnoc_cfg_nodes),
.bcms = cnoc_cfg_bcms,
@@ -1960,16 +1603,7 @@ static struct qcom_icc_node * const cnoc_main_nodes[] = {
[SLAVE_PCIE_6B] = &xs_pcie_6b,
};
-static const struct regmap_config x1e80100_cnoc_main_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x14400,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_cnoc_main = {
- .config = &x1e80100_cnoc_main_regmap_config,
.nodes = cnoc_main_nodes,
.num_nodes = ARRAY_SIZE(cnoc_main_nodes),
.bcms = cnoc_main_bcms,
@@ -1999,16 +1633,7 @@ static struct qcom_icc_node * const gem_noc_nodes[] = {
[SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie,
};
-static const struct regmap_config x1e80100_gem_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x311200,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_gem_noc = {
- .config = &x1e80100_gem_noc_regmap_config,
.nodes = gem_noc_nodes,
.num_nodes = ARRAY_SIZE(gem_noc_nodes),
.bcms = gem_noc_bcms,
@@ -2023,16 +1648,7 @@ static struct qcom_icc_node * const lpass_ag_noc_nodes[] = {
[SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc,
};
-static const struct regmap_config x1e80100_lpass_ag_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0xe080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_lpass_ag_noc = {
- .config = &x1e80100_lpass_ag_noc_regmap_config,
.nodes = lpass_ag_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes),
.bcms = lpass_ag_noc_bcms,
@@ -2048,16 +1664,7 @@ static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = {
[SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc,
};
-static const struct regmap_config x1e80100_lpass_lpiaon_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x19080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_lpass_lpiaon_noc = {
- .config = &x1e80100_lpass_lpiaon_noc_regmap_config,
.nodes = lpass_lpiaon_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes),
.bcms = lpass_lpiaon_noc_bcms,
@@ -2072,16 +1679,7 @@ static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = {
[SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc,
};
-static const struct regmap_config x1e80100_lpass_lpicx_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x3a200,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_lpass_lpicx_noc = {
- .config = &x1e80100_lpass_lpicx_noc_regmap_config,
.nodes = lpass_lpicx_noc_nodes,
.num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes),
.bcms = lpass_lpicx_noc_bcms,
@@ -2126,16 +1724,7 @@ static struct qcom_icc_node * const mmss_noc_nodes[] = {
[SLAVE_SERVICE_MNOC] = &srvc_mnoc,
};
-static const struct regmap_config x1e80100_mmss_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x5b800,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_mmss_noc = {
- .config = &x1e80100_mmss_noc_regmap_config,
.nodes = mmss_noc_nodes,
.num_nodes = ARRAY_SIZE(mmss_noc_nodes),
.bcms = mmss_noc_bcms,
@@ -2151,16 +1740,7 @@ static struct qcom_icc_node * const nsp_noc_nodes[] = {
[SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc,
};
-static const struct regmap_config x1e80100_nsp_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0xe080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_nsp_noc = {
- .config = &x1e80100_nsp_noc_regmap_config,
.nodes = nsp_noc_nodes,
.num_nodes = ARRAY_SIZE(nsp_noc_nodes),
.bcms = nsp_noc_bcms,
@@ -2177,16 +1757,7 @@ static struct qcom_icc_node * const pcie_center_anoc_nodes[] = {
[SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc,
};
-static const struct regmap_config x1e80100_pcie_center_anoc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x7000,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_pcie_center_anoc = {
- .config = &x1e80100_pcie_center_anoc_regmap_config,
.nodes = pcie_center_anoc_nodes,
.num_nodes = ARRAY_SIZE(pcie_center_anoc_nodes),
.bcms = pcie_center_anoc_bcms,
@@ -2203,16 +1774,7 @@ static struct qcom_icc_node * const pcie_north_anoc_nodes[] = {
[SLAVE_PCIE_NORTH] = &qns_pcie_north_gem_noc,
};
-static const struct regmap_config x1e80100_pcie_north_anoc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x9080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_pcie_north_anoc = {
- .config = &x1e80100_pcie_north_anoc_regmap_config,
.nodes = pcie_north_anoc_nodes,
.num_nodes = ARRAY_SIZE(pcie_north_anoc_nodes),
.bcms = pcie_north_anoc_bcms,
@@ -2231,16 +1793,7 @@ static struct qcom_icc_node * const pcie_south_anoc_nodes[] = {
[SLAVE_PCIE_SOUTH] = &qns_pcie_south_gem_noc,
};
-static const struct regmap_config x1e80100_pcie_south_anoc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0xd080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_pcie_south_anoc = {
- .config = &x1e80100_pcie_south_anoc_regmap_config,
.nodes = pcie_south_anoc_nodes,
.num_nodes = ARRAY_SIZE(pcie_south_anoc_nodes),
.bcms = pcie_south_anoc_bcms,
@@ -2262,16 +1815,7 @@ static struct qcom_icc_node * const system_noc_nodes[] = {
[SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf,
};
-static const struct regmap_config x1e80100_system_noc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x1c080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_system_noc = {
- .config = &x1e80100_system_noc_regmap_config,
.nodes = system_noc_nodes,
.num_nodes = ARRAY_SIZE(system_noc_nodes),
.bcms = system_noc_bcms,
@@ -2287,16 +1831,7 @@ static struct qcom_icc_node * const usb_center_anoc_nodes[] = {
[SLAVE_USB_NOC_SNOC] = &qns_aggre_usb_snoc,
};
-static const struct regmap_config x1e80100_usb_center_anoc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x8800,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_usb_center_anoc = {
- .config = &x1e80100_usb_center_anoc_regmap_config,
.nodes = usb_center_anoc_nodes,
.num_nodes = ARRAY_SIZE(usb_center_anoc_nodes),
.bcms = usb_center_anoc_bcms,
@@ -2312,21 +1847,11 @@ static struct qcom_icc_node * const usb_north_anoc_nodes[] = {
[SLAVE_AGGRE_USB_NORTH] = &qns_aggre_usb_north_snoc,
};
-static const struct regmap_config x1e80100_usb_north_anoc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0x7080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_usb_north_anoc = {
- .config = &x1e80100_usb_north_anoc_regmap_config,
.nodes = usb_north_anoc_nodes,
.num_nodes = ARRAY_SIZE(usb_north_anoc_nodes),
.bcms = usb_north_anoc_bcms,
.num_bcms = ARRAY_SIZE(usb_north_anoc_bcms),
- .qos_requires_clocks = true,
};
static struct qcom_icc_bcm * const usb_south_anoc_bcms[] = {
@@ -2342,21 +1867,11 @@ static struct qcom_icc_node * const usb_south_anoc_nodes[] = {
[SLAVE_AGGRE_USB_SOUTH] = &qns_aggre_usb_south_snoc,
};
-static const struct regmap_config x1e80100_usb_south_anoc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = 0xf080,
- .fast_io = true,
-};
-
static const struct qcom_icc_desc x1e80100_usb_south_anoc = {
- .config = &x1e80100_usb_south_anoc_regmap_config,
.nodes = usb_south_anoc_nodes,
.num_nodes = ARRAY_SIZE(usb_south_anoc_nodes),
.bcms = usb_south_anoc_bcms,
.num_bcms = ARRAY_SIZE(usb_south_anoc_bcms),
- .qos_requires_clocks = true,
};
static const struct of_device_id qnoc_of_match[] = {
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 6049ba2d6bcb..60d82a1acb01 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -1716,12 +1716,24 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
}
+/*
+ * Keep noinline: inlined, the map_word temporaries put do_write_buffer() over
+ * the frame-size limit with MTD_MAP_BANK_WIDTH_32 and KASAN_STACK.
+ */
+static noinline void __xipram cfi_write_cmd(struct map_info *map,
+ unsigned long cmd, unsigned long adr)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+
+ map_write(map, CMD(cmd), adr);
+}
+
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const struct kvec **pvec,
unsigned long *pvec_seek, int len)
{
struct cfi_private *cfi = map->fldrv_priv;
- map_word status, write_cmd, datum;
+ map_word status, datum;
unsigned long cmd_adr;
int ret, wbufsize, word_gap, words;
const struct kvec *vec;
@@ -1740,9 +1752,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
if (is_LH28F640BF(cfi))
cmd_adr = adr;
- /* Let's determine this according to the interleave only once */
- write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
-
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, cmd_adr, FL_WRITING);
if (ret) {
@@ -1759,7 +1768,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
So we must check here and reset those bits if they're set. Otherwise
we're just pissing in the wind */
if (chip->state != FL_STATUS) {
- map_write(map, CMD(0x70), cmd_adr);
+ cfi_write_cmd(map, 0x70, cmd_adr);
chip->state = FL_STATUS;
}
status = map_read(map, cmd_adr);
@@ -1767,21 +1776,22 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
xip_enable(map, chip, cmd_adr);
printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
xip_disable(map, chip, cmd_adr);
- map_write(map, CMD(0x50), cmd_adr);
- map_write(map, CMD(0x70), cmd_adr);
+ cfi_write_cmd(map, 0x50, cmd_adr);
+ cfi_write_cmd(map, 0x70, cmd_adr);
}
chip->state = FL_WRITING_TO_BUFFER;
- map_write(map, write_cmd, cmd_adr);
+ cfi_write_cmd(map, (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? 0xe8 : 0xe9,
+ cmd_adr);
ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0, 0);
if (ret) {
/* Argh. Not ready for write to buffer */
map_word Xstatus = map_read(map, cmd_adr);
- map_write(map, CMD(0x70), cmd_adr);
+ cfi_write_cmd(map, 0x70, cmd_adr);
chip->state = FL_STATUS;
status = map_read(map, cmd_adr);
- map_write(map, CMD(0x50), cmd_adr);
- map_write(map, CMD(0x70), cmd_adr);
+ cfi_write_cmd(map, 0x50, cmd_adr);
+ cfi_write_cmd(map, 0x70, cmd_adr);
xip_enable(map, chip, cmd_adr);
printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
map->name, Xstatus.x[0], status.x[0]);
@@ -1800,7 +1810,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
}
/* Write length of data to come */
- map_write(map, CMD(words), cmd_adr );
+ cfi_write_cmd(map, words, cmd_adr);
/* Write data */
vec = *pvec;
@@ -1837,7 +1847,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
*pvec_seek = vec_seek;
/* GO GO GO */
- map_write(map, CMD(0xd0), cmd_adr);
+ cfi_write_cmd(map, 0xd0, cmd_adr);
chip->state = FL_WRITING;
ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
@@ -1845,7 +1855,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
chip->buffer_write_time,
chip->buffer_write_time_max);
if (ret) {
- map_write(map, CMD(0x70), cmd_adr);
+ cfi_write_cmd(map, 0x70, cmd_adr);
chip->state = FL_STATUS;
xip_enable(map, chip, cmd_adr);
printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
@@ -1858,8 +1868,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long chipstatus = MERGESTATUS(status);
/* reset status */
- map_write(map, CMD(0x50), cmd_adr);
- map_write(map, CMD(0x70), cmd_adr);
+ cfi_write_cmd(map, 0x50, cmd_adr);
+ cfi_write_cmd(map, 0x70, cmd_adr);
xip_enable(map, chip, cmd_adr);
if (chipstatus & 0x02) {
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 03e80b2c4f5a..349fa07be314 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -293,7 +293,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
}
size = bdev_nr_bytes(bdev);
- if ((long)size % erase_size) {
+ if (!erase_size || (long)size % erase_size) {
pr_err("erasesize must be a divisor of device size\n");
goto err_free_block2mtd;
}
diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c
index a566e86eb5e3..55a945c1a67a 100644
--- a/drivers/mtd/devices/mtd_intel_dg.c
+++ b/drivers/mtd/devices/mtd_intel_dg.c
@@ -387,7 +387,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a
void __iomem *base2 = nvm->base2;
void __iomem *base = nvm->base;
const u32 block = 0x10;
- u32 iter = 0;
+ u32 iter;
u32 reg;
u64 i;
@@ -396,6 +396,7 @@ idg_erase(struct intel_dg_nvm *nvm, u8 region, loff_t from, u64 len, u64 *fail_a
iowrite32(region << 24 | block, base + NVM_ERASE_REG);
if (nvm->non_posted_erase) {
/* Wait for Erase Done */
+ iter = 0;
reg = ioread32(base2 + NVM_DEBUG_REG);
while (!(reg & NVM_NON_POSTED_ERASE_DONE) &&
++iter < NVM_NON_POSTED_ERASE_DONE_ITER) {
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 16629382a787..491a27d0315f 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1083,6 +1083,7 @@ static int mtd_otp_nvmem_add(struct mtd_info *mtd)
err:
nvmem_unregister(mtd->otp_user_nvmem);
+ mtd->otp_user_nvmem = NULL;
/* Don't report error if OTP is not supported. */
if (err == -EOPNOTSUPP)
return 0;
@@ -1332,7 +1333,7 @@ int __get_mtd_device(struct mtd_info *mtd)
int err;
if (master->_get_device) {
- err = master->_get_device(mtd);
+ err = master->_get_device(master);
if (err)
return err;
}
diff --git a/drivers/mtd/nand/ecc.c b/drivers/mtd/nand/ecc.c
index 6ccdff3fc913..1d809cbd47e6 100644
--- a/drivers/mtd/nand/ecc.c
+++ b/drivers/mtd/nand/ecc.c
@@ -159,6 +159,13 @@ int nand_ecc_finish_io_req(struct nand_device *nand,
}
EXPORT_SYMBOL(nand_ecc_finish_io_req);
+bool nand_ecc_is_pipelined(const struct nand_device *nand)
+{
+ return nand->ecc.engine &&
+ nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED;
+}
+EXPORT_SYMBOL(nand_ecc_is_pipelined);
+
/* Define default OOB placement schemes for large and small page devices */
static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c
index d53b35a8b3cb..2085632631c0 100644
--- a/drivers/mtd/nand/raw/cadence-nand-controller.c
+++ b/drivers/mtd/nand/raw/cadence-nand-controller.c
@@ -3143,6 +3143,9 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
goto free_buf_desc;
}
+ spin_lock_init(&cdns_ctrl->irq_lock);
+ init_completion(&cdns_ctrl->complete);
+
if (devm_request_irq(cdns_ctrl->dev, cdns_ctrl->irq, cadence_nand_isr,
IRQF_SHARED, "cadence-nand-controller",
cdns_ctrl)) {
@@ -3151,9 +3154,6 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
goto free_buf;
}
- spin_lock_init(&cdns_ctrl->irq_lock);
- init_completion(&cdns_ctrl->complete);
-
ret = cadence_nand_hw_init(cdns_ctrl);
if (ret)
goto disable_irq;
diff --git a/drivers/mtd/nand/raw/vf610_nfc.c b/drivers/mtd/nand/raw/vf610_nfc.c
index 9940681810cf..1b9b370adfab 100644
--- a/drivers/mtd/nand/raw/vf610_nfc.c
+++ b/drivers/mtd/nand/raw/vf610_nfc.c
@@ -505,6 +505,11 @@ static int vf610_nfc_exec_op(struct nand_chip *chip,
check_only);
}
+static unsigned int vf610_nfc_spare_size(struct mtd_info *mtd)
+{
+ return min_t(unsigned int, mtd->oobsize, 64);
+}
+
static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
uint8_t *oob, int page)
{
@@ -514,6 +519,7 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
u8 ecc_status;
u8 ecc_count;
int flips_threshold = nfc->chip.ecc.strength / 2;
+ int ret;
ecc_status = vf610_nfc_read(nfc, ecc_status_off) & 0xff;
ecc_count = ecc_status & ECC_STATUS_ERR_COUNT;
@@ -521,16 +527,22 @@ static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
if (!(ecc_status & ECC_STATUS_MASK))
return ecc_count;
+ /* The failed decode leaves a bogus correction in SRAM; re-read without ECC */
nfc->data_access = true;
- nand_read_oob_op(&nfc->chip, page, 0, oob, mtd->oobsize);
+ ret = nand_read_page_op(&nfc->chip, page, 0, dat, mtd->writesize);
+ if (!ret)
+ ret = nand_read_oob_op(&nfc->chip, page, 0, oob,
+ vf610_nfc_spare_size(mtd));
nfc->data_access = false;
+ if (ret)
+ return ret;
/*
- * On an erased page, bit count (including OOB) should be zero or
- * at least less then half of the ECC strength.
+ * Run the erased-page check with the driver's historic threshold
+ * of half the ECC strength.
*/
- return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
- mtd->oobsize, NULL, 0,
+ return nand_check_erased_ecc_chunk(dat, mtd->writesize, oob,
+ vf610_nfc_spare_size(mtd), NULL, 0,
flips_threshold);
}
@@ -551,7 +563,7 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf,
{
struct vf610_nfc *nfc = chip_to_nfc(chip);
struct mtd_info *mtd = nand_to_mtd(chip);
- int trfr_sz = mtd->writesize + mtd->oobsize;
+ int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd);
u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
int stat;
@@ -577,11 +589,16 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf,
*/
vf610_nfc_rd_from_sram(buf, nfc->regs + NFC_MAIN_AREA(0),
mtd->writesize, false);
- if (oob_required)
+ if (oob_required) {
+ unsigned int spare = vf610_nfc_spare_size(mtd);
+
vf610_nfc_rd_from_sram(chip->oob_poi,
nfc->regs + NFC_MAIN_AREA(0) +
mtd->writesize,
- mtd->oobsize, false);
+ spare, false);
+ /* Not transferred, and never written: reads back erased */
+ memset(chip->oob_poi + spare, 0xff, mtd->oobsize - spare);
+ }
stat = vf610_nfc_correct_data(chip, buf, chip->oob_poi, page);
@@ -599,7 +616,7 @@ static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf,
{
struct vf610_nfc *nfc = chip_to_nfc(chip);
struct mtd_info *mtd = nand_to_mtd(chip);
- int trfr_sz = mtd->writesize + mtd->oobsize;
+ int trfr_sz = mtd->writesize + vf610_nfc_spare_size(mtd);
u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
u8 status;
int ret;
@@ -740,6 +757,49 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc)
}
}
+/*
+ * With 64 byte OOB chips the core's large page layout matches what
+ * U-Boot uses, and on chips with more, U-Boot and older kernels clamped
+ * mtd->oobsize to 64. Modifying the OOB size is no longer possible, the
+ * actual chip geometry must be respected, so to avoid breaking those
+ * existing setups use our own layout: the core's large page one,
+ * computed over the first 64 spare bytes only.
+ */
+static int vf610_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+ if (section || !total_ecc_bytes)
+ return -ERANGE;
+
+ oobregion->length = total_ecc_bytes;
+ oobregion->offset = vf610_nfc_spare_size(mtd) - oobregion->length;
+
+ return 0;
+}
+
+static int vf610_nfc_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ struct nand_device *nand = mtd_to_nanddev(mtd);
+ unsigned int total_ecc_bytes = nand->ecc.ctx.total;
+
+ if (section)
+ return -ERANGE;
+
+ oobregion->length = vf610_nfc_spare_size(mtd) - total_ecc_bytes - 2;
+ oobregion->offset = 2;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops vf610_nfc_ooblayout_ops = {
+ .ecc = vf610_nfc_ooblayout_ecc,
+ .free = vf610_nfc_ooblayout_free,
+};
+
static int vf610_nfc_attach_chip(struct nand_chip *chip)
{
struct mtd_info *mtd = nand_to_mtd(chip);
@@ -770,12 +830,7 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip)
return -ENXIO;
}
- /* Only 64 byte ECC layouts known */
- if (mtd->oobsize > 64)
- mtd->oobsize = 64;
-
- /* Use default large page ECC layout defined in NAND core */
- mtd_set_ooblayout(mtd, nand_get_large_page_ooblayout());
+ mtd_set_ooblayout(mtd, &vf610_nfc_ooblayout_ops);
if (chip->ecc.strength == 32) {
nfc->ecc_mode = ECC_60_BYTE;
chip->ecc.bytes = 60;
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 8bf9301f25e7..95353777d7f7 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -281,8 +281,26 @@ static int spinand_init_cfg_cache(struct spinand_device *spinand)
static int spinand_init_quad_enable(struct spinand_device *spinand,
bool enable)
{
- return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
- enable ? CFG_QUAD_ENABLE : 0);
+ struct nand_device *nand = spinand_to_nand(spinand);
+ unsigned int target;
+ int ret;
+
+ /*
+ * QE is a per-die setting on some devices. Program each target
+ * individually when enabling or disabling quad I/O mode.
+ */
+ for (target = 0; target < nand->memorg.ntargets; target++) {
+ ret = spinand_select_target(spinand, target);
+ if (ret)
+ return ret;
+
+ ret = spinand_upd_cfg(spinand, CFG_QUAD_ENABLE,
+ enable ? CFG_QUAD_ENABLE : 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
static int spinand_ecc_enable(struct spinand_device *spinand,
@@ -508,8 +526,7 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
else
rdesc->info.op_tmpl = &rdesc->info.primary_op_tmpl;
- if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
- req->mode != MTD_OPS_RAW)
+ if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW)
rdesc->info.op_tmpl->data.ecc = true;
else
rdesc->info.op_tmpl->data.ecc = false;
@@ -603,8 +620,7 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
wdesc = spinand->dirmaps[req->pos.plane].wdesc;
- if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
- req->mode != MTD_OPS_RAW)
+ if (nand_ecc_is_pipelined(nand) && req->mode != MTD_OPS_RAW)
wdesc->info.op_tmpl->data.ecc = true;
else
wdesc->info.op_tmpl->data.ecc = false;
@@ -1261,7 +1277,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
struct spi_mem_dirmap_desc *desc;
bool enable_ecc = false, secondary_op = false;
- if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
+ if (nand_ecc_is_pipelined(nand))
enable_ecc = true;
if (spinand->cont_read_possible && spinand->op_templates->cont_read_cache)
@@ -1797,11 +1813,11 @@ try_ssdr:
spinand->ssdr_op_templates.write_cache->data.buswidth == 4 ||
spinand->ssdr_op_templates.update_cache->data.buswidth == 4)
quad_enable = true;
- }
- ret = spinand_init_quad_enable(spinand, quad_enable);
- if (ret)
- return ret;
+ ret = spinand_init_quad_enable(spinand, quad_enable);
+ if (ret)
+ return ret;
+ }
if (spinand->configure_chip) {
ret = spinand->configure_chip(spinand, SSDR);
@@ -1809,7 +1825,7 @@ try_ssdr:
return ret;
}
- return ret;
+ return 0;
}
static int spinand_init_flash(struct spinand_device *spinand)
@@ -1980,12 +1996,17 @@ static int spinand_init(struct spinand_device *spinand)
goto err_cleanup_ecc_engine;
}
- if (nand->ecc.engine) {
- ret = mtd_ooblayout_count_freebytes(mtd);
- if (ret < 0)
- goto err_cleanup_ecc_engine;
+ if (!nand->ecc.engine) {
+ if (spinand->eccinfo.ooblayout)
+ mtd_set_ooblayout(mtd, spinand->eccinfo.ooblayout);
+ else
+ mtd_set_ooblayout(mtd, &spinand_noecc_ooblayout);
}
+ ret = mtd_ooblayout_count_freebytes(mtd);
+ if (ret < 0)
+ goto err_cleanup_ecc_engine;
+
mtd->oobavail = ret;
/* Propagate ECC information to mtd_info */
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index ccf4396cdcd0..8bc117b46e02 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1310,7 +1310,7 @@ static bool spi_nor_rww_start_exclusive(struct spi_nor *nor)
{
struct spi_nor_rww *rww = &nor->rww;
- mutex_lock(&nor->lock);
+ guard(mutex)(&nor->lock);
if (rww->ongoing_io || rww->ongoing_rd || rww->ongoing_pe)
return false;
diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c
index 4d06c6ba6641..78909f516823 100644
--- a/drivers/net/ethernet/broadcom/bcmsysport.c
+++ b/drivers/net/ethernet/broadcom/bcmsysport.c
@@ -391,8 +391,10 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv)
if (priv->is_lite)
continue;
- if (s->type != BCM_SYSPORT_STAT_MIB_RX)
+ if (s->type == BCM_SYSPORT_STAT_MIB_TX)
offset = UMAC_MIB_STAT_OFFSET;
+ else if (s->type == BCM_SYSPORT_STAT_RUNT)
+ offset = 2 * UMAC_MIB_STAT_OFFSET;
val = umac_readl(priv, UMAC_MIB_START + j + offset);
break;
case BCM_SYSPORT_STAT_RXCHK:
@@ -482,10 +484,10 @@ static void bcm_sysport_get_stats(struct net_device *dev,
s->type == BCM_SYSPORT_STAT_NETDEV64) {
do {
start = u64_stats_fetch_begin(syncp);
- data[i] = *(u64 *)p;
+ data[j] = *(u64 *)p;
} while (u64_stats_fetch_retry(syncp, start));
} else
- data[i] = *(u32 *)p;
+ data[j] = *(u32 *)p;
j++;
}
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 21668e41b696..e8908916558b 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -57,6 +57,9 @@
*/
#define GENET_RSB_PAD (sizeof(struct status_64) + 2)
+/* RX buffer plus the skb_shared_info napi_build_skb() places behind it */
+#define GENET_RX_BUF_SIZE SKB_HEAD_ALIGN(RX_BUF_LENGTH)
+
/* Tx/Rx DMA register offset, skip 256 descriptors */
#define WORDS_PER_BD(p) (p->hw_params->words_per_bd)
#define DMA_DESC_SIZE (WORDS_PER_BD(priv) * sizeof(u32))
@@ -2254,11 +2257,12 @@ static int bcmgenet_rx_refill(struct bcmgenet_rx_ring *ring,
struct enet_cb *cb)
{
struct bcmgenet_priv *priv = ring->priv;
+ unsigned int size = GENET_RX_BUF_SIZE;
+ unsigned int offset;
dma_addr_t mapping;
struct page *page;
- page = page_pool_alloc_pages(ring->page_pool,
- GFP_ATOMIC);
+ page = page_pool_dev_alloc(ring->page_pool, &offset, &size);
if (!page) {
priv->mib.alloc_rx_buff_failed++;
netif_err(priv, rx_err, priv->dev,
@@ -2267,9 +2271,13 @@ static int bcmgenet_rx_refill(struct bcmgenet_rx_ring *ring,
}
/* page_pool handles DMA mapping via PP_FLAG_DMA_MAP */
- mapping = page_pool_get_dma_addr(page);
+ mapping = page_pool_get_dma_addr(page) + offset;
+ dma_sync_single_for_device(&priv->pdev->dev, mapping, RX_BUF_LENGTH,
+ DMA_FROM_DEVICE);
cb->rx_page = page;
+ cb->rx_offset = offset;
+ cb->rx_size = size;
dmadesc_set_addr(priv, cb->bd_addr, mapping);
return 0;
@@ -2323,6 +2331,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
while ((rxpktprocessed < rxpkttoprocess) &&
(rxpktprocessed < budget)) {
+ unsigned int rx_offset, rx_size;
struct status_64 *status;
struct page *rx_page;
void *hard_start;
@@ -2332,6 +2341,8 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
/* Save the received page before refilling */
rx_page = cb->rx_page;
+ rx_offset = cb->rx_offset;
+ rx_size = cb->rx_size;
if (bcmgenet_rx_refill(ring, cb)) {
BCMGENET_STATS64_INC(stats, dropped);
@@ -2341,10 +2352,10 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
/* Sync the full buffer; the HW may have written anywhere
* up to RX_BUF_LENGTH.
*/
- page_pool_dma_sync_for_cpu(ring->page_pool, rx_page, 0,
+ page_pool_dma_sync_for_cpu(ring->page_pool, rx_page, rx_offset,
RX_BUF_LENGTH);
- hard_start = page_address(rx_page);
+ hard_start = page_address(rx_page) + rx_offset;
status = (struct status_64 *)hard_start;
dma_length_status = status->length_status;
@@ -2410,7 +2421,7 @@ static unsigned int bcmgenet_desc_rx(struct bcmgenet_rx_ring *ring,
/* Build SKB from the page - data starts at hard_start,
* frame begins after RSB(64) + pad(2) = 66 bytes.
*/
- skb = napi_build_skb(hard_start, PAGE_SIZE);
+ skb = napi_build_skb(hard_start, rx_size);
if (unlikely(!skb)) {
BCMGENET_STATS64_INC(stats, dropped);
page_pool_put_full_page(ring->page_pool, rx_page,
@@ -2762,14 +2773,16 @@ static void bcmgenet_init_tx_ring(struct bcmgenet_priv *priv,
static int bcmgenet_rx_ring_create_pool(struct bcmgenet_priv *priv,
struct bcmgenet_rx_ring *ring)
{
+ /* Buffers share a page. bcmgenet_rx_refill() syncs each one for the
+ * device, PP_FLAG_DMA_SYNC_DEV would sync the whole page.
+ */
struct page_pool_params pp_params = {
.order = 0,
- .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+ .flags = PP_FLAG_DMA_MAP,
.pool_size = ring->size,
.nid = NUMA_NO_NODE,
.dev = &priv->pdev->dev,
.dma_dir = DMA_FROM_DEVICE,
- .max_len = RX_BUF_LENGTH,
};
int err;
@@ -4138,8 +4151,11 @@ static int bcmgenet_probe(struct platform_device *pdev)
netif_set_real_num_rx_queues(priv->dev, priv->hw_params->rx_queues + 1);
/* Set default coalescing parameters */
- for (i = 0; i <= priv->hw_params->rx_queues; i++)
+ for (i = 0; i <= priv->hw_params->rx_queues; i++) {
+ priv->rx_rings[i].priv = priv;
+ priv->rx_rings[i].index = i;
priv->rx_rings[i].rx_max_coalesced_frames = 1;
+ }
/* Initialize u64 stats seq counter for 32bit machines */
for (i = 0; i <= GENET_MAX_MQ_CNT; i++) {
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
index 22a958ba9902..86f2aed20dbe 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h
@@ -470,6 +470,8 @@ struct bcmgenet_rx_stats64 {
struct enet_cb {
struct sk_buff *skb;
struct page *rx_page;
+ unsigned int rx_offset;
+ unsigned int rx_size;
void __iomem *bd_addr;
DEFINE_DMA_UNMAP_ADDR(dma_addr);
DEFINE_DMA_UNMAP_LEN(dma_len);
diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c
index 616c1921aebe..ad99cbb77e29 100644
--- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c
@@ -595,25 +595,25 @@ static int gve_prep_tso(struct sk_buff *skb)
err = skb_cow_head(skb, 0);
if (err < 0)
return err;
+ shinfo = skb_shinfo(skb);
l4_start = skb_transport_offset(skb);
paylen = skb->len - l4_start;
- switch (shinfo->gso_type) {
- case SKB_GSO_TCPV4:
- case SKB_GSO_TCPV6:
+ /* gso_type is a bitmask: SKB_GSO_DODGY, SKB_GSO_TCP_ECN and
+ * SKB_GSO_TCP_FIXEDID may be set alongside the protocol bit.
+ */
+ if (shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6)) {
tcp = tcp_hdr(skb);
csum_replace_by_diff(&tcp->check,
(__force __wsum)htonl(paylen));
header_len = skb_tcp_all_headers(skb);
- break;
- case SKB_GSO_UDP_L4:
+ } else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
udp = udp_hdr(skb);
csum_replace_by_diff(&udp->check,
(__force __wsum)htonl(paylen));
header_len = sizeof(struct udphdr) + l4_start;
- break;
- default:
+ } else {
return -EINVAL;
}
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 543e566425c1..4f479b6f731e 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -2340,7 +2340,7 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp,
/* Prefetch header */
prefetch(data);
- xdp_buff_clear_frags_flag(xdp);
+ xdp_reinit_buff(xdp);
xdp_prepare_buff(xdp, data, pp->rx_offset_correction + MVNETA_MH_SIZE,
data_len, true);
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
index 211c10aa5880..30c672d526a0 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs.c
@@ -314,7 +314,7 @@ int mcs_clear_all_stats(struct mcs *mcs, u16 pcifunc, int dir)
}
/* Clear SC stats */
- for (id = 0; id < map->secy.max; id++) {
+ for (id = 0; id < map->sc.max; id++) {
if (map->sc2pf_map[id] != pcifunc)
continue;
mcs_clear_stats(mcs, MCS_SC_STATS, id, dir);
@@ -738,7 +738,7 @@ int mcs_free_all_rsrc(struct mcs *mcs, int dir, u16 pcifunc)
}
/* free sc entries */
- for (id = 0; id < map->secy.max; id++) {
+ for (id = 0; id < map->sc.max; id++) {
if (map->sc2pf_map[id] != pcifunc)
continue;
mcs_free_rsrc(&map->sc, map->sc2pf_map, id, pcifunc);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
index 8e41431c7f9c..e92b77789f08 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
@@ -242,15 +242,6 @@ int cn20k_register_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs)
#define RQ_BP_LVL_AURA (255 - ((85 * 256) / 100)) /* BP when 85% is full */
-static u8 cn20k_aura_bpid_idx(struct otx2_nic *pfvf, int aura_id)
-{
-#ifdef CONFIG_DCB
- return pfvf->queue_to_pfc_map[aura_id];
-#else
- return 0;
-#endif
-}
-
static int cn20k_tc_get_entry_index(struct otx2_flow_config *flow_cfg,
struct otx2_tc_flow *node)
{
@@ -580,7 +571,7 @@ static int cn20k_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
if (pfvf->nix_blkaddr == BLKADDR_NIX1)
aq->aura.bp_ena = 1;
- bpid_idx = cn20k_aura_bpid_idx(pfvf, aura_id);
+ bpid_idx = otx2_get_bpid_idx(pfvf, aura_id);
aq->aura.bpid = pfvf->bpid[bpid_idx];
/* Set backpressure level for RQ's Aura */
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index 175992188c18..b11389a841d1 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -394,7 +394,7 @@ int otx2_rss_init(struct otx2_nic *pfvf)
struct otx2_rss_info *rss = &pfvf->hw.rss_info;
int idx, ret = 0;
- rss->rss_size = sizeof(*rss->ind_tbl);
+ rss->rss_size = ARRAY_SIZE(rss->ind_tbl);
/* Init RSS key if it is not setup already */
if (!rss->enable)
@@ -1055,6 +1055,7 @@ int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx)
struct nix_aq_enq_req *aq;
struct otx2_cq_queue *cq;
struct otx2_pool *pool;
+ u8 bpid_idx;
cq = &qset->cq[qidx];
cq->cq_idx = qidx;
@@ -1132,11 +1133,8 @@ int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx)
if (!is_otx2_lbkvf(pfvf->pdev)) {
/* Enable receive CQ backpressure */
aq->cq.bp_ena = 1;
-#ifdef CONFIG_DCB
- aq->cq.bpid = pfvf->bpid[pfvf->queue_to_pfc_map[qidx]];
-#else
- aq->cq.bpid = pfvf->bpid[0];
-#endif
+ bpid_idx = otx2_get_bpid_idx(pfvf, qidx);
+ aq->cq.bpid = pfvf->bpid[bpid_idx];
/* Set backpressure level is same as cq pass level */
aq->cq.bp = RQ_PASS_LVL_CQ(pfvf->hw.rq_skid, qset->rqe_cnt);
@@ -1378,6 +1376,7 @@ int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
{
struct npa_aq_enq_req *aq;
struct otx2_pool *pool;
+ u8 bpid_idx;
int err;
pool = &pfvf->qset.pool[pool_id];
@@ -1433,11 +1432,8 @@ int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
*/
if (pfvf->nix_blkaddr == BLKADDR_NIX1)
aq->aura.bp_ena = 1;
-#ifdef CONFIG_DCB
- aq->aura.nix0_bpid = pfvf->bpid[pfvf->queue_to_pfc_map[aura_id]];
-#else
- aq->aura.nix0_bpid = pfvf->bpid[0];
-#endif
+ bpid_idx = otx2_get_bpid_idx(pfvf, aura_id);
+ aq->aura.nix0_bpid = pfvf->bpid[bpid_idx];
/* Set backpressure level for RQ's Aura */
aq->aura.bp = RQ_BP_LVL_AURA;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index eecee612b7b2..44cd56a0c2c4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -1029,6 +1029,15 @@ static inline int otx2_tc_flower_rule_cnt(struct otx2_nic *pfvf)
return pfvf->flow_cfg->nr_flows;
}
+static inline u8 otx2_get_bpid_idx(struct otx2_nic *pfvf, int qidx)
+{
+#ifdef CONFIG_DCB
+ if (pfvf->queue_to_pfc_map)
+ return pfvf->queue_to_pfc_map[qidx];
+#endif
+ return 0;
+}
+
/* MSI-X APIs */
void otx2_free_cints(struct otx2_nic *pfvf, int n);
void otx2_set_cints_affinity(struct otx2_nic *pfvf);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 2ea5dfe85539..ad0fdc4133fb 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -5341,6 +5341,22 @@ static int mtk_probe(struct platform_device *pdev)
}
}
+ /* we run 2 devices on the same DMA ring so we need a dummy device
+ * for NAPI to work. Allocate it before registering the netdevs so a
+ * concurrent ndo_open (e.g. netifd bringing the first netdev up the
+ * instant it is registered) never observes eth->dummy_dev == NULL in
+ * mtk_dma_init() -> mtk_rx_alloc() -> __xdp_rxq_info_reg() (net/core/xdp.c
+ * "Missing net_device from driver") and fails the first open.
+ */
+ eth->dummy_dev = alloc_netdev_dummy(0);
+ if (!eth->dummy_dev) {
+ err = -ENOMEM;
+ dev_err(eth->dev, "failed to allocated dummy device\n");
+ goto err_unreg_netdev;
+ }
+ netif_napi_add(eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
+ netif_napi_add(eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);
+
for (i = 0; i < MTK_MAX_DEVS; i++) {
if (!eth->netdev[i])
continue;
@@ -5355,18 +5371,6 @@ static int mtk_probe(struct platform_device *pdev)
eth->netdev[i]->base_addr, eth->irq[MTK_FE_IRQ_SHARED]);
}
- /* we run 2 devices on the same DMA ring so we need a dummy device
- * for NAPI to work
- */
- eth->dummy_dev = alloc_netdev_dummy(0);
- if (!eth->dummy_dev) {
- err = -ENOMEM;
- dev_err(eth->dev, "failed to allocated dummy device\n");
- goto err_unreg_netdev;
- }
- netif_napi_add(eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
- netif_napi_add(eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);
-
platform_set_drvdata(pdev, eth);
schedule_delayed_work(&eth->reset.monitor_work,
MTK_DMA_MONITOR_TIMEOUT);
@@ -5376,6 +5380,16 @@ static int mtk_probe(struct platform_device *pdev)
err_unreg_netdev:
mtk_unreg_dev(eth);
err_deinit_ppe:
+ /* A netdev opened while probe was still registering can have queued
+ * the frame-engine reset worker, which takes rtnl and drives the
+ * hardware; cancel it before anything below is torn down.
+ */
+ cancel_work_sync(&eth->pending_work);
+ if (eth->dummy_dev) {
+ netif_napi_del(&eth->tx_napi);
+ netif_napi_del(&eth->rx_napi);
+ free_netdev(eth->dummy_dev);
+ }
mtk_ppe_deinit(eth);
mtk_mdio_cleanup(eth);
err_free_dev:
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
index 77ea51bfbaae..2cca20e1b9b7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
@@ -293,6 +293,9 @@ static u64 mlx5e_xsk_fill_timestamp(void *_priv)
struct mlx5e_xsk_tx_complete *priv = _priv;
u64 ts;
+ if (!priv->cqe)
+ return 0;
+
ts = get_cqe_ts(priv->cqe);
if (mlx5_is_real_time_rq(priv->cq->mdev) || mlx5_is_real_time_sq(priv->cq->mdev))
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index dd14cdc378de..3b34bec559e0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -1943,9 +1943,11 @@ static int mlx5_handle_changeupper_event(struct mlx5_lag *ldev,
}
}
if (i < MLX5_MAX_PORTS) {
- slave = bond_slave_get_rcu(ndev_tmp);
- if (slave)
- has_inactive |= bond_is_slave_inactive(slave);
+ if (netif_is_bond_master(upper)) {
+ slave = bond_slave_get_rcu(ndev_tmp);
+ if (slave)
+ has_inactive |= bond_is_slave_inactive(slave);
+ }
bond_status |= (1 << idx);
}
@@ -2029,7 +2031,7 @@ static int mlx5_handle_changeinfodata_event(struct mlx5_lag *ldev,
bool has_inactive = 0;
int idx;
- if (!netif_is_lag_master(ndev))
+ if (!netif_is_bond_master(ndev))
return 0;
rcu_read_lock();
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index dad713e9ddd5..b530fb6d3d0c 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -898,6 +898,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
eth_random_addr(sparx5->base_mac);
sparx5->base_mac[5] = 0;
+ sparx5->base_mac_assign_type = NET_ADDR_RANDOM;
}
sparx5->fdma_irq = platform_get_irq_byname(sparx5->pdev, "fdma");
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
index eb57b86fbe22..0f7d8440f614 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.h
@@ -392,6 +392,7 @@ struct sparx5 {
struct notifier_block switchdev_blocking_nb;
/* Switch state */
u8 base_mac[ETH_ALEN];
+ u8 base_mac_assign_type;
/* Associated bridge device (when bridged) */
struct net_device *hw_bridge_dev;
/* Bridged interfaces */
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
index 4dedf5d92641..b3725cf2dd44 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_netdev.c
@@ -294,6 +294,7 @@ struct net_device *sparx5_create_netdev(struct sparx5 *sparx5, u32 portno)
ndev->ethtool_ops = &sparx5_ethtool_ops;
eth_hw_addr_gen(ndev, sparx5->base_mac, portno + 1);
+ ndev->addr_assign_type = sparx5->base_mac_assign_type;
return ndev;
}
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
index a16ec8136d6d..5953e04ef14e 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
@@ -673,6 +673,9 @@ void sparx5_ptp_deinit(struct sparx5 *sparx5)
struct sparx5_port *port;
int i;
+ if (!sparx5->ptp)
+ return;
+
if (sparx5->ptp_irq >= 0) {
disable_irq(sparx5->ptp_irq);
sparx5->ptp_irq = -ENXIO;
diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c
index 788c0728d763..0f8d98f2094d 100644
--- a/drivers/net/ethernet/microchip/vcap/vcap_api.c
+++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c
@@ -3527,8 +3527,8 @@ int vcap_filter_rule_keys(struct vcap_rule *rule,
if (key == keylist[idx]) {
list_del(&ckf->ctrl.list);
kfree(ckf);
- idx++;
err = 0;
+ break;
}
}
}
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index ec4fc21fa21f..ee1bc9bd0447 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -5484,6 +5484,12 @@ static int r8169_mdio_register(struct rtl8169_private *tp)
phy_disable_eee_mode(tp->phydev,
ETHTOOL_LINK_MODE_2500baseT_Full_BIT);
+ /* Keep EEE off but the version stays EEE-capable so that link-up
+ * still clears the MAC TX-LPI bits.
+ */
+ if (tp->mac_version == RTL_GIGA_MAC_VER_46)
+ phy_disable_eee(tp->phydev);
+
/* PHY will be woken up in rtl_open() */
phy_suspend(tp->phydev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 3f34d491c959..ec62fa7418f4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1629,6 +1629,16 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv,
stmmac_clear_tx_descriptors(priv, dma_conf, queue);
}
+static bool stmmac_rx_check_buf2_cap(struct stmmac_priv *priv)
+{
+ /* Only cores that can program an independent secondary RX buffer
+ * (used for scatter-gather overflow or split-header payload) back
+ * buffer2. Legacy cores have no set_sec_addr op, so buffer2 is
+ * never handed to the hardware there.
+ */
+ return priv->hw->desc && priv->hw->desc->set_sec_addr;
+}
+
/**
* stmmac_init_rx_buffers - init the RX descriptor buffer.
* @priv: driver private structure
@@ -1659,16 +1669,13 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
buf->page_offset = stmmac_rx_offset(priv);
}
- if (priv->sph_active && !buf->sec_page) {
+ if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) {
buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
if (!buf->sec_page)
return -ENOMEM;
buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
- } else {
- buf->sec_page = NULL;
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
}
buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
@@ -2256,13 +2263,8 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
pp_params.nid = dev_to_node(priv->device);
pp_params.dev = priv->device;
pp_params.dma_dir = xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE;
- pp_params.offset = stmmac_rx_offset(priv);
- pp_params.max_len = dma_conf->dma_buf_sz;
-
- if (priv->sph_active) {
- pp_params.offset = 0;
- pp_params.max_len += stmmac_rx_offset(priv);
- }
+ pp_params.offset = 0;
+ pp_params.max_len = dma_conf->dma_buf_sz + stmmac_rx_offset(priv);
rx_q->page_pool = page_pool_create(&pp_params);
if (IS_ERR(rx_q->page_pool)) {
@@ -5099,7 +5101,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
break;
}
- if (priv->sph_active && !buf->sec_page) {
+ if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) {
buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
if (!buf->sec_page)
break;
@@ -5110,10 +5112,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
stmmac_set_desc_addr(priv, p, buf->addr);
- if (priv->sph_active)
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
- else
- stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
+ stmmac_set_desc_sec_addr(priv, p, buf->sec_addr,
+ stmmac_rx_check_buf2_cap(priv));
stmmac_refill_desc3(priv, rx_q, p);
rx_q->rx_count_frames++;
@@ -5144,7 +5144,9 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
unsigned int plen = 0, hlen = 0;
int coe = priv->hw->rx_csum;
- /* Not first descriptor, buffer is always zero */
+ /* Not first descriptor, SPH enabled: buffer1 only carries the
+ * split header of the first descriptor, so it is zero here.
+ */
if (priv->sph_active && len)
return 0;
@@ -5155,14 +5157,16 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
return hlen;
}
- /* First descriptor, not last descriptor and not split header */
+ /* Not last descriptor and not split header: buffer1 is fully filled */
if (status & rx_not_ls)
return priv->dma_conf.dma_buf_sz;
plen = stmmac_get_rx_frame_len(priv, p, coe);
- /* First descriptor and last descriptor and not split header */
- return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen);
+ /* Last descriptor and not split header: buffer1 holds the remaining
+ * bytes of the frame, up to dma_buf_sz
+ */
+ return min_t(unsigned int, priv->dma_conf.dma_buf_sz, plen - len);
}
static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
@@ -5172,8 +5176,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
int coe = priv->hw->rx_csum;
unsigned int plen = 0;
- /* Not split header, buffer is not available */
- if (!priv->sph_active)
+ if (!stmmac_rx_check_buf2_cap(priv))
return 0;
/* For GMAC4, when split header is enabled, in some rare cases, the
@@ -5190,14 +5193,15 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
* Thus 'plen - len' always gives the correct length of buf2.
*/
- /* Not GMAC4 and not last descriptor */
- if (priv->plat->core_type != DWMAC_CORE_GMAC4 && (status & rx_not_ls))
+ /* Not GMAC4, or non-SPH and not last descriptor */
+ if ((priv->plat->core_type != DWMAC_CORE_GMAC4 || !priv->sph_active) &&
+ (status & rx_not_ls))
return priv->dma_conf.dma_buf_sz;
/* GMAC4 or last descriptor */
plen = stmmac_get_rx_frame_len(priv, p, coe);
- return plen - len;
+ return plen > len ? plen - len : 0;
}
static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue,
@@ -5720,6 +5724,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
unsigned int desc_size;
struct sk_buff *skb = NULL;
struct stmmac_xdp_buff ctx;
+ bool fcs_stripped = false;
int xdp_status = 0;
int bufsz;
@@ -5809,24 +5814,25 @@ read_again:
len += buf2_len;
/* ACS is disabled; strip manually. */
- if (likely(!(status & rx_not_ls))) {
- if (buf2_len) {
- buf2_len -= ETH_FCS_LEN;
- len -= ETH_FCS_LEN;
- } else if (buf1_len) {
- buf1_len -= ETH_FCS_LEN;
- len -= ETH_FCS_LEN;
- }
- }
+ if (likely(!(status & rx_not_ls)))
+ len -= ETH_FCS_LEN;
if (!skb) {
unsigned int pre_len, sync_len;
+ /* Each frame starts here: reset the FCS handling */
+ fcs_stripped = false;
+
dma_sync_single_for_cpu(priv->device, buf->addr,
buf1_len, dma_dir);
net_prefetch(page_address(buf->page) +
buf->page_offset);
+ if (stmmac_xdp_is_enabled(priv) && !buf2_len) {
+ buf1_len -= ETH_FCS_LEN;
+ fcs_stripped = true;
+ }
+
xdp_init_buff(&ctx.xdp, bufsz, &rx_q->xdp_rxq);
xdp_prepare_buff(&ctx.xdp, page_address(buf->page),
buf->page_offset, buf1_len, true);
@@ -5927,6 +5933,15 @@ drain_data:
/* Got entire packet into SKB. Finish it. */
+ /* Remove FCS if needed */
+ if (!fcs_stripped && pskb_trim(skb, len)) {
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+ rx_dropped++;
+ count++;
+ continue;
+ }
+
stmmac_get_rx_hwtstamp(priv, p, np, skb);
if (priv->hw->hw_vlan_en)
diff --git a/drivers/net/pcs/pcs-rzn1-miic.c b/drivers/net/pcs/pcs-rzn1-miic.c
index cb74861e823c..3aa513467060 100644
--- a/drivers/net/pcs/pcs-rzn1-miic.c
+++ b/drivers/net/pcs/pcs-rzn1-miic.c
@@ -270,6 +270,9 @@ static struct miic_port *phylink_pcs_to_miic_port(struct phylink_pcs *pcs)
static void miic_unlock_regs(struct miic *miic)
{
+ /* Clear protection state */
+ writel(0x0000, miic->base + MIIC_PRCMD);
+
/* Unprotect register writes */
writel(0x00A5, miic->base + MIIC_PRCMD);
writel(0x0001, miic->base + MIIC_PRCMD);
diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c
index e1cca779d2ec..3ecd43cafdf0 100644
--- a/drivers/net/pfcp.c
+++ b/drivers/net/pfcp.c
@@ -105,6 +105,9 @@ drop:
static void pfcp_del_sock(struct pfcp_dev *pfcp)
{
+ if (!pfcp->sk)
+ return;
+
udp_tunnel_sock_release(pfcp->sk);
pfcp->sk = NULL;
}
diff --git a/drivers/net/phy/aquantia/aquantia_main.c b/drivers/net/phy/aquantia/aquantia_main.c
index e95d391a6dc2..096407bebd52 100644
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -544,7 +544,7 @@ static int aqr_gen1_read_status(struct phy_device *phydev)
if (ret)
return ret;
- if (!phydev->link || phydev->autoneg == AUTONEG_DISABLE)
+ if (!phydev->link)
return 0;
/* The status register is not immediately correct on line side link up.
@@ -594,6 +594,9 @@ static int aqr_gen1_read_status(struct phy_device *phydev)
phydev->interface = aqr_translate_interface(phydev, interface);
+ if (phydev->autoneg == AUTONEG_DISABLE)
+ return 0;
+
/* Read rate from vendor register */
return aqr_gen1_read_rate(phydev);
}
diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c
index 6872dbf77856..cacbadf1f484 100644
--- a/drivers/net/phy/qcom/at803x.c
+++ b/drivers/net/phy/qcom/at803x.c
@@ -1051,11 +1051,15 @@ static int ipq5018_config_init(struct phy_device *phydev)
if (priv->set_short_cable_dac) {
/* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */
phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC,
- IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL);
+ IPQ5018_PHY_DAC_MASK,
+ FIELD_PREP(IPQ5018_PHY_DAC_MASK,
+ IPQ5018_PHY_MMD1_MDAC_VAL));
/* setting EDAC (Error-detection and Correction) in debug register */
at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC,
- IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL);
+ IPQ5018_PHY_DAC_MASK,
+ FIELD_PREP(IPQ5018_PHY_DAC_MASK,
+ IPQ5018_PHY_DEBUG_EDAC_VAL));
}
return 0;
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 0e2ab567dd40..a44d1b9756a1 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1455,6 +1455,7 @@ static const struct usb_device_id products[] = {
{QMI_QUIRK_SET_DTR(0x1546, 0x1312, 4)}, /* u-blox LARA-R6 01B */
{QMI_QUIRK_SET_DTR(0x1546, 0x1342, 4)}, /* u-blox LARA-L6 */
{QMI_QUIRK_SET_DTR(0x33f8, 0x0104, 4)}, /* Rolling RW101 RMNET */
+ {QMI_QUIRK_SET_DTR(0x33f8, 0x9300, 8)}, /* Rolling RN947R RMNET */
{QMI_FIXED_INTF(0x2dee, 0x4d22, 5)}, /* MeiG Smart SRM825L */
{QMI_QUIRK_SET_DTR(0x2dee, 0x4d63, 0)}, /* MeiG SRM813Q w/ Modem(PPP) */
{QMI_QUIRK_SET_DTR(0x2dee, 0x4d64, 0)}, /* MeiG SRM813Q w/ NMEA */
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index ae91b57c8422..c1fa42edd363 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -6716,6 +6716,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw)
ar->num_started_vdevs = 0;
ar->num_created_vdevs = 0;
ar->num_peers = 0;
+ ar->num_stations = 0;
ar->allocated_vdev_map = 0;
/* Configure monitor status ring with default rx_filter to get rx status
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index d3491ff08e6e..7d2f292e20c5 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -14,6 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <linux/math.h>
#include <linux/unaligned.h>
#include "htc.h"
@@ -328,11 +329,14 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM);
for (i = 0; i < tx_skb_cnt; i++) {
- nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue);
+ nskb = skb_peek(&hif_dev->tx.tx_skb_queue);
+ if (!nskb)
+ break;
- /* Should never be NULL */
- BUG_ON(!nskb);
+ if (tx_buf->offset + nskb->len + 4 > MAX_TX_BUF_SIZE)
+ break;
+ nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue);
hif_dev->tx.tx_skb_cnt--;
buf = tx_buf->buf;
@@ -342,13 +346,8 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
*hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG);
buf += 4;
memcpy(buf, nskb->data, nskb->len);
- tx_buf->len = nskb->len + 4;
-
- if (i < (tx_skb_cnt - 1))
- tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4;
-
- if (i == (tx_skb_cnt - 1))
- tx_buf->len += tx_buf->offset;
+ tx_buf->len = tx_buf->offset + nskb->len + 4;
+ tx_buf->offset += round_up(nskb->len + 4, 4);
__skb_queue_tail(&tx_buf->skb_queue, nskb);
TX_STAT_INC(hif_dev, skb_queued);
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 6c33e898b300..4d2b75d0212e 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -281,7 +281,6 @@ struct ath9k_htc_rxbuf {
struct ath9k_htc_rx {
struct list_head rxbuf;
spinlock_t rxbuflock;
- bool initialized;
};
#define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 6de78ae85726..911eaa4f776a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -910,6 +910,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv,
ath9k_init_leds(priv);
ath9k_start_rfkill_poll(priv);
+ /* signal completion to ath9k_htc_rxep() and ath9k_wmi_event_tasklet() */
+ smp_store_release(&priv->initialized, true);
+
return 0;
err_world:
@@ -966,10 +969,6 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
htc_handle->drv_priv = priv;
- /* Allow ath9k_wmi_event_tasklet() to operate. */
- smp_wmb();
- priv->initialized = true;
-
return 0;
err_init:
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index bed7ea2425a0..95f48c7dca15 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -1141,8 +1141,8 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
unsigned long flags;
- /* Check if ath9k_rx_init() completed. */
- if (!data_race(priv->rx.initialized))
+ /* Check if ath9k_init_device() completed. */
+ if (!smp_load_acquire(&priv->initialized))
goto err;
spin_lock_irqsave(&priv->rx.rxbuflock, flags);
@@ -1200,10 +1200,6 @@ int ath9k_rx_init(struct ath9k_htc_priv *priv)
list_add_tail(&rxbuf->list, &priv->rx.rxbuf);
}
- /* Allow ath9k_htc_rxep() to operate. */
- smp_wmb();
- priv->rx.initialized = true;
-
return 0;
err:
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index 284e8c13b043..4a08babb6d9d 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -146,6 +146,12 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
unsigned long flags;
u16 cmd_id;
+ /* Check if ath9k_init_device() completed. */
+ if (!smp_load_acquire(&priv->initialized)) {
+ tasklet_schedule(t);
+ return;
+ }
+
do {
spin_lock_irqsave(&wmi->wmi_lock, flags);
skb = __skb_dequeue(&wmi->wmi_event_queue);
@@ -155,12 +161,6 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
}
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
- /* Check if ath9k_htc_probe_device() completed. */
- if (!data_race(priv->initialized)) {
- kfree_skb(skb);
- continue;
- }
-
hdr = (struct wmi_cmd_hdr *) skb->data;
cmd_id = be16_to_cpu(hdr->command_id);
wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
@@ -206,9 +206,16 @@ static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
{
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
- if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
+ if (wmi->cmd_rsp_buf && wmi->cmd_rsp_len) {
+ if (skb->len < wmi->cmd_rsp_len) {
+ wmi->cmd_rsp_status = -EMSGSIZE;
+ goto complete;
+ }
memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
+ }
+ wmi->cmd_rsp_status = 0;
+complete:
complete(&wmi->cmd_wait);
}
@@ -300,6 +307,7 @@ static int ath9k_wmi_cmd_issue(struct wmi *wmi,
/* record the rsp buffer and length */
wmi->cmd_rsp_buf = rsp_buf;
wmi->cmd_rsp_len = rsp_len;
+ wmi->cmd_rsp_status = 0;
wmi->last_seq_id = wmi->tx_seq_id;
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
@@ -356,9 +364,13 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
return -ETIMEDOUT;
}
+ spin_lock_irqsave(&wmi->wmi_lock, flags);
+ ret = wmi->cmd_rsp_status;
+ spin_unlock_irqrestore(&wmi->wmi_lock, flags);
+
mutex_unlock(&wmi->op_mutex);
- return 0;
+ return ret;
out:
ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 5c3b710b8f31..0bb9d31f1a1c 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -157,6 +157,7 @@ struct wmi {
u16 tx_seq_id;
u8 *cmd_rsp_buf;
u32 cmd_rsp_len;
+ int cmd_rsp_status;
bool stopped;
struct list_head pending_tx_events;
diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
index cbaf250626c5..f7ca182d1494 100644
--- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c
+++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c
@@ -3807,6 +3807,7 @@ il3945_pci_remove(struct pci_dev *pdev)
il_free_channel_map(il);
il_free_geos(il);
+ il_eeprom_free(il);
kfree(il->scan_cmd);
dev_kfree_skb(il->beacon_skb);
ieee80211_free_hw(il->hw);
diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c
index a3d9053f043c..f028a7bfa734 100644
--- a/drivers/net/wireless/intersil/p54/fwio.c
+++ b/drivers/net/wireless/intersil/p54/fwio.c
@@ -52,6 +52,13 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
u32 code = le32_to_cpu(bootrec->code);
switch (code) {
case BR_CODE_COMPONENT_ID:
+ if (len < sizeof(struct bootrec_comp_id) /
+ sizeof(*bootrec->data)) {
+ wiphy_err(priv->hw->wiphy,
+ "firmware component ID is too short\n");
+ return -EINVAL;
+ }
+
priv->fw_interface = be32_to_cpup((__be32 *)
bootrec->data);
switch (priv->fw_interface) {
@@ -72,16 +79,41 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
}
break;
case BR_CODE_COMPONENT_VERSION:
+ if (len < DIV_ROUND_UP(sizeof(struct bootrec_comp_ver),
+ sizeof(*bootrec->data))) {
+ wiphy_err(priv->hw->wiphy,
+ "firmware component version is too short\n");
+ return -EINVAL;
+ }
+
/* 24 bytes should be enough for all firmwares */
- if (strnlen((unsigned char *) bootrec->data, 24) < 24)
- fw_version = (unsigned char *) bootrec->data;
+ if (strnlen((unsigned char *)bootrec->data,
+ sizeof(struct bootrec_comp_ver)) <
+ sizeof(struct bootrec_comp_ver))
+ fw_version = (unsigned char *)bootrec->data;
break;
case BR_CODE_DESCR: {
struct bootrec_desc *desc =
(struct bootrec_desc *)bootrec->data;
- priv->rx_start = le32_to_cpu(desc->rx_start);
- /* FIXME add sanity checking */
- priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
+ u32 rx_start, rx_end;
+
+ /* 0xa is the shortest descriptor in supported firmware. */
+ if (len < 0xa) {
+ wiphy_err(priv->hw->wiphy,
+ "firmware descriptor is too short\n");
+ return -EINVAL;
+ }
+
+ rx_start = le32_to_cpu(desc->rx_start);
+ rx_end = le32_to_cpu(desc->rx_end);
+ if (rx_end < 0x3500 || rx_end - 0x3500 <= rx_start) {
+ wiphy_err(priv->hw->wiphy,
+ "firmware descriptor has invalid RX range\n");
+ return -EINVAL;
+ }
+
+ priv->rx_start = rx_start;
+ priv->rx_end = rx_end - 0x3500;
priv->headroom = desc->headroom;
priv->tailroom = desc->tailroom;
priv->privacy_caps = desc->privacy_caps;
diff --git a/drivers/net/wireless/silabs/wfx/hif_tx.c b/drivers/net/wireless/silabs/wfx/hif_tx.c
index 9f403d275cb1..9efb108bd877 100644
--- a/drivers/net/wireless/silabs/wfx/hif_tx.c
+++ b/drivers/net/wireless/silabs/wfx/hif_tx.c
@@ -207,9 +207,11 @@ int wfx_hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, si
dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__);
ret = -EIO;
}
- if (ret == -ENOMEM)
+ if (!ret && le16_to_cpu(reply->length) > val_len) {
dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n",
wfx_get_mib_name(mib_id), val_len, le16_to_cpu(reply->length));
+ ret = -EIO;
+ }
if (!ret)
memcpy(val, &reply->mib_data, le16_to_cpu(reply->length));
else
diff --git a/drivers/net/wireless/st/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c
index b034bab4b489..9367a981f1de 100644
--- a/drivers/net/wireless/st/cw1200/bh.c
+++ b/drivers/net/wireless/st/cw1200/bh.c
@@ -344,6 +344,7 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv,
int *tx_burst)
{
size_t tx_len;
+ size_t aligned_len;
u8 *data;
int ret;
struct wsm_hdr *wsm;
@@ -376,9 +377,14 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv,
atomic_inc(&priv->bh_tx);
- tx_len = priv->hwbus_ops->align_size(
+ aligned_len = priv->hwbus_ops->align_size(
priv->hwbus_priv, tx_len);
+ /* Zero the bus alignment padding so no stale data is sent. */
+ if (aligned_len > tx_len)
+ memset(data + tx_len, 0, aligned_len - tx_len);
+ tx_len = aligned_len;
+
/* Check if not exceeding CW1200 capabilities */
if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
pr_debug("Write aligned len: %zu\n", tx_len);
diff --git a/drivers/net/wireless/st/cw1200/txrx.c b/drivers/net/wireless/st/cw1200/txrx.c
index 084d52b11f5b..e13695667a00 100644
--- a/drivers/net/wireless/st/cw1200/txrx.c
+++ b/drivers/net/wireless/st/cw1200/txrx.c
@@ -14,6 +14,7 @@
#include "wsm.h"
#include "bh.h"
#include "sta.h"
+#include "hwbus.h"
#include "debug.h"
#define CW1200_INVALID_RATE_ID (0xFF)
@@ -716,6 +717,7 @@ void cw1200_tx(struct ieee80211_hw *dev,
struct ieee80211_sta *sta;
struct wsm_tx *wsm;
bool tid_update = false;
+ size_t pad_len;
u8 flags = 0;
int ret;
@@ -764,6 +766,18 @@ void cw1200_tx(struct ieee80211_hw *dev,
sta = t.sta;
+ /*
+ * The bus transfer is padded up to the bus alignment and the
+ * padding is transferred from the frame buffer. Make sure the
+ * buffer has room for the padding so the device never receives
+ * data past the end of the frame.
+ */
+ pad_len = priv->hwbus_ops->align_size(priv->hwbus_priv, skb->len) -
+ skb->len;
+ if (skb_tailroom(skb) < pad_len &&
+ pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC))
+ goto drop;
+
spin_lock_bh(&priv->ps_state_lock);
{
tid_update = cw1200_tx_h_pm_state(priv, &t);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index edf6ca23c6c3..26aabf4ea127 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -6809,8 +6809,10 @@ void wlcore_remove(struct platform_device *pdev)
if (pdev_data->family && pdev_data->family->nvs_name)
wait_for_completion(&wl->nvs_loading_complete);
- if (!wl->initialized)
+ if (!wl->initialized) {
+ pm_runtime_put_noidle(wl->dev);
return;
+ }
if (wl->wakeirq >= 0) {
dev_pm_clear_wake_irq(wl->dev);
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index beea23d04a70..9bcab3dc4c11 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -598,7 +598,6 @@ bool nvme_change_ctrl_state(struct nvme_ctrl *ctrl,
break;
case NVME_CTRL_RESETTING:
switch (old_state) {
- case NVME_CTRL_NEW:
case NVME_CTRL_LIVE:
changed = true;
atomic_long_inc(&ctrl->nr_reset);
@@ -3195,9 +3194,16 @@ static void nvme_release_subsystem(struct device *dev)
{
struct nvme_subsystem *subsys =
container_of(dev, struct nvme_subsystem, dev);
+ struct nvme_effects_log *cel;
+ unsigned long i;
if (subsys->instance >= 0)
ida_free(&nvme_instance_ida, subsys->instance);
+ xa_for_each(&subsys->cels, i, cel) {
+ xa_erase(&subsys->cels, i);
+ kfree(cel);
+ }
+ xa_destroy(&subsys->cels);
kfree(subsys);
}
@@ -3311,6 +3317,7 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
kref_init(&subsys->ref);
INIT_LIST_HEAD(&subsys->ctrls);
INIT_LIST_HEAD(&subsys->nsheads);
+ xa_init(&subsys->cels);
nvme_init_subnqn(subsys, ctrl, id);
memcpy(subsys->serial, id->sn, sizeof(subsys->serial));
memcpy(subsys->model, id->mn, sizeof(subsys->model));
@@ -3413,7 +3420,7 @@ int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi,
static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi,
struct nvme_effects_log **log)
{
- struct nvme_effects_log *old, *cel = xa_load(&ctrl->cels, csi);
+ struct nvme_effects_log *old, *cel = xa_load(&ctrl->subsys->cels, csi);
int ret;
if (cel)
@@ -3430,7 +3437,7 @@ static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi,
return ret;
}
- old = xa_store(&ctrl->cels, csi, cel, GFP_KERNEL);
+ old = xa_store(&ctrl->subsys->cels, csi, cel, GFP_KERNEL);
if (xa_is_err(old)) {
kfree(cel);
return xa_err(old);
@@ -3505,7 +3512,7 @@ static int nvme_init_effects_log(struct nvme_ctrl *ctrl,
if (!effects)
return -ENOMEM;
- old = xa_store(&ctrl->cels, csi, effects, GFP_KERNEL);
+ old = xa_store(&ctrl->subsys->cels, csi, effects, GFP_KERNEL);
if (xa_is_err(old)) {
kfree(effects);
return xa_err(old);
@@ -3551,23 +3558,27 @@ static int nvme_init_effects(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
{
int ret = 0;
+ mutex_lock(&ctrl->subsys->lock);
+ ctrl->effects = xa_load(&ctrl->subsys->cels, NVME_CSI_NVM);
if (ctrl->effects)
- return 0;
+ goto out_unlock;
if (id->lpa & NVME_CTRL_LPA_CMD_EFFECTS_LOG) {
ret = nvme_get_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects);
if (ret < 0)
- return ret;
+ goto out_unlock;
}
if (!ctrl->effects) {
ret = nvme_init_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects);
if (ret < 0)
- return ret;
+ goto out_unlock;
}
nvme_init_known_nvm_effects(ctrl);
- return 0;
+out_unlock:
+ mutex_unlock(&ctrl->subsys->lock);
+ return ret;
}
static int nvme_check_ctrl_fabric_info(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
@@ -3931,9 +3942,9 @@ void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device)
put_device(cdev_device);
}
-int nvme_cdev_add(const char *name, struct cdev *cdev,
- struct device *cdev_device,
- const struct file_operations *fops, struct module *owner)
+int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device,
+ const struct file_operations *fops, struct module *owner,
+ int ctrl, int head)
{
int minor, ret;
@@ -3941,7 +3952,7 @@ int nvme_cdev_add(const char *name, struct cdev *cdev,
if (minor < 0)
return minor;
- ret = dev_set_name(cdev_device, name);
+ ret = dev_set_name(cdev_device, "ng%dn%d", ctrl, head);
if (ret) {
ida_free(&nvme_ns_chr_minor_ida, minor);
return ret;
@@ -3982,17 +3993,14 @@ static const struct file_operations nvme_ns_chr_fops = {
static void nvme_add_ns_cdev(struct nvme_ns *ns)
{
- char name[32];
-
ns->cdev_device.parent = ns->ctrl->device;
- snprintf(name, sizeof(name), "ng%dn%d", ns->ctrl->instance,
- ns->head->instance);
nvme_get_ns(ns); /* Undone in nvme_cdev_rel() */
- if (nvme_cdev_add(name, &ns->cdev, &ns->cdev_device,
- &nvme_ns_chr_fops, ns->ctrl->ops->module)) {
- dev_err(ns->ctrl->device, "Unable to create the %s device\n",
- name);
+ if (nvme_cdev_add(&ns->cdev, &ns->cdev_device,
+ &nvme_ns_chr_fops, ns->ctrl->ops->module,
+ ns->ctrl->instance, ns->head->instance)) {
+ dev_err(ns->ctrl->device, "Unable to create the ng%dn%d device\n",
+ ns->ctrl->instance, ns->head->instance);
nvme_put_ns(ns);
return;
}
@@ -5155,19 +5163,6 @@ void nvme_uninit_ctrl(struct nvme_ctrl *ctrl)
}
EXPORT_SYMBOL_GPL(nvme_uninit_ctrl);
-static void nvme_free_cels(struct nvme_ctrl *ctrl)
-{
- struct nvme_effects_log *cel;
- unsigned long i;
-
- xa_for_each(&ctrl->cels, i, cel) {
- xa_erase(&ctrl->cels, i);
- kfree(cel);
- }
-
- xa_destroy(&ctrl->cels);
-}
-
static void nvme_free_ctrl(struct device *dev)
{
struct nvme_ctrl *ctrl =
@@ -5180,7 +5175,6 @@ static void nvme_free_ctrl(struct device *dev)
blk_put_queue(ctrl->fabrics_q);
if (!subsys || ctrl->instance != subsys->instance)
ida_free(&nvme_instance_ida, ctrl->instance);
- nvme_free_cels(ctrl);
nvme_mpath_uninit(ctrl);
cleanup_srcu_struct(&ctrl->srcu);
nvme_auth_stop(ctrl);
@@ -5226,7 +5220,6 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
mutex_init(&ctrl->scan_lock);
INIT_LIST_HEAD(&ctrl->namespaces);
- xa_init(&ctrl->cels);
ctrl->dev = dev;
ctrl->ops = ops;
ctrl->quirks = quirks;
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 48454cb7a0fc..3a4030bcfab7 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -3338,7 +3338,7 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status)
ctrl->cnum, min_t(int, portptr->dev_loss_tmo,
(ctrl->ctrl.opts->max_reconnects *
ctrl->ctrl.opts->reconnect_delay)));
- WARN_ON(nvme_delete_ctrl(&ctrl->ctrl));
+ nvme_delete_ctrl(&ctrl->ctrl);
}
}
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 75dbb58286a3..99ecc73393e5 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -646,17 +646,15 @@ static const struct file_operations nvme_ns_head_chr_fops = {
static void nvme_add_ns_head_cdev(struct nvme_ns_head *head)
{
- char name[32];
-
head->cdev_device.parent = &head->subsys->dev;
- snprintf(name, sizeof(name), "ng%dn%d", head->subsys->instance,
- head->instance);
nvme_get_ns_head(head); /* Undone in nvme_cdev_rel() */
- if (nvme_cdev_add(name, &head->cdev, &head->cdev_device,
- &nvme_ns_head_chr_fops, THIS_MODULE)) {
+ if (nvme_cdev_add(&head->cdev, &head->cdev_device,
+ &nvme_ns_head_chr_fops, THIS_MODULE,
+ head->subsys->instance, head->instance)) {
dev_err(disk_to_dev(head->disk),
- "Unable to create the %s device\n", name);
+ "Unable to create the ng%dn%d device\n",
+ head->subsys->instance, head->instance);
nvme_put_ns_head(head);
return;
}
@@ -763,8 +761,6 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
lim.dma_alignment = 3;
lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT |
BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES | BLK_FEAT_PCI_P2PDMA;
- if (head->ids.csi == NVME_CSI_ZNS)
- lim.features |= BLK_FEAT_ZONED;
head->disk = blk_alloc_disk(&lim, ctrl->numa_node);
if (IS_ERR(head->disk))
@@ -845,7 +841,8 @@ static int nvme_parse_ana_log(struct nvme_ctrl *ctrl, void *data,
u32 nr_nsids;
size_t nsid_buf_size;
- if (WARN_ON_ONCE(offset > ctrl->ana_log_size - sizeof(*desc)))
+ if (WARN_ON_ONCE(offset > ctrl->ana_log_size ||
+ sizeof(*desc) > ctrl->ana_log_size - offset))
return -EINVAL;
nr_nsids = le32_to_cpu(desc->nnsids);
@@ -861,7 +858,7 @@ static int nvme_parse_ana_log(struct nvme_ctrl *ctrl, void *data,
return -EINVAL;
offset += sizeof(*desc);
- if (WARN_ON_ONCE(offset > ctrl->ana_log_size - nsid_buf_size))
+ if (WARN_ON_ONCE(nsid_buf_size > ctrl->ana_log_size - offset))
return -EINVAL;
error = cb(ctrl, desc, data);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 2cff9fcbf740..a643c4723a69 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -415,7 +415,6 @@ struct nvme_ctrl {
unsigned long quirks;
struct nvme_id_power_state psd[32];
struct nvme_effects_log *effects;
- struct xarray cels;
struct work_struct scan_work;
struct work_struct async_event_work;
struct delayed_work ka_work;
@@ -509,6 +508,8 @@ struct nvme_subsystem {
__guarded_by(&nvme_subsystems_lock);
struct list_head nsheads
__guarded_by(&lock);
+ /* Command effects logs, indexed by CSI and protected by lock. */
+ struct xarray cels;
char subnqn[NVMF_NQN_SIZE];
char serial[20];
char model[40];
@@ -1013,9 +1014,9 @@ int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi,
void nvme_get_ns_head(struct nvme_ns_head *head);
bool nvme_tryget_ns_head(struct nvme_ns_head *head);
void nvme_put_ns_head(struct nvme_ns_head *head);
-int nvme_cdev_add(const char *name, struct cdev *cdev,
- struct device *cdev_device,
- const struct file_operations *fops, struct module *owner);
+int nvme_cdev_add(struct cdev *cdev, struct device *cdev_device,
+ const struct file_operations *fops, struct module *owner,
+ int ctrl, int head);
void nvme_cdev_del(struct cdev *cdev, struct device *cdev_device);
int nvme_ioctl(struct block_device *bdev, blk_mode_t mode,
unsigned int cmd, unsigned long arg);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 5440cf18b55b..77317e5d00f9 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -4196,6 +4196,8 @@ static const struct pci_device_id nvme_id_table[] = {
.driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN },
{ PCI_DEVICE(0x1344, 0x6001), /* Micron Nitro NVMe */
.driver_data = NVME_QUIRK_BOGUS_NID, },
+ { PCI_DEVICE(0x1344, 0x6004), /* Micron 4100AT */
+ .driver_data = NVME_QUIRK_DMAPOOL_ALIGN_512, },
{ PCI_DEVICE(0x1c5c, 0x1504), /* SK Hynix PC400 */
.driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
{ PCI_DEVICE(0x1c5c, 0x174a), /* SK Hynix P31 SSD */
diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index 02a2490a9ed7..e1e3dcfd084b 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -679,7 +679,7 @@ static ssize_t cntrltype_show(struct device *dev,
if (ctrl->cntrltype > NVME_CTRL_ADMIN || !type[ctrl->cntrltype])
return sysfs_emit(buf, "reserved\n");
- return sysfs_emit(buf, type[ctrl->cntrltype]);
+ return sysfs_emit(buf, "%s", type[ctrl->cntrltype]);
}
static DEVICE_ATTR_RO(cntrltype);
@@ -696,7 +696,7 @@ static ssize_t dctype_show(struct device *dev,
if (ctrl->dctype > NVME_DCTYPE_CDC || !type[ctrl->dctype])
return sysfs_emit(buf, "reserved\n");
- return sysfs_emit(buf, type[ctrl->dctype]);
+ return sysfs_emit(buf, "%s", type[ctrl->dctype]);
}
static DEVICE_ATTR_RO(dctype);
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 921934028e0b..7eec983c0ead 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -144,11 +144,6 @@ struct nvme_tcp_queue {
void (*state_change)(struct sock *);
void (*data_ready)(struct sock *);
void (*write_space)(struct sock *);
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- struct lock_class_key nvme_tcp_sk_key;
- struct lock_class_key nvme_tcp_slock_key;
-#endif
};
static DEFINE_MUTEX(nvme_tcp_ctrl_mutex);
@@ -185,9 +180,12 @@ static int nvme_tcp_try_send(struct nvme_tcp_queue *queue);
* a separate class prevents lockdep from conflating nvme-tcp socket use with
* user-space socket API use.
*/
-static void nvme_tcp_reclassify_socket(struct nvme_tcp_queue *queue)
+static struct lock_class_key nvme_tcp_sk_key[2];
+static struct lock_class_key nvme_tcp_slock_key[2];
+
+static void nvme_tcp_reclassify_socket(struct socket *sock)
{
- struct sock *sk = queue->sock->sk;
+ struct sock *sk = sock->sk;
if (WARN_ON_ONCE(!sock_allow_reclassification(sk)))
return;
@@ -195,20 +193,22 @@ static void nvme_tcp_reclassify_socket(struct nvme_tcp_queue *queue)
switch (sk->sk_family) {
case AF_INET:
sock_lock_init_class_and_name(sk, "slock-AF_INET-NVME",
- &queue->nvme_tcp_slock_key,
+ &nvme_tcp_slock_key[0],
"sk_lock-AF_INET-NVME",
- &queue->nvme_tcp_sk_key);
+ &nvme_tcp_sk_key[0]);
break;
case AF_INET6:
sock_lock_init_class_and_name(sk, "slock-AF_INET6-NVME",
- &queue->nvme_tcp_slock_key,
+ &nvme_tcp_slock_key[1],
"sk_lock-AF_INET6-NVME",
- &queue->nvme_tcp_sk_key);
+ &nvme_tcp_sk_key[1]);
break;
default:
WARN_ON_ONCE(1);
}
}
+#else
+static void nvme_tcp_reclassify_socket(struct socket *sock) { }
#endif
static inline struct nvme_tcp_ctrl *to_tcp_ctrl(struct nvme_ctrl *ctrl)
@@ -1523,11 +1523,6 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid)
mutex_destroy(&queue->send_mutex);
mutex_destroy(&queue->queue_lock);
mutex_destroy(&queue->pf_cache_lock);
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- lockdep_unregister_key(&queue->nvme_tcp_sk_key);
- lockdep_unregister_key(&queue->nvme_tcp_slock_key);
-#endif
}
static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue)
@@ -1875,12 +1870,6 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
sk_net_refcnt_upgrade(queue->sock->sk);
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- lockdep_register_key(&queue->nvme_tcp_sk_key);
- lockdep_register_key(&queue->nvme_tcp_slock_key);
- nvme_tcp_reclassify_socket(queue);
-#endif
-
/* Single syn retry */
tcp_sock_set_syncnt(queue->sock->sk, 1);
@@ -1968,6 +1957,8 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
goto err_init_connect;
}
+ nvme_tcp_reclassify_socket(queue->sock);
+
ret = nvme_tcp_init_connection(queue);
if (ret)
goto err_init_connect;
@@ -1984,10 +1975,6 @@ err_sock:
/* Use sync variant - see nvme_tcp_free_queue() for explanation */
__fput_sync(queue->sock->file);
queue->sock = NULL;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
- lockdep_unregister_key(&queue->nvme_tcp_sk_key);
- lockdep_unregister_key(&queue->nvme_tcp_slock_key);
-#endif
err_destroy_mutex:
mutex_destroy(&queue->send_mutex);
mutex_destroy(&queue->queue_lock);
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 6286e38436dd..b03b5d1c2dc3 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -534,6 +534,7 @@ static ssize_t nvmet_ns_device_path_store(struct config_item *item,
struct nvmet_subsys *subsys = ns->subsys;
size_t len;
int ret;
+ char *new_path = NULL;
mutex_lock(&subsys->lock);
ret = -EBUSY;
@@ -545,12 +546,14 @@ static ssize_t nvmet_ns_device_path_store(struct config_item *item,
if (!len)
goto out_unlock;
- kfree(ns->device_path);
ret = -ENOMEM;
- ns->device_path = kmemdup_nul(page, len, GFP_KERNEL);
- if (!ns->device_path)
+ new_path = kmemdup_nul(page, len, GFP_KERNEL);
+ if (!new_path)
goto out_unlock;
+ kfree(ns->device_path);
+ ns->device_path = new_path;
+
mutex_unlock(&subsys->lock);
return count;
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index 43871a8f56ca..8eea0a504308 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -446,20 +446,27 @@ u16 nvmet_req_find_ns(struct nvmet_req *req)
{
u32 nsid = le32_to_cpu(req->cmd->common.nsid);
struct nvmet_subsys *subsys = nvmet_req_subsys(req);
+ u16 status = NVME_SC_SUCCESS;
+ rcu_read_lock();
req->ns = xa_load(&subsys->namespaces, nsid);
- if (unlikely(!req->ns || !req->ns->enabled)) {
+ if (unlikely(!req->ns) ||
+ !test_bit(NVMET_NS_IO_LIVE, &req->ns->flags) ||
+ !percpu_ref_tryget_live_rcu(&req->ns->ref)) {
req->error_loc = offsetof(struct nvme_common_command, nsid);
- if (!req->ns) /* ns doesn't exist! */
- return NVME_SC_INVALID_NS | NVME_STATUS_DNR;
+ if (!req->ns) { /* ns doesn't exist! */
+ status = NVME_SC_INVALID_NS | NVME_STATUS_DNR;
+ goto unlock;
+ }
/* ns exists but it's disabled */
req->ns = NULL;
- return NVME_SC_INTERNAL_PATH_ERROR;
+ status = NVME_SC_INTERNAL_PATH_ERROR;
}
+unlock:
+ rcu_read_unlock();
- percpu_ref_get(&req->ns->ref);
- return NVME_SC_SUCCESS;
+ return status;
}
static void nvmet_destroy_namespace(struct percpu_ref *ref)
@@ -623,6 +630,7 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
ns->enabled = true;
xa_set_mark(&subsys->namespaces, ns->nsid, NVMET_NS_ENABLED);
nvmet_debugfs_ns_setup(ns);
+ set_bit(NVMET_NS_IO_LIVE, &ns->flags);
ret = 0;
out_unlock:
mutex_unlock(&subsys->lock);
@@ -643,11 +651,11 @@ void nvmet_ns_disable(struct nvmet_ns *ns)
struct nvmet_subsys *subsys = ns->subsys;
struct nvmet_ctrl *ctrl;
+ if (!test_and_clear_bit(NVMET_NS_IO_LIVE, &ns->flags))
+ return;
+
mutex_lock(&subsys->lock);
- if (!ns->enabled)
- goto out_unlock;
- ns->enabled = false;
xa_clear_mark(&subsys->namespaces, ns->nsid, NVMET_NS_ENABLED);
nvmet_debugfs_ns_free(ns);
@@ -675,7 +683,7 @@ void nvmet_ns_disable(struct nvmet_ns *ns)
mutex_lock(&subsys->lock);
nvmet_ns_changed(subsys, ns->nsid);
nvmet_ns_dev_disable(ns);
-out_unlock:
+ ns->enabled = false;
mutex_unlock(&subsys->lock);
}
@@ -1648,6 +1656,8 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
INIT_DELAYED_WORK(&ctrl->ka_work, nvmet_keep_alive_timer);
memcpy(ctrl->hostnqn, args->hostnqn, NVMF_NQN_SIZE);
+ if (args->hostid)
+ uuid_copy(&ctrl->hostid, args->hostid);
kref_init(&ctrl->ref);
ctrl->subsys = subsys;
@@ -1706,9 +1716,6 @@ struct nvmet_ctrl *nvmet_alloc_ctrl(struct nvmet_alloc_ctrl_args *args)
nvmet_start_keep_alive_timer(ctrl);
- if (args->hostid)
- uuid_copy(&ctrl->hostid, args->hostid);
-
dhchap_status = nvmet_setup_auth(ctrl, args->sq, false);
if (dhchap_status) {
pr_err("Failed to setup authentication, dhchap status %u\n",
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index 92f8a76f10ff..dccdb55a367c 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -427,7 +427,7 @@ static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al)
struct nvmet_ctrl *ctrl = req->sq->ctrl;
int ret = 0;
int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id);
- int data_size = sizeof(*d) + hash_len;
+ int data_size = sizeof(*data) + hash_len;
if (ctrl->dh_tfm)
data_size += ctrl->dh_keysize;
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index dbda55895f4f..162e2fdd848e 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -112,6 +112,8 @@ struct nvmet_ns {
bool buffered_io;
bool enabled;
+#define NVMET_NS_IO_LIVE 0
+ unsigned long flags;
struct nvmet_subsys *subsys;
const char *device_path;
diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 9c28367435a5..c683450df422 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -978,6 +978,9 @@ nvmet_pci_epf_get_sgl_segment(struct nvmet_pci_epf_ctrl *ctrl,
int nr_descs, ret;
void *buf;
+ if (length < sizeof(struct nvme_sgl_desc))
+ return NULL;
+
buf = kmalloc(length, GFP_KERNEL);
if (!buf)
return NULL;
diff --git a/drivers/of/base.c b/drivers/of/base.c
index d71ae0449360..f63a9486b130 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -96,9 +96,17 @@ static bool __of_node_is_type(const struct device_node *np, const char *type)
return !strcmp(match, type);
}
+static bool of_coreboot_present(void)
+{
+ struct device_node *np __free(device_node) =
+ of_find_compatible_node(NULL, NULL, "coreboot");
+
+ return np;
+}
+
#define EXCLUDED_DEFAULT_CELLS_PLATFORMS ( \
IS_ENABLED(CONFIG_SPARC) || \
- of_find_compatible_node(NULL, NULL, "coreboot") \
+ of_coreboot_present() \
)
int of_bus_n_addr_cells(struct device_node *np)
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index ec035367c950..ef1ed9743907 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -754,6 +754,8 @@ void __init of_irq_init(const struct of_device_id *matches)
list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
list_del(&desc->list);
+ of_node_put(desc->interrupt_parent);
+ of_node_put(desc->dev);
kfree(desc);
}
err:
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 08d5351746be..83c56dd9b5d8 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -256,6 +256,9 @@ static struct property *dup_and_fixup_symbol_prop(
if (!target_path)
return NULL;
target_path_len = strlen(target_path);
+ /* a root target renders as "/"; drop it to avoid "//" results */
+ if (target_path_len == 1 && target_path[0] == '/' && path_tail_len)
+ target_path_len = 0;
new_prop = kzalloc_obj(*new_prop);
if (!new_prop)
@@ -358,12 +361,13 @@ static int add_changeset_property(struct overlay_changeset *ovcs,
return -ENOMEM;
if (!prop) {
- if (!target->in_livetree) {
+ ret = of_changeset_add_property(&ovcs->cset, target->np,
+ new_prop);
+ /* the detached node owns the property until the apply */
+ if (!ret && !target->in_livetree) {
new_prop->next = target->np->deadprops;
target->np->deadprops = new_prop;
}
- ret = of_changeset_add_property(&ovcs->cset, target->np,
- new_prop);
} else {
ret = of_changeset_update_property(&ovcs->cset, target->np,
new_prop);
@@ -853,6 +857,10 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
err_out:
pr_err("%s() failed, ret = %d\n", __func__, ret);
+ /* let free_overlay_changeset() put the fragments set up so far */
+ if (ovcs->fragments)
+ ovcs->count = cnt;
+
return ret;
}
@@ -863,7 +871,8 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
if (ovcs->cset.entries.next)
of_changeset_destroy(&ovcs->cset);
- if (ovcs->id) {
+ /* a failed idr_alloc() leaves its negative error in ovcs->id */
+ if (ovcs->id > 0) {
idr_remove(&ovcs_idr, ovcs->id);
list_del(&ovcs->ovcs_list);
ovcs->id = 0;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index b2879a6be5f8..8cc6a89130b5 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3769,8 +3769,21 @@ int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask)
}
root = pcie_find_root_port(dev);
- if (!root)
+ if (!root) {
+
+ /*
+ * A hypervisor may expose a topology with the root port
+ * not visible to the guest. If the hypervisor has already
+ * set AtomicOp Requester Enable in the endpoint, we assume
+ * it has verified support in the root port, so it is safe
+ * for the driver to use AtomicOps.
+ */
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCTL2, &ctl2);
+ if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_REQ)
+ return 0;
+
return -EINVAL;
+ }
pcie_capability_read_dword(root, PCI_EXP_DEVCAP2, &cap);
if ((cap & cap_mask) != cap_mask)
diff --git a/drivers/rtc/dev.c b/drivers/rtc/dev.c
index 8ba7c25d2565..be173c9511fa 100644
--- a/drivers/rtc/dev.c
+++ b/drivers/rtc/dev.c
@@ -216,7 +216,7 @@ static long rtc_dev_ioctl(struct file *file,
struct rtc_device *rtc = file->private_data;
const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
- struct rtc_wkalrm alarm;
+ struct rtc_wkalrm alarm = { 0 };
struct rtc_param param;
void __user *uarg = (void __user *)arg;
diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c
index bba7115ff3ad..c579c4bf5578 100644
--- a/drivers/rtc/rtc-ac100.c
+++ b/drivers/rtc/rtc-ac100.c
@@ -317,10 +317,12 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
if (!chip->clk_data)
return -ENOMEM;
- chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev,
- AC100_RTC_32K_NAME,
- NULL, 0,
- AC100_RTC_32K_RATE);
+ chip->clk_data->num = AC100_CLKOUT_NUM;
+
+ chip->rtc_32k_clk = devm_clk_hw_register_fixed_rate(chip->dev,
+ AC100_RTC_32K_NAME,
+ NULL, 0,
+ AC100_RTC_32K_RATE);
if (IS_ERR(chip->rtc_32k_clk)) {
ret = PTR_ERR(chip->rtc_32k_clk);
dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n",
@@ -354,29 +356,14 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
if (ret) {
dev_err(chip->dev, "Failed to register clk '%s': %d\n",
init.name, ret);
- goto err_unregister_rtc_32k;
+ return ret;
}
chip->clk_data->hws[i] = &clk->hw;
}
- chip->clk_data->num = i;
- ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data);
- if (ret)
- goto err_unregister_rtc_32k;
-
- return 0;
-
-err_unregister_rtc_32k:
- clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
-
- return ret;
-}
-
-static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip)
-{
- of_clk_del_provider(chip->dev->of_node);
- clk_unregister_fixed_rate(chip->rtc_32k_clk->clk);
+ return devm_of_clk_add_hw_provider(chip->dev, of_clk_hw_onecell_get,
+ chip->clk_data);
}
/*
@@ -614,13 +601,6 @@ static int ac100_rtc_probe(struct platform_device *pdev)
return devm_rtc_register_device(chip->rtc);
}
-static void ac100_rtc_remove(struct platform_device *pdev)
-{
- struct ac100_rtc_dev *chip = platform_get_drvdata(pdev);
-
- ac100_rtc_unregister_clks(chip);
-}
-
static const struct of_device_id ac100_rtc_match[] = {
{ .compatible = "x-powers,ac100-rtc" },
{ },
@@ -629,7 +609,6 @@ MODULE_DEVICE_TABLE(of, ac100_rtc_match);
static struct platform_driver ac100_rtc_driver = {
.probe = ac100_rtc_probe,
- .remove = ac100_rtc_remove,
.driver = {
.name = "ac100-rtc",
.of_match_table = of_match_ptr(ac100_rtc_match),
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index b4f44999ef0f..a8c18ebc5599 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -52,6 +52,7 @@ compute_wday(efi_time_t *eft, int yday)
static void
convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
{
+ memset(eft, 0, sizeof(*eft));
eft->year = wtime->tm_year + 1900;
eft->month = wtime->tm_mon + 1;
eft->day = wtime->tm_mday;
@@ -112,6 +113,35 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
return true;
}
+static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ memset(&eft, 0, sizeof(eft));
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ if (!convert_from_efi_time(&eft, &wkalrm->time))
+ return -EIO;
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ status = efi.set_wakeup_time((efi_bool_t)!!wkalrm->enabled, &eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
static int efi_read_time(struct device *dev, struct rtc_time *tm)
{
efi_status_t status;
@@ -146,13 +176,21 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
static int efi_procfs(struct device *dev, struct seq_file *seq)
{
- efi_time_t eft;
+ efi_time_t eft, alm;
efi_time_cap_t cap;
+ efi_bool_t enabled, pending;
+ struct rtc_device *rtc = dev_get_drvdata(dev);
memset(&eft, 0, sizeof(eft));
+ memset(&alm, 0, sizeof(alm));
memset(&cap, 0, sizeof(cap));
efi.get_time(&eft, &cap);
+ if (test_bit(RTC_FEATURE_ALARM, rtc->features) &&
+ efi.get_wakeup_time(&enabled, &pending, &alm) != EFI_SUCCESS) {
+ enabled = 0;
+ pending = 0;
+ }
seq_printf(seq,
"Time\t\t: %u:%u:%u.%09u\n"
@@ -168,6 +206,25 @@ static int efi_procfs(struct device *dev, struct seq_file *seq)
/* XXX fixme: convert to string? */
seq_printf(seq, "Timezone\t: %u\n", eft.timezone);
+ if (test_bit(RTC_FEATURE_ALARM, rtc->features)) {
+ seq_printf(seq,
+ "Alarm Time\t: %u:%u:%u.%09u\n"
+ "Alarm Date\t: %u-%u-%u\n"
+ "Alarm Daylight\t: %u\n"
+ "Enabled\t\t: %s\n"
+ "Pending\t\t: %s\n",
+ alm.hour, alm.minute, alm.second, alm.nanosecond,
+ alm.year, alm.month, alm.day,
+ alm.daylight,
+ enabled == 1 ? "yes" : "no",
+ pending == 1 ? "yes" : "no");
+
+ if (alm.timezone == EFI_UNSPECIFIED_TIMEZONE)
+ seq_puts(seq, "Alarm Timezone\t: unspecified\n");
+ else
+ seq_printf(seq, "Alarm Timezone\t: %d\n", alm.timezone);
+ }
+
/*
* now prints the capabilities
*/
@@ -183,6 +240,8 @@ static int efi_procfs(struct device *dev, struct seq_file *seq)
static const struct rtc_class_ops efi_rtc_ops = {
.read_time = efi_read_time,
.set_time = efi_set_time,
+ .read_alarm = efi_read_alarm,
+ .set_alarm = efi_set_alarm,
.proc = efi_procfs,
};
@@ -191,6 +250,7 @@ static int __init efi_rtc_probe(struct platform_device *dev)
struct rtc_device *rtc;
efi_time_t eft;
efi_time_cap_t cap;
+ efi_bool_t enabled, pending;
/* First check if the RTC is usable */
if (efi.get_time(&eft, &cap) != EFI_SUCCESS)
@@ -203,7 +263,23 @@ static int __init efi_rtc_probe(struct platform_device *dev)
platform_set_drvdata(dev, rtc);
rtc->ops = &efi_rtc_ops;
- clear_bit(RTC_FEATURE_ALARM, rtc->features);
+ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
+
+ /*
+ * The EFI_RT_SUPPORTED_WAKEUP_SERVICES bit defaults to enabled
+ * and only gets cleared when the RT_PROP table explicitly says
+ * wakeup is unsupported. Many platforms lack an RT_PROP table
+ * even though they don't implement the wakeup runtime service,
+ * so probe by actually calling GetWakeupTime() to avoid exposing
+ * a broken alarm to userspace.
+ */
+ if (efi_rt_services_supported(EFI_RT_SUPPORTED_WAKEUP_SERVICES) &&
+ efi.get_wakeup_time(&enabled, &pending, &eft) == EFI_SUCCESS) {
+ set_bit(RTC_FEATURE_ALARM, rtc->features);
+ set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features);
+ } else {
+ clear_bit(RTC_FEATURE_ALARM, rtc->features);
+ }
device_init_wakeup(&dev->dev, true);
diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c
index ece6de4a6adb..60596b0ea9d8 100644
--- a/drivers/rtc/rtc-mpfs.c
+++ b/drivers/rtc/rtc-mpfs.c
@@ -256,8 +256,12 @@ static int mpfs_rtc_probe(struct platform_device *pdev)
return ret;
}
+ clk = devm_clk_get(&pdev->dev, "rtcref");
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
/* prescaler hardware adds 1 to reg value */
- prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1;
+ prescaler = clk_get_rate(clk) - 1;
if (prescaler > MAX_PRESCALER_COUNT) {
dev_dbg(&pdev->dev, "invalid prescaler %lu\n", prescaler);
return -EINVAL;
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index 959acff8faff..d12bccb3cf5e 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -363,6 +363,12 @@ static int spear_rtc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ config->ioaddr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(config->ioaddr))
+ return PTR_ERR(config->ioaddr);
+
+ spin_lock_init(&config->lock);
+
status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name,
config);
if (status) {
@@ -371,10 +377,6 @@ static int spear_rtc_probe(struct platform_device *pdev)
return status;
}
- config->ioaddr = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(config->ioaddr))
- return PTR_ERR(config->ioaddr);
-
config->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(config->clk))
return PTR_ERR(config->clk);
@@ -383,7 +385,6 @@ static int spear_rtc_probe(struct platform_device *pdev)
if (status < 0)
return status;
- spin_lock_init(&config->lock);
platform_set_drvdata(pdev, config);
config->rtc->ops = &spear_rtc_ops;
diff --git a/drivers/spi/spi-cs42l43.c b/drivers/spi/spi-cs42l43.c
index 7106a8a4f280..6772abb483dd 100644
--- a/drivers/spi/spi-cs42l43.c
+++ b/drivers/spi/spi-cs42l43.c
@@ -11,6 +11,7 @@
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/device.h>
+#include <linux/dmi.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
@@ -215,12 +216,108 @@ static size_t cs42l43_spi_max_length(struct spi_device *spi)
return CS42L43_SPI_MAX_LENGTH;
}
+/*
+ * Workaround needed for two speaker ID pins in one ACPI GpioIo() but
+ * the Linux-specific _DSD property only contains one pin.
+ * Create a temporary acpi_gpio_mapping pointing at both pins.
+ */
+static const struct acpi_gpio_params cs42l43_2bit_speaker_id_from_one_gpioio_params[] = {
+ [0] = {
+ .crs_entry_index = 0,
+ .line_index = 0,
+ },
+ [1] = {
+ .crs_entry_index = 0,
+ .line_index = 1,
+ },
+};
+
+static const struct acpi_gpio_mapping cs42l43_2bit_speaker_id_from_one_gpioio_mapping[] = {
+ {
+ .name = "spk-id-quirk-gpios",
+ .data = cs42l43_2bit_speaker_id_from_one_gpioio_params,
+ .size = ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params),
+ },
+ { }
+};
+
+static int cs42l43_get_2bit_speaker_id_from_one_gpioio(struct cs42l43_spi *priv, int *result)
+{
+ struct fwnode_reference_args args;
+ struct acpi_device *adev;
+ struct gpio_desc *desc;
+ u32 spkid = 0;
+ int i, ret;
+
+ /* Use the _DSD property to get the node containing the GpioIo() */
+ ret = fwnode_property_get_reference_args(dev_fwnode(priv->dev), "spk-id-gpios",
+ NULL, 3, 0, &args);
+ if (ret)
+ return ret;
+
+ struct fwnode_handle *fwnode __free(fwnode_handle) = args.fwnode;
+
+ /* An acpi_gpio_mapping must be added to the node that contains the GpioIo() */
+ adev = to_acpi_device_node(fwnode);
+ if (!adev)
+ return -EINVAL;
+
+ ret = acpi_dev_add_driver_gpios(adev, cs42l43_2bit_speaker_id_from_one_gpioio_mapping);
+ if (ret)
+ return ret;
+
+ /* gpiod_get_array() can't read from a mapping in a child node */
+ for (i = 0; i < ARRAY_SIZE(cs42l43_2bit_speaker_id_from_one_gpioio_params); i++) {
+ desc = fwnode_gpiod_get_index(fwnode, "spk-id-quirk", i, GPIOD_IN,
+ dev_name(priv->dev));
+ if (IS_ERR(desc)) {
+ ret = PTR_ERR(desc);
+ goto out;
+ }
+
+ ret = gpiod_get_value_cansleep(desc);
+ gpiod_put(desc);
+ if (ret < 0)
+ goto out;
+
+ spkid |= (u32)ret << i;
+ }
+
+ dev_dbg(priv->dev, "spk-id-gpios = %u\n", spkid);
+ *result = spkid;
+ ret = 0;
+out:
+ acpi_dev_remove_driver_gpios(adev);
+
+ return ret;
+}
+
+static const struct dmi_system_id cs42l43_spk_id_quirks[] = {
+ {
+ .ident = "Dell XPS 13 DX13260",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0E53"),
+ },
+ .driver_data = cs42l43_get_2bit_speaker_id_from_one_gpioio,
+ },
+ { }
+};
+
static int cs42l43_get_speaker_id_gpios(struct cs42l43_spi *priv, int *result)
{
+ const struct dmi_system_id *dmi_id;
struct gpio_descs *descs;
u32 spkid;
int i, ret;
+ dmi_id = dmi_first_match(cs42l43_spk_id_quirks);
+ if (dmi_id) {
+ int (*get_speaker_id)(struct cs42l43_spi *priv, int *result) = dmi_id->driver_data;
+
+ return get_speaker_id(priv, result);
+ }
+
descs = gpiod_get_array_optional(priv->dev, "spk-id", GPIOD_IN);
if (!descs)
return 0;
diff --git a/drivers/staging/greybus/uart.c b/drivers/staging/greybus/uart.c
index 24b4dab069c3..172cf900eaf0 100644
--- a/drivers/staging/greybus/uart.c
+++ b/drivers/staging/greybus/uart.c
@@ -948,7 +948,8 @@ static int gb_tty_init(void)
int retval = 0;
gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, TTY_DRIVER_REAL_RAW |
- TTY_DRIVER_DYNAMIC_DEV);
+ TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_RESET_SAVED_TERMIOS);
if (IS_ERR(gb_tty_driver)) {
pr_err("Can not allocate tty driver\n");
retval = PTR_ERR(gb_tty_driver);
diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c
index cd47b627f97b..965988b18608 100644
--- a/drivers/thunderbolt/ctl.c
+++ b/drivers/thunderbolt/ctl.c
@@ -73,7 +73,6 @@ struct tb_ctl {
#define tb_ctl_dbg_once(ctl, format, arg...) \
dev_dbg_once((ctl)->nhi->dev, format, ## arg)
-static DECLARE_WAIT_QUEUE_HEAD(tb_cfg_request_cancel_queue);
/* Serializes access to request kref_get/put */
static DEFINE_MUTEX(tb_cfg_request_lock);
@@ -133,41 +132,42 @@ void tb_cfg_request_put(struct tb_cfg_request *req)
static int tb_cfg_request_enqueue(struct tb_ctl *ctl,
struct tb_cfg_request *req)
{
+ tb_cfg_request_get(req);
+
WARN_ON(test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags));
WARN_ON(req->ctl);
- mutex_lock(&ctl->request_queue_lock);
+ guard(mutex)(&ctl->request_queue_lock);
if (!ctl->running) {
- mutex_unlock(&ctl->request_queue_lock);
+ tb_cfg_request_put(req);
return -ENOTCONN;
}
req->ctl = ctl;
list_add_tail(&req->list, &ctl->request_queue);
set_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
- mutex_unlock(&ctl->request_queue_lock);
return 0;
}
-static void tb_cfg_request_dequeue(struct tb_cfg_request *req)
+static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
{
- struct tb_ctl *ctl = req->ctl;
-
- mutex_lock(&ctl->request_queue_lock);
- if (!test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags)) {
- mutex_unlock(&ctl->request_queue_lock);
- return;
- }
+ return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
+}
- list_del(&req->list);
- clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
- if (test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
- wake_up(&tb_cfg_request_cancel_queue);
- mutex_unlock(&ctl->request_queue_lock);
+static bool tb_cfg_request_is_canceled(struct tb_cfg_request *req)
+{
+ return test_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
}
-static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
+static void tb_cfg_request_dequeue(struct tb_cfg_request *req)
{
- return test_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
+ struct tb_ctl *ctl = req->ctl;
+
+ guard(mutex)(&ctl->request_queue_lock);
+ if (tb_cfg_request_is_active(req)) {
+ list_del(&req->list);
+ clear_bit(TB_CFG_REQUEST_ACTIVE, &req->flags);
+ tb_cfg_request_put(req);
+ }
}
static struct tb_cfg_request *
@@ -178,7 +178,7 @@ tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg)
mutex_lock(&pkg->ctl->request_queue_lock);
list_for_each_entry(iter, &pkg->ctl->request_queue, list) {
tb_cfg_request_get(iter);
- if (iter->match(iter, pkg)) {
+ if (!tb_cfg_request_is_canceled(iter) && iter->match(iter, pkg)) {
req = iter;
break;
}
@@ -512,8 +512,11 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
trace_tb_rx(pkg->ctl->index, frame->eof, pkg->buffer, frame->size, !req);
if (req) {
- if (req->copy(req, pkg))
- schedule_work(&req->work);
+ scoped_guard(mutex, &pkg->ctl->request_queue_lock) {
+ if (!tb_cfg_request_is_canceled(req) &&
+ req->copy(req, pkg))
+ schedule_work(&req->work);
+ }
tb_cfg_request_put(req);
}
@@ -525,11 +528,10 @@ static void tb_cfg_request_work(struct work_struct *work)
{
struct tb_cfg_request *req = container_of(work, typeof(*req), work);
- if (!test_bit(TB_CFG_REQUEST_CANCELED, &req->flags))
+ if (!tb_cfg_request_is_canceled(req))
req->callback(req->callback_data);
tb_cfg_request_dequeue(req);
- tb_cfg_request_put(req);
}
/**
@@ -555,10 +557,9 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req,
INIT_WORK(&req->work, tb_cfg_request_work);
INIT_LIST_HEAD(&req->list);
- tb_cfg_request_get(req);
ret = tb_cfg_request_enqueue(ctl, req);
if (ret)
- goto err_put;
+ return ret;
ret = tb_ctl_tx(ctl, req->request, req->request_size,
req->request_type);
@@ -572,9 +573,6 @@ int tb_cfg_request(struct tb_ctl *ctl, struct tb_cfg_request *req,
err_dequeue:
tb_cfg_request_dequeue(req);
-err_put:
- tb_cfg_request_put(req);
-
return ret;
}
@@ -588,9 +586,10 @@ err_put:
*/
void tb_cfg_request_cancel(struct tb_cfg_request *req, int err)
{
- set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
- schedule_work(&req->work);
- wait_event(tb_cfg_request_cancel_queue, !tb_cfg_request_is_active(req));
+ scoped_guard(mutex, &req->ctl->request_queue_lock)
+ set_bit(TB_CFG_REQUEST_CANCELED, &req->flags);
+ cancel_work_sync(&req->work);
+ tb_cfg_request_dequeue(req);
req->result.err = err;
}
diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
index 12c88509a54f..24611f05b3cd 100644
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -788,21 +788,6 @@ int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
transmit_ring, receive_path, receive_ring);
}
-static void tb_domain_reset_interface(struct tb *tb)
-{
- struct tb_nhi *nhi = tb->nhi;
-
- if (!nhi->ops->reset_interface)
- return;
-
- guard(mutex)(&tb->lock);
-
- /* The reset clears the ring state so stop the control channel */
- tb_ctl_stop(tb->ctl);
- nhi->ops->reset_interface(nhi);
- tb_ctl_start(tb->ctl);
-}
-
/**
* tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain
* @tb: Domain disabling the DMA paths
@@ -825,20 +810,11 @@ int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd,
int transmit_path, int transmit_ring,
int receive_path, int receive_ring)
{
- int ret;
-
if (!tb->cm_ops->disconnect_xdomain_paths)
return -ENOTSUPP;
- ret = tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path,
+ return tb->cm_ops->disconnect_xdomain_paths(tb, xd, transmit_path,
transmit_ring, receive_path, receive_ring);
- if (ret)
- return ret;
-
- if (tb->nhi->quirks & QUIRK_RESET_DMA_ON_TEARDOWN)
- tb_domain_reset_interface(tb);
-
- return 0;
}
static int disconnect_xdomain(struct device *dev, void *data)
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 5809809f64d4..be18f7b6595a 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -15,6 +15,7 @@
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
+#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/property.h>
@@ -560,6 +561,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size,
INIT_LIST_HEAD(&ring->in_flight);
INIT_WORK(&ring->work, ring_work);
init_waitqueue_head(&ring->wait);
+ lockdep_register_key(&ring->lock_key);
+ lockdep_init_map(&ring->work.lockdep_map, "ring.work", &ring->lock_key, 0);
ring->nhi = nhi;
ring->hop = hop;
@@ -599,6 +602,7 @@ err_free_descs:
ring->size * sizeof(*ring->descriptors),
ring->descriptors, ring->descriptors_dma);
err_free_ring:
+ lockdep_unregister_key(&ring->lock_key);
kfree(ring);
return NULL;
@@ -848,6 +852,7 @@ void tb_ring_free(struct tb_ring *ring)
* to finish before freeing the ring.
*/
flush_work(&ring->work);
+ lockdep_unregister_key(&ring->lock_key);
kfree(ring);
}
EXPORT_SYMBOL_GPL(tb_ring_free);
@@ -1175,32 +1180,6 @@ static void nhi_reset(struct tb_nhi *nhi)
dev_warn(nhi->dev, "timeout resetting host router\n");
}
-/**
- * nhi_reset_interface() - Reset the host interface
- * @nhi: Host interface to reset
- *
- * Brings the registers in the memory BAR back to their default state and
- * clears the End-to-End Flow Control state. The caller is responsible for
- * stopping the control channel over the reset because it clears the ring
- * state as well.
- */
-void nhi_reset_interface(struct tb_nhi *nhi)
-{
- u32 val;
-
- val = ioread32(nhi->iobase + REG_CAPS);
- /* Only v1 host interfaces implement the reset */
- if (FIELD_GET(REG_CAPS_VERSION_MASK, val) >= REG_CAPS_VERSION_2)
- return;
-
- dev_dbg(nhi->dev, "issuing host interface reset\n");
-
- iowrite32(REG_HOST_INTERFACE_RESET_RST,
- nhi->iobase + REG_HOST_INTERFACE_RESET);
- /* Wait for tHIReset (10 ms) to complete */
- usleep_range(10000, 20000);
-}
-
static struct tb *nhi_select_cm(struct tb_nhi *nhi)
{
struct tb *tb;
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index f72d6b274501..d488eadadfce 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -36,8 +36,6 @@ irqreturn_t nhi_msi(int irq, void *data);
irqreturn_t ring_msix(int irq, void *data);
int nhi_probe(struct tb_nhi *nhi);
void nhi_shutdown(struct tb_nhi *nhi);
-void nhi_reset_interface(struct tb_nhi *nhi);
-
extern const struct dev_pm_ops nhi_pm_ops;
/**
@@ -54,7 +52,6 @@ extern const struct dev_pm_ops nhi_pm_ops;
* @release_ring_irq: NHI specific interrupt release hook
* @is_present: Whether the device is currently present on the parent bus
* @init_interrupts: NHI specific interrupt initialization hook
- * @reset_interface: Resets the host interface
*/
struct tb_nhi_ops {
int (*init)(struct tb_nhi *nhi);
@@ -69,7 +66,6 @@ struct tb_nhi_ops {
void (*release_ring_irq)(struct tb_ring *ring);
bool (*is_present)(struct tb_nhi *nhi);
int (*init_interrupts)(struct tb_nhi *nhi);
- void (*reset_interface)(struct tb_nhi *nhi);
};
/*
@@ -120,24 +116,11 @@ struct tb_nhi_ops {
#define PCI_DEVICE_ID_INTEL_PTL_P_NHI0 0xe433
#define PCI_DEVICE_ID_INTEL_PTL_P_NHI1 0xe434
-#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI0 0x1120
-#define PCI_DEVICE_ID_AMD_1AH_M60H_NHI1 0x1121
-#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI0 0x113b
-#define PCI_DEVICE_ID_AMD_1AH_M68H_NHI1 0x113c
-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI0 0x1155
-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI1 0x1158
-#define PCI_DEVICE_ID_AMD_1AH_M80H_NHI2 0x1159
-#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI0 0x151c
-#define PCI_DEVICE_ID_AMD_1AH_M24H_NHI1 0x151d
-#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI0 0x158d
-#define PCI_DEVICE_ID_AMD_1AH_M70H_NHI1 0x158e
-
#define PCI_CLASS_SERIAL_USB_USB4 0x0c0340
/* Host interface quirks */
-#define QUIRK_AUTO_CLEAR_INT BIT(0)
-#define QUIRK_E2E BIT(1)
-#define QUIRK_RESET_DMA_ON_TEARDOWN BIT(2)
+#define QUIRK_AUTO_CLEAR_INT BIT(0)
+#define QUIRK_E2E BIT(1)
/*
* Minimal number of vectors when we use MSI-X. Two for control channel
diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h
index 99df60b6db36..d6a197fabc74 100644
--- a/drivers/thunderbolt/nhi_regs.h
+++ b/drivers/thunderbolt/nhi_regs.h
@@ -115,10 +115,6 @@ struct ring_desc {
#define REG_CAPS_VERSION_MASK GENMASK(23, 16)
#define REG_CAPS_VERSION_2 0x40
-/* Host Interface Reset - resets TX/RX rings and E2E flow control counters */
-#define REG_HOST_INTERFACE_RESET 0x39858
-#define REG_HOST_INTERFACE_RESET_RST BIT(0)
-
#define REG_DMA_MISC 0x39864
#define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2)
#define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17)
diff --git a/drivers/thunderbolt/pci.c b/drivers/thunderbolt/pci.c
index 99333729f3c2..8462ccb59b7e 100644
--- a/drivers/thunderbolt/pci.c
+++ b/drivers/thunderbolt/pci.c
@@ -62,27 +62,6 @@ static void nhi_pci_check_quirks(struct tb_nhi_pci *nhi_pci)
nhi->quirks |= QUIRK_E2E;
break;
}
- } else if (pdev->vendor == PCI_VENDOR_ID_AMD) {
- switch (pdev->device) {
- case PCI_DEVICE_ID_AMD_1AH_M60H_NHI0:
- case PCI_DEVICE_ID_AMD_1AH_M60H_NHI1:
- case PCI_DEVICE_ID_AMD_1AH_M68H_NHI0:
- case PCI_DEVICE_ID_AMD_1AH_M68H_NHI1:
- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI0:
- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI1:
- case PCI_DEVICE_ID_AMD_1AH_M80H_NHI2:
- case PCI_DEVICE_ID_AMD_1AH_M24H_NHI0:
- case PCI_DEVICE_ID_AMD_1AH_M24H_NHI1:
- case PCI_DEVICE_ID_AMD_1AH_M70H_NHI0:
- case PCI_DEVICE_ID_AMD_1AH_M70H_NHI1:
- /*
- * These AMD hosts may hang the Tx ring when the
- * DMA paths are torn down so they need the host
- * interface reset after each teardown.
- */
- nhi->quirks |= QUIRK_RESET_DMA_ON_TEARDOWN;
- break;
- }
}
}
@@ -279,7 +258,6 @@ static const struct tb_nhi_ops pci_nhi_default_ops = {
.shutdown = nhi_pci_release_irq,
.is_present = nhi_pci_is_present,
.init_interrupts = nhi_pci_init_msi,
- .reset_interface = nhi_reset_interface,
};
/* Ice Lake specific NHI operations */
@@ -463,7 +441,6 @@ static const struct tb_nhi_ops icl_nhi_ops = {
.release_ring_irq = nhi_pci_ring_release_msix,
.is_present = nhi_pci_is_present,
.init_interrupts = nhi_pci_init_msi,
- .reset_interface = nhi_reset_interface,
};
static int nhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c
index 9f7914ac2f48..96b7fe0386e3 100644
--- a/drivers/thunderbolt/quirks.c
+++ b/drivers/thunderbolt/quirks.c
@@ -99,6 +99,9 @@ static const struct tb_quirk tb_quirks[] = {
quirk_usb3_maximum_bandwidth },
{ 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000,
quirk_usb3_maximum_bandwidth },
+ /* CLx is unstable on the Anker Prime TB5 dock (DROM 01ea:83b5) */
+ { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x01ea, 0x83b5,
+ quirk_clx_disable },
/*
* Block Runtime PM in DP redrive mode for Intel Barlow Ridge host
* controllers.
diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c
index 25c259dd0760..988765c98c8c 100644
--- a/drivers/thunderbolt/stream.c
+++ b/drivers/thunderbolt/stream.c
@@ -978,6 +978,9 @@ static int tbstream_dev_fops_open(struct inode *inode, struct file *file)
}
mutex_unlock(&sdev->lock);
+
+ /* Stream handles IOCB_NOWAIT just fine */
+ file->f_mode |= FMODE_NOWAIT;
return 0;
err_unlock:
diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c
index 404c0693df50..cf571a7c98d0 100644
--- a/drivers/thunderbolt/switch.c
+++ b/drivers/thunderbolt/switch.c
@@ -774,6 +774,7 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
{
int port_max_hopid;
struct ida *ida;
+ int ret;
if (in) {
port_max_hopid = port->config.max_in_hop_id;
@@ -793,7 +794,11 @@ static int tb_port_alloc_hopid(struct tb_port *port, bool in, int min_hopid,
if (max_hopid < 0 || max_hopid > port_max_hopid)
max_hopid = port_max_hopid;
- return ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
+ ret = ida_alloc_range(ida, min_hopid, max_hopid, GFP_KERNEL);
+ if (ret >= 0)
+ tb_switch_get(port->sw);
+
+ return ret;
}
/**
@@ -832,6 +837,7 @@ int tb_port_alloc_out_hopid(struct tb_port *port, int min_hopid, int max_hopid)
void tb_port_release_in_hopid(struct tb_port *port, int hopid)
{
ida_free(&port->in_hopids, hopid);
+ tb_switch_put(port->sw);
}
/**
@@ -842,6 +848,7 @@ void tb_port_release_in_hopid(struct tb_port *port, int hopid)
void tb_port_release_out_hopid(struct tb_port *port, int hopid)
{
ida_free(&port->out_hopids, hopid);
+ tb_switch_put(port->sw);
}
static inline bool tb_switch_is_reachable(const struct tb_switch *parent,
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 47753a5c0f2e..4e5d0578fd4b 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -89,6 +89,7 @@ static void tb_dp_resource_unavailable(struct tb *tb, struct tb_port *port,
const char *reason);
static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port,
int retry, unsigned long delay);
+static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data);
static void tb_queue_hotplug(struct tb *tb, u64 route, u8 port, bool unplug)
{
@@ -385,7 +386,8 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw,
switch (port->config.type) {
case TB_TYPE_DP_HDMI_IN:
- tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids);
+ tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids,
+ tb_dp_tunnel_active, tb);
tb_increase_tmu_accuracy(tunnel);
break;
@@ -1910,6 +1912,18 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
struct tb *tb = data;
mutex_lock(&tb->lock);
+
+ /*
+ * If the DPRX read was canceled the tunnel is already being torn
+ * down by whoever canceled it. Do not touch the adapters here
+ * because the routers may be gone by now.
+ */
+ if (tunnel->dprx_canceled) {
+ tb_tunnel_dbg(tunnel, "DPRX read canceled, not activating\n");
+ mutex_unlock(&tb->lock);
+ return;
+ }
+
if (tb_tunnel_is_active(tunnel)) {
int consumed_up, consumed_down, ret;
@@ -1964,8 +1978,6 @@ static void tb_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
tb_dp_resource_unavailable(tb, in, "DPRX negotiation failed");
}
mutex_unlock(&tb->lock);
-
- tb_domain_put(tb);
}
static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
@@ -2026,8 +2038,7 @@ static void tb_tunnel_one_dp(struct tb *tb, struct tb_port *in,
available_up, available_down);
tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
- available_down, tb_dp_tunnel_active,
- tb_domain_get(tb));
+ available_down, tb_dp_tunnel_active, tb);
if (!tunnel) {
tb_port_dbg(out, "could not allocate DP tunnel\n");
goto err_reclaim_usb;
@@ -2048,7 +2059,6 @@ err_free:
tb_tunnel_put(tunnel);
err_reclaim_usb:
tb_reclaim_usb3_bandwidth(tb, in, out);
- tb_domain_put(tb);
err_detach_group:
tb_detach_bandwidth_group(in);
err_dealloc_dp:
@@ -2950,11 +2960,12 @@ static void tb_stop(struct tb *tb)
/* tunnels are only present after everything has been initialized */
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
/*
- * DMA tunnels require the driver to be functional so we
- * tear them down. Other protocol tunnels can be left
- * intact.
+ * DMA tunnels and DP tunnels which are not yet active require
+ * the driver to be functional so we tear them down.
+ * Other protocol tunnels can be left intact.
*/
- if (tb_tunnel_is_dma(tunnel))
+ if (tb_tunnel_is_dma(tunnel) ||
+ (tb_tunnel_is_dp(tunnel) && !tb_tunnel_is_active(tunnel)))
tb_tunnel_deactivate(tunnel);
tb_tunnel_put(tunnel);
}
@@ -3274,11 +3285,11 @@ static void tb_remove_work(struct work_struct *work)
struct tb *tb = tcm_to_tb(tcm);
mutex_lock(&tb->lock);
- if (tb->root_switch)
+ if (tb->root_switch) {
tb_free_unplugged_children(tb->root_switch);
+ tb_free_unplugged_xdomains(tb->root_switch);
+ }
mutex_unlock(&tb->lock);
-
- tb_free_unplugged_xdomains(tb->root_switch);
}
static int tb_runtime_resume(struct tb *tb)
diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c
index 05652ee82fbf..c8c648f31107 100644
--- a/drivers/thunderbolt/test.c
+++ b/drivers/thunderbolt/test.c
@@ -33,6 +33,18 @@ static void kunit_ida_init(struct kunit *test, struct ida *ida)
kunit_alloc_resource(test, __ida_init, __ida_destroy, GFP_KERNEL, ida);
}
+static void tb_test_switch_release(struct device *dev)
+{
+ /* The memory is owned by KUnit, nothing to do here */
+}
+
+static void tb_test_switch_put(void *data)
+{
+ struct tb_switch *sw = data;
+
+ put_device(&sw->dev);
+}
+
static struct tb_switch *alloc_switch(struct kunit *test, u64 route,
u8 upstream_port, u8 max_port_number)
{
@@ -44,6 +56,15 @@ static struct tb_switch *alloc_switch(struct kunit *test, u64 route,
if (!sw)
return NULL;
+ /*
+ * HopID allocations take a reference to their routers and those devices
+ * have to be initialized for that to work.
+ */
+ sw->dev.release = tb_test_switch_release;
+ device_initialize(&sw->dev);
+ if (kunit_add_action_or_reset(test, tb_test_switch_put, sw))
+ return NULL;
+
sw->config.upstream_port_number = upstream_port;
sw->config.depth = tb_route_length(route);
sw->config.route_hi = upper_32_bits(route);
@@ -1386,6 +1407,10 @@ static void tb_test_tunnel_pcie(struct kunit *test)
tb_tunnel_put(tunnel1);
}
+static void tb_test_dp_tunnel_active(struct tb_tunnel *tunnel, void *data)
+{
+}
+
static void tb_test_tunnel_dp(struct kunit *test)
{
struct tb_switch *host, *dev;
@@ -1406,7 +1431,8 @@ static void tb_test_tunnel_dp(struct kunit *test)
in = &host->ports[5];
out = &dev->ports[13];
- tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1452,7 +1478,8 @@ static void tb_test_tunnel_dp_chain(struct kunit *test)
in = &host->ports[5];
out = &dev4->ports[14];
- tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1502,7 +1529,8 @@ static void tb_test_tunnel_dp_tree(struct kunit *test)
in = &dev2->ports[13];
out = &dev5->ports[13];
- tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1567,7 +1595,8 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test)
in = &dev6->ports[13];
out = &dev12->ports[13];
- tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1637,7 +1666,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
out2 = &dev5->ports[13];
out3 = &dev4->ports[14];
- tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0, NULL, NULL);
+ tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_TRUE(test, tunnel1 != NULL);
KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1);
@@ -1645,7 +1675,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3);
KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3);
- tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0, NULL, NULL);
+ tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_TRUE(test, tunnel2 != NULL);
KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2);
@@ -1653,7 +1684,8 @@ static void tb_test_tunnel_3dp(struct kunit *test)
KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3);
KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4);
- tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0, NULL, NULL);
+ tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_TRUE(test, tunnel3 != NULL);
KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP);
KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3);
@@ -1751,7 +1783,8 @@ static void tb_test_tunnel_port_on_path(struct kunit *test)
in = &dev2->ports[13];
out = &dev5->ports[13];
- dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel);
KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
@@ -2183,7 +2216,8 @@ static void tb_test_credit_alloc_dp(struct kunit *test)
in = &host->ports[5];
out = &dev->ports[14];
- tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, tunnel);
KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
@@ -2419,7 +2453,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test,
in = &host->ports[5];
out = &dev->ports[13];
- dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel1);
KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
@@ -2456,7 +2491,8 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test,
in = &host->ports[6];
out = &dev->ports[14];
- dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0, NULL, NULL);
+ dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0,
+ tb_test_dp_tunnel_active, NULL);
KUNIT_ASSERT_NOT_NULL(test, dp_tunnel2);
KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 7e8284575dff..ffd2d04b3bfa 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -510,6 +510,7 @@ struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
goto err_deactivate;
}
+ tb_tunnel_set_active(tunnel, true);
tb_tunnel_dbg(tunnel, "discovered\n");
return tunnel;
@@ -1093,8 +1094,14 @@ static void tb_dp_dprx_work(struct work_struct *work)
struct tb_tunnel *tunnel = container_of(work, typeof(*tunnel), dprx_work.work);
struct tb *tb = tunnel->tb;
+ /*
+ * The DPRX read can be canceled while this work is waiting for
+ * tb->lock. Check the flag only once it is held: while the lock is
+ * held the tunnel cannot be torn down under us and the adapters are
+ * safe to access.
+ */
+ mutex_lock(&tb->lock);
if (!tunnel->dprx_canceled) {
- mutex_lock(&tb->lock);
if (tb_dp_is_usb4(tunnel->src_port->sw) &&
tb_dp_wait_dprx(tunnel, TB_DPRX_WAIT_TIMEOUT)) {
if (ktime_before(ktime_get(), tunnel->dprx_timeout)) {
@@ -1106,41 +1113,42 @@ static void tb_dp_dprx_work(struct work_struct *work)
} else {
tb_tunnel_set_active(tunnel, true);
}
- mutex_unlock(&tb->lock);
}
+ mutex_unlock(&tb->lock);
- if (tunnel->callback)
- tunnel->callback(tunnel, tunnel->callback_data);
+ tunnel->callback(tunnel, tunnel->callback_data);
tb_tunnel_put(tunnel);
+ tb_domain_put(tb);
}
static int tb_dp_dprx_start(struct tb_tunnel *tunnel)
{
/*
- * Bump up the reference to keep the tunnel around. It will be
- * dropped in tb_dp_dprx_stop() once the tunnel is deactivated.
+ * Bump up the references to keep the tunnel and the domain around
+ * until the work has run or has been canceled.
*/
tb_tunnel_get(tunnel);
+ tb_domain_get(tunnel->tb);
tunnel->dprx_started = true;
+ tunnel->dprx_canceled = false;
+ tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
+ queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0);
- if (tunnel->callback) {
- tunnel->dprx_timeout = dprx_timeout_to_ktime(dprx_timeout);
- queue_delayed_work(tunnel->tb->wq, &tunnel->dprx_work, 0);
- return -EINPROGRESS;
- }
-
- return tb_dp_is_usb4(tunnel->src_port->sw) ?
- tb_dp_wait_dprx(tunnel, dprx_timeout) : 0;
+ return -EINPROGRESS;
}
static void tb_dp_dprx_stop(struct tb_tunnel *tunnel)
{
+ struct tb *tb = tunnel->tb;
+
if (tunnel->dprx_started) {
tunnel->dprx_started = false;
tunnel->dprx_canceled = true;
- if (cancel_delayed_work(&tunnel->dprx_work))
+ if (cancel_delayed_work(&tunnel->dprx_work)) {
tb_tunnel_put(tunnel);
+ tb_domain_put(tb);
+ }
}
}
@@ -1582,20 +1590,28 @@ static void tb_dp_dump(struct tb_tunnel *tunnel)
* @tb: Pointer to the domain structure
* @in: DP in adapter
* @alloc_hopid: Allocate HopIDs from visited ports
+ * @callback: Callback that is called when the DP tunnel is fully
+ * activated (or there is an error)
+ * @callback_data: Data for @callback
*
* If @in adapter is active, follows the tunnel to the DP out adapter
* and back. Returns the discovered tunnel or %NULL if there was no
- * tunnel.
+ * tunnel. See tb_tunnel_alloc_dp() for @callback.
*
* Return: Pointer to &struct tb_tunnel or %NULL if no tunnel found.
*/
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
- bool alloc_hopid)
+ bool alloc_hopid,
+ void (*callback)(struct tb_tunnel *, void *),
+ void *callback_data)
{
struct tb_tunnel *tunnel;
struct tb_port *port;
struct tb_path *path;
+ if (WARN_ON(!callback))
+ return NULL;
+
if (!tb_dp_port_is_enabled(in))
return NULL;
@@ -1611,6 +1627,9 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
tunnel->alloc_bandwidth = tb_dp_alloc_bandwidth;
tunnel->consumed_bandwidth = tb_dp_consumed_bandwidth;
tunnel->src_port = in;
+ tunnel->callback = callback;
+ tunnel->callback_data = callback_data;
+ INIT_DELAYED_WORK(&tunnel->dprx_work, tb_dp_dprx_work);
path = tb_path_discover(in, TB_DP_VIDEO_HOPID, NULL, -1,
&tunnel->dst_port, "Video", alloc_hopid);
@@ -1656,6 +1675,7 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
tb_dp_dump(tunnel);
+ tb_tunnel_set_active(tunnel, true);
tb_tunnel_dbg(tunnel, "discovered\n");
return tunnel;
@@ -1677,16 +1697,16 @@ err_free:
* %0 if no available bandwidth.
* @max_down: Maximum available downstream bandwidth for the DP tunnel.
* %0 if no available bandwidth.
- * @callback: Optional callback that is called when the DP tunnel is
- * fully activated (or there is an error)
- * @callback_data: Optional data for @callback
+ * @callback: Callback that is called when the DP tunnel is fully
+ * activated (or there is an error)
+ * @callback_data: Data for @callback
*
* Allocates a tunnel between @in and @out that is capable of tunneling
- * Display Port traffic. If @callback is not %NULL it will be called
- * after tb_tunnel_activate() once the tunnel has been fully activated.
- * It can call tb_tunnel_is_active() to check if activation was
- * successful (or if it returns %false there was some sort of issue).
- * The @callback is called without @tb->lock held.
+ * Display Port traffic. The @callback is called after tb_tunnel_activate()
+ * once the tunnel has been fully activated. It can call
+ * tb_tunnel_is_active() to check if activation was successful (or if it
+ * returns %false there was some sort of issue). The @callback is called
+ * without @tb->lock held.
*
* Return: Pointer to @struct tb_tunnel or %NULL in case of failure.
*/
@@ -1701,7 +1721,7 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
struct tb_path *path;
bool pm_support;
- if (WARN_ON(!in->cap_adap || !out->cap_adap))
+ if (WARN_ON(!in->cap_adap || !out->cap_adap || !callback))
return NULL;
tunnel = tb_tunnel_alloc(tb, 3, TB_TUNNEL_DP);
@@ -2288,6 +2308,7 @@ struct tb_tunnel *tb_tunnel_discover_usb3(struct tb *tb, struct tb_port *down,
tb_usb3_reclaim_available_bandwidth;
}
+ tb_tunnel_set_active(tunnel, true);
tb_tunnel_dbg(tunnel, "discovered\n");
return tunnel;
diff --git a/drivers/thunderbolt/tunnel.h b/drivers/thunderbolt/tunnel.h
index 4878763a82b3..7d1d255ab5a7 100644
--- a/drivers/thunderbolt/tunnel.h
+++ b/drivers/thunderbolt/tunnel.h
@@ -66,8 +66,8 @@ enum tb_tunnel_state {
* @dprx_canceled: Was DPRX capabilities read poll canceled
* @dprx_timeout: If set DPRX capabilities read poll work will timeout after this passes
* @dprx_work: Worker that is scheduled to poll completion of DPRX capabilities read
- * @callback: Optional callback called when DP tunnel is fully activated
- * @callback_data: Optional data for @callback
+ * @callback: Callback called when DP tunnel is fully activated
+ * @callback_data: Data for @callback
* @paths: All paths required by the tunnel
*/
struct tb_tunnel {
@@ -117,7 +117,9 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
bool tb_tunnel_reserved_pci(struct tb_port *port, int *reserved_up,
int *reserved_down);
struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
- bool alloc_hopid);
+ bool alloc_hopid,
+ void (*callback)(struct tb_tunnel *, void *),
+ void *callback_data);
struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
struct tb_port *out, int link_nr,
int max_up, int max_down,
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index c179bd751fe4..b187a60663f5 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -377,6 +377,10 @@ static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route,
len += sizeof(res->hdr.xd_hdr) / 4;
len -= sizeof(*res) / 4;
+ if (len > TB_XDP_PROPERTIES_MAX_DATA_LENGTH) {
+ ret = -EINVAL;
+ goto err;
+ }
if (res->offset != req.offset) {
ret = -EINVAL;
@@ -1814,7 +1818,6 @@ static void tb_xdomain_state_work(struct work_struct *work)
tb_xdomain_failed(xd);
} else {
xd->state = XDOMAIN_STATE_ENUMERATED;
- tb_xdomain_queue_properties_changed(xd);
}
break;
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index 28af0fd98181..60f6060b893d 100644
--- a/drivers/tty/amiserial.c
+++ b/drivers/tty/amiserial.c
@@ -1138,11 +1138,6 @@ static int rs_ioctl(struct tty_struct *tty,
local_irq_save(flags);
cnow = info->icount; /* atomic copy */
local_irq_restore(flags);
- if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
- cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
- ret = -EIO; /* no change => error */
- break;
- }
if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
@@ -1150,6 +1145,10 @@ static int rs_ioctl(struct tty_struct *tty,
ret = 0;
break;
}
+ if (tty_io_error(tty)) {
+ ret = -EIO;
+ break;
+ }
schedule();
/* see if a signal did it */
if (signal_pending(current)) {
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index c13e050de83b..e55779ff0af2 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -454,14 +454,13 @@ static const u8 gsm_fcs8[256] = {
static void gsm_dlci_close(struct gsm_dlci *dlci);
static int gsmld_output(struct gsm_mux *gsm, u8 *data, int len);
-static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk);
+static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk, bool wait);
static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len,
u8 ctrl);
static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg);
static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr);
static void gsmld_write_trigger(struct gsm_mux *gsm);
static void gsmld_write_task(struct work_struct *work);
-static int gsm_modem_send_initial_msc(struct gsm_dlci *dlci);
/**
* gsm_fcs_add - update FCS
@@ -2174,7 +2173,7 @@ static void gsm_dlci_open(struct gsm_dlci *dlci)
pr_debug("DLCI %d goes open.\n", dlci->addr);
/* Send current modem state */
if (dlci->addr) {
- gsm_modem_send_initial_msc(dlci);
+ gsm_modem_update(dlci, 0, false);
} else {
/* Start keep-alive control */
gsm->ka_num = 0;
@@ -4136,9 +4135,10 @@ static void gsm_modem_upd_via_data(struct gsm_dlci *dlci, u8 brk)
* gsm_modem_upd_via_msc - send modem bits via control frame
* @dlci: channel
* @brk: break signal
+ * @wait: wait for the MSC command
*/
-static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
+static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk, bool wait)
{
u8 modembits[3];
struct gsm_control *ctrl;
@@ -4155,6 +4155,8 @@ static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
modembits[2] = (brk << 4) | 2 | EA; /* Length, Break, EA */
len++;
}
+ if (!wait)
+ return gsm_control_command(dlci->gsm, CMD_MSC, modembits, len);
ctrl = gsm_control_send(dlci->gsm, CMD_MSC, modembits, len);
if (ctrl == NULL)
return -ENOMEM;
@@ -4162,34 +4164,13 @@ static int gsm_modem_upd_via_msc(struct gsm_dlci *dlci, u8 brk)
}
/**
- * gsm_modem_send_initial_msc - Send initial modem status message
- *
- * @dlci: channel
- *
- * Send an initial MSC message after DLCI open to set the initial
- * modem status lines. This is only done for basic mode.
- * Does not wait for a response as we cannot block the input queue
- * processing.
- */
-static int gsm_modem_send_initial_msc(struct gsm_dlci *dlci)
-{
- u8 modembits[2];
-
- if (dlci->adaption != 1 || dlci->gsm->encoding != GSM_BASIC_OPT)
- return 0;
-
- modembits[0] = (dlci->addr << 2) | 2 | EA; /* DLCI, Valid, EA */
- modembits[1] = (gsm_encode_modem(dlci) << 1) | EA;
- return gsm_control_command(dlci->gsm, CMD_MSC, (const u8 *)&modembits, 2);
-}
-
-/**
* gsm_modem_update - send modem status line state
* @dlci: channel
* @brk: break signal
+ * @wait: wait for the MSC command
*/
-static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk)
+static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk, bool wait)
{
if (dlci->gsm->dead)
return -EL2HLT;
@@ -4199,7 +4180,7 @@ static int gsm_modem_update(struct gsm_dlci *dlci, u8 brk)
return 0;
} else if (dlci->gsm->encoding == GSM_BASIC_OPT) {
/* Send as MSC control message. */
- return gsm_modem_upd_via_msc(dlci, brk);
+ return gsm_modem_upd_via_msc(dlci, brk, wait);
}
/* Modem status lines are not supported. */
@@ -4265,7 +4246,7 @@ static void gsm_dtr_rts(struct tty_port *port, bool active)
modem_tx &= ~(TIOCM_DTR | TIOCM_RTS);
if (modem_tx != dlci->modem_tx) {
dlci->modem_tx = modem_tx;
- gsm_modem_update(dlci, 0);
+ gsm_modem_update(dlci, 0, true);
}
}
@@ -4471,7 +4452,7 @@ static int gsmtty_tiocmset(struct tty_struct *tty,
if (modem_tx != dlci->modem_tx) {
dlci->modem_tx = modem_tx;
- return gsm_modem_update(dlci, 0);
+ return gsm_modem_update(dlci, 0, true);
}
return 0;
}
@@ -4553,7 +4534,7 @@ static void gsmtty_throttle(struct tty_struct *tty)
dlci->modem_tx &= ~TIOCM_RTS;
dlci->throttled = true;
/* Send an MSC with RTS cleared */
- gsm_modem_update(dlci, 0);
+ gsm_modem_update(dlci, 0, true);
}
static void gsmtty_unthrottle(struct tty_struct *tty)
@@ -4565,7 +4546,7 @@ static void gsmtty_unthrottle(struct tty_struct *tty)
dlci->modem_tx |= TIOCM_RTS;
dlci->throttled = false;
/* Send an MSC with RTS set */
- gsm_modem_update(dlci, 0);
+ gsm_modem_update(dlci, 0, true);
}
static int gsmtty_break_ctl(struct tty_struct *tty, int state)
@@ -4583,7 +4564,7 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
if (encode > 0x0F)
encode = 0x0F; /* Best effort */
}
- return gsm_modem_update(dlci, encode);
+ return gsm_modem_update(dlci, encode, true);
}
static void gsmtty_cleanup(struct tty_struct *tty)
diff --git a/drivers/tty/serial/8250/8250_bcm7271.c b/drivers/tty/serial/8250/8250_bcm7271.c
index cb7d594d263e..5f1140f05150 100644
--- a/drivers/tty/serial/8250/8250_bcm7271.c
+++ b/drivers/tty/serial/8250/8250_bcm7271.c
@@ -1121,8 +1121,8 @@ static void brcmuart_remove(struct platform_device *pdev)
struct brcmuart_priv *priv = platform_get_drvdata(pdev);
debugfs_remove_recursive(priv->debugfs_dir);
- hrtimer_cancel(&priv->hrt);
serial8250_unregister_port(priv->line);
+ hrtimer_cancel(&priv->hrt);
brcmuart_free_bufs(&pdev->dev, priv);
if (priv->dma_enabled)
brcmuart_arbitration(priv, 0);
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 3c7775df27ef..ceecb39fb82d 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -711,12 +711,6 @@ static int omap_8250_startup(struct uart_port *port)
struct uart_8250_dma *dma = &priv->omap8250_dma;
int ret;
- if (priv->wakeirq) {
- ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq);
- if (ret)
- return ret;
- }
-
#ifdef CONFIG_PM
up->capabilities |= UART_CAP_RPM;
#endif
@@ -787,7 +781,6 @@ static void omap_8250_shutdown(struct uart_port *port)
}
disable_irq_nosync(port->irq);
- dev_pm_clear_wake_irq(port->dev);
serial8250_release_dma(up);
up->dma = NULL;
@@ -1588,6 +1581,12 @@ static int omap8250_probe(struct platform_device *pdev)
priv->wakeirq = irq_of_parse_and_map(np, 1);
+ if (priv->wakeirq) {
+ ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, priv->wakeirq);
+ if (ret)
+ goto err;
+ }
+
ret = serial8250_register_8250_port(&up);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register 8250 port\n");
@@ -1603,6 +1602,7 @@ static int omap8250_probe(struct platform_device *pdev)
return 0;
err:
+ dev_pm_clear_wake_irq(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
flush_work(&priv->qos_work);
@@ -1625,6 +1625,7 @@ static void omap8250_remove(struct platform_device *pdev)
omap_8250_shutdown(&up->port);
serial8250_unregister_port(priv->line);
priv->line = -ENODEV;
+ dev_pm_clear_wake_irq(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
flush_work(&priv->qos_work);
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 38fa45e74a37..1acb73e45f8a 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -3597,7 +3597,7 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
up->console_line_ended = true;
up->console_msr_work_allow = true;
- init_irq_work(&up->console_msr_work, console_msr_handler);
+ up->console_msr_work = IRQ_WORK_INIT_LAZY(console_msr_handler);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 09648d643897..64e3f8cf2f55 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -188,6 +188,11 @@ static void cleanup_kgdboc(void)
kgdboc_unregister_kbd();
kgdb_unregister_io_module(&kgdboc_io_ops);
+
+ if (kgdb_tty_driver) {
+ tty_driver_kref_put(kgdb_tty_driver);
+ kgdb_tty_driver = NULL;
+ }
}
static int configure_kgdboc(void)
@@ -254,6 +259,10 @@ do_register:
noconfig:
kgdboc_unregister_kbd();
+ if (kgdb_tty_driver) {
+ tty_driver_kref_put(kgdb_tty_driver);
+ kgdb_tty_driver = NULL;
+ }
configured = 0;
return err;
diff --git a/drivers/tty/serial/ma35d1_serial.c b/drivers/tty/serial/ma35d1_serial.c
index 920fe7ff5083..0809655f4395 100644
--- a/drivers/tty/serial/ma35d1_serial.c
+++ b/drivers/tty/serial/ma35d1_serial.c
@@ -728,7 +728,7 @@ static int ma35d1serial_probe(struct platform_device *pdev)
ret = clk_prepare_enable(up->clk);
if (ret)
- goto err_iounmap;
+ goto err_put_clk;
if (up->port.line != 0)
up->port.uartclk = clk_get_rate(up->clk);
@@ -755,6 +755,9 @@ err_free_irq:
err_clk_disable:
clk_disable_unprepare(up->clk);
+err_put_clk:
+ clk_put(up->clk);
+
err_iounmap:
iounmap(up->port.membase);
return ret;
@@ -770,6 +773,7 @@ static void ma35d1serial_remove(struct platform_device *dev)
uart_remove_one_port(&ma35d1serial_reg, port);
clk_disable_unprepare(up->clk);
+ clk_put(up->clk);
}
static int ma35d1serial_suspend(struct platform_device *dev, pm_message_t state)
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index a5a5cd68f696..f858a6669011 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -535,11 +535,11 @@ static void max3100_shutdown(struct uart_port *port)
timer_delete_sync(&s->timer);
if (s->workqueue) {
+ free_irq(port->irq, s);
+ cancel_work_sync(&s->work);
destroy_workqueue(s->workqueue);
s->workqueue = NULL;
}
- if (port->irq)
- free_irq(port->irq, s);
/* set shutdown mode to save power */
max3100_sr(s, MAX3100_WC | MAX3100_SHDN, &rx);
@@ -753,6 +753,15 @@ static void max3100_remove(struct spi_device *spi)
if (max3100s[i] == s) {
dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
+
+ s->force_end_work = 1;
+ timer_shutdown_sync(&s->timer);
+ if (s->workqueue) {
+ free_irq(s->port.irq, s);
+ cancel_work_sync(&s->work);
+ destroy_workqueue(s->workqueue);
+ s->workqueue = NULL;
+ }
kfree(max3100s[i]);
max3100s[i] = NULL;
break;
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index b566206f42a2..472ec39caf27 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -634,6 +634,9 @@ static void __exit mpc512x_psc_fifoc_uninit(void)
}
}
+static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM];
+static struct clk *psc_ipg_clk[MPC52xx_PSC_MAXNUM];
+
/* 512x specific interrupt handler. The caller holds the port lock */
static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port)
{
@@ -654,9 +657,6 @@ static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port)
return IRQ_NONE;
}
-static struct clk *psc_mclk_clk[MPC52xx_PSC_MAXNUM];
-static struct clk *psc_ipg_clk[MPC52xx_PSC_MAXNUM];
-
/* called from within the .request_port() callback (allocation) */
static int mpc512x_psc_alloc_clock(struct uart_port *port)
{
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 3633723acef8..a851b8f0ef04 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -171,7 +171,6 @@ static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport);
static int qcom_geni_serial_port_setup(struct uart_port *uport);
static void qcom_geni_serial_start_tx_fifo(struct uart_port *uport);
static void qcom_geni_serial_resume_tx(struct uart_port *uport);
-static void qcom_geni_serial_poll_rx_fifo_locked(struct uart_port *uport);
static inline struct qcom_geni_serial_port *to_dev_port(struct uart_port *uport)
{
@@ -467,6 +466,102 @@ static int qcom_geni_serial_poll_init(struct uart_port *uport)
#endif
#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
+static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+{
+ u32 i;
+ unsigned char buf[sizeof(u32)];
+ struct tty_port *tport;
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+
+ tport = &uport->state->port;
+ for (i = 0; i < bytes; ) {
+ int c;
+ int chunk = min_t(int, bytes - i, BYTES_PER_FIFO_WORD);
+
+ ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, buf, 1);
+ i += chunk;
+ if (drop)
+ continue;
+
+ for (c = 0; c < chunk; c++) {
+ int sysrq;
+
+ uport->icount.rx++;
+ if (port->brk && buf[c] == 0) {
+ port->brk = false;
+ if (uart_handle_break(uport))
+ continue;
+ }
+
+ sysrq = uart_prepare_sysrq_char(uport, buf[c]);
+
+ if (!sysrq)
+ tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
+ }
+ }
+ if (!drop)
+ tty_flip_buffer_push(tport);
+}
+#else
+static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
+{
+
+}
+#endif
+
+static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop)
+{
+ u32 status;
+ u32 word_cnt;
+ u32 last_word_byte_cnt;
+ u32 last_word_partial;
+ u32 total_bytes;
+
+ status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ word_cnt = status & RX_FIFO_WC_MSK;
+ last_word_partial = status & RX_LAST;
+ last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
+ RX_LAST_BYTE_VALID_SHFT;
+
+ if (!word_cnt)
+ return;
+ total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1);
+ if (last_word_partial && last_word_byte_cnt)
+ total_bytes += last_word_byte_cnt;
+ else
+ total_bytes += BYTES_PER_FIFO_WORD;
+ handle_rx_console(uport, total_bytes, drop);
+}
+
+#ifdef CONFIG_SERIAL_QCOM_GENI_CONSOLE
+/* Caller holds the UART port lock. */
+static void qcom_geni_serial_poll_rx_fifo_locked(struct uart_port *uport)
+{
+ struct qcom_geni_serial_port *port = to_dev_port(uport);
+ struct tty_port *tport = &uport->state->port;
+ u32 s_irq_status;
+ bool drop_rx = false;
+
+ s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
+ writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+
+ if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
+ uport->icount.overrun++;
+ tty_insert_flip_char(tport, 0, TTY_OVERRUN);
+ }
+
+ if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) {
+ if (s_irq_status & S_GP_IRQ_0_EN)
+ uport->icount.parity++;
+ drop_rx = true;
+ } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) {
+ uport->icount.brk++;
+ port->brk = true;
+ }
+
+ qcom_geni_serial_handle_rx_fifo(uport, drop_rx);
+}
+
static void qcom_geni_serial_drain_fifo(struct uart_port *uport)
{
struct qcom_geni_serial_port *port = to_dev_port(uport);
@@ -676,47 +771,6 @@ static void qcom_geni_serial_console_device_unlock(struct console *co,
#endif
}
-static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
-{
- u32 i;
- unsigned char buf[sizeof(u32)];
- struct tty_port *tport;
- struct qcom_geni_serial_port *port = to_dev_port(uport);
-
- tport = &uport->state->port;
- for (i = 0; i < bytes; ) {
- int c;
- int chunk = min_t(int, bytes - i, BYTES_PER_FIFO_WORD);
-
- ioread32_rep(uport->membase + SE_GENI_RX_FIFOn, buf, 1);
- i += chunk;
- if (drop)
- continue;
-
- for (c = 0; c < chunk; c++) {
- int sysrq;
-
- uport->icount.rx++;
- if (port->brk && buf[c] == 0) {
- port->brk = false;
- if (uart_handle_break(uport))
- continue;
- }
-
- sysrq = uart_prepare_sysrq_char(uport, buf[c]);
-
- if (!sysrq)
- tty_insert_flip_char(tport, buf[c], TTY_NORMAL);
- }
- }
- if (!drop)
- tty_flip_buffer_push(tport);
-}
-#else
-static void handle_rx_console(struct uart_port *uport, u32 bytes, bool drop)
-{
-
-}
#endif /* CONFIG_SERIAL_QCOM_GENI_CONSOLE */
static void handle_rx_uart(struct uart_port *uport, u32 bytes)
@@ -890,58 +944,6 @@ static void qcom_geni_serial_cancel_tx_cmd(struct uart_port *uport)
port->tx_queued = 0;
}
-static void qcom_geni_serial_handle_rx_fifo(struct uart_port *uport, bool drop)
-{
- u32 status;
- u32 word_cnt;
- u32 last_word_byte_cnt;
- u32 last_word_partial;
- u32 total_bytes;
-
- status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
- word_cnt = status & RX_FIFO_WC_MSK;
- last_word_partial = status & RX_LAST;
- last_word_byte_cnt = (status & RX_LAST_BYTE_VALID_MSK) >>
- RX_LAST_BYTE_VALID_SHFT;
-
- if (!word_cnt)
- return;
- total_bytes = BYTES_PER_FIFO_WORD * (word_cnt - 1);
- if (last_word_partial && last_word_byte_cnt)
- total_bytes += last_word_byte_cnt;
- else
- total_bytes += BYTES_PER_FIFO_WORD;
- handle_rx_console(uport, total_bytes, drop);
-}
-
-/* Caller holds the UART port lock. */
-static void qcom_geni_serial_poll_rx_fifo_locked(struct uart_port *uport)
-{
- struct qcom_geni_serial_port *port = to_dev_port(uport);
- struct tty_port *tport = &uport->state->port;
- u32 s_irq_status;
- bool drop_rx = false;
-
- s_irq_status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
- writel(s_irq_status, uport->membase + SE_GENI_S_IRQ_CLEAR);
-
- if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
- uport->icount.overrun++;
- tty_insert_flip_char(tport, 0, TTY_OVERRUN);
- }
-
- if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) {
- if (s_irq_status & S_GP_IRQ_0_EN)
- uport->icount.parity++;
- drop_rx = true;
- } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) {
- uport->icount.brk++;
- port->brk = true;
- }
-
- qcom_geni_serial_handle_rx_fifo(uport, drop_rx);
-}
-
static void qcom_geni_serial_stop_rx_fifo(struct uart_port *uport)
{
u32 irq_en;
@@ -1482,9 +1484,8 @@ static int geni_serial_set_rate(struct geni_se *se, unsigned long baud)
* Bump up BW vote on CPU and CORE path as driver supports FIFO mode
* only.
*/
- avg_bw_core = (baud > 115200) ? Bps_to_icc(CORE_2X_50_MHZ)
- : GENI_DEFAULT_BW;
- port->se.icc_paths[GENI_TO_CORE].avg_bw = avg_bw_core;
+ avg_bw_core = baud > 115200 ? CORE_2X_50_MHZ : CORE_2X_19_2_MHZ;
+ port->se.icc_paths[GENI_TO_CORE].avg_bw = Bps_to_icc(avg_bw_core);
port->se.icc_paths[CPU_TO_GENI].avg_bw = Bps_to_icc(baud);
geni_icc_set_bw(&port->se);
@@ -1625,16 +1626,37 @@ static int qcom_geni_console_setup(struct console *co, char *options)
if (unlikely(!uport->membase))
return -ENXIO;
+ ret = pm_runtime_resume_and_get(uport->dev);
+ if (ret < 0)
+ return ret;
+
if (!port->setup) {
ret = qcom_geni_serial_port_setup(uport);
- if (ret)
+ if (ret) {
+ pm_runtime_put_sync(uport->dev);
return ret;
+ }
}
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
- return uart_set_options(uport, co, baud, parity, bits, flow);
+ ret = uart_set_options(uport, co, baud, parity, bits, flow);
+ if (ret)
+ pm_runtime_put_sync(uport->dev);
+
+ return ret;
+}
+
+static int qcom_geni_console_exit(struct console *co)
+{
+ struct qcom_geni_serial_port *port;
+
+ port = get_port_from_line(co->index, true, NULL);
+ if (IS_ERR(port))
+ return PTR_ERR(port);
+
+ return pm_runtime_put_sync(port->uport.dev);
}
static void qcom_geni_serial_earlycon_write(struct console *con,
@@ -1751,6 +1773,7 @@ static struct console cons_ops = {
.device_unlock = qcom_geni_serial_console_device_unlock,
.device = uart_console_device,
.setup = qcom_geni_console_setup,
+ .exit = qcom_geni_console_exit,
.flags = CON_PRINTBUFFER | CON_NBCON,
.index = -1,
.data = &qcom_geni_console_driver,
@@ -2099,9 +2122,11 @@ static int qcom_geni_serial_resume(struct device *dev)
struct uart_port *uport = &port->uport;
struct qcom_geni_private_data *private_data = uport->private_data;
- ret = pm_runtime_force_resume(dev);
- if (ret)
- return ret;
+ if (console_suspend_enabled || !uart_console(uport)) {
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+ }
ret = uart_resume_port(private_data->drv, uport);
if (uart_console(uport)) {
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 7107a0fb1e7b..296079c16fdb 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -216,6 +216,8 @@
#define SC16IS7XX_TLR_TX_TRIGGER(words) ((((words) / 4) & 0x0f) << 0)
#define SC16IS7XX_TLR_RX_TRIGGER(words) ((((words) / 4) & 0x0f) << 4)
+#define SC16IS7XX_TX_TRIGGER_LEVEL 32
+
/* IOControl register bits (Only 75x/76x) */
#define SC16IS7XX_IOCONTROL_LATCH_BIT BIT(0) /* Enable input latching */
#define SC16IS7XX_IOCONTROL_MODEM_A_BIT BIT(1) /* Enable GPIO[7:4] as modem A pins */
@@ -647,12 +649,26 @@ static void sc16is7xx_handle_rx(struct uart_port *port, unsigned int rxlen,
tty_flip_buffer_push(&port->state->port);
}
+static unsigned int sc16is7xx_txlvl(struct uart_port *port)
+{
+ unsigned int txlvl;
+
+ txlvl = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
+ if (txlvl > SC16IS7XX_FIFO_SIZE) {
+ dev_err_ratelimited(port->dev,
+ "chip reports %u free bytes in TX FIFO, but it only has %u\n",
+ txlvl, SC16IS7XX_FIFO_SIZE);
+ return 0;
+ }
+
+ return txlvl;
+}
+
static void sc16is7xx_handle_tx(struct uart_port *port)
{
struct tty_port *tport = &port->state->port;
unsigned long flags;
unsigned int txlen;
- unsigned char *tail;
if (unlikely(port->x_char)) {
sc16is7xx_port_write(port, SC16IS7XX_THR_REG, port->x_char);
@@ -669,17 +685,28 @@ static void sc16is7xx_handle_tx(struct uart_port *port)
}
/* Limit to space available in TX FIFO */
- txlen = sc16is7xx_port_read(port, SC16IS7XX_TXLVL_REG);
- if (txlen > SC16IS7XX_FIFO_SIZE) {
- dev_err_ratelimited(port->dev,
- "chip reports %d free bytes in TX fifo, but it only has %d",
- txlen, SC16IS7XX_FIFO_SIZE);
- txlen = 0;
- }
+ txlen = sc16is7xx_txlvl(port);
+
+ /* Handle circular buffer wrap-around by sending multiple segments */
+ while (txlen > 0 && !kfifo_is_empty(&tport->xmit_fifo)) {
+ unsigned char *tail;
+ unsigned int to_send;
+
+ to_send = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
+ if (!to_send)
+ break;
- txlen = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, txlen);
- sc16is7xx_fifo_write(port, tail, txlen);
- uart_xmit_advance(port, txlen);
+ sc16is7xx_fifo_write(port, tail, to_send);
+ uart_xmit_advance(port, to_send);
+
+ if (kfifo_is_empty(&tport->xmit_fifo))
+ break;
+
+ /* Refill below the trigger to enable the next THRI crossing. */
+ txlen = sc16is7xx_txlvl(port);
+ if (txlen < SC16IS7XX_TX_TRIGGER_LEVEL)
+ break;
+ }
uart_port_lock_irqsave(port, &flags);
if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)
@@ -1130,6 +1157,10 @@ static int sc16is7xx_startup(struct uart_port *port)
SC16IS7XX_TCR_RX_RESUME(24) |
SC16IS7XX_TCR_RX_HALT(48));
+ /* Sync hardware and software TX trigger levels */
+ sc16is7xx_port_write(port, SC16IS7XX_TLR_REG,
+ SC16IS7XX_TLR_TX_TRIGGER(SC16IS7XX_TX_TRIGGER_LEVEL));
+
/* Disable TCR/TLR access */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_TCRTLR_BIT, 0);
diff --git a/drivers/tty/serial/serial-tegra.c b/drivers/tty/serial/serial-tegra.c
index 8004fc00fb9c..b0fc4ae3686f 100644
--- a/drivers/tty/serial/serial-tegra.c
+++ b/drivers/tty/serial/serial-tegra.c
@@ -306,11 +306,19 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
{
unsigned long fcr = tup->fcr_shadow;
unsigned int lsr, tmout = 10000;
+ bool clear_tx = !!(fcr_bits & UART_FCR_CLEAR_XMIT);
if (tup->rts_active)
set_rts(tup, false);
- if (tup->cdata->allow_txfifo_reset_fifo_mode) {
+ /*
+ * Leaving FIFO mode below is a workaround for a Tegra30
+ * restriction on clearing the Tx FIFO while FIFO mode is
+ * enabled. It empties both FIFOs, so applying it to an
+ * Rx-only reset would destroy an in-flight transmission.
+ * Only take that path when the caller asked for CLEAR_XMIT.
+ */
+ if (tup->cdata->allow_txfifo_reset_fifo_mode || !clear_tx) {
fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
tegra_uart_write(tup, fcr, UART_FCR);
} else {
@@ -335,9 +343,15 @@ static void tegra_uart_fifo_reset(struct tegra_uart_port *tup, u8 fcr_bits)
*/
tegra_uart_wait_cycle_time(tup, 32);
+ /*
+ * Only wait for the transmitter to drain when the Tx FIFO was
+ * part of the reset. For an Rx-only reset it is left intact,
+ * and waiting for TEMT here would spin for a full frame time.
+ */
do {
lsr = tegra_uart_read(tup, UART_LSR);
- if ((lsr & UART_LSR_TEMT) && !(lsr & UART_LSR_DR))
+ if ((!clear_tx || (lsr & UART_LSR_TEMT)) &&
+ !(lsr & UART_LSR_DR))
break;
udelay(1);
} while (--tmout);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 95774b0f1484..6332ed545c89 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -896,7 +896,7 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
upf_t old_flags, new_flags;
int retval;
- if (!uport)
+ if (!uport || tty_io_error(tty))
return -EIO;
new_port = new_info->port;
@@ -1119,7 +1119,7 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state)
guard(mutex)(&port->mutex);
uport = uart_port_check(state);
- if (!uport)
+ if (!uport || tty_io_error(tty))
return -EIO;
if (uport->type != PORT_UNKNOWN && uport->ops->break_ctl)
@@ -1144,7 +1144,7 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state)
*/
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &port->mutex) {
uport = uart_port_check(state);
- if (!uport)
+ if (!uport || tty_io_error(tty))
return -EIO;
if (tty_port_users(port) != 1)
@@ -1199,7 +1199,7 @@ static void uart_enable_ms(struct uart_port *uport)
* FIXME: This wants extracting into a common all driver implementation
* of TIOCMWAIT using tty_port.
*/
-static int uart_wait_modem_status(struct uart_state *state, unsigned long arg)
+static int uart_wait_modem_status(struct tty_struct *tty, struct uart_state *state, unsigned long arg)
{
struct uart_port *uport;
struct tty_port *port = &state->port;
@@ -1213,18 +1213,29 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg)
uport = uart_port_ref(state);
if (!uport)
return -EIO;
- scoped_guard(uart_port_lock_irq, uport) {
- memcpy(&cprev, &uport->icount, sizeof(struct uart_icount));
- uart_enable_ms(uport);
+
+ mutex_lock(&port->mutex);
+ if (tty_io_error(tty)) {
+ mutex_unlock(&port->mutex);
+ ret = -EIO;
+ goto out_deref;
}
+ uart_port_lock_irq(uport);
+ memcpy(&cprev, &uport->icount, sizeof(struct uart_icount));
+ uart_enable_ms(uport);
+ uart_port_unlock_irq(uport);
+
+ mutex_unlock(&port->mutex);
+
add_wait_queue(&port->delta_msr_wait, &wait);
for (;;) {
- scoped_guard(uart_port_lock_irq, uport)
- memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
-
set_current_state(TASK_INTERRUPTIBLE);
+ uart_port_lock_irq(uport);
+ memcpy(&cnow, &uport->icount, sizeof(struct uart_icount));
+ uart_port_unlock_irq(uport);
+
if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
@@ -1233,6 +1244,11 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg)
break;
}
+ if (tty_io_error(tty)) {
+ ret = -EIO;
+ break;
+ }
+
schedule();
/* see if a signal did it */
@@ -1245,6 +1261,7 @@ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg)
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(&port->delta_msr_wait, &wait);
+out_deref:
uart_port_deref(uport);
return ret;
@@ -1567,7 +1584,7 @@ uart_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
/* This should only be used when the hardware is present. */
if (cmd == TIOCMIWAIT)
- return uart_wait_modem_status(state, arg);
+ return uart_wait_modem_status(tty, state, arg);
/* rs485_config requires more locking than others */
if (cmd == TIOCSRS485)
@@ -1623,14 +1640,13 @@ static void uart_set_ldisc(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *uport;
- struct tty_port *port = &state->port;
-
- if (!tty_port_initialized(port))
- return;
guard(mutex)(&state->port.mutex);
uport = uart_port_check(state);
- if (uport && uport->ops->set_ldisc)
+ if (!uport || tty_io_error(tty))
+ return;
+
+ if (uport->ops->set_ldisc)
uport->ops->set_ldisc(uport, &tty->termios);
}
@@ -1646,7 +1662,7 @@ static void uart_set_termios(struct tty_struct *tty,
guard(mutex)(&state->port.mutex);
uport = uart_port_check(state);
- if (!uport)
+ if (!uport || tty_io_error(tty))
return;
/*
@@ -1798,7 +1814,14 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
* 'timeout' / 'expire' give us the maximum amount of time
* we wait.
*/
- while (!port->ops->tx_empty(port)) {
+ for (;;) {
+ mutex_lock(&state->port.mutex);
+ if (tty_io_error(tty) || port->ops->tx_empty(port)) {
+ mutex_unlock(&state->port.mutex);
+ break;
+ }
+ mutex_unlock(&state->port.mutex);
+
msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
@@ -3358,6 +3381,7 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port)
err_unregister_port_dev:
serial_base_port_device_remove(port->port_dev);
+ port->port_dev = NULL;
err_unregister_ctrl_dev:
serial_base_ctrl_device_remove(new_ctrl_dev);
@@ -3372,12 +3396,24 @@ err_unregister_ctrl_dev:
void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port)
{
struct device *phys_dev = port->dev;
- struct serial_port_device *port_dev = port->port_dev;
- struct serial_ctrl_device *ctrl_dev = serial_core_get_ctrl_dev(port_dev);
+ struct serial_port_device *port_dev;
+ struct serial_ctrl_device *ctrl_dev;
int ctrl_id = port->ctrl_id;
guard(mutex)(&port_mutex);
+ /*
+ * A NULL port device means there is no registered port device to
+ * remove: serial_core_remove_one_port() clears port_dev on
+ * teardown, and it is never set if registration failed before
+ * serial_core_port_device_add().
+ */
+ port_dev = port->port_dev;
+ if (!port_dev)
+ return;
+
+ ctrl_dev = serial_core_get_ctrl_dev(port_dev);
+
port->flags |= UPF_DEAD;
serial_core_remove_one_port(drv, port);
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 78a1c1eea11b..fc6fdc751b16 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -656,6 +656,7 @@ static int vt8500_serial_probe(struct platform_device *pdev)
ret = clk_prepare_enable(vt8500_port->clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
+ clk_put(vt8500_port->clk);
return ret;
}
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 48569035da56..1a6c8a1bc764 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -620,6 +620,8 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
tty_ldisc_hangup(tty, cons_filp != NULL);
+ wake_up_interruptible(&tty->break_wait);
+
spin_lock_irq(&tty->ctrl.lock);
clear_bit(TTY_THROTTLED, &tty->flags);
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
@@ -2431,6 +2433,7 @@ static int tiocgetd(struct tty_struct *tty, int __user *p)
* send_break - performed time break
* @tty: device to break on
* @duration: timeout in mS
+ * @file: file object
*
* Perform a timed break on hardware that lacks its own driver level timed
* break functionality.
@@ -2438,8 +2441,9 @@ static int tiocgetd(struct tty_struct *tty, int __user *p)
* Locking:
* @tty->atomic_write_lock serializes
*/
-static int send_break(struct tty_struct *tty, unsigned int duration)
+static int send_break(struct file *file, struct tty_struct *tty, unsigned int duration)
{
+ long timeout;
int retval;
if (tty->ops->break_ctl == NULL)
@@ -2453,13 +2457,26 @@ static int send_break(struct tty_struct *tty, unsigned int duration)
return -EINTR;
retval = tty->ops->break_ctl(tty, -1);
- if (!retval) {
- msleep_interruptible(duration);
- retval = tty->ops->break_ctl(tty, 0);
- } else if (retval == -EOPNOTSUPP) {
- /* some drivers can tell only dynamically */
- retval = 0;
+ if (retval) {
+ if (retval == -EOPNOTSUPP) {
+ /* some drivers can tell only dynamically */
+ retval = 0;
+ }
+ goto out_unlock;
+ }
+
+ timeout = msecs_to_jiffies(duration);
+ timeout = wait_event_interruptible_timeout(tty->break_wait,
+ tty_hung_up_p(file),
+ timeout);
+ /* return early on hangup only */
+ if (timeout > 0) {
+ retval = -EIO;
+ goto out_unlock;
}
+
+ retval = tty->ops->break_ctl(tty, 0);
+out_unlock:
tty_write_unlock(tty);
if (signal_pending(current))
@@ -2727,10 +2744,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
* This is used by the tcdrain() termios function.
*/
if (!arg)
- return send_break(tty, 250);
+ return send_break(file, tty, 250);
return 0;
case TCSBRKP: /* support for POSIX tcsendbreak() */
- return send_break(tty, arg ? arg*100 : 250);
+ return send_break(file, tty, arg ? arg * 100 : 250);
case TIOCMGET:
return tty_tiocmget(tty, p);
@@ -3090,6 +3107,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
init_ldsem(&tty->ldisc_sem);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
+ init_waitqueue_head(&tty->break_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup);
mutex_init(&tty->atomic_write_lock);
spin_lock_init(&tty->ctrl.lock);
@@ -3238,14 +3256,16 @@ struct device *tty_register_device_attr(struct tty_driver *driver,
goto err_put;
if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
- /*
- * Free any saved termios data so that the termios state is
- * reset when reusing a minor number.
- */
- tp = driver->termios[index];
- if (tp) {
- driver->termios[index] = NULL;
- kfree(tp);
+ if (driver->flags & TTY_DRIVER_RESET_SAVED_TERMIOS) {
+ /*
+ * Free any saved termios data so that the termios
+ * state is reset when reusing a minor number.
+ */
+ tp = driver->termios[index];
+ if (tp) {
+ driver->termios[index] = NULL;
+ kfree(tp);
+ }
}
retval = tty_cdev_add(driver, devt, index, 1);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 54359310e293..b22ff9eb7778 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -340,19 +340,9 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty)
}
EXPORT_SYMBOL(tty_port_tty_set);
-/**
- * tty_port_shutdown - internal helper to shutdown the device
- * @port: tty port to be shut down
- * @tty: the associated tty
- *
- * It is used by tty_port_hangup() and tty_port_close(). Its task is to
- * shutdown the device if it was initialized (note consoles remain
- * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes
- * @port->ops->shutdown().
- */
-static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
+static void tty_port_shutdown_locked(struct tty_port *port, struct tty_struct *tty)
{
- guard(mutex)(&port->mutex);
+ lockdep_assert_held(&port->mutex);
if (port->console)
return;
@@ -373,6 +363,23 @@ static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
}
/**
+ * tty_port_shutdown - internal helper to shutdown the device
+ * @port: tty port to be shut down
+ * @tty: the associated tty
+ *
+ * It is used by tty_port_hangup() and tty_port_close(). Its task is to
+ * shutdown the device if it was initialized (note consoles remain
+ * functioning). It lowers DTR/RTS (if @tty has HUPCL set) and invokes
+ * @port->ops->shutdown().
+ */
+static void tty_port_shutdown(struct tty_port *port, struct tty_struct *tty)
+{
+ guard(mutex)(&port->mutex);
+
+ tty_port_shutdown_locked(port, tty);
+}
+
+/**
* tty_port_hangup - hangup helper
* @port: tty port
*
@@ -385,36 +392,58 @@ void tty_port_hangup(struct tty_port *port)
{
struct tty_struct *tty;
- scoped_guard(spinlock_irqsave, &port->lock) {
- port->count = 0;
- tty = port->tty;
- if (tty)
- set_bit(TTY_IO_ERROR, &tty->flags);
- port->tty = NULL;
- }
+ scoped_guard(mutex, &port->mutex) {
+ scoped_guard(spinlock_irqsave, &port->lock) {
+ port->count = 0;
+ tty = port->tty;
+ if (tty)
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ port->tty = NULL;
+ }
- tty_port_set_active(port, false);
- tty_port_shutdown(port, tty);
+ tty_port_set_active(port, false);
+ tty_port_shutdown_locked(port, tty);
+ }
tty_kref_put(tty);
wake_up_interruptible(&port->open_wait);
wake_up_interruptible(&port->delta_msr_wait);
}
EXPORT_SYMBOL(tty_port_hangup);
-void __tty_port_tty_hangup(struct tty_port *port, bool check_clocal, bool async)
+/**
+ * tty_port_tty_hangup - helper to hang up a tty asynchronously
+ * @port: tty port
+ * @check_clocal: hang only ttys with %CLOCAL unset?
+ */
+void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
{
scoped_guard(tty_port_tty, port) {
struct tty_struct *tty = scoped_tty();
- if (!check_clocal || !C_CLOCAL(tty)) {
- if (async)
- tty_hangup(tty);
- else
- tty_vhangup(tty);
- }
+ if (!check_clocal || !C_CLOCAL(tty))
+ tty_hangup(tty);
+ }
+}
+EXPORT_SYMBOL_GPL(tty_port_tty_hangup);
+
+/**
+ * tty_port_tty_vhangup - helper to hang up a tty synchronously
+ * @port: tty port
+ */
+void tty_port_tty_vhangup(struct tty_port *port)
+{
+ struct tty_struct *tty;
+
+ mutex_lock(&port->mutex);
+ tty = tty_port_tty_get(port);
+ mutex_unlock(&port->mutex);
+
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
}
}
-EXPORT_SYMBOL_GPL(__tty_port_tty_hangup);
+EXPORT_SYMBOL_GPL(tty_port_tty_vhangup);
/**
* tty_port_tty_wakeup - helper to wake up a tty
@@ -767,8 +796,10 @@ int tty_port_open(struct tty_port *port, struct tty_struct *tty,
clear_bit(TTY_IO_ERROR, &tty->flags);
if (port->ops->activate) {
int retval = port->ops->activate(port, tty);
- if (retval)
+ if (retval) {
+ set_bit(TTY_IO_ERROR, &tty->flags);
return retval;
+ }
}
tty_port_set_initialized(port, true);
}
diff --git a/drivers/tty/vcc.c b/drivers/tty/vcc.c
index 27a55465bf5e..9adb533f7034 100644
--- a/drivers/tty/vcc.c
+++ b/drivers/tty/vcc.c
@@ -492,7 +492,7 @@ static ssize_t domain_show(struct device *dev,
static int vcc_send_ctl(struct vcc_port *port, int ctl)
{
- struct vio_vcc pkt;
+ struct vio_vcc pkt = {};
int rv;
pkt.tag.type = VIO_TYPE_CTRL;
@@ -986,7 +986,8 @@ static void vcc_cleanup(struct tty_struct *tty)
port = vcc_get(tty->index, true);
if (port) {
- port->tty = NULL;
+ scoped_guard(spinlock_irqsave, &port->lock)
+ port->tty = NULL;
if (port->removed) {
vcc_table_remove(tty->index);
diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c
index 13f4e48b4142..f1a3bc5b5fad 100644
--- a/drivers/tty/vt/selection.c
+++ b/drivers/tty/vt/selection.c
@@ -434,8 +434,8 @@ int paste_selection(struct tty_struct *tty)
bps = NULL;
}
- count = vc_sel.buf_len - pasted;
- if (count) {
+ if (vc_sel.buf_len > pasted) {
+ count = vc_sel.buf_len - pasted;
pasted += tty_ldisc_receive_buf(ld, vc_sel.buffer + pasted,
NULL, count);
if (vc_sel.buf_len > pasted)
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index bf1502fd5bd4..79453abcf4d9 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -631,6 +631,18 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
ret = copy_from_user(con_buf, buf, this_round);
console_lock();
+ /* The vc might have been freed or vcs_size might have changed
+ * while we slept to grab the user buffer; recheck here, before
+ * if (ret), so every break path below passes a fresh vc to the
+ * post-loop vcs_scr_updated(). Return data written so far.
+ */
+ vc = vcs_vc(inode, &viewed);
+ if (!vc) {
+ if (written)
+ break;
+ return -ENXIO;
+ }
+
if (ret) {
this_round -= ret;
if (!this_round) {
@@ -642,17 +654,6 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
return -EFAULT;
}
}
-
- /* The vc might have been freed or vcs_size might have changed
- * while we slept to grab the user buffer, so recheck.
- * Return data written up to now on failure.
- */
- vc = vcs_vc(inode, &viewed);
- if (!vc) {
- if (written)
- break;
- return -ENXIO;
- }
size = vcs_size(vc, attr, false);
if (size < 0) {
if (written)
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 57edf37495a8..49a140b49eff 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1134,6 +1134,8 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
return 0;
err_free:
visual_deinit(vc);
+ if (*vc->uni_pagedict_loc)
+ con_free_unimap(vc);
kfree(vc);
vc_cons[currcons].d = NULL;
return err;
@@ -2860,7 +2862,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
csi_J(vc, CSI_J_VISIBLE);
vc->vc_video_erase_char =
(vc->vc_video_erase_char & 0xff00) | ' ';
- do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
+ if (con_should_update(vc))
+ do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
}
return;
case ESsetG0: /* ESC ( */
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index 42311c1bfada..0d272e9c556c 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdns *cdns)
usb_del_gadget(&priv_dev->gadget);
devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
+ /* The works can still be queued until the IRQ is freed. */
+ cancel_work_sync(&priv_dev->pending_status_wq);
+ cancel_work_sync(&priv_dev->aligned_buf_wq);
+
cdns3_free_all_eps(priv_dev);
while (!list_empty(&priv_dev->aligned_buf_list)) {
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
index eb5760f75b9d..fd06a3d8e638 100644
--- a/drivers/usb/cdns3/cdns3-pci-wrap.c
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_dev *pdev,
if (pci_is_enabled(func)) {
wrap = pci_get_drvdata(func);
+ if (!wrap) {
+ dev_err(&pdev->dev,
+ "second function not initialized, retrying\n");
+ return -EPROBE_DEFER;
+ }
} else {
wrap = kzalloc_obj(*wrap);
if (!wrap)
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 372788f0f970..26225b034d3b 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
err = pm_runtime_resume_and_get(&pdev->dev);
- if (err)
+ if (err) {
+ pm_runtime_disable(&pdev->dev);
return err;
+ }
if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
usb->needs_double_reset = true;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 7bc5329fa3ed..0d3f815b2f1e 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -331,7 +331,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
spin_lock_irqsave(&acm->read_lock, flags);
acm->ctrlin = newctrl;
- acm->oldcount = acm->iocount;
if (difference & USB_CDC_SERIAL_STATE_DSR)
acm->iocount.dsr++;
@@ -788,6 +787,9 @@ static void acm_port_shutdown(struct tty_port *port)
usb_autopm_put_interface_async(acm->control);
}
+ if (acm->disconnected)
+ return;
+
acm_unpoison_urbs(acm);
if (acm->quirks & ALWAYS_POLL_CTRL) {
@@ -1027,11 +1029,16 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;
- do {
+ spin_lock_irq(&acm->read_lock);
+ old = acm->iocount;
+ spin_unlock_irq(&acm->read_lock);
+
+ add_wait_queue(&acm->wioctl, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
spin_lock_irq(&acm->read_lock);
- old = acm->oldcount;
new = acm->iocount;
- acm->oldcount = new;
spin_unlock_irq(&acm->read_lock);
if ((arg & TIOCM_DSR) &&
@@ -1044,22 +1051,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
old.rng != new.rng)
break;
- add_wait_queue(&acm->wioctl, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- remove_wait_queue(&acm->wioctl, &wait);
if (acm->disconnected) {
- if (arg & TIOCM_CD)
- break;
- else
- rv = -ENODEV;
- } else {
- if (signal_pending(current))
- rv = -ERESTARTSYS;
+ rv = -ENODEV;
+ break;
}
- } while (!rv);
-
+ schedule();
+
+ if (signal_pending(current)) {
+ rv = -ERESTARTSYS;
+ break;
+ }
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&acm->wioctl, &wait);
return rv;
}
@@ -2140,7 +2145,7 @@ static int __init acm_init(void)
{
int retval;
acm_tty_driver = tty_alloc_driver(ACM_TTY_MINORS, TTY_DRIVER_REAL_RAW |
- TTY_DRIVER_DYNAMIC_DEV);
+ TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_RESET_SAVED_TERMIOS);
if (IS_ERR(acm_tty_driver))
return PTR_ERR(acm_tty_driver);
acm_tty_driver->driver_name = "acm",
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 01f448a783c0..d245e60eb9f5 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -90,7 +90,6 @@ struct acm {
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
unsigned int ctrlout; /* output control lines (DTR, RTS) */
struct async_icount iocount; /* counters for control line changes */
- struct async_icount oldcount; /* for comparison of counter */
wait_queue_head_t wioctl; /* for ioctl */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3345b3298daf..b446905b48be 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -73,8 +73,11 @@
/*
* Give SS hubs 200ms time after wake to train downstream links before
* assuming no port activity and allowing hub to runtime suspend back.
+ * Root hubs have no upstream hub whose wake propagation needs to be
+ * accounted for, so they need less time, use 120ms for them.
*/
#define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */
+#define USB_SS_RH_PORT_U0_WAKE_TIME 120 /* ms */
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
@@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
queue_delayed_work(system_power_efficient_wq,
&hub->post_resume_work,
- msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
+ msecs_to_jiffies(hdev->parent ?
+ USB_SS_PORT_U0_WAKE_TIME :
+ USB_SS_RH_PORT_U0_WAKE_TIME));
return;
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 75e2bfd744a9..0cd6dd2b6334 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
*
* Disables the endpoint for URB submission and nukes all pending URBs.
* If @reset_hardware is set then also deallocates hcd/hardware state
- * for the endpoint.
+ * for the endpoint (clearing both ep_in and ep_out pointers for
+ * bidirectional non-ep0 control endpoints).
*/
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
bool reset_hardware)
@@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
dev->ep_in[epnum] = NULL;
}
if (ep) {
+ if (reset_hardware && epnum != 0 &&
+ usb_endpoint_xfer_control(&ep->desc)) {
+ dev->ep_out[epnum] = NULL;
+ dev->ep_in[epnum] = NULL;
+ }
ep->enabled = 0;
usb_hcd_flush_endpoint(dev, ep);
if (reset_hardware)
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 2414291aa908..cd0dc876b421 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
}
cancel_work_sync(&hsotg->phy_reset_work);
-
- timer_delete(&hsotg->wkp_timer);
}
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
{
/* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg);
+ timer_shutdown_sync(&hsotg->wkp_timer);
dwc2_hcd_free(hsotg);
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 608daeb7ef10..f3dabb37f806 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -49,6 +49,7 @@
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_ISOC_MAX_RETRIES 5
+#define DWC3_ERR_RECOVERY_MAX 3
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
#define DWC3_EVENT_BUFFERS_SIZE 4096
@@ -841,6 +842,12 @@ enum dwc3_link_state {
DWC3_LINK_STATE_MASK = 0x0f,
};
+enum dwc3_err_state {
+ DWC3_ERR_NONE = 0,
+ DWC3_ERR_RECOVERY,
+ DWC3_ERR_UNRECOVERABLE,
+};
+
/* TRB Length, PCM and Status */
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
@@ -1004,6 +1011,7 @@ struct dwc3_glue_ops {
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
+ * @err_recovery_work: workqueue used for controller error recovery
* @ep0_trb: trb which is used for the ctrl_req
* @bounce: address of bounce buffer
* @setup_buf: used while precessing STD USB requests
@@ -1013,6 +1021,7 @@ struct dwc3_glue_ops {
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
* @mutex: for mode switching
+ * @connect_mutex: for the pull-up and err_recovery_work
* @dev: pointer to our struct device
* @sysdev: pointer to the DMA-capable device
* @xhci: pointer to our xHCI child
@@ -1079,6 +1088,9 @@ struct dwc3_glue_ops {
* @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero
* @link_state: link state
+ * @err_state: current error recovery state.
+ * @err_recovery_count: number of consecutive error recovery attempts until
+ * confirmed healthy and reset to 0 on reset event.
* @speed: device speed (super, high, full, low)
* @hwparams: copy of hwparams registers
* @regset: debugfs pointer to regdump file
@@ -1189,6 +1201,7 @@ struct dwc3_glue_ops {
*/
struct dwc3 {
struct work_struct drd_work;
+ struct work_struct err_recovery_work;
struct dwc3_trb *ep0_trb;
void *bounce;
u8 *setup_buf;
@@ -1203,6 +1216,9 @@ struct dwc3 {
/* mode switching lock */
struct mutex mutex;
+ /* serializes pull-up run/stop vs error recovery */
+ struct mutex connect_mutex;
+
struct device *dev;
struct device *sysdev;
@@ -1332,6 +1348,9 @@ struct dwc3 {
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
enum dwc3_link_state link_state;
+ enum dwc3_err_state err_state;
+
+ u32 err_recovery_count;
u16 u2sel;
u16 u2pel;
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
index 632634d6e81e..d03b3950ce04 100644
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -299,6 +299,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
err_pm_disable:
clk_disable_unprepare(am62->usb2_refclk);
+ pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
return ret;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 310b5ffb236a..46a34c4a046b 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
@@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep0_set_halt(ep, value);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index f245e66cd13d..ee837235630a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1149,6 +1149,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return 0;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2060,6 +2064,10 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep_queue(dep, req);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2292,6 +2300,10 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2306,6 +2318,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
dep->flags |= DWC3_EP_WEDGE;
if (dep->number == 0 || dep->number == 1)
@@ -2437,6 +2453,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
}
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
if (!dwc->gadget->wakeup_armed) {
dev_err(dwc->dev, "not armed for remote wakeup\n");
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2464,6 +2484,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
}
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
/*
* If the link is in U3, signal for remote wakeup and wait for the
* link to transition to U0 before sending device notification.
@@ -2833,10 +2857,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
synchronize_irq(dwc->irq_gadget);
+ /* Serialize against dwc3_err_recovery_work() */
+ mutex_lock(&dwc->connect_mutex);
if (!is_on)
ret = dwc3_gadget_soft_disconnect(dwc);
else
ret = dwc3_gadget_soft_connect(dwc);
+ mutex_unlock(&dwc->connect_mutex);
pm_runtime_put(dwc->dev);
@@ -4153,6 +4180,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc->suspended = false;
+ /* The controller is recovered. */
+ if (dwc->err_state == DWC3_ERR_NONE)
+ dwc->err_recovery_count = 0;
+
/*
* Ideally, dwc3_reset_gadget() would trigger the function
* drivers to stop any active transfers through ep disable.
@@ -4640,6 +4671,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
return ret;
}
+static void dwc3_schedule_err_recovery(struct dwc3 *dwc)
+{
+ dwc->err_state = DWC3_ERR_RECOVERY;
+ schedule_work(&dwc->err_recovery_work);
+}
+
static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
{
struct dwc3 *dwc = evt->dwc;
@@ -4673,9 +4710,21 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
return IRQ_NONE;
if (count > evt->length) {
- dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
+ dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
count, evt->length);
- return IRQ_NONE;
+ /*
+ * This is a fatal error - the driver and controller are out of
+ * sync on which event has been consumed. Reinitializing the
+ * controller is required to recover. Write the bogus count back
+ * to GEVNTCOUNT to clear the IRQ source, consistent with the
+ * stale event clearing in dwc3_event_buffers_setup(), then
+ * schedule error recovery.
+ */
+ spin_lock(&dwc->lock);
+ dwc3_schedule_err_recovery(dwc);
+ spin_unlock(&dwc->lock);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count);
+ return IRQ_HANDLED;
}
evt->count = count;
@@ -4735,6 +4784,57 @@ static void dwc_gadget_release(struct device *dev)
kfree(gadget);
}
+static void dwc3_err_recovery_work(struct work_struct *work)
+{
+ struct dwc3 *dwc = container_of(work, struct dwc3, err_recovery_work);
+ unsigned long flags;
+ int ret;
+
+ /* serializes against dwc3_gadget_pullup() */
+ mutex_lock(&dwc->connect_mutex);
+
+ ret = dwc3_gadget_soft_disconnect(dwc);
+
+ dwc3_disconnect_gadget_sleepable(dwc);
+
+ if (ret)
+ goto err_unrecoverable;
+
+ if (dwc->softconnect) {
+ u32 count;
+ /*
+ * Wait irq to finish before soft_connect resets evt->lpos and
+ * the event buffer registers to avoid racing with
+ * dwc3_process_event_buf().
+ */
+ synchronize_irq(dwc->irq_gadget);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ count = ++dwc->err_recovery_count;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (count > DWC3_ERR_RECOVERY_MAX)
+ goto err_unrecoverable;
+
+ ret = dwc3_gadget_soft_connect(dwc);
+ if (ret)
+ goto err_unrecoverable;
+ }
+ mutex_unlock(&dwc->connect_mutex);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->err_state = DWC3_ERR_NONE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+
+err_unrecoverable:
+ dev_err(dwc->dev, "Unable to recover the controller\n");
+ mutex_unlock(&dwc->connect_mutex);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->err_state = DWC3_ERR_UNRECOVERABLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
/**
* dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -4778,6 +4878,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
init_completion(&dwc->ep0_in_setup);
+ INIT_WORK(&dwc->err_recovery_work, dwc3_err_recovery_work);
+ mutex_init(&dwc->connect_mutex);
dwc->gadget = kzalloc_obj(struct usb_gadget);
if (!dwc->gadget) {
ret = -ENOMEM;
@@ -4874,6 +4976,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
if (!dwc->gadget)
return;
+ cancel_work_sync(&dwc->err_recovery_work);
+ mutex_destroy(&dwc->connect_mutex);
dwc3_enable_susphy(dwc, true);
usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 43962e05eacf..c64a268e98a4 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1877,8 +1877,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
break;
case FUNCTIONFS_ENDPOINT_DESC:
{
+ const struct usb_endpoint_descriptor *desc;
+ struct usb_endpoint_descriptor desc1;
int desc_idx;
- struct usb_endpoint_descriptor desc1, *desc;
switch (epfile->ffs->gadget->speed) {
case USB_SPEED_SUPER:
@@ -1892,7 +1893,17 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
desc_idx = 0;
}
- desc = epfile->ep->descs[desc_idx];
+ do {
+ desc = epfile->ep->descs[desc_idx];
+ } while (!desc && --desc_idx >= 0);
+
+ if (!desc)
+ desc = epfile->ep->ep->desc;
+ if (!desc) {
+ ret = -EINVAL;
+ break;
+ }
+
memcpy(&desc1, desc, desc->bLength);
spin_unlock_irq(&epfile->ffs->eps_lock);
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index 5b8b18281989..d693de427641 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -1633,8 +1633,8 @@ struct f_midi2_usb_config {
/* MIDI 1.0 jacks */
unsigned char jack_in, jack_out, jack_id;
- struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
- struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
+ struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES * 2];
+ struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES * 2];
};
static int append_config(struct f_midi2_usb_config *config, void *d)
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index 3f52099a4fdd..7c8d60c7e87d 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -797,6 +797,9 @@ fail:
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
{
+ if (cmd >= ARRAY_SIZE(con->data))
+ return -EINVAL;
+
con->data[cmd] = value;
return 0;
@@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
{
+ if (cmd >= ARRAY_SIZE(con->data))
+ return -EINVAL;
+
return con->data[cmd];
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
index 4a8b9ff8368f..069673f0d359 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platform_device *pdev)
for (i = 0; i < vhub->max_ports; i++)
ast_vhub_del_dev(&vhub->ports[i].dev);
+ /* Final drain; the worker takes vhub->lock, so stay outside of it */
+ cancel_work_sync(&vhub->wake_work);
+
spin_lock_irqsave(&vhub->lock, flags);
/* Mask & ack all interrupts */
@@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
return -ENOMEM;
spin_lock_init(&vhub->lock);
+ INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
vhub->pdev = pdev;
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
VHUB_IRQ_DEV1_BIT);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
index 8b9449d16324..4b389de0d935 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usb_gadget* gadget)
int rc = -EINVAL;
spin_lock_irqsave(&d->vhub->lock, flags);
- if (!d->wakeup_en)
+ if (!d->wakeup_en || !d->registered)
goto err;
DDBG(d, "Device initiated wakeup\n");
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
index 02fe1a08d575..d0345f31037a 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_vhub *vhub,
ast_vhub_send_host_wakeup(vhub);
}
-static void ast_vhub_wake_work(struct work_struct *work)
+void ast_vhub_wake_work(struct work_struct *work)
{
struct ast_vhub *vhub = container_of(work,
struct ast_vhub,
@@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct work_struct *work)
void ast_vhub_hub_wake_all(struct ast_vhub *vhub)
{
+ lockdep_assert_held(&vhub->lock);
+
/*
* A device is trying to wake the world, because this
* can recurse into the device, we break the call chain
@@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub)
int ast_vhub_init_hub(struct ast_vhub *vhub)
{
vhub->speed = USB_SPEED_UNKNOWN;
- INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
return ast_vhub_init_desc(vhub);
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
index aca2050e2db0..96f1c3709042 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub);
void ast_vhub_hub_resume(struct ast_vhub *vhub);
void ast_vhub_hub_reset(struct ast_vhub *vhub);
void ast_vhub_hub_wake_all(struct ast_vhub *vhub);
+void ast_vhub_wake_work(struct work_struct *work);
/* dev.c */
int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index c0e40fa6dde5..0d30dd67b9ca 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
{
bool fifo = req == &dum->fifo_req;
+ ++dum->callback_usage;
spin_unlock(&dum->lock);
usb_gadget_giveback_request(_ep, &req->req);
spin_lock(&dum->lock);
+ --dum->callback_usage;
if (fifo)
dum->fifo_req_busy = 0;
}
@@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
req->req.complete = fifo_complete;
list_add_tail(&req->queue, &ep->queue);
+ ++dum->callback_usage;
spin_unlock(&dum->lock);
_req->actual = _req->length;
_req->status = 0;
usb_gadget_giveback_request(_ep, _req);
spin_lock(&dum->lock);
+ --dum->callback_usage;
} else
list_add_tail(&req->queue, &ep->queue);
spin_unlock_irqrestore(&dum->lock, flags);
diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c
index 34ce771fa0f5..256e459ff6d8 100644
--- a/drivers/usb/host/octeon-hcd.c
+++ b/drivers/usb/host/octeon-hcd.c
@@ -378,8 +378,22 @@ struct octeon_hcd {
struct cvmx_usb_transaction *active_split;
struct cvmx_usb_tx_fifo periodic;
struct cvmx_usb_tx_fifo nonperiodic;
+ struct hrtimer sof_timer;
};
+static int cvmx_usb_poll(struct octeon_hcd *usb);
+
+static enum hrtimer_restart octeon_usb_sof_timer(struct hrtimer *t)
+{
+ struct octeon_hcd *usb = container_of(t, struct octeon_hcd, sof_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb->lock, flags);
+ cvmx_usb_poll(usb);
+ spin_unlock_irqrestore(&usb->lock, flags);
+ return HRTIMER_NORESTART;
+}
+
/*
* This macro logically sets a single field in a CSR. It does the sequence
* read, modify, and write
@@ -578,7 +592,7 @@ static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type)
int result;
u64 address = CVMX_USBCX_GRSTCTL(usb->index);
u64 done = cvmx_get_cycle() + 100 *
- (u64)octeon_get_clock_rate / 1000000;
+ (u64)octeon_get_clock_rate() / 1000000;
union cvmx_usbcx_grstctl c;
while (1) {
@@ -853,6 +867,14 @@ retry:
* USBC_GAHBCFG[PTXFEMPLVL]
* Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1
*/
+ usbcx_gahbcfg.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GHWCFG3(usb->index));
+ if (usbcx_gahbcfg.u32 == 0xffffffff || usbcx_gahbcfg.u32 == 0) {
+ dev_err(dev, "USB core is not responding (GHWCFG3=0x%08x)\n",
+ usbcx_gahbcfg.u32);
+ return -ENODEV;
+ }
+
usbcx_gahbcfg.u32 = 0;
usbcx_gahbcfg.s.dmaen = !(usb->init_flags &
CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
@@ -1900,6 +1922,7 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
int channel;
struct cvmx_usb_pipe *pipe;
int need_sof;
+ u64 min_due;
enum cvmx_usb_transfer ttype;
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
@@ -1940,15 +1963,33 @@ done:
* future that might need to be scheduled
*/
need_sof = 0;
+ min_due = ~0ull;
for (ttype = CVMX_USB_TRANSFER_CONTROL;
ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) {
list_for_each_entry(pipe, &usb->active_pipes[ttype], node) {
- if (pipe->next_tx_frame > usb->frame_number) {
- need_sof = 1;
- break;
- }
+ if (pipe->next_tx_frame > usb->frame_number &&
+ pipe->next_tx_frame < min_due)
+ min_due = pipe->next_tx_frame;
}
}
+ if (min_due != ~0ull) {
+ u64 delta = min_due - usb->frame_number;
+
+ /*
+ * frame_number is resynced from HFNUM on every poll, so a
+ * deadline that is many frames away does not need an
+ * interrupt on every SOF to count them down - sleep on the
+ * timer instead and keep SOF interrupts for deadlines within
+ * a few frames. Stay well below the 16383-frame wrap of
+ * HFNUM. One (micro)frame is 125us in high-speed mode.
+ */
+ if (delta <= 4 || delta > 8000)
+ need_sof = 1;
+ else
+ hrtimer_start(&usb->sof_timer,
+ ns_to_ktime((delta - 2) * 125000),
+ HRTIMER_MODE_REL);
+ }
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
cvmx_usbcx_gintmsk, sofmsk, need_sof);
}
@@ -3638,6 +3679,8 @@ static int octeon_usb_probe(struct platform_device *pdev)
usb = (struct octeon_hcd *)hcd->hcd_priv;
spin_lock_init(&usb->lock);
+ hrtimer_setup(&usb->sof_timer, octeon_usb_sof_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
usb->init_flags = initialize_flags;
@@ -3688,6 +3731,7 @@ static void octeon_usb_remove(struct platform_device *pdev)
unsigned long flags;
usb_remove_hcd(hcd);
+ hrtimer_cancel(&usb->sof_timer);
spin_lock_irqsave(&usb->lock, flags);
status = cvmx_usb_shutdown(usb);
spin_unlock_irqrestore(&usb->lock, flags);
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
index 0938c0e7a8b6..a7cd5fb4f119 100644
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
err_remove_hcd:
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
err:
usb_put_hcd(hcd);
return error;
@@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
usb_put_hcd(hcd);
}
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index e623e24d3f8e..7c0e35be58f1 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev)
struct usb_hcd *hcd = platform_get_drvdata(dev);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
s3c2410_stop_hc(dev);
usb_put_hcd(hcd);
}
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
index 6843d7cb3f9f..5aeabb051265 100644
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev)
struct spear_ohci *sohci_p = to_spear_ohci(hcd);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
clk_disable_unprepare(sohci_p->clk);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c
index d1656fce5400..53010a277b67 100644
--- a/drivers/usb/host/ohci-st.c
+++ b/drivers/usb/host/ohci-st.c
@@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struct platform_device *dev)
int clk;
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
if (pdata->power_off)
pdata->power_off(dev);
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 9e2a18c0b218..ea1fe5d1e449 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -165,7 +165,11 @@ int usb_serial_bus_register(struct usb_serial_driver *driver)
void usb_serial_bus_deregister(struct usb_serial_driver *driver)
{
- free_dynids(driver);
driver_unregister(&driver->driver);
+ free_dynids(driver);
}
+void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver)
+{
+ driver_remove_file(&driver->driver, &driver_attr_new_id);
+}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 333b8b463704..c3f9add98333 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
+ { USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */
{ USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */
{ USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */
{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 6eaf74930aa3..17272701fab0 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
unsigned int bps;
unsigned long period;
unsigned long expire;
@@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
__func__, jiffies_to_msecs(timeout),
jiffies_to_msecs(period));
expire = jiffies + timeout;
- while (!port->serial->type->tx_empty(port)) {
+ for (;;) {
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty) || port->serial->type->tx_empty(port)) {
+ mutex_unlock(&tport->mutex);
+ break;
+ }
+ mutex_unlock(&tport->mutex);
+
schedule_timeout_interruptible(period);
if (signal_pending(current))
break;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index e4ad14375d6a..828b1be3fee2 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -260,6 +260,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EM060K_12a 0x012a
#define QUECTEL_PRODUCT_EM060K_12b 0x012b
#define QUECTEL_PRODUCT_EM060K_12c 0x012c
+#define QUECTEL_PRODUCT_RG660QB 0x013d
#define QUECTEL_PRODUCT_EG91 0x0191
#define QUECTEL_PRODUCT_EG95 0x0195
#define QUECTEL_PRODUCT_BG96 0x0296
@@ -280,6 +281,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EC200U 0x0901
#define QUECTEL_PRODUCT_EG912Y 0x6001
#define QUECTEL_PRODUCT_EC200S_CN 0x6002
+#define QUECTEL_PRODUCT_EG060W 0x6004
#define QUECTEL_PRODUCT_EC200A 0x6005
#define QUECTEL_PRODUCT_EG916Q 0x6007
#define QUECTEL_PRODUCT_EM061K_LWW 0x6008
@@ -1268,12 +1270,15 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) },
@@ -2168,6 +2173,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */
.driver_info = RSVD(7) },
+ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff),
+ .driver_info = RSVD(4) },
{ USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */
.driver_info = RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */
@@ -2429,6 +2436,13 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */
.driver_info = RSVD(3) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) }, /* Compal EXC-T1 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index dc2b39810a26..3b4df3ac7c73 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -52,6 +52,7 @@
#define QT2_XMIT_FLUSH 0x05 /* no following info */
#define QT2_CONTROL_ESCAPE 0xff /* pass through previous 2 control bytes */
+#define MIN_BAUD_RATE 50
#define MAX_BAUD_RATE 921600
#define DEFAULT_BAUD_RATE 9600
@@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int baudrate)
static inline int qt2_set_port_config(struct usb_device *dev,
unsigned char port_number,
- u16 baudrate, u16 lcr)
+ speed_t baudrate, u16 lcr)
{
int divisor = calc_baud_divisor(baudrate);
u16 index = ((u16) (lcr << 8) | (u16) (port_number));
@@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_struct *tty,
struct usb_device *dev = port->serial->dev;
struct qt2_port_private *port_priv;
struct ktermios *termios = &tty->termios;
- u16 baud;
unsigned int cflag = termios->c_cflag;
u16 new_lcr = 0;
+ speed_t baud;
int status;
port_priv = usb_get_serial_port_data(port);
@@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_struct *tty,
if (!baud)
baud = 9600;
+ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
+ if (old_termios)
+ baud = tty_termios_baud_rate(old_termios);
+ else
+ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
+
+ tty_encode_baud_rate(tty, baud, baud);
+
+ if (!baud)
+ baud = 9600;
+ }
+
status = qt2_set_port_config(dev, port_priv->device_port, baud,
new_lcr);
if (status < 0)
@@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
port_priv->device_port = (u8) device_port;
if (tty)
- qt2_set_termios(tty, port, &tty->termios);
+ qt2_set_termios(tty, port, NULL);
return 0;
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index b0d51558b73c..8c5b9ef708b3 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -32,6 +32,7 @@
#define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR)
+#define MIN_BAUD_RATE 50
#define MAX_BAUD_RATE 460800
#define ATC_DISABLED 0x00
@@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tty_struct *tty,
{
struct usb_device *dev = port->serial->dev;
struct ktermios *termios = &tty->termios;
- u16 baud, divisor, remainder;
+ speed_t baud, remainder;
unsigned int cflag = termios->c_cflag;
u16 urb_value = 0; /* will hold the new flags */
+ u16 divisor;
int result;
if (cflag & PARENB) {
@@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tty_struct *tty,
if (!baud)
baud = 9600;
+ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
+ if (old_termios)
+ baud = tty_termios_baud_rate(old_termios);
+ else
+ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
+
+ tty_encode_baud_rate(tty, baud, baud);
+
+ if (!baud)
+ baud = 9600;
+ }
+
dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
@@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
if (tty)
- ssu100_set_termios(tty, port, &tty->termios);
+ ssu100_set_termios(tty, port, NULL);
return usb_serial_generic_open(tty, port);
}
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 17edc057a311..4a19621f08ba 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -400,17 +400,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty)
static void serial_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct usb_serial_port *port = tty->driver_data;
- struct usb_serial *serial = port->serial;
dev_dbg(&port->dev, "%s\n", __func__);
if (!port->serial->type->wait_until_sent)
return;
- mutex_lock(&serial->disc_mutex);
- if (!serial->disconnected)
- port->serial->type->wait_until_sent(tty, timeout);
- mutex_unlock(&serial->disc_mutex);
+ port->serial->type->wait_until_sent(tty, timeout);
}
static void serial_throttle(struct tty_struct *tty)
@@ -438,8 +434,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
struct usb_serial_port *port = tty->driver_data;
struct tty_port *tport = &port->port;
unsigned int close_delay, closing_wait;
+ int ret = 0;
mutex_lock(&tport->mutex);
+ if (tty_io_error(tty)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
close_delay = jiffies_to_msecs(tport->close_delay) / 10;
closing_wait = tport->closing_wait;
@@ -452,10 +453,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
if (port->serial->type->get_serial)
port->serial->type->get_serial(tty, ss);
-
+out_unlock:
mutex_unlock(&tport->mutex);
- return 0;
+ return ret;
}
static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
@@ -471,6 +472,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
closing_wait = msecs_to_jiffies(closing_wait * 10);
mutex_lock(&tport->mutex);
+ if (tty_io_error(tty)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
if (!capable(CAP_SYS_ADMIN)) {
if (close_delay != tport->close_delay ||
@@ -498,6 +503,7 @@ static int serial_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
int retval = -ENOIOCTLCMD;
dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd);
@@ -508,8 +514,21 @@ static int serial_ioctl(struct tty_struct *tty,
retval = port->serial->type->tiocmiwait(tty, arg);
break;
default:
- if (port->serial->type->ioctl)
+ if (!port->serial->type->ioctl)
+ break;
+
+ if (cmd == TIOCSRS485)
+ down_write(&tty->termios_rwsem);
+
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ retval = -EIO;
+ else
retval = port->serial->type->ioctl(tty, cmd, arg);
+ mutex_unlock(&tport->mutex);
+
+ if (cmd == TIOCSRS485)
+ up_write(&tty->termios_rwsem);
}
return retval;
@@ -519,25 +538,40 @@ static void serial_set_termios(struct tty_struct *tty,
const struct ktermios *old)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->set_termios)
- port->serial->type->set_termios(tty, port, old);
- else
+ if (!port->serial->type->set_termios) {
tty_termios_copy_hw(&tty->termios, old);
+ return;
+ }
+
+ mutex_lock(&tport->mutex);
+ if (!tty_io_error(tty))
+ port->serial->type->set_termios(tty, port, old);
+ mutex_unlock(&tport->mutex);
}
static int serial_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
+ int ret;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->break_ctl)
- return port->serial->type->break_ctl(tty, break_state);
+ if (!port->serial->type->break_ctl)
+ return -ENOTTY;
- return -ENOTTY;
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ ret = -EIO;
+ else
+ ret = port->serial->type->break_ctl(tty, break_state);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
}
static int serial_proc_show(struct seq_file *m, void *v)
@@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v)
static int serial_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
+ int ret;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->tiocmget)
- return port->serial->type->tiocmget(tty);
- return -ENOTTY;
+ if (!port->serial->type->tiocmget)
+ return -ENOTTY;
+
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ ret = -EIO;
+ else
+ ret = port->serial->type->tiocmget(tty);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
}
static int serial_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
+ int ret;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->tiocmset)
- return port->serial->type->tiocmset(tty, set, clear);
- return -ENOTTY;
+ if (!port->serial->type->tiocmset)
+ return -ENOTTY;
+
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ ret = -EIO;
+ else
+ ret = port->serial->type->tiocmset(tty, set, clear);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
}
static int serial_get_icount(struct tty_struct *tty,
@@ -1191,12 +1245,17 @@ static void usb_serial_disconnect(struct usb_interface *interface)
usb_serial_port_poison_urbs(port);
wake_up_interruptible(&port->port.delta_msr_wait);
cancel_work_sync(&port->work);
- if (device_is_registered(&port->dev))
- device_del(&port->dev);
}
+
if (serial->type->disconnect)
serial->type->disconnect(serial);
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = serial->port[i];
+ if (device_is_registered(&port->dev))
+ device_del(&port->dev);
+ }
+
release_sibling(serial, interface);
/* let the last holder of this object cause it to be cleaned up */
@@ -1313,7 +1372,8 @@ static int __init usb_serial_init(void)
int result;
usb_serial_tty_driver = tty_alloc_driver(USB_SERIAL_TTY_MINORS,
- TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
+ TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_RESET_SAVED_TERMIOS);
if (IS_ERR(usb_serial_tty_driver))
return PTR_ERR(usb_serial_tty_driver);
@@ -1464,7 +1524,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
{
int rc;
struct usb_driver *udriver;
- struct usb_serial_driver * const *sd;
+ struct usb_serial_driver * const *sd, * const *s;
/*
* udriver must be registered before any of the serial drivers,
@@ -1517,9 +1577,11 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
return 0;
err_deregister_drivers:
+ for (s = serial_drivers; s < sd; ++s)
+ usb_serial_bus_remove_new_id(*s);
+ usb_deregister(udriver);
while (sd-- > serial_drivers)
usb_serial_deregister(*sd);
- usb_deregister(udriver);
err_free_driver:
kfree(udriver);
return rc;
@@ -1537,10 +1599,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_drivers);
void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[])
{
struct usb_driver *udriver = (*serial_drivers)->usb_driver;
+ struct usb_serial_driver * const *sd;
+
+ /*
+ * udriver must be deregistered before the serial drivers so that
+ * I/O is stopped before unbinding the ports.
+ *
+ * Remove the new_id attributes to prevent ids from being added and
+ * triggering a probe of udriver after it has been deregistered.
+ */
+ for (sd = serial_drivers; *sd; ++sd)
+ usb_serial_bus_remove_new_id(*sd);
- for (; *serial_drivers; ++serial_drivers)
- usb_serial_deregister(*serial_drivers);
usb_deregister(udriver);
+
+ for (sd = serial_drivers; *sd; ++sd)
+ usb_serial_deregister(*sd);
+
kfree(udriver);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c
index 352c765d8803..c08f4aa14a3d 100644
--- a/drivers/usb/serial/xr_serial.c
+++ b/drivers/usb/serial/xr_serial.c
@@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct xr_data *data = usb_get_serial_port_data(port);
- down_read(&tty->termios_rwsem);
- if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
- up_read(&tty->termios_rwsem);
+ /* core holds port mutex */
+ if (copy_to_user(argp, &data->rs485, sizeof(data->rs485)))
return -EFAULT;
- }
- up_read(&tty->termios_rwsem);
return 0;
}
@@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tty_struct *tty,
return -EFAULT;
xr_sanitize_serial_rs485(&rs485);
- down_write(&tty->termios_rwsem);
+ /* core holds termios rwsem and port mutex */
data->rs485 = rs485;
xr_set_flow_mode(tty, port, NULL);
- up_write(&tty->termios_rwsem);
if (copy_to_user(argp, &rs485, sizeof(rs485)))
return -EFAULT;
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
index c5e3eeeb2144..992685ca6f1a 100644
--- a/drivers/usb/storage/sierra_ms.c
+++ b/drivers/usb/storage/sierra_ms.c
@@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usb_device *udev,
sizeof(struct swoc_info), /* __u16 size */
USB_CTRL_SET_TIMEOUT); /* int timeout */
+ /*
+ * A short IN transfer leaves the tail of swocInfo uninitialized;
+ * only a full transfer is valid.
+ */
+ if (result != sizeof(struct swoc_info))
+ return -EIO;
+
swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU);
swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer);
return result;
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index 41df115912b9..c3d36da692e4 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2c_client *client)
anx7411_partner_unregister_altmode(plat);
anx7411_unregister_partner(plat);
- if (plat->workqueue)
+ if (plat->workqueue) {
+ disable_irq(plat->intp_irq);
+ cancel_work_sync(&plat->work);
destroy_workqueue(plat->workqueue);
+ }
i2c_unregister_device(plat->spi_client);
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
index cdbb7c11d714..3a97b084c3bd 100644
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -42,6 +42,23 @@ static int usb4_port_compare(struct device *dev, void *fwnode)
return usb4_usb3_port_match(dev, fwnode);
}
+static bool typec_has_usb4_host_interface(const struct fwnode_handle *fwnode)
+{
+ if (!IS_REACHABLE(CONFIG_USB4))
+ return false;
+
+ struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(fwnode, "usb4-host-interface", 0);
+
+ /*
+ * The USB4 port can only appear if the host interface is enabled in
+ * the firmware and has been enumerated as a device. The latter is
+ * the same check as in usb_acpi_add_usb4_devlink().
+ */
+ return !IS_ERR(nhi_fwnode) && fwnode_device_is_available(nhi_fwnode) &&
+ nhi_fwnode->dev;
+}
+
static int typec_port_compare(struct device *dev, void *fwnode)
{
return device_match_fwnode(dev, fwnode);
@@ -64,11 +81,11 @@ static int typec_port_match(struct device *dev, void *data)
adev_fwnode);
/*
- * If dev is USB 3.x port, it may have reference to the
+ * If dev is USB 3.x port, it may have reference to an available
* USB4 host interface in which case we can also link the
* Type-C port with the USB4 port.
*/
- if (fwnode_property_present(adev_fwnode, "usb4-host-interface"))
+ if (typec_has_usb4_host_interface(adev_fwnode))
component_match_add(&arg->port->dev, &arg->match,
usb4_port_compare, adev_fwnode);
}
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 2d6b14aa2085..06692feb6d46 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1756,7 +1756,8 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del
static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port)
{
hrtimer_cancel(&port->vdm_discovery_timer);
- kthread_cancel_work_sync(&port->vdm_discovery_work);
+ if (port->wq)
+ kthread_cancel_work_sync(&port->vdm_discovery_work);
}
static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
@@ -7113,7 +7114,7 @@ static void tcpm_pd_event_handler(struct kthread_work *work)
port->upcoming_state = FR_SWAP_SEND;
ret = tcpm_ams_start(port, FAST_ROLE_SWAP);
if (ret == -EAGAIN)
- port->upcoming_state = INVALID_STATE;
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
} else {
tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
}
@@ -8977,6 +8978,7 @@ void tcpm_unregister_port(struct tcpm_port *port)
port->registered = false;
kthread_destroy_worker(port->wq);
+ port->wq = NULL;
hrtimer_cancel(&port->vdm_discovery_timer);
hrtimer_cancel(&port->enable_frs_timer);
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index f76f563dc42b..483539185177 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -343,7 +343,7 @@ static void tps6598x_set_data_role(struct tps6598x *tps,
static int tps6598x_connect(struct tps6598x *tps, u32 status)
{
- struct typec_partner_desc desc;
+ struct typec_partner_desc desc = { };
enum typec_pwr_opmode mode;
int ret;
@@ -354,7 +354,6 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
- desc.identity = NULL;
if (desc.usb_pd) {
ret = tps6598x_read_partner_identity(tps);
@@ -850,11 +849,10 @@ static void cd321x_update_work(struct work_struct *work)
/* Set up partner if we were previously disconnected (or changed). */
if (!tps->partner) {
- struct typec_partner_desc desc;
+ struct typec_partner_desc desc = { };
desc.usb_pd = is_pd;
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
- desc.identity = NULL;
if (desc.usb_pd)
desc.identity = &st.partner_identity;
@@ -1792,6 +1790,7 @@ static int tps6598x_probe(struct i2c_client *client)
const struct tipd_data *data;
struct tps6598x *tps;
struct fwnode_handle *fwnode;
+ bool patch_loaded = false;
u32 status;
u32 vid = 0;
int ret;
@@ -1847,6 +1846,7 @@ static int tps6598x_probe(struct i2c_client *client)
return ret;
if (ret == TPS_MODE_PTCH) {
+ patch_loaded = true;
ret = tps->data->init(tps);
if (ret)
return ret;
@@ -1937,7 +1937,8 @@ err_clear_mask:
tps6598x_write64(tps, TPS_REG_INT_MASK1, 0);
err_reset_controller:
/* Reset PD controller to remove any applied patch */
- tps->data->reset(tps);
+ if (patch_loaded)
+ tps->data->reset(tps);
return ret;
}
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 8d2032d0762c..572da7bbd29c 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -71,11 +71,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
if (ret < 0) {
if (ucsi->version > 0x0100)
goto err_unlock;
- cur = 0xff;
}
- if (cur < UCSI_MAX_ALTMODES) {
- ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
+ if (cur != 0xff) {
+ if (cur < UCSI_MAX_ALTMODES)
+ ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
+ else
+ ret = -EINVAL;
+
goto err_unlock;
}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index bef3f9b71d71..c1450639c795 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
{
struct fwnode_handle *fwnode;
int i = 1;
+ int ret;
+ u32 port;
- device_for_each_child_node(con->ucsi->dev, fwnode)
- if (i++ == con->num)
- return fwnode;
+ device_for_each_child_node(con->ucsi->dev, fwnode) {
+ ret = fwnode_property_read_u32(fwnode, "reg", &port);
+ if (ret < 0) {
+ if (i == con->num)
+ return fwnode;
+ } else {
+ if (port == con->num - 1)
+ return fwnode;
+ }
+
+ i++;
+ }
return NULL;
}
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index 18286d3e9cc5..5697ccc24ec3 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi_acpi_ops = {
.async_control = ucsi_acpi_async_control
};
+static int ucsi_acer_read_version(struct ucsi *ucsi, u16 *version)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
+
+ ret = ucsi_acpi_read_version(ucsi, version);
+ if (ret)
+ return ret;
+
+ if (!*version) {
+ dev_warn(ua->dev, "UCSI version is zero, assuming 1.2\n");
+ *version = UCSI_VERSION_1_2;
+ }
+
+ return 0;
+}
+
+static const struct ucsi_operations ucsi_acer_ops = {
+ .read_version = ucsi_acer_read_version,
+ .read_cci = ucsi_acpi_read_cci,
+ .poll_cci = ucsi_acpi_poll_cci,
+ .read_message_in = ucsi_acpi_read_message_in,
+ .write_message_out = ucsi_acpi_write_message_out,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_acpi_async_control
+};
+
static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
void *val, size_t len, void *msg_out,
size_t msg_out_size)
@@ -164,6 +191,13 @@ static const struct ucsi_operations ucsi_gram_ops = {
static const struct dmi_system_id ucsi_acpi_quirks[] = {
{
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
+ },
+ .driver_data = (void *)&ucsi_acer_ops,
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
DMI_MATCH(DMI_PRODUCT_FAMILY, "LG gram PC"),
DMI_MATCH(DMI_PRODUCT_NAME, "90Q"),
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index 12e07b9fe622..1db0c88d817e 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void *data)
{
struct pmic_glink_ucsi *ucsi = data;
+ /* Drain the work items before ucsi_destroy() frees the ucsi instance */
+ cancel_work_sync(&ucsi->notify_work);
+ cancel_work_sync(&ucsi->register_work);
+
/* Protect to make sure we're not in a middle of a transaction from a glink callback */
mutex_lock(&ucsi->lock);
ucsi_destroy(ucsi->ucsi);
@@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struct auxiliary_device *adev)
{
struct pmic_glink_ucsi *ucsi = dev_get_drvdata(&adev->dev);
- /* Unregister first to stop having read & writes */
- ucsi_unregister(ucsi->ucsi);
+ /* Callbacks can queue work until devres releases the client */
+ disable_work_sync(&ucsi->notify_work);
+ disable_work_sync(&ucsi->register_work);
+
+ /* register_work may have unregistered the instance already */
+ if (ucsi->ucsi_registered) {
+ ucsi->ucsi_registered = false;
+ ucsi_unregister(ucsi->ucsi);
+ }
}
static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = {
diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c
index c91300a73f50..0d7bdad924b7 100644
--- a/drivers/virt/nitro_enclaves/ne_misc_dev.c
+++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c
@@ -1706,6 +1706,7 @@ static int ne_create_vm_ioctl(struct ne_pci_dev *ne_pci_dev, u64 __user *slot_ui
return enclave_fd;
put_file:
+ enclave_file->private_data = NULL;
fput(enclave_file);
put_fd:
put_unused_fd(enclave_fd);
diff --git a/drivers/virt/vmgenid.c b/drivers/virt/vmgenid.c
index 66135eac3abf..ce957f5b1c31 100644
--- a/drivers/virt/vmgenid.c
+++ b/drivers/virt/vmgenid.c
@@ -25,7 +25,7 @@ struct vmgenid_state {
static void vmgenid_notify(struct device *device)
{
- struct vmgenid_state *state = device->driver_data;
+ struct vmgenid_state *state = dev_get_drvdata(device);
u8 old_id[VMGENID_SIZE];
memcpy(old_id, state->this_id, sizeof(old_id));
@@ -75,13 +75,16 @@ static int vmgenid_add_acpi(struct device *dev, struct vmgenid_state *state)
phys_addr = (obj->package.elements[0].integer.value << 0) |
(obj->package.elements[1].integer.value << 32);
- virt_addr = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE, MEMREMAP_WB);
+ virt_addr = devm_memremap(&device->dev, phys_addr, VMGENID_SIZE,
+ MEMREMAP_WB | MEMREMAP_DEC);
if (IS_ERR(virt_addr)) {
ret = PTR_ERR(virt_addr);
goto out;
}
setup_vmgenid_state(state, virt_addr);
+ dev_set_drvdata(dev, state);
+
status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
vmgenid_acpi_handler, dev);
if (ACPI_FAILURE(status)) {
@@ -89,7 +92,6 @@ static int vmgenid_add_acpi(struct device *dev, struct vmgenid_state *state)
goto out;
}
- dev->driver_data = state;
out:
ACPI_FREE(parsed.pointer);
return ret;
@@ -123,12 +125,13 @@ static int vmgenid_add_of(struct platform_device *pdev,
if (ret < 0)
return ret;
+ dev_set_drvdata(&pdev->dev, state);
+
ret = devm_request_irq(&pdev->dev, ret, vmgenid_of_irq_handler,
IRQF_SHARED, "vmgenid", &pdev->dev);
if (ret < 0)
return ret;
- pdev->dev.driver_data = state;
return 0;
}
diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
index 105194de6e13..e6506942aeda 100644
--- a/fs/netfs/buffered_read.c
+++ b/fs/netfs/buffered_read.c
@@ -541,6 +541,13 @@ static int netfs_read_gaps(struct file *file, struct folio *folio)
ret = netfs_wait_for_read(rreq);
if (ret >= 0) {
+ if (ret < flen) {
+ struct iov_iter iter;
+
+ iov_iter_bvec(&iter, ITER_DEST, bvec, i, flen);
+ iov_iter_advance(&iter, ret);
+ iov_iter_zero(flen - ret, &iter);
+ }
if (group)
folio_change_private(folio, group);
else
diff --git a/fs/netfs/buffered_write.c b/fs/netfs/buffered_write.c
index 2cdb68e6b16f..ecf119b4f916 100644
--- a/fs/netfs/buffered_write.c
+++ b/fs/netfs/buffered_write.c
@@ -469,6 +469,7 @@ ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *fr
struct netfs_group *netfs_group)
{
struct file *file = iocb->ki_filp;
+ struct inode *inode = file_inode(file);
ssize_t ret;
trace_netfs_write_iter(iocb, from);
@@ -481,6 +482,14 @@ ssize_t netfs_buffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *fr
if (ret)
return ret;
+ if (iocb->ki_pos > i_size_read(inode)) {
+ ret = netfs_clear_stale_pre_isize(inode, i_size_read(inode),
+ iocb->ki_pos,
+ iocb->ki_flags & IOCB_NOWAIT);
+ if (ret)
+ return ret;
+ }
+
return netfs_perform_write(iocb, from, netfs_group);
}
EXPORT_SYMBOL(netfs_buffered_write_iter_locked);
diff --git a/fs/netfs/direct_write.c b/fs/netfs/direct_write.c
index 2361277416c7..8be74706d85e 100644
--- a/fs/netfs/direct_write.c
+++ b/fs/netfs/direct_write.c
@@ -359,6 +359,15 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from)
ret = file_update_time(file);
if (ret < 0)
goto out;
+
+ if (iocb->ki_pos > i_size_read(inode)) {
+ ret = netfs_clear_stale_pre_isize(inode, i_size_read(inode),
+ iocb->ki_pos,
+ iocb->ki_flags & IOCB_NOWAIT);
+ if (ret < 0)
+ goto out;
+ }
+
if (iocb->ki_flags & IOCB_NOWAIT) {
/* We could block if there are any pages in the range. */
ret = -EAGAIN;
diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
index c79c8e69d60c..786af76da98c 100644
--- a/fs/netfs/internal.h
+++ b/fs/netfs/internal.h
@@ -80,6 +80,8 @@ ssize_t netfs_wait_for_write(struct netfs_io_request *rreq);
void netfs_wait_for_paused_read(struct netfs_io_request *rreq);
void netfs_wait_for_paused_write(struct netfs_io_request *rreq);
void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq);
+int netfs_clear_stale_pre_isize(struct inode *inode, uoff_t from,
+ uoff_t to, bool nowait);
/*
* objects.c
diff --git a/fs/netfs/misc.c b/fs/netfs/misc.c
index f5c1c463f4ff..523057390d6f 100644
--- a/fs/netfs/misc.c
+++ b/fs/netfs/misc.c
@@ -6,6 +6,7 @@
*/
#include <linux/swap.h>
+#include <linux/rmap.h>
#include "internal.h"
/**
@@ -582,3 +583,101 @@ void netfs_wait_for_put_ra_refs(struct netfs_io_request *rreq)
trace_netfs_rreq(rreq, netfs_rreq_trace_waited_put_ra_refs);
finish_wait(&rreq->waitq, &myself);
}
+
+/**
+ * netfs_clear_stale_isize - Clear stale pagecache in a to-be-created hole
+ * @inode: The inode to act upon.
+ * @from: The base of the hole to be made.
+ * @to: The top of the hole to be made.
+ * @nowait: True to return -EAGAIN rather than block.
+ * @exclusive: True if the caller holds i_rwsem exclusively for the resize.
+ *
+ * Zero any data left in the pagecache within the [@from, @to) hole by a
+ * write through an mmap so that it isn't exposed as file content once the
+ * file is extended. Only the uptodate folio straddling @from can hold such
+ * data as pages wholly beyond the EOF can't be faulted in, so the zeroing
+ * is limited to that folio. The folio is zeroed rather than dropped so
+ * that a concurrent extending write can't lose data.
+ *
+ * If @exclusive is false, @from is re-read from i_size and used to clamp
+ * the zeroed range, for callers that may race with another writer also
+ * extending the file (eg. multiple buffered writes extending the same file
+ * under a shared i_rwsem). If @exclusive is true, for callers that hold
+ * i_rwsem exclusively across the whole resize and have already updated
+ * i_size to @to, staleness is decided from the folio's dirty state instead:
+ * since no genuine concurrent buffered writer can be racing, a lockless
+ * stat() adopting a server-confirmed size mid-resize has no data behind it
+ * and never dirties the folio, so it can't fool this check into skipping
+ * the zeroing the way it could fool the @exclusive false clamp.
+ *
+ * pagecache_isize_extended() can't be reused here: it is keyed on a
+ * sub-page block size (a no-op when the block size is >= PAGE_SIZE, as on
+ * network filesystems), runs after i_size is updated, can't honour
+ * @nowait, and doesn't wait for writeback. Keep the two in sync if either
+ * is changed.
+ *
+ * Return: 0 on success, or -EAGAIN if @nowait is set and the folio is
+ * mapped or under writeback and so can't be cleaned without blocking.
+ */
+static int netfs_clear_stale_isize(struct inode *inode, uoff_t from,
+ uoff_t to, bool nowait, bool exclusive)
+{
+ struct address_space *mapping = inode->i_mapping;
+ fgf_t fgp = FGP_LOCK;
+ struct folio *folio;
+ int ret;
+
+ if (from >= to)
+ return 0;
+
+ if (nowait)
+ fgp |= FGP_NOWAIT;
+
+ folio = __filemap_get_folio(mapping, from >> PAGE_SHIFT, fgp, 0);
+ if (IS_ERR(folio))
+ return PTR_ERR(folio) == -EAGAIN ? -EAGAIN : 0;
+
+ ret = 0;
+ if (nowait && (folio_mapped(folio) || folio_test_writeback(folio))) {
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ folio_wait_writeback(folio);
+
+ if (folio_mkclean(folio))
+ folio_mark_dirty(folio);
+
+ if (folio_test_uptodate(folio) &&
+ (!exclusive || folio_test_dirty(folio))) {
+ uoff_t fpos = folio_pos(folio);
+
+ if (!exclusive)
+ from = umax(from, i_size_read(inode));
+ if (from < to && from < fpos + folio_size(folio)) {
+ size_t end = umin(to - fpos, folio_size(folio));
+ size_t offset = from - fpos;
+
+ folio_zero_segment(folio, offset, end);
+ }
+ }
+out:
+ folio_unlock(folio);
+ folio_put(folio);
+ return ret;
+}
+
+/* Clear stale pagecache before an extending buffered/DIO write. */
+int netfs_clear_stale_pre_isize(struct inode *inode, uoff_t from,
+ uoff_t to, bool nowait)
+{
+ return netfs_clear_stale_isize(inode, from, to, nowait, false);
+}
+
+/* Clear stale pagecache when extending a file under an exclusive resize. */
+void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
+ uoff_t to)
+{
+ netfs_clear_stale_isize(inode, from, to, false, true);
+}
+EXPORT_SYMBOL(netfs_clear_stale_post_isize);
diff --git a/fs/netfs/read_collect.c b/fs/netfs/read_collect.c
index a94197ef0181..c5bfaf2c6f25 100644
--- a/fs/netfs/read_collect.c
+++ b/fs/netfs/read_collect.c
@@ -33,6 +33,22 @@ static void netfs_clear_unread(struct netfs_io_subrequest *subreq)
__set_bit(NETFS_SREQ_HIT_EOF, &subreq->flags);
}
+static void netfs_clear_unread_dio(struct netfs_io_subrequest *subreq)
+{
+ uoff_t pos = subreq->start + subreq->transferred;
+ struct netfs_io_request *rreq = subreq->rreq;
+ size_t fill;
+
+ if (pos >= rreq->i_size)
+ return;
+
+ fill = min_t(uoff_t, rreq->i_size - pos,
+ subreq->len - subreq->transferred);
+
+ netfs_reset_iter(subreq);
+ subreq->transferred += iov_iter_zero(fill, &subreq->io_iter);
+}
+
/*
* Cancel the copy-to-cache mark on a folio.
*/
@@ -311,6 +327,14 @@ reassess:
test_bit(NETFS_SREQ_HIT_EOF, &front->flags))
netfs_read_unlock_folios(rreq, &notes);
} else {
+ if (!(notes & HIT_PENDING) &&
+ front->error == 0 &&
+ transferred < front->len &&
+ test_bit(NETFS_SREQ_CLEAR_TAIL, &front->flags)) {
+ netfs_clear_unread_dio(front);
+ transferred = front->transferred;
+ trace_netfs_sreq(front, netfs_sreq_trace_clear);
+ }
stream->collected_to = front->start + transferred;
rreq->collected_to = stream->collected_to;
}
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 7ecd70efdfea..98b610b2a114 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -1412,6 +1412,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
* server could even support copy of range where source = target
*/
lock_two_nondirectories(target_inode, src_inode);
+ filemap_invalidate_lock(target_inode->i_mapping);
if (len == 0) {
loff_t src_size = i_size_read(src_inode);
@@ -1467,9 +1468,15 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
i_size = target_inode->i_size;
spin_unlock(&target_inode->i_lock);
- /* Discard all the folios that overlap the destination region. */
+ /*
+ * Discard all the folios that overlap the destination region. Start at
+ * the old EOF when extending so the folio straddling it, which may hold
+ * data written past EOF through an mmap, is dropped too.
+ */
cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
- truncate_inode_pages_range(&target_inode->i_data, fstart, fend);
+ truncate_inode_pages_range(&target_inode->i_data,
+ min(fstart, i_size), fend);
+ netfs_wait_for_outstanding_io(target_inode);
fscache_invalidate(cifs_inode_cookie(target_inode), NULL, i_size, 0);
@@ -1508,6 +1515,7 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
if (rc)
CIFS_I(target_inode)->time = 0;
unlock:
+ filemap_invalidate_unlock(target_inode->i_mapping);
/* although unlocking in the reverse order from locking is not
strictly necessary here it is a little cleaner to be consistent */
unlock_two_nondirectories(src_inode, target_inode);
@@ -1531,6 +1539,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
struct cifs_tcon *target_tcon;
ssize_t rc;
+ if (len == 0)
+ return 0;
+
cifs_dbg(FYI, "copychunk range\n");
if (!src_file->private_data || !dst_file->private_data) {
@@ -1560,6 +1571,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
* server could even support copy of range where source = target
*/
lock_two_nondirectories(target_inode, src_inode);
+ filemap_invalidate_lock(target_inode->i_mapping);
cifs_dbg(FYI, "about to flush pages\n");
@@ -1581,10 +1593,28 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
/* Flush and invalidate all the folios in the destination region. If
* the copy was successful, then some of the flush is extra overhead,
* but we need to allow for the copy failing in some way (eg. ENOSPC).
+ *
+ * Start at the old EOF when extending so the folio straddling it, which
+ * may hold data written past EOF through an mmap, is dropped too.
*/
- rc = filemap_invalidate_inode(target_inode, true, destoff, destoff + len - 1);
- if (rc)
- goto unlock;
+ if (target_inode->i_mapping->nrpages) {
+ loff_t fstart = min(destoff, i_size_read(target_inode));
+ loff_t fend = destoff + len - 1;
+
+ unmap_mapping_pages(target_inode->i_mapping,
+ fstart >> PAGE_SHIFT,
+ (fend >> PAGE_SHIFT) -
+ (fstart >> PAGE_SHIFT) + 1,
+ false);
+ rc = filemap_write_and_wait_range(target_inode->i_mapping,
+ fstart, fend);
+ if (rc)
+ goto unlock;
+ invalidate_inode_pages2_range(target_inode->i_mapping,
+ fstart >> PAGE_SHIFT,
+ fend >> PAGE_SHIFT);
+ }
+ netfs_wait_for_outstanding_io(target_inode);
fscache_invalidate(cifs_inode_cookie(target_inode), NULL,
i_size_read(target_inode), 0);
@@ -1616,6 +1646,7 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
CIFS_I(target_inode)->time = 0;
unlock:
+ filemap_invalidate_unlock(target_inode->i_mapping);
/* although unlocking in the reverse order from locking is not
* strictly necessary here it is a little cleaner to be consistent
*/
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index 0c85daa8386e..e3d820c9778b 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -146,8 +146,9 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, struct file *src_file,
unsigned int flags);
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg);
-void cifs_setsize(struct inode *inode, loff_t offset);
-void cifs_resize_file_locked(struct inode *inode, loff_t offset);
+void cifs_setsize(struct inode *inode, loff_t old_size, loff_t offset);
+void cifs_resize_file_locked(struct inode *inode, loff_t old_size,
+ loff_t offset);
struct fs_context;
struct smb3_fs_context;
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 79e4e84f8985..5c5b76a9e9fe 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1450,11 +1450,11 @@ struct cifsFileInfo {
struct dentry *dentry;
struct tcon_link *tlink;
unsigned int f_flags;
- bool invalidHandle:1; /* file closed via session abend */
- bool swapfile:1;
- bool oplock_break_cancelled:1;
- bool status_file_deleted:1; /* file has been deleted */
- bool offload:1; /* offload final part of _put to a wq */
+ bool invalidHandle; /* file closed via session abend */
+ bool swapfile;
+ bool oplock_break_cancelled;
+ bool status_file_deleted; /* file has been deleted */
+ bool offload; /* offload final part of _put to a wq */
__u16 oplock_epoch; /* epoch from the lease break */
__u32 oplock_level; /* oplock/lease level from the lease break */
int count;
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index 0d428517f454..11355d6bf89d 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -1012,11 +1012,14 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
}
mapping_set_error(inode->i_mapping, rc);
+ netfs_wait_for_outstanding_io(inode);
+
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
rc = cifs_file_flush(xid, inode, cfile);
if (!rc) {
if (cfile) {
struct netfs_inode *ictx = netfs_inode(inode);
+ loff_t old_size = i_size_read(inode);
tcon = tlink_tcon(cfile->tlink);
server = tcon->ses->server;
@@ -1025,7 +1028,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
cfile, 0, false);
if (!rc) {
netfs_resize_file(&cinode->netfs, 0, true);
- cifs_setsize(inode, 0);
+ cifs_setsize(inode, old_size, 0);
cifs_invalidate_cache(inode, 0);
}
netfs_wb_end(ictx);
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 1fe0ef0a95db..f29555f3c7d0 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -88,6 +88,8 @@ static void cifs_set_ops(struct inode *inode)
else
inode->i_data.a_ops = &cifs_addr_ops;
mapping_set_large_folios(inode->i_mapping);
+ if (tcon->ses->server->sign)
+ mapping_set_stable_writes(inode->i_mapping);
break;
case S_IFDIR:
if (IS_AUTOMOUNT(inode)) {
@@ -3053,15 +3055,12 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
return -EOPNOTSUPP;
}
-void cifs_setsize(struct inode *inode, loff_t offset)
+void cifs_setsize(struct inode *inode, loff_t old_size, loff_t offset)
{
- loff_t old_size;
u64 blocks = CIFS_INO_BLOCKS(offset);
spin_lock(&inode->i_lock);
- old_size = i_size_read(inode);
i_size_write(inode, offset);
-
/*
* Extending EOF does not allocate the intervening range. Only clamp
* i_blocks on shrink; allocation growth comes from writes or from the
@@ -3071,20 +3070,28 @@ void cifs_setsize(struct inode *inode, loff_t offset)
inode->i_blocks = blocks;
spin_unlock(&inode->i_lock);
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
+
+ /*
+ * Zero the tail of the folio straddling the old EOF so data dirtied
+ * past EOF through an mmap isn't exposed. truncate_pagecache() then
+ * drops any pagecache beyond the new EOF, as in truncate_setsize().
+ */
if (offset > old_size)
- pagecache_isize_extended(inode, old_size, offset);
+ netfs_clear_stale_post_isize(inode, old_size, offset);
+
truncate_pagecache(inode, offset);
netfs_wait_for_outstanding_io(inode);
}
-void cifs_resize_file_locked(struct inode *inode, loff_t offset)
+void cifs_resize_file_locked(struct inode *inode, loff_t old_size,
+ loff_t offset)
{
struct fscache_cookie *cookie = cifs_inode_cookie(inode);
lockdep_assert_held_write(&inode->i_rwsem);
netfs_resize_file(netfs_inode(inode), offset, true);
- cifs_setsize(inode, offset);
+ cifs_setsize(inode, old_size, offset);
if (!cookie)
return;
@@ -3101,6 +3108,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
struct inode *inode = d_inode(dentry);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
+ loff_t old_size = i_size_read(inode);
struct tcon_link *tlink = NULL;
struct cifs_tcon *tcon = NULL;
struct TCP_Server_Info *server;
@@ -3159,7 +3167,7 @@ int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
set_size_out:
if (rc == 0)
- cifs_resize_file_locked(inode, size);
+ cifs_resize_file_locked(inode, old_size, size);
return rc;
}
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index aa142420dae2..583ab4c4d773 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2287,6 +2287,7 @@ smb2_duplicate_extents(const unsigned int xid,
struct duplicate_extents_to_file dup_ext_buf;
struct timespec64 ts;
struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
+ loff_t old_size;
u64 asize;
/* server fileays advertise duplicate extent support with this flag */
@@ -2305,11 +2306,12 @@ smb2_duplicate_extents(const unsigned int xid,
trgtfile->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dest_off, len);
inode = d_inode(trgtfile->dentry);
- if (i_size_read(inode) < dest_off + len) {
+ old_size = i_size_read(inode);
+ if (old_size < dest_off + len) {
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
- cifs_resize_file_locked(inode, dest_off + len);
+ cifs_resize_file_locked(inode, old_size, dest_off + len);
}
rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid,
@@ -3520,6 +3522,21 @@ static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon,
0, NULL, NULL);
}
+static long query_server_eof(const unsigned int xid,
+ struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile,
+ unsigned long long *eof)
+{
+ struct smb2_file_all_info file_inf = {};
+ long rc;
+
+ rc = SMB2_query_info(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, &file_inf);
+ if (!rc)
+ *eof = le64_to_cpu(file_inf.EndOfFile);
+ return rc;
+}
+
static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
unsigned long long offset, unsigned long long len,
bool keep_size)
@@ -3547,25 +3564,32 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
filemap_invalidate_lock(inode->i_mapping);
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
- if (offset + len >= remote_i_size && offset < i_size) {
- unsigned long long top = umin(offset + len, i_size);
- rc = filemap_write_and_wait_range(inode->i_mapping, offset, top - 1);
- if (rc < 0)
- goto zero_range_exit;
- }
+ rc = filemap_write_and_wait_range(inode->i_mapping, offset,
+ offset + len - 1);
+ if (rc < 0)
+ goto zero_range_exit;
/*
* We zero the range through ioctl, so we need remove the page caches
* first, otherwise the data may be inconsistent with the server.
+ *
+ * Start at the old EOF when extending so the folio straddling it, which
+ * may hold data written past EOF through an mmap, is dropped too.
*/
- truncate_pagecache_range(inode, offset, offset + len - 1);
+ truncate_pagecache_range(inode, min(offset, i_size), offset + len - 1);
netfs_wait_for_outstanding_io(inode);
- /* if file not oplocked can't be sure whether asking to extend size */
- rc = -EOPNOTSUPP;
- if (keep_size == false && !CIFS_CACHE_READ(cifsi))
- goto zero_range_exit;
+ if (!keep_size && !CIFS_CACHE_READ(cifsi)) {
+ rc = query_server_eof(xid, tcon, cfile, &remote_i_size);
+ if (rc)
+ goto zero_range_exit;
+ i_size = max(i_size, remote_i_size);
+ if (i_size < new_size) {
+ rc = -EOPNOTSUPP;
+ goto zero_range_exit;
+ }
+ }
fscache_invalidate(cifs_inode_cookie(inode), NULL,
i_size_read(inode), 0);
@@ -3577,7 +3601,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
/*
* do we also need to change the size of the file?
*/
- if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
+ if (!keep_size && umax(i_size, i_size_read(inode)) < new_size) {
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_size);
if (rc >= 0) {
@@ -3723,7 +3747,8 @@ static int smb3_simple_fallocate_write_range(unsigned int xid,
static int smb3_simple_fallocate_range(unsigned int xid,
struct cifs_tcon *tcon,
struct cifsFileInfo *cfile,
- loff_t off, loff_t len)
+ loff_t off, loff_t len,
+ loff_t old_eof)
{
struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
struct inode *inode = d_inode(cfile->dentry);
@@ -3739,12 +3764,30 @@ static int smb3_simple_fallocate_range(unsigned int xid,
goto out;
}
- if (off >= i_size_read(inode)) {
+ if (off >= old_eof) {
rc = smb3_simple_fallocate_write_range(xid, tcon, cfile,
off, len, buf);
goto out;
}
+ filemap_invalidate_lock(inode->i_mapping);
+
+ /*
+ * Flush and commit the data to the server, otherwise
+ * FSCTL_QUERY_ALLOCATED_RANGES might report recently written data as
+ * unallocated holes on Windows Servers, and the loop below would
+ * then zero-fill them and corrupt the file.
+ */
+ rc = filemap_write_and_wait_range(inode->i_mapping, off,
+ off + len - 1);
+ if (rc)
+ goto out_unlock;
+ netfs_wait_for_outstanding_io(inode);
+ rc = SMB2_flush(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid);
+ if (rc)
+ goto out_unlock;
+
in_data.file_offset = cpu_to_le64(off);
in_data.length = cpu_to_le64(len);
rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
@@ -3754,7 +3797,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
1024 * sizeof(struct file_allocated_range_buffer),
(char **)&out_data, &out_data_len);
if (rc)
- goto out;
+ goto out_unlock;
tmp_data = out_data;
while (len) {
@@ -3764,12 +3807,12 @@ static int smb3_simple_fallocate_range(unsigned int xid,
if (out_data_len == 0) {
rc = smb3_simple_fallocate_write_range(xid, tcon,
cfile, off, len, buf);
- goto out;
+ goto out_unlock;
}
if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
rc = -EINVAL;
- goto out;
+ goto out_unlock;
}
range_start = le64_to_cpu(tmp_data->file_offset);
@@ -3777,7 +3820,7 @@ static int smb3_simple_fallocate_range(unsigned int xid,
if (check_add_overflow(range_start, range_len, &range_end) ||
range_end > S64_MAX) {
rc = -EINVAL;
- goto out;
+ goto out_unlock;
}
if (off < range_start) {
@@ -3792,11 +3835,11 @@ static int smb3_simple_fallocate_range(unsigned int xid,
rc = smb3_simple_fallocate_write_range(xid, tcon,
cfile, off, l, buf);
if (rc)
- goto out;
+ goto out_unlock;
off = off + l;
len = len - l;
if (len == 0)
- goto out;
+ goto out_unlock;
}
/*
* We are at a section of allocated data, just skip forward
@@ -3815,6 +3858,8 @@ static int smb3_simple_fallocate_range(unsigned int xid,
out_data_len -= sizeof(struct file_allocated_range_buffer);
}
+ out_unlock:
+ filemap_invalidate_unlock(inode->i_mapping);
out:
kfree(out_data);
kvfree(buf);
@@ -3830,7 +3875,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
long rc = -EOPNOTSUPP;
unsigned int xid;
- loff_t old_eof, new_eof;
+ loff_t old_eof, new_eof, local_eof;
struct smb2_file_all_info file_inf;
u64 asize;
int qrc;
@@ -3839,18 +3884,37 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
inode = d_inode(cfile->dentry);
cifsi = CIFS_I(inode);
- old_eof = i_size_read(inode);
+ old_eof = local_eof = i_size_read(inode);
trace_smb3_falloc_enter(xid, cfile->fid.persistent_fid, tcon->tid,
tcon->ses->Suid, off, len);
- /* if file not oplocked can't be sure whether asking to extend size */
- if (!CIFS_CACHE_READ(cifsi))
- if (!keep_size) {
+
+ if (!keep_size && !CIFS_CACHE_READ(cifsi)) {
+ unsigned long long server_eof;
+
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (rc) {
trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
tcon->tid, tcon->ses->Suid, off, len, rc);
free_xid(xid);
return rc;
}
+ netfs_wait_for_outstanding_io(inode);
+
+ rc = query_server_eof(xid, tcon, cfile, &server_eof);
+ if (rc) {
+ trace_smb3_falloc_err(xid, cfile->fid.persistent_fid,
+ tcon->tid, tcon->ses->Suid, off, len, rc);
+ free_xid(xid);
+ return rc;
+ }
+ /*
+ * Only use the larger EOF to decide whether we're extending.
+ * The pagecache zeroing below must still key off the local
+ * i_size, so keep local_eof for that.
+ */
+ old_eof = max_t(loff_t, old_eof, server_eof);
+ }
/*
* Extending the file
@@ -3874,7 +3938,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
rc = smb3_simple_fallocate_range(xid, tcon, cfile,
- off, len);
+ off, len, old_eof);
if (rc) {
spin_lock(&inode->i_lock);
cifsi->time = 0;
@@ -3883,7 +3947,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
new_eof = off + len;
- cifs_resize_file_locked(inode, new_eof);
+ cifs_resize_file_locked(inode, local_eof, new_eof);
qrc = SMB2_query_info(xid, tcon,
cfile->fid.persistent_fid,
@@ -3931,7 +3995,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
if (rc)
goto out;
- cifs_resize_file_locked(inode, new_eof);
+ cifs_resize_file_locked(inode, local_eof, new_eof);
qrc = SMB2_query_info(xid, tcon,
cfile->fid.persistent_fid,
@@ -3976,7 +4040,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
}
}
- if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
+ if (keep_size || old_eof >= off + len) {
/*
* At this point, we are trying to fallocate an internal
* regions of a sparse file. Since smb2 does not have a
@@ -3993,7 +4057,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
*/
if (len <= 1024 * 1024) {
rc = smb3_simple_fallocate_range(xid, tcon, cfile,
- off, len);
+ off, len, old_eof);
goto out;
}
@@ -4005,7 +4069,7 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
* ie potentially making a few extra pages at the beginning
* or end of the file non-sparse via set_sparse is harmless.
*/
- if ((off > 8192) || (off + len + 8192 < i_size_read(inode))) {
+ if (off > 8192 || off + len + 8192 < old_eof) {
rc = -EOPNOTSUPP;
goto out;
}
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 3d7ead36d1a0..cfe3c4b4abe7 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -4796,14 +4796,20 @@ do_retry:
rdata->got_bytes);
if (rdata->result == -ENODATA) {
- __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
rdata->result = 0;
+ if (rdata->subreq.start + rdata->subreq.transferred >= i_size_read(inode))
+ __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+ else
+ __set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags);
} else {
size_t trans = rdata->subreq.transferred + rdata->got_bytes;
if (trans < rdata->subreq.len &&
rdata->subreq.start + trans >= netfs_read_remote_i_size(inode)) {
- __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
rdata->result = 0;
+ if (rdata->subreq.start + trans >= i_size_read(inode))
+ __set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
+ else
+ __set_bit(NETFS_SREQ_CLEAR_TAIL, &rdata->subreq.flags);
}
if (rdata->got_bytes)
__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 1d2676782d70..0ecb9418dfbd 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1258,11 +1258,15 @@ struct btf_func_model {
#define BPF_TRAMP_F_INDIRECT BIT(8)
/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
- * bytes on x86.
+ * bytes on x86. The trampoline image has to fit in PAGE_SIZE.
*/
enum {
-#if defined(__s390x__)
+#if defined(__s390x__) || defined(__powerpc64__)
BPF_MAX_TRAMP_LINKS = 27,
+#elif defined(__x86_64__)
+ BPF_MAX_TRAMP_LINKS = 36,
+#elif defined(__aarch64__)
+ BPF_MAX_TRAMP_LINKS = 37,
#else
BPF_MAX_TRAMP_LINKS = 38,
#endif
@@ -1315,6 +1319,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
void *arch_alloc_bpf_trampoline(unsigned int size);
void arch_free_bpf_trampoline(void *image, unsigned int size);
int __must_check arch_protect_bpf_trampoline(void *image, unsigned int size);
+int arch_bpf_trampoline_skip(void *nop, void *target);
int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,
struct bpf_tramp_nodes *tnodes, void *func_addr);
@@ -1363,19 +1368,48 @@ enum bpf_tramp_prog_type {
BPF_TRAMP_FSESSION,
};
+/*
+ * Each prog call in a trampoline image is preceded by a nop. When the prog is
+ * detached, the nop is patched to a jump to target, right after the call, so
+ * that tasks still running in the image skip the prog.
+ */
+struct bpf_tramp_skip {
+ struct bpf_prog *prog;
+ void *nop;
+ void *target;
+};
+
struct bpf_tramp_image {
void *image;
int size;
struct bpf_ksym ksym;
struct percpu_ref pcref;
- void *ip_after_call;
- void *ip_epilogue;
+ bool call_orig;
+ /* entry in tr->images, the image holds a reference on tr */
+ struct bpf_trampoline *tr;
+ struct list_head list;
+ int nr_skips;
+ struct bpf_tramp_skip *skips;
union {
struct rcu_head rcu;
struct work_struct work;
};
};
+static inline void bpf_tramp_image_add_skip(struct bpf_tramp_image *im, struct bpf_prog *prog,
+ void *nop, void *target)
+{
+ struct bpf_tramp_skip *skip;
+
+ /* struct_ops trampolines and arch_bpf_trampoline_size() have no image */
+ if (!im || !im->skips)
+ return;
+ skip = &im->skips[im->nr_skips++];
+ skip->prog = prog;
+ skip->nop = nop;
+ skip->target = target;
+}
+
struct bpf_trampoline {
/* hlist for trampoline_key_table */
struct hlist_node hlist_key;
@@ -1402,6 +1436,8 @@ struct bpf_trampoline {
int progs_cnt[BPF_TRAMP_MAX];
/* Executable image of trampoline */
struct bpf_tramp_image *cur_image;
+ /* Images not freed yet, cur_image and older ones still in use */
+ struct list_head images;
/* Used as temporary old image storage for multi_attach */
struct {
struct bpf_tramp_image *old_image;
@@ -1770,6 +1806,7 @@ struct bpf_prog_aux {
bool offload_requested; /* Program is bound and offloaded to the netdev. */
bool attach_btf_trace; /* true if attaching to BTF-enabled raw tp */
bool attach_tracing_prog; /* true if tracing another tracing program */
+ bool tramp_linked; /* true if it was ever linked to a trampoline */
bool func_proto_unreliable;
bool tail_call_reachable;
bool xdp_has_frags;
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 35ce665edfd8..d3d0d9d02aa4 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -420,6 +420,9 @@ struct cpufreq_driver {
/* Will be called after the driver is fully initialized */
void (*ready)(struct cpufreq_policy *policy);
+ /* Return the capacity reference frequency for policy. */
+ unsigned int (*scale_freq_ref)(struct cpufreq_policy *policy);
+
struct freq_attr **attr;
/* platform specific boost support code */
diff --git a/include/linux/hrtimer_rearm.h b/include/linux/hrtimer_rearm.h
index 17a81826bd9a..12e42721c8e0 100644
--- a/include/linux/hrtimer_rearm.h
+++ b/include/linux/hrtimer_rearm.h
@@ -45,7 +45,7 @@ hrtimer_rearm_deferred_user_irq(unsigned long *tif_work, const unsigned long tif
clear_thread_flag(TIF_HRTIMER_REARM);
__hrtimer_rearm_deferred();
/* Don't go into the loop if HRTIMER_REARM was the only flag */
- *tif_work &= ~TIF_HRTIMER_REARM;
+ *tif_work &= ~_TIF_HRTIMER_REARM;
return !*tif_work;
}
return false;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index cf7fe835c4ad..c844def6dd95 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -791,7 +791,6 @@ struct kvm {
/* The current active memslot set for each address space */
struct kvm_memslots __rcu *memslots[KVM_MAX_NR_ADDRESS_SPACES];
struct xarray vcpu_array;
- DECLARE_BITMAP(vcpu_ids, KVM_MAX_VCPU_IDS);
/*
* Protected by slots_lock, but can be read outside if an
* incorrect answer is acceptable.
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 09c8c93e4dba..6936180b6ea5 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -305,6 +305,8 @@ int nand_ecc_prepare_io_req(struct nand_device *nand,
struct nand_page_io_req *req);
int nand_ecc_finish_io_req(struct nand_device *nand,
struct nand_page_io_req *req);
+bool nand_ecc_is_pipelined(const struct nand_device *nand);
+
bool nand_ecc_is_strong_enough(struct nand_device *nand);
#if IS_REACHABLE(CONFIG_MTD_NAND_CORE)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 87cafc932e9e..3cff2174dc03 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -887,6 +887,7 @@ enum net_device_path_type {
DEV_PATH_DSA,
DEV_PATH_MTK_WDMA,
DEV_PATH_TUN,
+ DEV_PATH_IEEE80211,
};
struct net_device_path {
@@ -953,6 +954,8 @@ struct net_device_path_ctx {
u16 id;
__be16 proto;
} vlan[NET_DEVICE_PATH_VLAN_MAX];
+
+ bool ieee80211;
};
enum tc_setup_type {
diff --git a/include/linux/netfilter_netdev.h b/include/linux/netfilter_netdev.h
index 3175073a66ba..2a854a0bbd4b 100644
--- a/include/linux/netfilter_netdev.h
+++ b/include/linux/netfilter_netdev.h
@@ -133,7 +133,7 @@ static inline struct sk_buff *nf_hook_egress(struct sk_buff *skb, int *rc,
static inline void nf_skip_egress(struct sk_buff *skb, bool skip)
{
-#ifdef CONFIG_NETFILTER_SKIP_EGRESS
+#ifdef CONFIG_NET_EGRESS
skb->nf_skip_egress = skip;
#endif
}
diff --git a/include/linux/netfs.h b/include/linux/netfs.h
index b4dd32863dd4..fe6275e55fc2 100644
--- a/include/linux/netfs.h
+++ b/include/linux/netfs.h
@@ -397,6 +397,8 @@ ssize_t netfs_unbuffered_write_iter(struct kiocb *iocb, struct iov_iter *from);
ssize_t netfs_unbuffered_write_iter_locked(struct kiocb *iocb, struct iov_iter *iter,
struct netfs_group *netfs_group);
ssize_t netfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from);
+void netfs_clear_stale_post_isize(struct inode *inode, uoff_t from,
+ uoff_t to);
/* Single, monolithic object read/write API. */
void netfs_single_mark_inode_dirty(struct inode *inode);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 915c6fd3f084..2d5e7687875b 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1506,9 +1506,10 @@ is_default_overflow_handler(struct perf_event *event)
}
extern void
-perf_event_header__init_id(struct perf_event_header *header,
- struct perf_sample_data *data,
- struct perf_event *event);
+perf_event_header__init(struct perf_event_header *header,
+ struct perf_sample_data *data,
+ u32 type, u16 misc, u16 size,
+ struct perf_event *event);
extern void
perf_event__output_id_sample(struct perf_event *event,
struct perf_output_handle *handle,
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 95729339e7a5..a54ec40d095c 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -186,7 +186,12 @@ void net_dec_ingress_queue(void);
#ifdef CONFIG_NET_EGRESS
void net_inc_egress_queue(void);
void net_dec_egress_queue(void);
-void netdev_xmit_skip_txqueue(bool skip);
+bool netdev_xmit_skip_txqueue(bool skip);
+#else
+static inline bool netdev_xmit_skip_txqueue(bool skip)
+{
+ return false;
+}
#endif
void rtnetlink_init(void);
diff --git a/include/linux/siphash.h b/include/linux/siphash.h
index 9153e77382e1..00c58bbe549a 100644
--- a/include/linux/siphash.h
+++ b/include/linux/siphash.h
@@ -75,8 +75,10 @@ static inline u64 ___siphash_aligned(const __le64 *data, size_t len,
/**
* siphash - compute 64-bit siphash PRF value
* @data: buffer to hash
- * @size: size of @data
+ * @len: size of @data
* @key: the siphash key
+ *
+ * Returns: siphash PRF value
*/
static inline u64 siphash(const void *data, size_t len,
const siphash_key_t *key)
@@ -125,8 +127,10 @@ static inline u32 ___hsiphash_aligned(const __le32 *data, size_t len,
/**
* hsiphash - compute 32-bit hsiphash PRF value
* @data: buffer to hash
- * @size: size of @data
+ * @len: size of @data
* @key: the hsiphash key
+ *
+ * Returns: hsiphash PRF value
*/
static inline u32 hsiphash(const void *data, size_t len,
const hsiphash_key_t *key)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 84308498a3a8..a3ff380d43f0 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1020,7 +1020,7 @@ struct sk_buff {
#ifdef CONFIG_NET_REDIRECT
__u8 from_ingress:1;
#endif
-#ifdef CONFIG_NETFILTER_SKIP_EGRESS
+#ifdef CONFIG_NET_EGRESS
__u8 nf_skip_egress:1;
#endif
#ifdef CONFIG_SKB_DECRYPTED
diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h
index 29a53bbc0dd4..5f18d281e6a4 100644
--- a/include/linux/soc/qcom/geni-se.h
+++ b/include/linux/soc/qcom/geni-se.h
@@ -347,17 +347,12 @@ struct geni_se {
#define QUP_SE_VERSION_2_5 0x20050000
/*
- * Define bandwidth thresholds that cause the underlying Core 2X interconnect
- * clock to run at the named frequency. These baseline values are recommended
- * by the hardware team, and are not dynamically scaled with GENI bandwidth
- * beyond basic on/off.
+ * QUP Core 2X clock votes used by GENI clients through the "qup-core" ICC
+ * path. Values are in Bps and must be converted with Bps_to_icc() before
+ * setting avg_bw.
*/
-#define CORE_2X_19_2_MHZ 960
-#define CORE_2X_50_MHZ 2500
-#define CORE_2X_100_MHZ 5000
-#define CORE_2X_150_MHZ 7500
-#define CORE_2X_200_MHZ 10000
-#define CORE_2X_236_MHZ 16383
+#define CORE_2X_19_2_MHZ 9600000
+#define CORE_2X_50_MHZ 25000000
#define GENI_DEFAULT_BW Bps_to_icc(1000)
diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
index d48623fda79b..b62dfa52b149 100644
--- a/include/linux/thunderbolt.h
+++ b/include/linux/thunderbolt.h
@@ -22,6 +22,7 @@ struct device;
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/mutex.h>
#include <linux/device-id/tb.h>
#include <linux/pci.h>
@@ -565,6 +566,7 @@ struct tb_nhi {
* @interval_nsec: Interval counter if interrupt throttling is to be
* used with this ring (in ns)
* @wait: Used to signal that the ring may be empty now
+ * @lock_key: Lock validator class key per-ring
*/
struct tb_ring {
spinlock_t lock;
@@ -590,6 +592,7 @@ struct tb_ring {
void *poll_data;
unsigned int interval_nsec;
wait_queue_head_t wait;
+ struct lock_class_key lock_key;
};
/* Leave ring interrupt enabled on suspend */
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 0a46e4054dec..833bb7b97ceb 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -168,6 +168,7 @@ struct tty_operations;
* @write_wait: concurrent writers are waiting in this queue until they are
* allowed to write
* @read_wait: readers wait for data in this queue
+ * @break_wait: wait queue for timed breaks
* @hangup_work: normally a work to perform a hangup (do_tty_hangup()); while
* freeing the tty, (re)used to release_one_tty()
* @disc_data: pointer to @ldisc's private data (e.g. to &struct n_tty_data)
@@ -230,6 +231,7 @@ struct tty_struct {
struct fasync_struct *fasync;
wait_queue_head_t write_wait;
wait_queue_head_t read_wait;
+ wait_queue_head_t break_wait;
struct work_struct hangup_work;
void *disc_data;
void *driver_data;
diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h
index 1f2896e56e77..40ae3bb13e11 100644
--- a/include/linux/tty_driver.h
+++ b/include/linux/tty_driver.h
@@ -73,6 +73,11 @@ struct serial_struct;
* @TTY_DRIVER_NO_WORKQUEUE:
* Do not create workqueue when tty_register_driver(). Whenever set, flip
* buffer workqueue can be set by tty_port_link_wq() for every port.
+ *
+ * @TTY_DRIVER_RESET_SAVED_TERMIOS:
+ * Reset any saved termios settings on device registration when reusing a
+ * minor number. Must only be set by drivers that guarantee that the minor
+ * number is no longer in use.
*/
enum tty_driver_flag {
TTY_DRIVER_INSTALLED = BIT(0),
@@ -84,6 +89,7 @@ enum tty_driver_flag {
TTY_DRIVER_DYNAMIC_ALLOC = BIT(6),
TTY_DRIVER_UNNUMBERED_NODE = BIT(7),
TTY_DRIVER_NO_WORKQUEUE = BIT(8),
+ TTY_DRIVER_RESET_SAVED_TERMIOS = BIT(9),
};
enum tty_driver_type {
diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h
index 23cad403bb8f..8d22c59c6f15 100644
--- a/include/linux/tty_port.h
+++ b/include/linux/tty_port.h
@@ -245,7 +245,8 @@ bool tty_port_carrier_raised(struct tty_port *port);
void tty_port_raise_dtr_rts(struct tty_port *port);
void tty_port_lower_dtr_rts(struct tty_port *port);
void tty_port_hangup(struct tty_port *port);
-void __tty_port_tty_hangup(struct tty_port *port, bool check_clocal, bool async);
+void tty_port_tty_hangup(struct tty_port *port, bool check_clocal);
+void tty_port_tty_vhangup(struct tty_port *port);
void tty_port_tty_wakeup(struct tty_port *port);
int tty_port_block_til_ready(struct tty_port *port, struct tty_struct *tty,
struct file *filp);
@@ -264,25 +265,6 @@ static inline int tty_port_users(struct tty_port *port)
return port->count + port->blocked_open;
}
-/**
- * tty_port_tty_hangup - helper to hang up a tty asynchronously
- * @port: tty port
- * @check_clocal: hang only ttys with %CLOCAL unset?
- */
-static inline void tty_port_tty_hangup(struct tty_port *port, bool check_clocal)
-{
- __tty_port_tty_hangup(port, check_clocal, true);
-}
-
-/**
- * tty_port_tty_vhangup - helper to hang up a tty synchronously
- * @port: tty port
- */
-static inline void tty_port_tty_vhangup(struct tty_port *port)
-{
- __tty_port_tty_hangup(port, false, false);
-}
-
#ifdef CONFIG_TTY
void tty_kref_put(struct tty_struct *tty);
__DEFINE_CLASS_IS_CONDITIONAL(tty_port_tty, true);
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index 534e6650e2aa..ab0a32a90629 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
int usb_serial_bus_register(struct usb_serial_driver *device);
void usb_serial_bus_deregister(struct usb_serial_driver *device);
+void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver);
extern const struct bus_type usb_serial_bus_type;
extern struct tty_driver *usb_serial_tty_driver;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4105c446ca98..fa2a367731b5 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -62,7 +62,7 @@ struct inquiry_entry {
NAME_PENDING,
NAME_KNOWN,
} name_state;
- __u32 timestamp;
+ unsigned long timestamp;
struct inquiry_data data;
};
@@ -78,7 +78,7 @@ struct discovery_state {
struct list_head all; /* All devices found during inquiry */
struct list_head unknown; /* Name state not known */
struct list_head resolve; /* Name needs to be resolved */
- __u32 timestamp;
+ unsigned long timestamp;
bdaddr_t last_adv_addr;
u8 last_adv_addr_type;
s8 last_adv_rssi;
@@ -562,6 +562,7 @@ struct hci_dev {
struct list_head link_keys;
struct list_head long_term_keys;
struct list_head identity_resolving_keys;
+ struct mutex remote_oob_lock;
struct list_head remote_oob_data;
struct list_head le_accept_list;
struct list_head le_resolv_list;
diff --git a/include/net/gso.h b/include/net/gso.h
index 29975440cad5..0749230d414e 100644
--- a/include/net/gso.h
+++ b/include/net/gso.h
@@ -19,10 +19,18 @@ struct skb_gso_cb {
int encap_level;
__wsum csum;
__u16 csum_start;
+ /* Number of IPv4/IPv6 GSO handler entries for this packet. */
+ u8 recursion_counter;
};
#define SKB_GSO_CB_OFFSET 32
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))
+static inline bool gso_recursion_inc_test(struct sk_buff *skb,
+ unsigned int limit)
+{
+ return ++SKB_GSO_CB(skb)->recursion_counter > limit;
+}
+
static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
{
return (skb_mac_header(inner_skb) - inner_skb->head) -
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
index f2e2771f188f..5b611efaa3cd 100644
--- a/include/net/netfilter/nf_flow_table.h
+++ b/include/net/netfilter/nf_flow_table.h
@@ -183,10 +183,10 @@ enum nf_flow_flags {
NF_FLOW_DNAT,
NF_FLOW_CLOSING,
NF_FLOW_TEARDOWN,
+ NF_FLOW_PENDING,
NF_FLOW_HW,
NF_FLOW_HW_DYING,
NF_FLOW_HW_DEAD,
- NF_FLOW_HW_PENDING,
NF_FLOW_HW_BIDIRECTIONAL,
NF_FLOW_HW_ESTABLISHED,
};
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index f35bd06a6bad..1acaadb3e2cf 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -810,6 +810,15 @@ static inline bool skb_at_tc_ingress(const struct sk_buff *skb)
#endif
}
+static inline bool skb_at_tc_egress(const struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_EGRESS
+ return skb->nf_skip_egress && !skb_at_tc_ingress(skb);
+#else
+ return false;
+#endif
+}
+
static inline bool skb_skip_tc_classify(struct sk_buff *skb)
{
#ifdef CONFIG_NET_CLS_ACT
diff --git a/include/net/xdp.h b/include/net/xdp.h
index aa742f413c35..07231adfb5f8 100644
--- a/include/net/xdp.h
+++ b/include/net/xdp.h
@@ -106,6 +106,11 @@ struct xdp_buff {
};
};
+static __always_inline void xdp_reinit_buff(struct xdp_buff *xdp)
+{
+ xdp->flags = 0;
+}
+
static __always_inline bool xdp_buff_has_frags(const struct xdp_buff *xdp)
{
return !!(xdp->flags & XDP_FLAGS_HAS_FRAGS);
diff --git a/include/sound/core.h b/include/sound/core.h
index 2ca24ac7e37f..15be947a730e 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -340,6 +340,15 @@ static inline void snd_card_unref(struct snd_card *card)
DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))
+/*
+ * For automatic error handling at probe time, assign the card like:
+ * struct snd_card *card __free(snd_card_free) = NULL;
+ * then create and process as usual.
+ * But, don't forget to clear to NULL at successful return for avoiding
+ * the unexpected cleanup!
+ */
+DEFINE_FREE(snd_card_free, struct snd_card *, if (_T) snd_card_free(_T))
+
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
/* device.c */
diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h
index e3ad94444864..7d4618bea043 100644
--- a/include/uapi/linux/alloc_tag.h
+++ b/include/uapi/linux/alloc_tag.h
@@ -38,6 +38,7 @@ struct allocinfo_counter {
__u64 bytes;
__u64 calls;
__u8 accurate;
+ __u8 pad[7];
} __attribute__((aligned(8)));
struct allocinfo_tag_data {
diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h
index 1dd047ec98a1..ef6614d9c704 100644
--- a/include/uapi/linux/random.h
+++ b/include/uapi/linux/random.h
@@ -58,7 +58,7 @@ struct rand_pool_info {
/**
* struct vgetrandom_opaque_params - arguments for allocating memory for vgetrandom
*
- * @size_per_opaque_state: Size of each state that is to be passed to vgetrandom().
+ * @size_of_opaque_state: Size of each state passed to vgetrandom().
* @mmap_prot: Value of the prot argument in mmap(2).
* @mmap_flags: Value of the flags argument in mmap(2).
* @reserved: Reserved for future use.
diff --git a/include/vdso/getrandom.h b/include/vdso/getrandom.h
index 6ca4d6de9e46..1d9dbd4d76c1 100644
--- a/include/vdso/getrandom.h
+++ b/include/vdso/getrandom.h
@@ -64,7 +64,7 @@ extern void __arch_chacha20_blocks_nostack(u8 *dst_bytes, const u32 *key, u32 *c
* @opaque_state: Passed to __cvdso_getrandom().
* @opaque_len: Passed to __cvdso_getrandom();
*
- * This function is implemented by making a single call to to __cvdso_getrandom(), whose
+ * This function is implemented by making a single call to __cvdso_getrandom(), whose
* documentation may be consulted for more information.
*
* Returns: The return value of __cvdso_getrandom().
diff --git a/init/Kconfig b/init/Kconfig
index 8583d9f06c52..ad592fdf29af 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -173,6 +173,11 @@ config CC_HAS_ALLOC_TOKEN
config CC_HAS_MULTIDIMENSIONAL_NONSTRING
def_bool $(success,echo 'char tag[][4] __attribute__((__nonstring__)) = { };' | $(CC) $(CLANG_FLAGS) -x c - -c -o /dev/null -Werror)
+config CC_OPT_INLINE_MEMSET
+ string
+ default "-finline-stringops=memset" if $(cc-option,-finline-stringops=memset)
+ default "-mllvm -max-store-memset=4294967294" if $(cc-option,-mllvm -max-store-memset=4294967294)
+
config LD_CAN_USE_KEEP_IN_OVERLAY
# ld.lld prior to 21.0.0 did not support KEEP within an overlay description
# https://github.com/llvm/llvm-project/pull/130661
diff --git a/io_uring/bpf_filter.c b/io_uring/bpf_filter.c
index c0037632b7af..4a21511c4811 100644
--- a/io_uring/bpf_filter.c
+++ b/io_uring/bpf_filter.c
@@ -253,6 +253,7 @@ void io_bpf_filter_clone(struct io_restriction *dst, struct io_restriction *src)
* If the src filter is going away, just ignore it.
*/
if (refcount_inc_not_zero(&src->bpf_filters->refs)) {
+ src->bpf_filters_cow = true;
dst->bpf_filters = src->bpf_filters;
dst->bpf_filters_cow = true;
}
diff --git a/io_uring/cmd_net.c b/io_uring/cmd_net.c
index 7cd411fc4f33..90d4ec7cc761 100644
--- a/io_uring/cmd_net.c
+++ b/io_uring/cmd_net.c
@@ -69,8 +69,8 @@ static inline int io_uring_cmd_setsockopt(struct socket *sock,
optlen);
}
-static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
- struct sk_buff *skb, unsigned issue_flags)
+static int io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
+ struct sk_buff *skb, unsigned int issue_flags)
{
struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
struct io_uring_cqe cqe[2];
@@ -83,7 +83,7 @@ static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
ret = skb_get_tx_timestamp(skb, sk, &ts);
if (ret < 0)
- return false;
+ return ret;
tskey = serr->ee.ee_data;
tstype = serr->ee.ee_info;
@@ -98,7 +98,9 @@ static bool io_process_timestamp_skb(struct io_uring_cmd *cmd, struct sock *sk,
iots = (struct io_timespec *)&cqe[1];
iots->tv_sec = ts.tv_sec;
iots->tv_nsec = ts.tv_nsec;
- return io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe);
+ if (!io_uring_cmd_post_mshot_cqe32(cmd, issue_flags, cqe))
+ return -ENOBUFS;
+ return 0;
}
static int io_uring_cmd_timestamp(struct socket *sock,
@@ -135,7 +137,8 @@ static int io_uring_cmd_timestamp(struct socket *sock,
skb = skb_peek(&list);
if (!skb)
break;
- if (!io_process_timestamp_skb(cmd, sk, skb, issue_flags))
+ ret = io_process_timestamp_skb(cmd, sk, skb, issue_flags);
+ if (ret)
break;
__skb_dequeue(&list);
consume_skb(skb);
@@ -145,6 +148,12 @@ static int io_uring_cmd_timestamp(struct socket *sock,
scoped_guard(spinlock_irqsave, &q->lock)
skb_queue_splice(&list, q);
}
+ /*
+ * Aux CQEs cannot overflow and the poll is edge triggered, so nothing
+ * re-runs the command once the CQ drains. End the multishot instead.
+ */
+ if (ret == -ENOBUFS)
+ return -ENOBUFS;
return -EAGAIN;
}
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index 61053421d809..cbea989c5a7c 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -733,7 +733,8 @@ bool io_cqe_cache_refill(struct io_ring_ctx *ctx, bool overflow, bool cqe32)
* Post dummy CQE if a 32b CQE is needed and there's only room for a
* 16b CQE before the ring wraps.
*/
- if (cqe32 && off + 1 == ctx->cq_entries) {
+ if (cqe32 && (ctx->flags & IORING_SETUP_CQE_MIXED) &&
+ off + 1 == ctx->cq_entries) {
if (!io_fill_nop_cqe(ctx, off))
return false;
off = 0;
@@ -742,12 +743,13 @@ bool io_cqe_cache_refill(struct io_ring_ctx *ctx, bool overflow, bool cqe32)
free = ctx->cq_entries - io_cqring_queued(ctx);
/* we need a contiguous range, limit based on the current array offset */
len = min(free, ctx->cq_entries - off);
- if (len < (cqe32 + 1))
- return false;
-
if (ctx->flags & IORING_SETUP_CQE32) {
+ if (!len)
+ return false;
off <<= 1;
len <<= 1;
+ } else if (len < (cqe32 + 1)) {
+ return false;
}
ctx->cqe_cached = &rings->cqes[off];
@@ -781,7 +783,7 @@ static bool io_fill_cqe_aux(struct io_ring_ctx *ctx, u64 user_data, s32 res,
WRITE_ONCE(cqe->res, res);
WRITE_ONCE(cqe->flags, cflags);
- if (cqe32) {
+ if (cqe32 || (ctx->flags & IORING_SETUP_CQE32)) {
WRITE_ONCE(cqe->big_cqe[0], 0);
WRITE_ONCE(cqe->big_cqe[1], 0);
}
@@ -2403,8 +2405,8 @@ static __cold void io_ring_exit_work(struct work_struct *work)
spin_lock(&ctx->completion_lock);
spin_unlock(&ctx->completion_lock);
- /* pairs with RCU read section in io_req_local_work_add() */
- if (ctx->flags & IORING_SETUP_DEFER_TASKRUN)
+ /* pairs with the RCU read sections in the task_work add paths */
+ if (ctx->flags & (IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SQPOLL))
synchronize_rcu();
io_ring_ctx_free(ctx);
diff --git a/io_uring/loop.c b/io_uring/loop.c
index bbbb6ef14e6a..3d472b7827fa 100644
--- a/io_uring/loop.c
+++ b/io_uring/loop.c
@@ -2,6 +2,7 @@
#include "io_uring.h"
#include "wait.h"
#include "loop.h"
+#include "tctx.h"
static inline int io_loop_nr_cqes(const struct io_ring_ctx *ctx,
const struct iou_loop_params *lp)
@@ -81,6 +82,10 @@ int io_run_loop(struct io_ring_ctx *ctx)
{
int ret;
+ ret = io_uring_add_tctx_node(ctx);
+ if (unlikely(ret))
+ return ret;
+
if (!io_allowed_run_tw(ctx))
return -EEXIST;
diff --git a/io_uring/tw.c b/io_uring/tw.c
index f573bcc3af6a..f9affd68dd26 100644
--- a/io_uring/tw.c
+++ b/io_uring/tw.c
@@ -210,6 +210,9 @@ void io_req_normal_work_add(struct io_kiocb *req)
struct io_uring_task *tctx = req->tctx;
struct io_ring_ctx *ctx = req->ctx;
+ /* SQPOLL can retire the request on push, see io_ring_exit_work() */
+ guard(rcu)();
+
/* tw run already pending, nothing else to do */
if (!mpscq_push(&tctx->task_list, &req->io_task_work.node))
return;
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 86d580d4410d..dcff0b995d34 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -374,6 +374,7 @@ struct io_zcrx_args {
struct io_kiocb *req;
struct io_zcrx_ifq *ifq;
unsigned nr_skbs;
+ bool stopped_early;
};
static const struct memory_provider_ops io_uring_pp_zc_ops;
@@ -1899,6 +1900,9 @@ io_zcrx_recv_skb(read_descriptor_t *desc, struct sk_buff *skb,
}
out:
+ /* Bytes left in len mean an error stopped the walk early. */
+ if (len)
+ args->stopped_early = true;
if (offset == start_off)
return ret;
desc->count -= (offset - start_off);
@@ -1935,8 +1939,9 @@ static int io_zcrx_tcp_recvmsg(struct io_kiocb *req, struct io_zcrx_ifq *ifq,
ret = -ENOTCONN;
else
ret = -EAGAIN;
- } else if (unlikely(args.nr_skbs > IO_SKBS_PER_CALL_LIMIT) &&
- (issue_flags & IO_URING_F_MULTISHOT)) {
+ } else if ((issue_flags & IO_URING_F_MULTISHOT) &&
+ (unlikely(args.nr_skbs > IO_SKBS_PER_CALL_LIMIT) ||
+ args.stopped_early)) {
ret = IOU_REQUEUE;
} else if (sock_flag(sk, SOCK_DONE)) {
/* Make it to retry until it finally gets 0. */
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 1ed19b775912..f2e81be8265e 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -545,22 +545,38 @@ static void kill_rules(struct audit_context *context, struct audit_tree *tree)
{
struct audit_krule *rule, *next;
struct audit_entry *entry;
+ bool need_sync = false;
list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
entry = container_of(rule, struct audit_entry, rule);
- list_del_init(&rule->rlist);
if (rule->tree) {
/* not a half-baked one */
audit_tree_log_remove_rule(context, rule);
- if (entry->rule.exe)
- audit_remove_mark(entry->rule.exe);
rule->tree = NULL;
list_del_rcu(&entry->list);
list_del(&entry->rule.list);
- call_rcu(&entry->rcu, audit_free_rule_rcu);
+ if (entry->rule.exe)
+ need_sync = true;
+ } else {
+ list_del_init(&rule->rlist);
}
}
+
+ if (list_empty(&tree->rules))
+ return;
+
+ if (need_sync)
+ synchronize_rcu();
+
+ list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
+ entry = container_of(rule, struct audit_entry, rule);
+
+ list_del_init(&rule->rlist);
+ if (entry->rule.exe)
+ audit_remove_mark(entry->rule.exe);
+ call_rcu(&entry->rcu, audit_free_rule_rcu);
+ }
}
/*
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 2e3bf8113ae9..7f11555a5070 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1362,7 +1362,7 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
{
struct bpf_insn *to = to_buff;
u32 imm_rnd = get_random_u32();
- s16 off;
+ int off;
BUILD_BUG_ON(BPF_REG_PARAMS + 2 != MAX_BPF_JIT_REG);
BUILD_BUG_ON(BPF_REG_AX + 1 != MAX_BPF_JIT_REG);
@@ -1438,6 +1438,8 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
off = from->off;
if (off < 0)
off -= 2;
+ if (off < S16_MIN)
+ return -ERANGE;
*to++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ from->imm);
*to++ = BPF_ALU64_IMM(BPF_XOR, BPF_REG_AX, imm_rnd);
*to++ = BPF_JMP_REG(from->code, from->dst_reg, BPF_REG_AX, off);
@@ -1458,6 +1460,8 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from,
off = from->off;
if (off < 0)
off -= 2;
+ if (off < S16_MIN)
+ return -ERANGE;
*to++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_AX, imm_rnd ^ from->imm);
*to++ = BPF_ALU32_IMM(BPF_XOR, BPF_REG_AX, imm_rnd);
*to++ = BPF_JMP32_REG(from->code, from->dst_reg, BPF_REG_AX,
@@ -1606,7 +1610,9 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
if (!rewritten)
continue;
- if (env)
+ if (rewritten < 0)
+ tmp = ERR_PTR(rewritten);
+ else if (env)
tmp = bpf_patch_insn_data(env, i, insn_buff, rewritten);
else
tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten);
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index f9464e566f10..13a2356c84cf 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -128,6 +128,7 @@ struct htab_elem {
struct htab_btf_record {
struct btf_record *record;
+ struct btf *btf;
u32 key_size;
};
@@ -497,8 +498,13 @@ static void htab_dtor_ctx_free(void *ctx)
{
struct htab_btf_record *hrec = ctx;
+ /*
+ * The duplicated record still points into the map BTF, so free it
+ * before dropping the reference that keeps that BTF alive.
+ */
btf_record_free(hrec->record);
- kfree(ctx);
+ btf_put(hrec->btf);
+ kfree(hrec);
}
static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma,
@@ -521,6 +527,15 @@ static int bpf_ma_set_dtor(struct bpf_map *map, struct bpf_mem_alloc *ma,
kfree(hrec);
return err;
}
+ /*
+ * btf_record_dup() only acquires kernel and module BTF. Fields whose
+ * types live in the map BTF keep pointing into it: kptrs to local
+ * types refer to map->btf, and graph roots carry a value record owned
+ * by its struct meta table. The context can outlive the map when the
+ * allocator defers its teardown, so hold a reference of our own.
+ */
+ hrec->btf = map->btf;
+ btf_get(hrec->btf);
bpf_mem_alloc_set_dtor(ma, dtor, htab_dtor_ctx_free, hrec);
return 0;
}
@@ -3359,8 +3374,10 @@ static int __rhtab_map_lookup_and_delete_batch(struct bpf_map *map,
}
if (do_delete) {
+ migrate_disable();
for (i = 0; i < total; i++)
rhtab_delete_elem(rhtab, del_elems[i], NULL, 0);
+ migrate_enable();
}
rcu_read_unlock();
diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c
index 8a8f088e83e6..15684d0fc883 100644
--- a/kernel/bpf/memalloc.c
+++ b/kernel/bpf/memalloc.c
@@ -118,6 +118,11 @@ struct bpf_mem_cache {
struct llist_head free_by_rcu_ttrace;
struct llist_head waiting_for_gp_ttrace;
struct rcu_head rcu_ttrace;
+ /*
+ * 0 - idle
+ * 1 - __free_rcu() is queued
+ * 2 - __free_rcu() is queued and free_by_rcu_ttrace got more objects since
+ */
atomic_t call_rcu_ttrace_in_progress;
raw_spinlock_t lock;
};
@@ -276,6 +281,8 @@ static int free_all(struct bpf_mem_cache *c, struct llist_node *llnode, bool per
return cnt;
}
+static void __do_call_rcu_ttrace(struct bpf_mem_cache *c);
+
static void __free_rcu(struct rcu_head *head)
{
struct bpf_mem_cache *c = container_of(head, struct bpf_mem_cache, rcu_ttrace);
@@ -285,7 +292,19 @@ static void __free_rcu(struct rcu_head *head)
llnode = llist_del_all(&c->waiting_for_gp_ttrace);
free_all(c, llnode, !!c->percpu_size);
- atomic_set(&c->call_rcu_ttrace_in_progress, 0);
+
+ /*
+ * do_call_rcu_ttrace() that ran while GP was in flight left its objects
+ * in free_by_rcu_ttrace. This cache may never free or alloc in bulk
+ * again, so start the next GP from here.
+ * 'c' can be freed as soon as call_rcu_ttrace_in_progress is zero.
+ */
+ if (atomic_cmpxchg(&c->call_rcu_ttrace_in_progress, 1, 0) == 1)
+ return;
+
+ /* Pairs with synchronize_rcu() in free_mem_alloc() */
+ guard(rcu)();
+ __do_call_rcu_ttrace(c);
}
static void enque_to_free(struct bpf_mem_cache *c, void *obj)
@@ -298,18 +317,15 @@ static void enque_to_free(struct bpf_mem_cache *c, void *obj)
llist_add(llnode, &c->free_by_rcu_ttrace);
}
-static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
+static void __do_call_rcu_ttrace(struct bpf_mem_cache *c)
{
struct llist_node *llnode, *t;
- if (atomic_xchg(&c->call_rcu_ttrace_in_progress, 1)) {
- if (unlikely(READ_ONCE(c->draining))) {
- scoped_guard(raw_spinlock_irqsave, &c->lock)
- llnode = llist_del_all(&c->free_by_rcu_ttrace);
- free_all(c, llnode, !!c->percpu_size);
- }
- return;
- }
+ /*
+ * Must be done before llist_del_all(). Objects that it misses were
+ * added by do_call_rcu_ttrace() that will set 2 after this store.
+ */
+ atomic_set(&c->call_rcu_ttrace_in_progress, 1);
WARN_ON_ONCE(!llist_empty(&c->waiting_for_gp_ttrace));
llist_for_each_safe(llnode, t, llist_del_all(&c->free_by_rcu_ttrace))
@@ -328,6 +344,22 @@ static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
call_rcu_tasks_trace(&c->rcu_ttrace, __free_rcu);
}
+static void do_call_rcu_ttrace(struct bpf_mem_cache *c)
+{
+ struct llist_node *llnode;
+
+ if (atomic_xchg(&c->call_rcu_ttrace_in_progress, 2)) {
+ if (unlikely(READ_ONCE(c->draining))) {
+ scoped_guard(raw_spinlock_irqsave, &c->lock)
+ llnode = llist_del_all(&c->free_by_rcu_ttrace);
+ free_all(c, llnode, !!c->percpu_size);
+ }
+ return;
+ }
+
+ __do_call_rcu_ttrace(c);
+}
+
static void free_bulk(struct bpf_mem_cache *c)
{
struct bpf_mem_cache *tgt = c->tgt;
@@ -700,7 +732,12 @@ static void free_mem_alloc(struct bpf_mem_alloc *ma)
* to wait for the pending __free_by_rcu(), and __free_rcu(). RCU Tasks
* Trace grace period implies RCU grace period, so all __free_rcu don't
* need extra call_rcu() (and thus extra rcu_barrier() here).
+ *
+ * __free_rcu() queues itself again unless it sees 'draining'. After
+ * synchronize_rcu() it either did that already or will not do it, so
+ * rcu_barrier_tasks_trace() cannot miss it.
*/
+ synchronize_rcu();
rcu_barrier(); /* wait for __free_by_rcu */
rcu_barrier_tasks_trace(); /* wait for __free_rcu */
free_mem_alloc_no_barrier(ma);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 244a939b9d2d..96217b99399d 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2448,6 +2448,21 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
bpf_prog_free(aux->prog);
}
+/*
+ * Progs called from a trampoline can also be reached by a task that was
+ * preempted in the trampoline before the prog's enter helper took its RCU
+ * read lock, wait for those first.
+ */
+static void __bpf_prog_put_rcu_tasks(struct rcu_head *rcu)
+{
+ struct bpf_prog *prog = container_of(rcu, struct bpf_prog_aux, rcu)->prog;
+
+ if (prog->sleepable)
+ call_rcu_tasks_trace(rcu, __bpf_prog_put_rcu);
+ else
+ call_rcu(rcu, __bpf_prog_put_rcu);
+}
+
static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
{
bpf_prog_kallsyms_del_all(prog);
@@ -2461,7 +2476,9 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
btf_put(prog->aux->attach_btf);
if (deferred) {
- if (prog->sleepable)
+ if (IS_ENABLED(CONFIG_TASKS_RCU) && prog->aux->tramp_linked)
+ call_rcu_tasks(&prog->aux->rcu, __bpf_prog_put_rcu_tasks);
+ else if (prog->sleepable)
call_rcu_tasks_trace(&prog->aux->rcu, __bpf_prog_put_rcu);
else
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 90b70ea0d370..bf4cb3dd444d 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -401,6 +401,7 @@ static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip)
head = &trampoline_ip_table[hash_64(tr->ip, TRAMPOLINE_HASH_BITS)];
hlist_add_head(&tr->hlist_ip, head);
refcount_set(&tr->refcnt, 1);
+ INIT_LIST_HEAD(&tr->images);
for (i = 0; i < BPF_TRAMP_MAX; i++)
INIT_HLIST_HEAD(&tr->progs_hlist[i]);
out:
@@ -565,15 +566,22 @@ static void bpf_tramp_image_free(struct bpf_tramp_image *im)
arch_free_bpf_trampoline(im->image, im->size);
bpf_jit_uncharge_modmem(im->size);
percpu_ref_exit(&im->pcref);
+ kfree(im->skips);
kfree_rcu(im, rcu);
}
static void __bpf_tramp_image_put_deferred(struct work_struct *work)
{
struct bpf_tramp_image *im;
+ struct bpf_trampoline *tr;
im = container_of(work, struct bpf_tramp_image, work);
+ tr = im->tr;
+ trampoline_lock(tr);
+ list_del(&im->list);
+ trampoline_unlock(tr);
bpf_tramp_image_free(im);
+ bpf_trampoline_put(tr);
}
/* callback, fexit step 3 or fentry step 2 */
@@ -601,7 +609,7 @@ static void __bpf_tramp_image_put_rcu_tasks(struct rcu_head *rcu)
struct bpf_tramp_image *im;
im = container_of(rcu, struct bpf_tramp_image, rcu);
- if (im->ip_after_call)
+ if (im->call_orig)
/* the case of fmod_ret/fexit trampoline and CONFIG_PREEMPTION=y */
percpu_ref_kill(&im->pcref);
else
@@ -621,9 +629,9 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im)
*
* The trampoline is unreachable before bpf_tramp_image_put().
*
- * First, patch the trampoline to avoid calling into fexit progs.
- * The progs will be freed even if the original function is still
- * executing or sleeping.
+ * Progs are patched out of the image when they are detached, see
+ * bpf_trampoline_skip_prog(), so they can be freed even if a task is
+ * still in the image.
* In case of CONFIG_PREEMPT=y use call_rcu_tasks() to wait on
* first few asm instructions to execute and call into
* __bpf_tramp_enter->percpu_ref_get.
@@ -637,11 +645,7 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im)
* percpu_ref_kill will be waiting for. Hence the first
* call_rcu_tasks() is not necessary.
*/
- if (im->ip_after_call) {
- int err = bpf_arch_text_poke(im->ip_after_call, BPF_MOD_NOP,
- BPF_MOD_JUMP, NULL,
- im->ip_epilogue);
- WARN_ON(err);
+ if (im->call_orig) {
if (IS_ENABLED(CONFIG_TASKS_RCU))
call_rcu_tasks(&im->rcu, __bpf_tramp_image_put_rcu_tasks);
else
@@ -658,7 +662,7 @@ static void bpf_tramp_image_put(struct bpf_tramp_image *im)
call_rcu_tasks_trace(&im->rcu, __bpf_tramp_image_put_rcu_tasks);
}
-static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size)
+static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size, int nr_progs)
{
struct bpf_tramp_image *im;
struct bpf_ksym *ksym;
@@ -669,6 +673,10 @@ static struct bpf_tramp_image *bpf_tramp_image_alloc(u64 key, int size)
if (!im)
goto out;
+ im->skips = kzalloc_objs(*im->skips, nr_progs);
+ if (!im->skips)
+ goto out_free_im;
+
err = bpf_jit_charge_modmem(size);
if (err)
goto out_free_im;
@@ -695,6 +703,7 @@ out_free_image:
out_uncharge:
bpf_jit_uncharge_modmem(size);
out_free_im:
+ kfree(im->skips);
kfree(im);
out:
return ERR_PTR(err);
@@ -771,11 +780,12 @@ again:
goto out;
}
- im = bpf_tramp_image_alloc(tr->key, size);
+ im = bpf_tramp_image_alloc(tr->key, size, total);
if (IS_ERR(im)) {
err = PTR_ERR(im);
goto out;
}
+ im->call_orig = tr->flags & BPF_TRAMP_F_CALL_ORIG;
err = arch_prepare_bpf_trampoline(im, im->image, im->image + size,
&tr->func.model, tr->flags, tnodes,
@@ -806,8 +816,14 @@ again:
#endif
out_free:
- if (err)
+ if (err) {
bpf_tramp_image_free(im);
+ } else {
+ /* track the image until it is freed, for bpf_trampoline_skip_prog() */
+ refcount_inc(&tr->refcnt);
+ im->tr = tr;
+ list_add(&im->list, &tr->images);
+ }
out:
/* If any error happens, restore previous flags */
if (err)
@@ -907,6 +923,7 @@ static int bpf_trampoline_add_prog(struct bpf_trampoline *tr,
}
hlist_add_head(&node->tramp_hlist, prog_list);
+ node->link->prog->aux->tramp_linked = true;
if (kind == BPF_TRAMP_FSESSION) {
tr->progs_cnt[BPF_TRAMP_FENTRY]++;
fexit = fsession_exit(node);
@@ -920,6 +937,41 @@ static int bpf_trampoline_add_prog(struct bpf_trampoline *tr,
return 0;
}
+/*
+ * Patch the nop in front of a prog call to a jump over it. A task can be
+ * preempted anywhere in the image, so archs that need several instructions for
+ * a jump of any range patch a single near branch here instead.
+ */
+int __weak arch_bpf_trampoline_skip(void *nop, void *target)
+{
+ return bpf_arch_text_poke(nop, BPF_MOD_NOP, BPF_MOD_JUMP, NULL, target);
+}
+
+/*
+ * prog was detached and can be freed, but tasks may still be running in images
+ * that call it, sleeping in an earlier prog for example. They can be in any
+ * image that is not freed yet, not only in cur_image, so patch all of them to
+ * jump over prog.
+ */
+static void bpf_trampoline_skip_prog(struct bpf_trampoline *tr, struct bpf_prog *prog)
+{
+ struct bpf_tramp_image *im;
+ int i, err;
+
+ list_for_each_entry(im, &tr->images, list) {
+ for (i = 0; i < im->nr_skips; i++) {
+ struct bpf_tramp_skip *skip = &im->skips[i];
+
+ if (skip->prog != prog)
+ continue;
+ err = arch_bpf_trampoline_skip(skip->nop, skip->target);
+ WARN_ON_ONCE(err);
+ /* not a nop anymore, and prog's address can be reused */
+ skip->prog = NULL;
+ }
+ }
+}
+
static void bpf_trampoline_remove_prog(struct bpf_trampoline *tr,
struct bpf_tramp_node *node)
{
@@ -937,6 +989,7 @@ static void bpf_trampoline_remove_prog(struct bpf_trampoline *tr,
}
hlist_del_init(&node->tramp_hlist);
tr->progs_cnt[kind]--;
+ bpf_trampoline_skip_prog(tr, node->link->prog);
}
static int __bpf_trampoline_link_prog(struct bpf_tramp_node *node,
@@ -1245,11 +1298,9 @@ void bpf_trampoline_put(struct bpf_trampoline *tr)
if (WARN_ON_ONCE(!hlist_empty(&tr->progs_hlist[i])))
goto out;
- /* This code will be executed even when the last bpf_tramp_image
- * is alive. All progs are detached from the trampoline and the
- * trampoline image is patched with jmp into epilogue to skip
- * fexit progs. The fentry-only trampoline will be freed via
- * multiple rcu callbacks.
+ /*
+ * All progs are detached and the last image has been freed, images
+ * hold a reference on the trampoline until then.
*/
hlist_del(&tr->hlist_key);
hlist_del(&tr->hlist_ip);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 41b49c56e123..5f874979b8d7 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -14826,7 +14826,15 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn
"Tighten the scalar bounds before the arithmetic so the resulting pointer remains within the allowed range.");
return -EINVAL;
}
- reg_bounds_sync(dst_reg);
+ /*
+ * A packet pointer that keeps its id or range is checked against a
+ * range set from the checked pointer's umax, so var_off must not tighten
+ * its umax. r32 must still match var_off for reg_bounds_sanity_check().
+ */
+ if (reg_is_pkt_pointer(dst_reg) && (known || dst_reg->range > 0))
+ __update_reg32_bounds(dst_reg);
+ else
+ reg_bounds_sync(dst_reg);
bounds_ret = sanitize_check_bounds(env, insn, dst_reg);
if (bounds_ret == -EACCES)
return bounds_ret;
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 634d2ccbab82..7846d70be57f 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7626,9 +7626,11 @@ static void perf_sigtrap(struct perf_event *event)
{
/*
* Both perf_pending_task() and perf_pending_irq() can race with the
- * task exiting.
+ * task exiting or exec-ing. We can determine if such a race has
+ * occurred by checking if perf_event_exit_task(), which will set
+ * ctx->task to TASK_TOMBSTONE, has already been called.
*/
- if (current->flags & PF_EXITING)
+ if (event->ctx->task == TASK_TOMBSTONE)
return;
/*
@@ -8129,10 +8131,15 @@ static void __perf_event_header__init_id(struct perf_sample_data *data,
}
}
-void perf_event_header__init_id(struct perf_event_header *header,
- struct perf_sample_data *data,
- struct perf_event *event)
+void perf_event_header__init(struct perf_event_header *header,
+ struct perf_sample_data *data,
+ u32 type, u16 misc, u16 size,
+ struct perf_event *event)
{
+ header->type = type;
+ header->misc = misc;
+ header->size = size;
+
if (event->attr.sample_id_all) {
header->size += event->id_header_size;
__perf_event_header__init_id(data, event, event->attr.sample_type);
@@ -8969,17 +8976,16 @@ perf_event_read_event(struct perf_event *event,
struct perf_output_handle handle;
struct perf_sample_data sample;
struct perf_read_event read_event = {
- .header = {
- .type = PERF_RECORD_READ,
- .misc = 0,
- .size = sizeof(read_event) + event->read_size,
- },
.pid = perf_event_pid(event, task),
.tid = perf_event_tid(event, task),
};
int ret;
- perf_event_header__init_id(&read_event.header, &sample, event);
+ perf_event_header__init(&read_event.header, &sample,
+ PERF_RECORD_READ,
+ /* misc= */ 0,
+ sizeof(read_event) + event->read_size,
+ event);
ret = perf_output_begin(&handle, &sample, event, read_event.header.size);
if (ret)
return;
@@ -9220,6 +9226,7 @@ struct perf_task_event {
u32 ptid;
u64 time;
} event_id;
+ int new;
};
static int perf_event_task_match(struct perf_event *event)
@@ -9236,17 +9243,21 @@ static void perf_event_task_output(struct perf_event *event,
struct perf_output_handle handle;
struct perf_sample_data sample;
struct task_struct *task = task_event->task;
- int ret, size = task_event->event_id.header.size;
+ int ret;
if (!perf_event_task_match(event))
return;
- perf_event_header__init_id(&task_event->event_id.header, &sample, event);
+ perf_event_header__init(&task_event->event_id.header, &sample,
+ task_event->new ? PERF_RECORD_FORK : PERF_RECORD_EXIT,
+ /* misc= */ 0,
+ sizeof(task_event->event_id),
+ event);
ret = perf_output_begin(&handle, &sample, event,
task_event->event_id.header.size);
if (ret)
- goto out;
+ return;
task_event->event_id.pid = perf_event_pid(event, task);
task_event->event_id.tid = perf_event_tid(event, task);
@@ -9268,8 +9279,6 @@ static void perf_event_task_output(struct perf_event *event,
perf_event__output_id_sample(event, &handle, &sample);
perf_output_end(&handle);
-out:
- task_event->event_id.header.size = size;
}
static void perf_event_task(struct task_struct *task,
@@ -9286,18 +9295,7 @@ static void perf_event_task(struct task_struct *task,
task_event = (struct perf_task_event){
.task = task,
.task_ctx = task_ctx,
- .event_id = {
- .header = {
- .type = new ? PERF_RECORD_FORK : PERF_RECORD_EXIT,
- .misc = 0,
- .size = sizeof(task_event.event_id),
- },
- /* .pid */
- /* .ppid */
- /* .tid */
- /* .ptid */
- /* .time */
- },
+ .new = new,
};
perf_iterate_sb(perf_event_task_output,
@@ -9374,6 +9372,7 @@ struct perf_comm_event {
u32 pid;
u32 tid;
} event_id;
+ bool exec;
};
static int perf_event_comm_match(struct perf_event *event)
@@ -9387,18 +9386,21 @@ static void perf_event_comm_output(struct perf_event *event,
struct perf_comm_event *comm_event = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
- int size = comm_event->event_id.header.size;
int ret;
if (!perf_event_comm_match(event))
return;
- perf_event_header__init_id(&comm_event->event_id.header, &sample, event);
+ perf_event_header__init(&comm_event->event_id.header, &sample,
+ PERF_RECORD_COMM,
+ comm_event->exec ? PERF_RECORD_MISC_COMM_EXEC : 0,
+ sizeof(comm_event->event_id) + comm_event->comm_size,
+ event);
ret = perf_output_begin(&handle, &sample, event,
comm_event->event_id.header.size);
if (ret)
- goto out;
+ return;
comm_event->event_id.pid = perf_event_pid(event, comm_event->task);
comm_event->event_id.tid = perf_event_tid(event, comm_event->task);
@@ -9410,8 +9412,6 @@ static void perf_event_comm_output(struct perf_event *event,
perf_event__output_id_sample(event, &handle, &sample);
perf_output_end(&handle);
-out:
- comm_event->event_id.header.size = size;
}
static void perf_event_comm_event(struct perf_comm_event *comm_event)
@@ -9426,8 +9426,6 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event)
comm_event->comm = comm;
comm_event->comm_size = size;
- comm_event->event_id.header.size = sizeof(comm_event->event_id) + size;
-
perf_iterate_sb(perf_event_comm_output,
comm_event,
NULL);
@@ -9444,15 +9442,8 @@ void perf_event_comm(struct task_struct *task, bool exec)
.task = task,
/* .comm */
/* .comm_size */
- .event_id = {
- .header = {
- .type = PERF_RECORD_COMM,
- .misc = exec ? PERF_RECORD_MISC_COMM_EXEC : 0,
- /* .size */
- },
- /* .pid */
- /* .tid */
- },
+ /* .event_id */
+ .exec = exec,
};
perf_event_comm_event(&comm_event);
@@ -9486,18 +9477,20 @@ static void perf_event_namespaces_output(struct perf_event *event,
struct perf_namespaces_event *namespaces_event = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
- u16 header_size = namespaces_event->event_id.header.size;
int ret;
if (!perf_event_namespaces_match(event))
return;
- perf_event_header__init_id(&namespaces_event->event_id.header,
- &sample, event);
+ perf_event_header__init(&namespaces_event->event_id.header, &sample,
+ PERF_RECORD_NAMESPACES,
+ /* misc= */ 0,
+ sizeof(namespaces_event->event_id),
+ event);
ret = perf_output_begin(&handle, &sample, event,
namespaces_event->event_id.header.size);
if (ret)
- goto out;
+ return;
namespaces_event->event_id.pid = perf_event_pid(event,
namespaces_event->task);
@@ -9509,8 +9502,6 @@ static void perf_event_namespaces_output(struct perf_event *event,
perf_event__output_id_sample(event, &handle, &sample);
perf_output_end(&handle);
-out:
- namespaces_event->event_id.header.size = header_size;
}
static void perf_fill_ns_link_info(struct perf_ns_link_info *ns_link_info,
@@ -9541,11 +9532,7 @@ void perf_event_namespaces(struct task_struct *task)
namespaces_event = (struct perf_namespaces_event){
.task = task,
.event_id = {
- .header = {
- .type = PERF_RECORD_NAMESPACES,
- .misc = 0,
- .size = sizeof(namespaces_event.event_id),
- },
+ /* .header */
/* .pid */
/* .tid */
.nr_namespaces = NR_NAMESPACES,
@@ -9613,18 +9600,19 @@ static void perf_event_cgroup_output(struct perf_event *event, void *data)
struct perf_cgroup_event *cgroup_event = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
- u16 header_size = cgroup_event->event_id.header.size;
int ret;
+ u16 size = sizeof(cgroup_event->event_id) + cgroup_event->path_size;
if (!perf_event_cgroup_match(event))
return;
- perf_event_header__init_id(&cgroup_event->event_id.header,
- &sample, event);
+ perf_event_header__init(&cgroup_event->event_id.header, &sample,
+ PERF_RECORD_CGROUP, /* misc= */ 0, size,
+ event);
ret = perf_output_begin(&handle, &sample, event,
cgroup_event->event_id.header.size);
if (ret)
- goto out;
+ return;
perf_output_put(&handle, cgroup_event->event_id);
__output_copy(&handle, cgroup_event->path, cgroup_event->path_size);
@@ -9632,8 +9620,6 @@ static void perf_event_cgroup_output(struct perf_event *event, void *data)
perf_event__output_id_sample(event, &handle, &sample);
perf_output_end(&handle);
-out:
- cgroup_event->event_id.header.size = header_size;
}
static void perf_event_cgroup(struct cgroup *cgrp)
@@ -9648,11 +9634,6 @@ static void perf_event_cgroup(struct cgroup *cgrp)
cgroup_event = (struct perf_cgroup_event){
.event_id = {
- .header = {
- .type = PERF_RECORD_CGROUP,
- .misc = 0,
- .size = sizeof(cgroup_event.event_id),
- },
.id = cgroup_id(cgrp),
},
};
@@ -9675,7 +9656,6 @@ static void perf_event_cgroup(struct cgroup *cgrp)
while (!IS_ALIGNED(size, sizeof(u64)))
cgroup_event.path[size++] = '\0';
- cgroup_event.event_id.header.size += size;
cgroup_event.path_size = size;
perf_iterate_sb(perf_event_cgroup_output,
@@ -9731,38 +9711,40 @@ static void perf_event_mmap_output(struct perf_event *event,
struct perf_mmap_event *mmap_event = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
- int size = mmap_event->event_id.header.size;
- u32 type = mmap_event->event_id.header.type;
- bool use_build_id;
+ int size = sizeof(mmap_event->event_id) + mmap_event->file_size;
+ u32 type = PERF_RECORD_MMAP;
+ u16 misc = PERF_RECORD_MISC_USER;
+ bool use_build_id = false;
int ret;
if (!perf_event_mmap_match(event, data))
return;
if (event->attr.mmap2) {
- mmap_event->event_id.header.type = PERF_RECORD_MMAP2;
- mmap_event->event_id.header.size += sizeof(mmap_event->maj);
- mmap_event->event_id.header.size += sizeof(mmap_event->min);
- mmap_event->event_id.header.size += sizeof(mmap_event->ino);
- mmap_event->event_id.header.size += sizeof(mmap_event->ino_generation);
- mmap_event->event_id.header.size += sizeof(mmap_event->prot);
- mmap_event->event_id.header.size += sizeof(mmap_event->flags);
- }
-
- perf_event_header__init_id(&mmap_event->event_id.header, &sample, event);
+ type = PERF_RECORD_MMAP2;
+ size += sizeof(mmap_event->maj);
+ size += sizeof(mmap_event->min);
+ size += sizeof(mmap_event->ino);
+ size += sizeof(mmap_event->ino_generation);
+ size += sizeof(mmap_event->prot);
+ size += sizeof(mmap_event->flags);
+ use_build_id = event->attr.build_id && mmap_event->build_id_size;
+ if (use_build_id)
+ misc |= PERF_RECORD_MISC_MMAP_BUILD_ID;
+ }
+ if (!(mmap_event->vma->vm_flags & VM_EXEC))
+ misc |= PERF_RECORD_MISC_MMAP_DATA;
+
+ perf_event_header__init(&mmap_event->event_id.header, &sample,
+ type, misc, size, event);
ret = perf_output_begin(&handle, &sample, event,
mmap_event->event_id.header.size);
if (ret)
- goto out;
+ return;
mmap_event->event_id.pid = perf_event_pid(event, current);
mmap_event->event_id.tid = perf_event_tid(event, current);
- use_build_id = event->attr.build_id && mmap_event->build_id_size;
-
- if (event->attr.mmap2 && use_build_id)
- mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_BUILD_ID;
-
perf_output_put(&handle, mmap_event->event_id);
if (event->attr.mmap2) {
@@ -9787,9 +9769,6 @@ static void perf_event_mmap_output(struct perf_event *event,
perf_event__output_id_sample(event, &handle, &sample);
perf_output_end(&handle);
-out:
- mmap_event->event_id.header.size = size;
- mmap_event->event_id.header.type = type;
}
static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
@@ -9885,11 +9864,6 @@ got_name:
mmap_event->prot = prot;
mmap_event->flags = flags;
- if (!(vma->vm_flags & VM_EXEC))
- mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_DATA;
-
- mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size;
-
if (atomic_read(&nr_build_id_events))
build_id_parse_nofault(vma, mmap_event->build_id, &mmap_event->build_id_size);
@@ -10009,11 +9983,7 @@ void perf_event_mmap(struct vm_area_struct *vma)
/* .file_name */
/* .file_size */
.event_id = {
- .header = {
- .type = PERF_RECORD_MMAP,
- .misc = PERF_RECORD_MISC_USER,
- /* .size */
- },
+ /* .header */
/* .pid */
/* .tid */
.start = vma->vm_start,
@@ -10043,18 +10013,15 @@ void perf_event_aux_event(struct perf_event *event, unsigned long head,
u64 size;
u64 flags;
} rec = {
- .header = {
- .type = PERF_RECORD_AUX,
- .misc = 0,
- .size = sizeof(rec),
- },
.offset = head,
.size = size,
.flags = flags,
};
int ret;
- perf_event_header__init_id(&rec.header, &sample, event);
+ perf_event_header__init(&rec.header, &sample,
+ PERF_RECORD_AUX, /* misc= */ 0, sizeof(rec),
+ event);
ret = perf_output_begin(&handle, &sample, event, rec.header.size);
if (ret)
@@ -10079,15 +10046,14 @@ void perf_log_lost_samples(struct perf_event *event, u64 lost)
struct perf_event_header header;
u64 lost;
} lost_samples_event = {
- .header = {
- .type = PERF_RECORD_LOST_SAMPLES,
- .misc = 0,
- .size = sizeof(lost_samples_event),
- },
.lost = lost,
};
- perf_event_header__init_id(&lost_samples_event.header, &sample, event);
+ perf_event_header__init(&lost_samples_event.header, &sample,
+ PERF_RECORD_LOST_SAMPLES,
+ /* misc= */ 0,
+ sizeof(lost_samples_event),
+ event);
ret = perf_output_begin(&handle, &sample, event,
lost_samples_event.header.size);
@@ -10112,6 +10078,8 @@ struct perf_switch_event {
u32 next_prev_pid;
u32 next_prev_tid;
} event_id;
+ bool sched_in;
+ bool preempt;
};
static int perf_event_switch_match(struct perf_event *event)
@@ -10124,6 +10092,9 @@ static void perf_event_switch_output(struct perf_event *event, void *data)
struct perf_switch_event *se = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
+ u32 type;
+ u16 misc;
+ u16 size;
int ret;
if (!perf_event_switch_match(event))
@@ -10131,18 +10102,22 @@ static void perf_event_switch_output(struct perf_event *event, void *data)
/* Only CPU-wide events are allowed to see next/prev pid/tid */
if (event->ctx->task) {
- se->event_id.header.type = PERF_RECORD_SWITCH;
- se->event_id.header.size = sizeof(se->event_id.header);
+ type = PERF_RECORD_SWITCH;
+ size = sizeof(se->event_id.header);
} else {
- se->event_id.header.type = PERF_RECORD_SWITCH_CPU_WIDE;
- se->event_id.header.size = sizeof(se->event_id);
+ type = PERF_RECORD_SWITCH_CPU_WIDE;
+ size = sizeof(se->event_id);
se->event_id.next_prev_pid =
perf_event_pid(event, se->next_prev);
se->event_id.next_prev_tid =
perf_event_tid(event, se->next_prev);
}
+ misc = se->sched_in ? 0 : PERF_RECORD_MISC_SWITCH_OUT;
+ if (se->preempt)
+ misc |= PERF_RECORD_MISC_SWITCH_OUT_PREEMPT;
- perf_event_header__init_id(&se->event_id.header, &sample, event);
+ perf_event_header__init(&se->event_id.header, &sample,
+ type, misc, size, event);
ret = perf_output_begin(&handle, &sample, event, se->event_id.header.size);
if (ret)
@@ -10168,22 +10143,11 @@ static void perf_event_switch(struct task_struct *task,
switch_event = (struct perf_switch_event){
.task = task,
.next_prev = next_prev,
- .event_id = {
- .header = {
- /* .type */
- .misc = sched_in ? 0 : PERF_RECORD_MISC_SWITCH_OUT,
- /* .size */
- },
- /* .next_prev_pid */
- /* .next_prev_tid */
- },
+ /* .event_id */
+ .sched_in = sched_in,
+ .preempt = !sched_in && task_is_runnable(task),
};
- if (!sched_in && task_is_runnable(task)) {
- switch_event.event_id.header.misc |=
- PERF_RECORD_MISC_SWITCH_OUT_PREEMPT;
- }
-
perf_iterate_sb(perf_event_switch_output, &switch_event, NULL);
}
@@ -10203,20 +10167,17 @@ static void perf_log_throttle(struct perf_event *event, int enable)
u64 id;
u64 stream_id;
} throttle_event = {
- .header = {
- .type = PERF_RECORD_THROTTLE,
- .misc = 0,
- .size = sizeof(throttle_event),
- },
.time = perf_event_clock(event),
.id = primary_event_id(event),
.stream_id = event->id,
};
- if (enable)
- throttle_event.header.type = PERF_RECORD_UNTHROTTLE;
-
- perf_event_header__init_id(&throttle_event.header, &sample, event);
+ perf_event_header__init(&throttle_event.header, &sample,
+ enable ? PERF_RECORD_UNTHROTTLE
+ : PERF_RECORD_THROTTLE,
+ /* misc= */ 0,
+ sizeof(throttle_event),
+ event);
ret = perf_output_begin(&handle, &sample, event,
throttle_event.header.size);
@@ -10255,12 +10216,14 @@ static void perf_event_ksymbol_output(struct perf_event *event, void *data)
struct perf_output_handle handle;
struct perf_sample_data sample;
int ret;
+ u16 size = sizeof(ksymbol_event->event_id) + ksymbol_event->name_len;
if (!perf_event_ksymbol_match(event))
return;
- perf_event_header__init_id(&ksymbol_event->event_id.header,
- &sample, event);
+ perf_event_header__init(&ksymbol_event->event_id.header, &sample,
+ PERF_RECORD_KSYMBOL, /* misc= */ 0, size,
+ event);
ret = perf_output_begin(&handle, &sample, event,
ksymbol_event->event_id.header.size);
if (ret)
@@ -10301,11 +10264,6 @@ void perf_event_ksymbol(u16 ksym_type, u64 addr, u32 len, bool unregister,
.name = name,
.name_len = name_len,
.event_id = {
- .header = {
- .type = PERF_RECORD_KSYMBOL,
- .size = sizeof(ksymbol_event.event_id) +
- name_len,
- },
.addr = addr,
.len = len,
.ksym_type = ksym_type,
@@ -10349,8 +10307,11 @@ static void perf_event_bpf_output(struct perf_event *event, void *data)
if (!perf_event_bpf_match(event))
return;
- perf_event_header__init_id(&bpf_event->event_id.header,
- &sample, event);
+ perf_event_header__init(&bpf_event->event_id.header, &sample,
+ PERF_RECORD_BPF_EVENT,
+ /* misc= */ 0,
+ sizeof(bpf_event->event_id),
+ event);
ret = perf_output_begin(&handle, &sample, event,
bpf_event->event_id.header.size);
if (ret)
@@ -10406,10 +10367,6 @@ void perf_event_bpf_event(struct bpf_prog *prog,
bpf_event = (struct perf_bpf_event){
.prog = prog,
.event_id = {
- .header = {
- .type = PERF_RECORD_BPF_EVENT,
- .size = sizeof(bpf_event.event_id),
- },
.type = type,
.flags = flags,
.id = prog->aux->id,
@@ -10437,18 +10394,23 @@ static void perf_callchain_deferred_output(struct perf_event *event, void *data)
struct perf_callchain_deferred_event *deferred_event = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
- int ret, size = deferred_event->event.header.size;
+ int ret;
+ u16 size = sizeof(deferred_event->event) + (deferred_event->trace->nr * sizeof(u64));
if (!event->attr.defer_output)
return;
/* XXX do we really need sample_id_all for this ??? */
- perf_event_header__init_id(&deferred_event->event.header, &sample, event);
+ perf_event_header__init(&deferred_event->event.header, &sample,
+ PERF_RECORD_CALLCHAIN_DEFERRED,
+ PERF_RECORD_MISC_USER,
+ size,
+ event);
ret = perf_output_begin(&handle, &sample, event,
deferred_event->event.header.size);
if (ret)
- goto out;
+ return;
perf_output_put(&handle, deferred_event->event);
for (int i = 0; i < deferred_event->trace->nr; i++) {
@@ -10458,8 +10420,6 @@ static void perf_callchain_deferred_output(struct perf_event *event, void *data)
perf_event__output_id_sample(event, &handle, &sample);
perf_output_end(&handle);
-out:
- deferred_event->event.header.size = size;
}
static void perf_unwind_deferred_callback(struct unwind_work *work,
@@ -10468,12 +10428,6 @@ static void perf_unwind_deferred_callback(struct unwind_work *work,
struct perf_callchain_deferred_event deferred_event = {
.trace = trace,
.event = {
- .header = {
- .type = PERF_RECORD_CALLCHAIN_DEFERRED,
- .misc = PERF_RECORD_MISC_USER,
- .size = sizeof(deferred_event.event) +
- (trace->nr * sizeof(u64)),
- },
.cookie = cookie,
.nr = trace->nr,
},
@@ -10485,7 +10439,8 @@ static void perf_unwind_deferred_callback(struct unwind_work *work,
struct perf_text_poke_event {
const void *old_bytes;
const void *new_bytes;
- size_t pad;
+ u16 tot;
+ u16 pad;
u16 old_len;
u16 new_len;
@@ -10506,13 +10461,18 @@ static void perf_event_text_poke_output(struct perf_event *event, void *data)
struct perf_text_poke_event *text_poke_event = data;
struct perf_output_handle handle;
struct perf_sample_data sample;
+ u16 size = sizeof(text_poke_event->event_id) + text_poke_event->tot + text_poke_event->pad;
u64 padding = 0;
int ret;
if (!perf_event_text_poke_match(event))
return;
- perf_event_header__init_id(&text_poke_event->event_id.header, &sample, event);
+ perf_event_header__init(&text_poke_event->event_id.header, &sample,
+ PERF_RECORD_TEXT_POKE,
+ PERF_RECORD_MISC_KERNEL,
+ size,
+ event);
ret = perf_output_begin(&handle, &sample, event,
text_poke_event->event_id.header.size);
@@ -10550,15 +10510,11 @@ void perf_event_text_poke(const void *addr, const void *old_bytes,
text_poke_event = (struct perf_text_poke_event){
.old_bytes = old_bytes,
.new_bytes = new_bytes,
+ .tot = tot,
.pad = pad,
.old_len = old_len,
.new_len = new_len,
.event_id = {
- .header = {
- .type = PERF_RECORD_TEXT_POKE,
- .misc = PERF_RECORD_MISC_KERNEL,
- .size = sizeof(text_poke_event.event_id) + tot + pad,
- },
.addr = (unsigned long)addr,
},
};
@@ -10589,13 +10545,12 @@ static void perf_log_itrace_start(struct perf_event *event)
event->attach_state & PERF_ATTACH_ITRACE)
return;
- rec.header.type = PERF_RECORD_ITRACE_START;
- rec.header.misc = 0;
- rec.header.size = sizeof(rec);
rec.pid = perf_event_pid(event, current);
rec.tid = perf_event_tid(event, current);
- perf_event_header__init_id(&rec.header, &sample, event);
+ perf_event_header__init(&rec.header, &sample,
+ PERF_RECORD_ITRACE_START, /* misc= */ 0, sizeof(rec),
+ event);
ret = perf_output_begin(&handle, &sample, event, rec.header.size);
if (ret)
@@ -10620,12 +10575,10 @@ void perf_report_aux_output_id(struct perf_event *event, u64 hw_id)
if (event->parent)
event = event->parent;
- rec.header.type = PERF_RECORD_AUX_OUTPUT_HW_ID;
- rec.header.misc = 0;
- rec.header.size = sizeof(rec);
- rec.hw_id = hw_id;
-
- perf_event_header__init_id(&rec.header, &sample, event);
+ rec.hw_id = hw_id;
+ perf_event_header__init(&rec.header, &sample,
+ PERF_RECORD_AUX_OUTPUT_HW_ID, /* misc= */ 0,
+ sizeof(rec), event);
ret = perf_output_begin(&handle, &sample, event, rec.header.size);
if (ret)
@@ -13953,7 +13906,7 @@ SYSCALL_DEFINE5(perf_event_open,
if (err)
return err;
- if (!attr.exclude_kernel ||
+ if (!attr.exclude_kernel || attr.text_poke ||
((attr.sample_type & PERF_SAMPLE_CALLCHAIN) &&
!attr.exclude_callchain_kernel)) {
err = perf_allow_kernel();
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 1b1ffe0533e5..13bd42e5493d 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -246,14 +246,13 @@ __perf_output_begin(struct perf_output_handle *handle,
handle->size = (1UL << page_shift) - offset;
if (unlikely(have_lost)) {
- lost_event.header.size = sizeof(lost_event);
- lost_event.header.type = PERF_RECORD_LOST;
- lost_event.header.misc = 0;
lost_event.id = event->id;
lost_event.lost = local_xchg(&rb->lost, 0);
/* XXX mostly redundant; @data is already fully initializes */
- perf_event_header__init_id(&lost_event.header, data, event);
+ perf_event_header__init(&lost_event.header, data,
+ PERF_RECORD_LOST, /* misc= */ 0,
+ sizeof(lost_event), event);
perf_output_put(handle, lost_event);
perf_event__output_id_sample(event, handle, data);
}
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index a061f54b606d..095f9fe440e3 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -213,10 +213,12 @@ static bool __futex_pivot_hash(struct mm_struct *mm, struct futex_private_hash *
futex_rehash_private(fph, new);
}
new->state = FR_PERCPU;
- scoped_guard(rcu) {
- mmph->batches = get_state_synchronize_rcu();
- rcu_assign_pointer(mmph->hash, new);
- }
+ rcu_assign_pointer(mmph->hash, new);
+ /*
+ * mmph->batches must reference a grace period which started after
+ * mmph->hash was assigned. See futex_ref_drop().
+ */
+ mmph->batches = get_state_synchronize_rcu();
kvfree_rcu(fph, rcu);
return true;
}
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index 05cba4e16dad..6923f37eaab4 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -150,6 +150,18 @@ config IRQ_KUNIT_TEST
If unsure, say N.
+config REFCOUNT_INTERRUPT_KUNIT_TEST
+ tristate "Test refcounted interrupt enable/disable" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds the kunit tests for the refcounted interrupt
+ infrastructure. It verifies the correctness of single, nested,
+ and multiple interrupt enable/disable state changes and ensures
+ that the underlying reference counting mechanisms work as expected.
+
+ If unsure, say N.
+
endmenu
config GENERIC_IRQ_MULTI_HANDLER
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 44c4d6fc502a..0e5df962a149 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -16,4 +16,4 @@ obj-$(CONFIG_SMP) += affinity.o
obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o
obj-$(CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR) += matrix.o
obj-$(CONFIG_IRQ_KUNIT_TEST) += irq_test.o
-obj-$(CONFIG_KUNIT) += refcount_interrupt_test.o
+obj-$(CONFIG_REFCOUNT_INTERRUPT_KUNIT_TEST) += refcount_interrupt_test.o
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 4edd8ca5c657..e787e4948c8b 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -496,14 +496,16 @@ static bool kprobe_queued(struct kprobe *p)
static struct kprobe *get_optimized_kprobe(kprobe_opcode_t *addr)
{
int i;
- struct kprobe *p = NULL;
+ struct kprobe *p;
struct optimized_kprobe *op;
/* Don't check i == 0, since that is a breakpoint case. */
- for (i = 1; !p && i < MAX_OPTIMIZED_LENGTH / sizeof(kprobe_opcode_t); i++)
+ for (i = 1; i < MAX_OPTIMIZED_LENGTH / sizeof(kprobe_opcode_t); i++) {
p = get_kprobe(addr - i);
+ /* A disabled probe can have prepared, but inactive, optinsns. */
+ if (!p || !kprobe_optready(p) || kprobe_disarmed(p))
+ continue;
- if (p && kprobe_optready(p)) {
op = container_of(p, struct optimized_kprobe, kp);
if (arch_within_optimized_kprobe(op, addr))
return p;
diff --git a/kernel/module/main.c b/kernel/module/main.c
index d0e1e0bd2ad0..c1b34dc1e89a 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3581,8 +3581,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
goto sysfs_cleanup;
}
- if (codetag_load_module(mod))
- goto sysfs_cleanup;
+ err = codetag_load_module(mod);
+ if (err)
+ goto livepatch_cleanup;
/* Get rid of temporary copy. */
free_copy(info, flags);
@@ -3592,6 +3593,9 @@ static int load_module(struct load_info *info, const char __user *uargs,
return do_init_module(mod);
+ livepatch_cleanup:
+ if (is_livepatch_module(mod))
+ free_module_elf(mod);
sysfs_cleanup:
mod_sysfs_teardown(mod);
coming_cleanup:
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f7b75985d542..38597f26b34c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -483,7 +483,7 @@ int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp,
if (val < 0) {
*negp = true;
- *u_ptr = k_ptr_op ? -k_ptr_op((ulong)val) : -(ulong)val;
+ *u_ptr = k_ptr_op ? k_ptr_op(-(ulong)val) : -(ulong)val;
} else {
*negp = false;
*u_ptr = k_ptr_op ? k_ptr_op((ulong)val) : (ulong) val;
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index 80c354811538..9b3487d40cd6 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -101,6 +101,8 @@ void __init register_refined_jiffies(long cycles_per_second)
#ifdef CONFIG_SYSCTL
static ulong mult_hz(const ulong val)
{
+ if (val >= ULONG_MAX / HZ)
+ return ULONG_MAX;
return val * HZ;
}
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
index 9f2d98181779..da286619c5d8 100644
--- a/kernel/trace/fprobe.c
+++ b/kernel/trace/fprobe.c
@@ -47,6 +47,10 @@ static struct rhltable fprobe_ip_table;
static DEFINE_MUTEX(fprobe_mutex);
static struct fgraph_ops fprobe_graph_ops;
+DEFINE_LOCK_GUARD_0(rcu_sched_notrace,
+ rcu_read_lock_sched_notrace(),
+ rcu_read_unlock_sched_notrace())
+
static u32 fprobe_node_hashfn(const void *data, u32 len, u32 seed)
{
return hash_ptr(*(unsigned long **)data, 32);
@@ -329,16 +333,14 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip,
struct fprobe *fp;
int bit;
+ if (!rcu_is_watching())
+ return;
+
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;
- /*
- * ftrace_test_recursion_trylock() disables preemption, but
- * rhltable_lookup() checks whether rcu_read_lcok is held.
- * So we take rcu_read_lock() here.
- */
- rcu_read_lock();
+ guard(rcu_sched_notrace)();
head = rhltable_lookup(&fprobe_ip_table, &ip, fprobe_rht_params);
rhl_for_each_entry_rcu(node, pos, head, hlist) {
@@ -353,7 +355,6 @@ static void fprobe_ftrace_entry(unsigned long ip, unsigned long parent_ip,
else
__fprobe_handler(ip, parent_ip, fp, fregs, NULL);
}
- rcu_read_unlock();
ftrace_test_recursion_unlock(bit);
}
NOKPROBE_SYMBOL(fprobe_ftrace_entry);
@@ -567,10 +568,13 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops
struct fprobe *fp;
int used, ret;
+ if (!rcu_is_watching())
+ return 0;
+
if (WARN_ON_ONCE(!fregs))
return 0;
- guard(rcu)();
+ guard(rcu_sched_notrace)();
head = rhltable_lookup(&fprobe_ip_table, &func, fprobe_rht_params);
reserved_words = 0;
rhl_for_each_entry_rcu(node, pos, head, hlist) {
@@ -665,13 +669,16 @@ static void fprobe_return(struct ftrace_graph_ret *trace,
int size, curr;
int size_words;
+ if (!rcu_is_watching())
+ return;
+
fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size);
if (WARN_ON_ONCE(!fgraph_data))
return;
size_words = SIZE_IN_LONG(size);
ret_ip = ftrace_regs_get_instruction_pointer(fregs);
- preempt_disable_notrace();
+ guard(rcu_sched_notrace)();
curr = 0;
while (size_words > curr) {
@@ -687,7 +694,6 @@ static void fprobe_return(struct ftrace_graph_ret *trace,
}
curr += size;
}
- preempt_enable_notrace();
}
NOKPROBE_SYMBOL(fprobe_return);
diff --git a/mm/damon/core.c b/mm/damon/core.c
index 1764620903f2..ab3c4d754964 100644
--- a/mm/damon/core.c
+++ b/mm/damon/core.c
@@ -3147,7 +3147,7 @@ static void damos_adjust_quota(struct damon_ctx *c, struct damos *s)
unsigned long cumulated_sz, cached_esz;
unsigned int score, max_score = 0;
- if (!quota->ms && !quota->sz && list_empty(&quota->goals))
+ if (!damos_quota_is_set(quota))
return;
/* First charge window */
diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index b7d05c2a6d93..7d21c4db68d2 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -114,12 +114,23 @@ void kasan_unpoison_task_stack(struct task_struct *task)
/* Unpoison the stack for the current task beyond a watermark sp value. */
asmlinkage void kasan_unpoison_task_stack_below(const void *watermark)
{
+ void *base;
+
+ /*
+ * Only the generic mode needs this. In the tag-based mode the
+ * compiler fully initializes the shadow of stack variables on
+ * function entry, so stale tags left below the watermark are
+ * harmless.
+ */
+ if (!IS_ENABLED(CONFIG_KASAN_GENERIC))
+ return;
+
/*
* Calculate the task stack base address. Avoid using 'current'
* because this function is called by early resume code which hasn't
* yet set up the percpu register (%gs).
*/
- void *base = (void *)((unsigned long)watermark & ~(THREAD_SIZE - 1));
+ base = (void *)((unsigned long)watermark & ~(THREAD_SIZE - 1));
kasan_unpoison(base, watermark - base, false);
}
diff --git a/mm/shmem.c b/mm/shmem.c
index 897fa2b61346..848316eaa7f4 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1847,8 +1847,11 @@ unsigned long shmem_allowable_huge_orders(struct inode *inode,
global_orders = shmem_huge_global_enabled(inode, index, write_end,
shmem_huge_force, vma, vm_flags);
- /* Tmpfs huge pages allocation */
- if (!vma || !vma_is_anon_shmem(vma))
+ /*
+ * Tmpfs huge pages allocation or forced collapse ignores
+ * sysfs configs.
+ */
+ if (!vma || !vma_is_anon_shmem(vma) || shmem_huge_force)
return global_orders;
/*
diff --git a/mm/slub.c b/mm/slub.c
index 54ec12503357..544cff39762c 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -5969,7 +5969,8 @@ alloc_empty:
if (!allow_spin)
return NULL;
- empty = alloc_empty_sheaf(s, GFP_NOWAIT, SLAB_ALLOC_DEFAULT);
+ /* Don't wake up kswapd, it will cause deadlock under pi_lock */
+ empty = alloc_empty_sheaf(s, __GFP_NOWARN, SLAB_ALLOC_DEFAULT);
if (empty)
goto got_empty;
@@ -6128,7 +6129,6 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj, unsigned int free_flags)
struct slab_sheaf *empty;
struct node_barn *barn;
unsigned int alloc_flags = to_alloc_flags(free_flags);
- gfp_t gfp = allow_spin ? GFP_NOWAIT : __GFP_NOWARN;
/* Bootstrap or debug cache, fall back */
if (unlikely(!cache_has_sheaves(s))) {
@@ -6157,7 +6157,8 @@ bool __kfree_rcu_sheaf(struct kmem_cache *s, void *obj, unsigned int free_flags)
local_unlock(&s->cpu_sheaves->lock);
- empty = alloc_empty_sheaf(s, gfp, alloc_flags);
+ /* Don't wake up kswapd, it will cause deadlock under pi_lock */
+ empty = alloc_empty_sheaf(s, __GFP_NOWARN, alloc_flags);
if (!empty)
goto fail;
diff --git a/mm/vma.c b/mm/vma.c
index 9f0a0acf694a..6cde67883fb0 100644
--- a/mm/vma.c
+++ b/mm/vma.c
@@ -2849,7 +2849,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
{
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma = NULL;
- bool have_mmap_prepare = file && file->f_op->mmap_prepare;
+ const bool have_mmap_prepare = file && file->f_op->mmap_prepare;
VMA_ITERATOR(vmi, mm, addr);
const pgoff_t anon_pgoff = addr >> PAGE_SHIFT;
MMAP_STATE(map, mm, &vmi, addr, len, pgoff, anon_pgoff, vma_flags, file);
@@ -2892,7 +2892,7 @@ static unsigned long __mmap_region(struct file *file, unsigned long addr,
allocated_new = true;
}
- if (have_mmap_prepare)
+ if (have_mmap_prepare && allocated_new)
set_vma_user_defined_fields(vma, &map);
__mmap_complete(&map, vma);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 96195d2fd10f..cf44452e0766 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -302,11 +302,13 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
+ hci_dev_lock(hdev);
+
switch (conn->codec.id) {
case BT_CODEC_MSBC:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
- return -EINVAL;
+ goto unlock;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x05;
@@ -332,7 +334,7 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
case BT_CODEC_TRANSPARENT:
if (!find_next_esco_param(conn, esco_param_msbc,
ARRAY_SIZE(esco_param_msbc)))
- return -EINVAL;
+ goto unlock;
param = &esco_param_msbc[conn->attempt - 1];
cp.tx_coding_format.id = 0x03;
@@ -359,11 +361,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
if (conn->parent && lmp_esco_capable(conn->parent)) {
if (!find_next_esco_param(conn, esco_param_cvsd,
ARRAY_SIZE(esco_param_cvsd)))
- return -EINVAL;
+ goto unlock;
param = &esco_param_cvsd[conn->attempt - 1];
} else {
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
- return -EINVAL;
+ goto unlock;
param = &sco_param_cvsd[conn->attempt - 1];
}
cp.tx_coding_format.id = 2;
@@ -386,9 +388,11 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
cp.out_transport_unit_size = 16;
break;
default:
- return -EINVAL;
+ goto unlock;
}
+ hci_dev_unlock(hdev);
+
cp.retrans_effort = param->retrans_effort;
cp.pkt_type = __cpu_to_le16(param->pkt_type);
cp.max_latency = __cpu_to_le16(param->max_latency);
@@ -397,6 +401,10 @@ static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
return -EIO;
return 0;
+
+unlock:
+ hci_dev_unlock(hdev);
+ return -EINVAL;
}
static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d183efaf9063..2076689eb302 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1489,8 +1489,10 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type);
+ mutex_lock(&hdev->remote_oob_lock);
list_del(&data->list);
kfree(data);
+ mutex_unlock(&hdev->remote_oob_lock);
return 0;
}
@@ -1499,10 +1501,12 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev)
{
struct oob_data *data, *n;
+ mutex_lock(&hdev->remote_oob_lock);
list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
list_del(&data->list);
kfree(data);
}
+ mutex_unlock(&hdev->remote_oob_lock);
}
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -1511,11 +1515,14 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
struct oob_data *data;
+ mutex_lock(&hdev->remote_oob_lock);
data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
if (!data) {
data = kmalloc_obj(*data);
- if (!data)
+ if (!data) {
+ mutex_unlock(&hdev->remote_oob_lock);
return -ENOMEM;
+ }
bacpy(&data->bdaddr, bdaddr);
data->bdaddr_type = bdaddr_type;
@@ -1548,6 +1555,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
BT_DBG("%s for %pMR", hdev->name, bdaddr);
+ mutex_unlock(&hdev->remote_oob_lock);
+
return 0;
}
@@ -2485,6 +2494,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
mutex_init(&hdev->lock);
mutex_init(&hdev->req_lock);
mutex_init(&hdev->mgmt_pending_lock);
+ mutex_init(&hdev->remote_oob_lock);
ida_init(&hdev->unset_handle_ida);
@@ -2557,8 +2567,10 @@ int hci_register_dev(struct hci_dev *hdev)
return id;
error = dev_set_name(&hdev->dev, "hci%u", id);
- if (error)
+ if (error) {
+ ida_free(&hci_index_ida, id);
return error;
+ }
hdev->name = dev_name(&hdev->dev);
hdev->id = id;
@@ -3325,6 +3337,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
skb_shinfo(skb)->frag_list = NULL;
+ spin_lock_bh(&queue->lock);
+
__skb_queue_tail(queue, skb);
do {
@@ -3339,6 +3353,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
__skb_queue_tail(queue, skb);
} while (list);
+
+ spin_unlock_bh(&queue->lock);
}
bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue));
@@ -3402,9 +3418,6 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
struct hci_conn *conn = NULL, *c;
unsigned int num = 0, min = ~0;
- /* We don't have to lock device here. Connections are always
- * added and removed with TX task disabled. */
-
rcu_read_lock();
list_for_each_entry_rcu(c, &h->list, list) {
@@ -3609,13 +3622,15 @@ static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type)
}
/* Schedule SCO */
-static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
+static void __hci_sched_sco(struct hci_dev *hdev, __u8 type)
{
struct hci_conn *conn;
struct sk_buff *skb;
int quote, *cnt;
unsigned int pkts = hdev->sco_pkts;
+ lockdep_assert_held(&hdev->lock);
+
bt_dev_dbg(hdev, "type %u", type);
if (!hci_conn_num(hdev, type) || !pkts)
@@ -3650,6 +3665,13 @@ static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
queue_work(hdev->workqueue, &hdev->tx_work);
}
+static void hci_sched_sco(struct hci_dev *hdev, __u8 type)
+{
+ hci_dev_lock(hdev);
+ __hci_sched_sco(hdev, type);
+ hci_dev_unlock(hdev);
+}
+
static void hci_sched_acl_pkt(struct hci_dev *hdev)
{
unsigned int cnt = hdev->acl_cnt;
@@ -3659,6 +3681,8 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
__check_timeout(hdev, cnt, ACL_LINK);
+ hci_dev_lock(hdev);
+
while (hdev->acl_cnt &&
(chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
u32 priority = (skb_peek(&chan->data_q))->priority;
@@ -3683,13 +3707,15 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
chan->conn->sent++;
/* Send pending SCO packets right away */
- hci_sched_sco(hdev, SCO_LINK);
- hci_sched_sco(hdev, ESCO_LINK);
+ __hci_sched_sco(hdev, SCO_LINK);
+ __hci_sched_sco(hdev, ESCO_LINK);
}
}
if (cnt != hdev->acl_cnt)
hci_prio_recalculate(hdev, ACL_LINK);
+
+ hci_dev_unlock(hdev);
}
static void hci_sched_acl(struct hci_dev *hdev)
@@ -3718,6 +3744,8 @@ static void hci_sched_le(struct hci_dev *hdev)
__check_timeout(hdev, *cnt, LE_LINK);
+ hci_dev_lock(hdev);
+
tmp = *cnt;
while (*cnt && (chan = hci_chan_sent(hdev, LE_LINK, &quote))) {
u32 priority = (skb_peek(&chan->data_q))->priority;
@@ -3739,13 +3767,15 @@ static void hci_sched_le(struct hci_dev *hdev)
chan->conn->sent++;
/* Send pending SCO packets right away */
- hci_sched_sco(hdev, SCO_LINK);
- hci_sched_sco(hdev, ESCO_LINK);
+ __hci_sched_sco(hdev, SCO_LINK);
+ __hci_sched_sco(hdev, ESCO_LINK);
}
}
if (*cnt != tmp)
hci_prio_recalculate(hdev, LE_LINK);
+
+ hci_dev_unlock(hdev);
}
/* Schedule iso */
@@ -3764,6 +3794,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type)
__check_timeout(hdev, *cnt, type);
+ hci_dev_lock(hdev);
+
while (*cnt && (conn = hci_low_sent(hdev, type, &quote))) {
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
BT_DBG("skb %p len %d", skb, skb->len);
@@ -3777,6 +3809,8 @@ static void hci_sched_iso(struct hci_dev *hdev, __u8 type)
(*cnt)--;
}
}
+
+ hci_dev_unlock(hdev);
}
static void hci_tx_work(struct work_struct *work)
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index aadffaaff20e..2559fb6324d6 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -364,7 +364,7 @@ static int inquiry_cache_show(struct seq_file *f, void *p)
list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
- seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+ seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %lu\n",
&data->bdaddr,
data->pscan_rep_mode, data->pscan_period_mode,
data->pscan_mode, data->dev_class[2],
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 74e2b04c84b2..c01c8b58d9e8 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -312,6 +312,10 @@ static void hci_cmd_sync_work(struct work_struct *work)
while (1) {
struct hci_cmd_sync_work_entry *entry;
+ /* Leave the backlog to hci_cmd_sync_clear() */
+ if (hci_dev_test_flag(hdev, HCI_UNREGISTER))
+ break;
+
mutex_lock(&hdev->cmd_sync_work_lock);
entry = list_first_entry_or_null(&hdev->cmd_sync_work_list,
struct hci_cmd_sync_work_entry,
@@ -658,6 +662,8 @@ void hci_cmd_sync_clear(struct hci_dev *hdev)
{
struct hci_cmd_sync_work_entry *entry, *tmp;
+ /* cmd_work is disabled, the pending request can only time out */
+ hci_cmd_sync_cancel_sync(hdev, ENODEV);
cancel_work_sync(&hdev->cmd_sync_work);
cancel_work_sync(&hdev->reenable_adv_work);
@@ -5764,6 +5770,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev)
{
struct discovery_state *d = &hdev->discovery;
struct inquiry_entry *e;
+ bdaddr_t addr;
int err;
bt_dev_dbg(hdev, "state %u", hdev->discovery.state);
@@ -5799,15 +5806,21 @@ int hci_stop_discovery_sync(struct hci_dev *hdev)
return 0;
if (d->state == DISCOVERY_RESOLVING || d->state == DISCOVERY_STOPPING) {
+ hci_dev_lock(hdev);
e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY,
NAME_PENDING);
- if (!e)
+ if (!e) {
+ hci_dev_unlock(hdev);
return 0;
+ }
+
+ bacpy(&addr, &e->data.bdaddr);
+ hci_dev_unlock(hdev);
/* Ignore cancel errors since it should interfere with stopping
* of the discovery.
*/
- hci_remote_name_cancel_sync(hdev, &e->data.bdaddr);
+ hci_remote_name_cancel_sync(hdev, &addr);
}
return 0;
@@ -7236,6 +7249,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
bacpy(&cp.bdaddr, &conn->dst);
cp.pscan_rep_mode = 0x02;
+ hci_dev_lock(hdev);
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
if (ie) {
if (inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
@@ -7247,6 +7261,7 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
memcpy(conn->dev_class, ie->data.dev_class, 3);
}
+ hci_dev_unlock(hdev);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index b2c1060394e6..dc3cdf614def 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -462,7 +462,7 @@ static int __rfcomm_release_dev(void __user *arg)
/* Shut down TTY synchronously before freeing rfcomm_dev */
tty_port_tty_vhangup(&dev->port);
- if (!test_bit(RFCOMM_TTY_OWNED, &dev->status))
+ if (!test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status))
tty_port_put(&dev->port);
tty_port_put(&dev->port);
@@ -724,10 +724,9 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
* when the last process closes the tty. The behaviour is expected by
* userspace.
*/
- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
- set_bit(RFCOMM_TTY_OWNED, &dev->status);
+ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
+ !test_and_set_bit(RFCOMM_TTY_OWNED, &dev->status))
tty_port_put(&dev->port);
- }
return 0;
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index d23f9d0729c4..2df303f6a38f 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -662,6 +662,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
else
bdaddr_type = BDADDR_LE_RANDOM;
+ mutex_lock(&hdev->remote_oob_lock);
oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
bdaddr_type);
if (oob_data && oob_data->present) {
@@ -672,6 +673,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
SMP_DBG("OOB Remote Confirmation: %16phN", smp->pcnf);
SMP_DBG("OOB Remote Random: %16phN", smp->rr);
}
+ mutex_unlock(&hdev->remote_oob_lock);
} else {
authreq &= ~SMP_AUTH_SC;
diff --git a/net/core/dev.c b/net/core/dev.c
index f660fccfc0db..18dc88990510 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3818,20 +3818,36 @@ static netdev_features_t dflt_features_check(struct sk_buff *skb,
return vlan_features_check(skb, features);
}
-static bool skb_gso_has_extension_hdr(const struct sk_buff *skb)
-{
- if (!skb->encapsulation)
- return ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 ||
- (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 &&
- vlan_get_protocol(skb) == htons(ETH_P_IPV6))) &&
- skb_transport_header_was_set(skb) &&
- skb_network_header_len(skb) != sizeof(struct ipv6hdr));
- else
- return (!skb_inner_network_header_was_set(skb) ||
- ((skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6 ||
- (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 &&
- inner_ip_hdr(skb)->version == 6)) &&
- skb_inner_network_header_len(skb) != sizeof(struct ipv6hdr)));
+static bool __skb_has_ipv6_ext_hdr(const struct sk_buff *skb, int nhoff)
+{
+ const struct ipv6hdr *ip6h;
+ struct ipv6hdr _ip6h;
+
+ ip6h = skb_header_pointer(skb, nhoff, sizeof(_ip6h), &_ip6h);
+ return ip6h && ip6h->version == 6 && ipv6_ext_hdr(ip6h->nexthdr);
+}
+
+static bool skb_has_ipv6_extension_hdr(const struct sk_buff *skb)
+{
+ if (vlan_get_protocol(skb) == htons(ETH_P_IPV6)) {
+ if (__skb_has_ipv6_ext_hdr(skb, skb_network_offset(skb)))
+ return true;
+
+ /* Catch tunnels without skb->encapsulation (e.g., virtio). */
+ if (!skb->encapsulation &&
+ skb_transport_header_was_set(skb) &&
+ skb_network_header_len(skb) != sizeof(struct ipv6hdr))
+ return true;
+ }
+
+ /* Tunnels without an inner network header, such as SCTP-in-UDP or
+ * PSP, have no inner IP header and thus no inner extension header.
+ */
+ if (skb->encapsulation && skb_inner_network_header_was_set(skb) &&
+ __skb_has_ipv6_ext_hdr(skb, skb_inner_network_offset(skb)))
+ return true;
+
+ return false;
}
static netdev_features_t gso_features_check(const struct sk_buff *skb,
@@ -3886,7 +3902,7 @@ static netdev_features_t gso_features_check(const struct sk_buff *skb,
* so neither does TSO that depends on it.
*/
if (features & NETIF_F_IPV6_CSUM &&
- skb_gso_has_extension_hdr(skb))
+ skb_has_ipv6_extension_hdr(skb))
features &= ~(NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | NETIF_F_GSO_UDP_L4);
return features;
@@ -3988,8 +4004,7 @@ int skb_csum_hwoffload_help(struct sk_buff *skb,
return 0;
if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) {
- if (vlan_get_protocol(skb) == htons(ETH_P_IPV6) &&
- skb_network_header_len(skb) != sizeof(struct ipv6hdr))
+ if (skb_has_ipv6_extension_hdr(skb))
goto sw_checksum;
switch (skb->csum_offset) {
@@ -4404,9 +4419,14 @@ EXPORT_SYMBOL(dev_loopback_xmit);
static struct netdev_queue *
netdev_tx_queue_mapping(struct net_device *dev, struct sk_buff *skb)
{
- int qm = skb_get_queue_mapping(skb);
+ int queue = skb_get_queue_mapping(skb);
+ int capped;
+
+ capped = netdev_cap_txqueue(dev, queue);
+ if (unlikely(capped != queue))
+ skb_set_queue_mapping(skb, capped);
- return netdev_get_tx_queue(dev, netdev_cap_txqueue(dev, qm));
+ return netdev_get_tx_queue(dev, capped);
}
#ifndef CONFIG_PREEMPT_RT
@@ -4415,9 +4435,13 @@ static bool netdev_xmit_txqueue_skipped(void)
return __this_cpu_read(softnet_data.xmit.skip_txqueue);
}
-void netdev_xmit_skip_txqueue(bool skip)
+bool netdev_xmit_skip_txqueue(bool skip)
{
+ bool prev = netdev_xmit_txqueue_skipped();
+
__this_cpu_write(softnet_data.xmit.skip_txqueue, skip);
+
+ return prev;
}
EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue);
@@ -4427,9 +4451,13 @@ static bool netdev_xmit_txqueue_skipped(void)
return current->net_xmit.skip_txqueue;
}
-void netdev_xmit_skip_txqueue(bool skip)
+bool netdev_xmit_skip_txqueue(bool skip)
{
+ bool prev = netdev_xmit_txqueue_skipped();
+
current->net_xmit.skip_txqueue = skip;
+
+ return prev;
}
EXPORT_SYMBOL_GPL(netdev_xmit_skip_txqueue);
#endif
@@ -4848,21 +4876,25 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
tcx_set_ingress(skb, false);
#ifdef CONFIG_NET_EGRESS
if (static_branch_unlikely(&egress_needed_key)) {
+ bool skip_txq;
+
if (nf_hook_egress_active()) {
skb = nf_hook_egress(skb, &rc, dev);
if (!skb)
goto out;
}
- netdev_xmit_skip_txqueue(false);
+ skip_txq = netdev_xmit_skip_txqueue(false);
nf_skip_egress(skb, true);
skb = sch_handle_egress(skb, &rc, dev);
- if (!skb)
+ if (!skb) {
+ netdev_xmit_skip_txqueue(skip_txq);
goto out;
+ }
nf_skip_egress(skb, false);
- if (netdev_xmit_txqueue_skipped())
+ if (netdev_xmit_skip_txqueue(skip_txq))
txq = netdev_tx_queue_mapping(dev, skb);
}
#endif
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 164643140a52..f6029f60c1dc 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -267,7 +267,8 @@ int dev_get_hwtstamp_phylib(struct net_device *dev,
hwprov->phydev)
return phy_hwtstamp_get(hwprov->phydev, cfg);
- if (hwprov->source == HWTSTAMP_SOURCE_NETDEV)
+ if (hwprov->source == HWTSTAMP_SOURCE_NETDEV &&
+ dev->netdev_ops->ndo_hwtstamp_get)
return dev->netdev_ops->ndo_hwtstamp_get(dev, cfg);
return -EOPNOTSUPP;
diff --git a/net/core/gso.c b/net/core/gso.c
index bcd156372f4d..e96ef6350064 100644
--- a/net/core/gso.c
+++ b/net/core/gso.c
@@ -117,6 +117,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
SKB_GSO_CB(skb)->encap_level = 0;
+ SKB_GSO_CB(skb)->recursion_counter = 0;
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
diff --git a/net/core/page_pool.c b/net/core/page_pool.c
index 08d7f35cf608..d7c88c0b67e5 100644
--- a/net/core/page_pool.c
+++ b/net/core/page_pool.c
@@ -951,8 +951,8 @@ static void page_pool_recycle_ring_bulk(struct page_pool *pool,
}
}
- page_pool_producer_unlock(pool, in_softirq);
recycle_stat_add(pool, ring, i);
+ page_pool_producer_unlock(pool, in_softirq);
/* Hopefully all pages were returned into ptr_ring */
if (likely(i == bulk_len))
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index c3042d822afa..4aea06d5167d 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -7409,7 +7409,8 @@ nodefer: kfree_skb_napi_cache(skb);
}
static void skb_splice_csum_page(struct sk_buff *skb, struct page *page,
- size_t offset, size_t len)
+ size_t offset, size_t len,
+ unsigned int csum_offset)
{
const char *kaddr;
__wsum csum;
@@ -7417,7 +7418,7 @@ static void skb_splice_csum_page(struct sk_buff *skb, struct page *page,
kaddr = kmap_local_page(page);
csum = csum_partial(kaddr + offset, len, 0);
kunmap_local(kaddr);
- skb->csum = csum_block_add(skb->csum, csum, skb->len);
+ skb->csum = csum_block_add(skb->csum, csum, csum_offset);
}
/**
@@ -7477,7 +7478,8 @@ ssize_t skb_splice_from_iter(struct sk_buff *skb, struct iov_iter *iter,
}
if (skb->ip_summed == CHECKSUM_NONE)
- skb_splice_csum_page(skb, page, off, part);
+ skb_splice_csum_page(skb, page, off, part,
+ skb->len + spliced);
off = 0;
spliced += part;
diff --git a/net/core/sock.c b/net/core/sock.c
index d23333bb4f3f..e8551df8330f 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1034,7 +1034,7 @@ static int sock_reserve_memory(struct sock *sk, int bytes)
bool charged;
int pages;
- if (!mem_cgroup_sk_enabled(sk) || !sk_has_account(sk))
+ if (!mem_cgroup_sk_enabled(sk) || !sk_is_tcp(sk))
return -EOPNOTSUPP;
if (!bytes)
@@ -1661,7 +1661,7 @@ set_sndbuf:
{
int delta;
- if (val < 0) {
+ if (val < 0 || val > SZ_1G) {
ret = -EINVAL;
break;
}
diff --git a/net/ethtool/tsconfig.c b/net/ethtool/tsconfig.c
index 6be3aa5d4bc1..2db0e7ba8b9f 100644
--- a/net/ethtool/tsconfig.c
+++ b/net/ethtool/tsconfig.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/net_tstamp.h>
+#include <linux/phy.h>
#include <linux/ptp_clock_kernel.h>
#include <net/netdev_lock.h>
@@ -42,7 +43,9 @@ static int tsconfig_prepare_data(const struct ethnl_req_info *req_base,
struct kernel_hwtstamp_config cfg = {};
int ret;
- if (!dev->netdev_ops->ndo_hwtstamp_get)
+ if (!dev->netdev_ops->ndo_hwtstamp_get &&
+ !phy_is_default_hwtstamp(dev->phydev) &&
+ !netdev_ops_lock_dereference(dev->hwprov, dev))
return -EOPNOTSUPP;
ret = ethnl_ops_begin(dev);
@@ -248,17 +251,6 @@ err_cleanup:
return ret;
}
-static int ethnl_set_tsconfig_validate(struct ethnl_req_info *req_base,
- struct genl_info *info)
-{
- const struct net_device_ops *ops = req_base->dev->netdev_ops;
-
- if (!ops->ndo_hwtstamp_set || !ops->ndo_hwtstamp_get)
- return -EOPNOTSUPP;
-
- return 1;
-}
-
static struct hwtstamp_provider *
tsconfig_set_hwprov_from_desc(struct net_device *dev,
struct genl_info *info,
@@ -272,7 +264,7 @@ tsconfig_set_hwprov_from_desc(struct net_device *dev,
int ret;
ret = ethtool_net_get_ts_info_by_phc(dev, &ts_info, hwprov_desc);
- if (!ret) {
+ if (!ret && dev->netdev_ops->ndo_hwtstamp_set) {
/* Found */
source = HWTSTAMP_SOURCE_NETDEV;
} else {
@@ -313,6 +305,11 @@ static int ethnl_set_tsconfig(struct ethnl_req_info *req_base,
if (!netif_device_present(dev))
return -ENODEV;
+ if (!dev->netdev_ops->ndo_hwtstamp_set &&
+ !phy_is_default_hwtstamp(dev->phydev) &&
+ !netdev_ops_lock_dereference(dev->hwprov, dev))
+ return -EOPNOTSUPP;
+
if (tb[ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER]) {
struct hwtstamp_provider_desc __hwprov_desc = {.index = -1};
struct hwtstamp_provider *__hwprov;
@@ -459,6 +456,5 @@ const struct ethnl_request_ops ethnl_tsconfig_request_ops = {
.reply_size = tsconfig_reply_size,
.fill_reply = tsconfig_fill_reply,
- .set_validate = ethnl_set_tsconfig_validate,
.set = ethnl_set_tsconfig,
};
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 32d006c1a8ee..4ce38c99fef9 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1375,6 +1375,9 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
int id;
skb_reset_network_header(skb);
+ if (unlikely(gso_recursion_inc_test(skb,
+ IP_TUNNEL_RECURSION_LIMIT)))
+ goto out;
nhoff = skb_network_header(skb) - skb_mac_header(skb);
if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
goto out;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 92bc60716f33..99baf14afdfd 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4465,6 +4465,12 @@ no_queue:
return 1;
old_ack:
+ /* An old ACK can carry new data. Update TS.Recent before SACK
+ * processing can trigger a retransmission.
+ */
+ if (flag & FLAG_UPDATE_TS_RECENT)
+ tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
+
/* If data was SACKed, tag it and see if we should send more data.
* If data was DSACKed, see if we can undo a cwnd reduction.
*/
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 9d89be7e0544..c90ee6dd7446 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -4874,7 +4874,7 @@ static int modify_prefix_route(struct net *net, struct inet6_ifaddr *ifp,
fib6_clean_expires(f6i);
fib6_may_remove_gc_list(net, f6i);
} else {
- fib6_set_expires(f6i, expires);
+ fib6_set_expires(f6i, jiffies + clock_t_to_jiffies(expires));
fib6_add_gc_list(f6i);
}
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 78f50c93c536..700f1861d8eb 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -17,6 +17,7 @@
#include <net/udp.h>
#include <net/gro.h>
#include <net/gso.h>
+#include <net/ip_tunnels.h>
#include "ip6_offload.h"
#include "tcpv6_offload.c"
@@ -104,6 +105,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
bool gso_partial;
skb_reset_network_header(skb);
+ if (unlikely(gso_recursion_inc_test(skb,
+ IP_TUNNEL_RECURSION_LIMIT)))
+ goto out;
nhoff = skb_network_header(skb) - skb_mac_header(skb);
if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
goto out;
diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c
index 61c6a27bf202..7cbe0685f8bb 100644
--- a/net/ipv6/seg6_iptunnel.c
+++ b/net/ipv6/seg6_iptunnel.c
@@ -129,7 +129,7 @@ static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb,
u32 hash;
if (do_flowlabel > 0) {
- hash = skb_get_hash(skb);
+ hash = skb_get_hash_net(net, skb);
hash = rol32(hash, 16);
flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
} else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) {
diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c
index d1070aec7b72..53f7b2452575 100644
--- a/net/ipv6/seg6_local.c
+++ b/net/ipv6/seg6_local.c
@@ -278,13 +278,43 @@ static bool decap_and_validate(struct sk_buff *skb, int proto)
return true;
}
-static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr)
+/* advance the SRH to the next segment and set the IPv6 DA accordingly.
+ * skb pointers may change: after this call, the caller must evaluate again
+ * any pointer into the packet data.
+ *
+ * This function returns:
+ * - the SRH on success;
+ * - NULL when the skb cannot be made writable. In this case, the function
+ * sets the drop reason to SKB_DROP_REASON_NOMEM.
+ */
+static struct ipv6_sr_hdr *advance_nextseg(struct sk_buff *skb,
+ struct ipv6_sr_hdr *srh,
+ enum skb_drop_reason *reason)
{
struct in6_addr *addr;
+ int srhoff;
+ int wlen;
+
+ srhoff = (unsigned char *)srh - skb->data;
+ /* we write only the Segment Left field and the IPv6 DA. The segment
+ * list is only read, and seg6_get_srh() already pulled it into the
+ * linear area.
+ */
+ wlen = srhoff + sizeof(*srh);
+
+ if (unlikely(skb_ensure_writable(skb, wlen))) {
+ *reason = SKB_DROP_REASON_NOMEM;
+ return NULL;
+ }
+
+ /* skb_ensure_writable() may change skb pointers; evaluate srh again */
+ srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
srh->segments_left--;
addr = srh->segments + srh->segments_left;
- *daddr = *addr;
+ ipv6_hdr(skb)->daddr = *addr;
+
+ return srh;
}
static int
@@ -382,12 +412,26 @@ static bool seg6_next_csid_is_arg_zero(const struct in6_addr *addr,
return true;
}
-/* assume that DA.Argument length > 0 */
-static void seg6_next_csid_advance_arg(struct in6_addr *addr,
- const struct seg6_flavors_info *finfo)
+/* assume that DA.Argument length > 0.
+ * skb pointers may change: after this call, the caller must evaluate again
+ * any pointer into the packet data.
+ *
+ * This function returns:
+ * - SKB_NOT_DROPPED_YET on success;
+ * - SKB_DROP_REASON_NOMEM when the skb cannot be made writable.
+ */
+static enum skb_drop_reason
+seg6_next_csid_advance_arg(struct sk_buff *skb,
+ const struct seg6_flavors_info *finfo)
{
__u8 fnc_octects = seg6_flv_lcnode_func_octects(finfo);
__u8 blk_octects = seg6_flv_lcblock_octects(finfo);
+ struct in6_addr *addr;
+
+ if (unlikely(skb_ensure_writable(skb, sizeof(struct ipv6hdr))))
+ return SKB_DROP_REASON_NOMEM;
+
+ addr = &ipv6_hdr(skb)->daddr;
/* advance DA.Argument */
memmove(&addr->s6_addr[blk_octects],
@@ -395,6 +439,8 @@ static void seg6_next_csid_advance_arg(struct in6_addr *addr,
16 - blk_octects - fnc_octects);
memset(&addr->s6_addr[16 - fnc_octects], 0x00, fnc_octects);
+
+ return SKB_NOT_DROPPED_YET;
}
static int input_action_end_finish(struct sk_buff *skb,
@@ -408,31 +454,42 @@ static int input_action_end_finish(struct sk_buff *skb,
static int input_action_end_core(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ipv6_sr_hdr *srh;
+ int err = -EINVAL;
srh = get_and_validate_srh(skb);
if (!srh)
goto drop;
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ err = -ENOMEM;
+ goto drop;
+ }
return input_action_end_finish(skb, slwt);
drop:
- kfree_skb(skb);
- return -EINVAL;
+ kfree_skb_reason(skb, reason);
+ return err;
}
static int end_next_csid_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{
const struct seg6_flavors_info *finfo = &slwt->flv_info;
struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
+ enum skb_drop_reason reason;
if (seg6_next_csid_is_arg_zero(daddr, finfo))
return input_action_end_core(skb, slwt);
/* update DA */
- seg6_next_csid_advance_arg(daddr, finfo);
+ reason = seg6_next_csid_advance_arg(skb, finfo);
+ if (reason) {
+ kfree_skb_reason(skb, reason);
+ return -ENOMEM;
+ }
return input_action_end_finish(skb, slwt);
}
@@ -448,19 +505,25 @@ static int input_action_end_x_finish(struct sk_buff *skb,
static int input_action_end_x_core(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ipv6_sr_hdr *srh;
+ int err = -EINVAL;
srh = get_and_validate_srh(skb);
if (!srh)
goto drop;
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ err = -ENOMEM;
+ goto drop;
+ }
return input_action_end_x_finish(skb, slwt);
drop:
- kfree_skb(skb);
- return -EINVAL;
+ kfree_skb_reason(skb, reason);
+ return err;
}
static int end_x_next_csid_core(struct sk_buff *skb,
@@ -468,12 +531,17 @@ static int end_x_next_csid_core(struct sk_buff *skb,
{
const struct seg6_flavors_info *finfo = &slwt->flv_info;
struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;
+ enum skb_drop_reason reason;
if (seg6_next_csid_is_arg_zero(daddr, finfo))
return input_action_end_x_core(skb, slwt);
/* update DA */
- seg6_next_csid_advance_arg(daddr, finfo);
+ reason = seg6_next_csid_advance_arg(skb, finfo);
+ if (reason) {
+ kfree_skb_reason(skb, reason);
+ return -ENOMEM;
+ }
return input_action_end_x_finish(skb, slwt);
}
@@ -760,10 +828,12 @@ pull:
*/
static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
const struct seg6_flavors_info *finfo = &slwt->flv_info;
enum seg6_local_flv_action action;
enum seg6_local_pktinfo pinfo;
struct ipv6_sr_hdr *srh;
+ int err = -EINVAL;
__u32 flvmask;
int srhoff;
@@ -787,10 +857,18 @@ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
switch (action) {
case SEG6_LOCAL_FLV_ACT_END:
/* process the packet as the "standard" End behavior */
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ err = -ENOMEM;
+ goto drop;
+ }
break;
case SEG6_LOCAL_FLV_ACT_PSP:
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ err = -ENOMEM;
+ goto drop;
+ }
if (unlikely(!seg6_pop_srh(skb, srhoff)))
goto drop;
@@ -807,8 +885,8 @@ static int end_flv8986_core(struct sk_buff *skb, struct seg6_local_lwt *slwt)
return input_action_end_finish(skb, slwt);
drop:
- kfree_skb(skb);
- return -EINVAL;
+ kfree_skb_reason(skb, reason);
+ return err;
}
/* regular endpoint function */
@@ -847,21 +925,27 @@ static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt)
static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ipv6_sr_hdr *srh;
+ int err = -EINVAL;
srh = get_and_validate_srh(skb);
if (!srh)
goto drop;
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ err = -ENOMEM;
+ goto drop;
+ }
seg6_lookup_nexthop(skb, NULL, slwt->table);
return dst_input(skb);
drop:
- kfree_skb(skb);
- return -EINVAL;
+ kfree_skb_reason(skb, reason);
+ return err;
}
/* decapsulate and forward inner L2 frame on specified interface */
@@ -1375,6 +1459,7 @@ drop:
static int input_action_end_b6_encap(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
{
+ enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct ipv6_sr_hdr *srh;
int err = -EINVAL;
@@ -1382,7 +1467,11 @@ static int input_action_end_b6_encap(struct sk_buff *skb,
if (!srh)
goto drop;
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ err = -ENOMEM;
+ goto drop;
+ }
skb_reset_inner_headers(skb);
skb->encapsulation = 1;
@@ -1398,7 +1487,7 @@ static int input_action_end_b6_encap(struct sk_buff *skb,
return dst_input(skb);
drop:
- kfree_skb(skb);
+ kfree_skb_reason(skb, reason);
return err;
}
@@ -1434,6 +1523,7 @@ static int input_action_end_bpf(struct sk_buff *skb,
struct seg6_local_lwt *slwt)
{
struct seg6_bpf_srh_state *srh_state;
+ enum skb_drop_reason reason;
struct ipv6_sr_hdr *srh;
int ret;
@@ -1442,7 +1532,11 @@ static int input_action_end_bpf(struct sk_buff *skb,
kfree_skb(skb);
return -EINVAL;
}
- advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
+ srh = advance_nextseg(skb, srh, &reason);
+ if (!srh) {
+ kfree_skb_reason(skb, reason);
+ return -ENOMEM;
+ }
/* The access to the per-CPU buffer srh_state is protected by running
* always in softirq context (with disabled BH). On PREEMPT_RT the
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 9629e00069a1..01d6703767c6 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -102,7 +102,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
if (!tid_rx)
return;
- timer_delete_sync(&tid_rx->session_timer);
+ timer_shutdown_sync(&tid_rx->session_timer);
/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
spin_lock_bh(&tid_rx->reorder_lock);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 75bb204ad743..9cba79507767 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -231,6 +231,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
return num;
}
+static bool
+ieee80211_chanctx_has_replace_partner(struct ieee80211_chanctx *ctx)
+{
+ return ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED &&
+ ctx->replace_ctx;
+}
+
static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
{
struct ieee80211_chanctx *ctx;
@@ -1405,6 +1412,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
{
struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_chanctx *ctx = link->reserved_chanctx;
+ struct ieee80211_chanctx *old_ctx = NULL;
lockdep_assert_wiphy(sdata->local->hw.wiphy);
@@ -1418,6 +1426,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
if (WARN_ON(!ctx->replace_ctx))
return;
+ old_ctx = ctx->replace_ctx;
WARN_ON(ctx->replace_ctx->replace_state !=
IEEE80211_CHANCTX_WILL_BE_REPLACED);
WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
@@ -1428,6 +1437,11 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
list_del_rcu(&ctx->list);
kfree_rcu(ctx, rcu_head);
+
+ if (ieee80211_chanctx_refcount(sdata->local,
+ old_ctx) == 0)
+ ieee80211_free_chanctx(sdata->local, old_ctx,
+ false);
} else {
ieee80211_free_chanctx(sdata->local, ctx, false);
}
@@ -1707,7 +1721,8 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
ieee80211_check_fast_xmit_iface(sdata);
- if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
+ if (ieee80211_chanctx_refcount(local, old_ctx) == 0 &&
+ !ieee80211_chanctx_has_replace_partner(old_ctx))
ieee80211_free_chanctx(local, old_ctx, false);
ieee80211_recalc_chanctx_min_def(local, new_ctx);
@@ -1772,7 +1787,8 @@ out:
}
static bool
-ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
+ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link,
+ struct ieee80211_chanctx *ctx)
{
struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_chanctx *old_ctx, *new_ctx;
@@ -1782,6 +1798,9 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
new_ctx = link->reserved_chanctx;
old_ctx = ieee80211_link_get_chanctx(link);
+ if (new_ctx != ctx)
+ return false;
+
if (!old_ctx)
return false;
@@ -1794,6 +1813,12 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
return false;
+ if (new_ctx->replace_ctx != old_ctx)
+ return false;
+
+ if (old_ctx->replace_ctx != new_ctx)
+ return false;
+
return true;
}
@@ -1823,7 +1848,8 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
}
for_each_chanctx_user_reserved(local, ctx, &iter) {
- if (!ieee80211_link_has_in_place_reservation(iter.link))
+ if (!ieee80211_link_has_in_place_reservation(iter.link,
+ ctx))
continue;
old_ctx = ieee80211_link_get_chanctx(iter.link);
@@ -1925,7 +1951,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
for_each_chanctx_user_assigned(local, ctx->replace_ctx, &iter) {
n_assigned++;
- if (iter.link && iter.link->reserved_chanctx) {
+ if (iter.link &&
+ ieee80211_link_has_in_place_reservation(iter.link,
+ ctx)) {
n_reserved++;
if (iter.link->reserved_ready)
n_ready++;
@@ -1950,7 +1978,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
use_reserved:
ctx->conf.radar_enabled = false;
for_each_chanctx_user_reserved(local, ctx, &iter) {
- if (ieee80211_link_has_in_place_reservation(iter.link) &&
+ if (ieee80211_link_has_in_place_reservation(iter.link,
+ ctx) &&
!iter.link->reserved_ready)
return -EAGAIN;
@@ -1991,7 +2020,8 @@ use_reserved:
}
for_each_chanctx_user_reserved(local, ctx, &iter) {
- if (!ieee80211_link_has_in_place_reservation(iter.link))
+ if (!ieee80211_link_has_in_place_reservation(iter.link,
+ ctx))
continue;
ieee80211_chan_bw_change(local,
@@ -2040,7 +2070,7 @@ use_reserved:
struct ieee80211_bss_conf *link_conf = link->conf;
u64 changed = 0;
- if (!ieee80211_link_has_in_place_reservation(link))
+ if (!ieee80211_link_has_in_place_reservation(link, ctx))
continue;
rcu_assign_pointer(link_conf->chanctx_conf,
@@ -2091,7 +2121,8 @@ use_reserved:
for_each_chanctx_user_reserved(local, ctx, &iter) {
struct ieee80211_link_data *link = iter.link;
- if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
+ if (WARN_ON(ieee80211_link_has_in_place_reservation(link,
+ ctx)))
continue;
if (!link->reserved_ready)
@@ -2176,7 +2207,8 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link,
}
ieee80211_assign_link_chanctx(link, NULL, false);
- if (ieee80211_chanctx_refcount(local, ctx) == 0)
+ if (ieee80211_chanctx_refcount(local, ctx) == 0 &&
+ !ieee80211_chanctx_has_replace_partner(ctx))
ieee80211_free_chanctx(local, ctx, skip_idle_recalc);
link->radar_required = false;
diff --git a/net/mac80211/drop.h b/net/mac80211/drop.h
index f06a8aa905c5..f433ee17cce5 100644
--- a/net/mac80211/drop.h
+++ b/net/mac80211/drop.h
@@ -108,6 +108,7 @@ typedef unsigned int __bitwise ieee80211_rx_result;
R(RX_DROP_U_UNHANDLED_PREQ) \
R(RX_DROP_U_UNHANDLED_MGMT_STYPE) \
R(RX_DROP_U_NO_LINK) \
+ R(RX_DROP_U_DEFRAG_OVERFLOW) \
/* this line for the trailing \ - add before this */
/* having two enums allows for checking ieee80211_rx_result use with sparse */
diff --git a/net/mac80211/fils_aead.c b/net/mac80211/fils_aead.c
index 293590976489..9c4f00d61981 100644
--- a/net/mac80211/fils_aead.c
+++ b/net/mac80211/fils_aead.c
@@ -89,6 +89,14 @@ static int aes_siv_encrypt(const u8 *key, size_t key_len,
if (res)
return res;
+ /* With an empty plaintext there is no ciphertext to produce and
+ * the S2V result is the complete output.
+ */
+ if (!plain_len) {
+ memcpy(out, v, AES_BLOCK_SIZE);
+ return 0;
+ }
+
/* Use a temporary buffer of the plaintext to handle need for
* overwriting this during AES-CTR.
*/
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d05f59467399..9374c2942489 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2475,7 +2475,7 @@ static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
/* sta_out needs to be checked for ERR_PTR() before using */
int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
- struct sta_info **sta_out);
+ struct sta_info **sta_out, bool bss);
static inline void
ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 889c32fd8de1..5f0c482bd4a5 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -998,8 +998,8 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
len_rthdr = ieee80211_get_radiotap_len(skb->data);
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
- if (skb->len < len_rthdr + 2 ||
- skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
+ if (skb_headlen(skb) < len_rthdr + 2 ||
+ skb_headlen(skb) < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
return 0; /* doesn't matter, frame will be dropped */
return ieee80211_select_queue_80211(sdata, skb, hdr);
@@ -1023,6 +1023,13 @@ static int ieee80211_netdev_fill_forward_path(struct net_device_path_ctx *ctx,
struct sta_info *sta;
int ret = -ENOENT;
+ if (ctx->ieee80211) {
+ path->type = DEV_PATH_IEEE80211;
+ path->dev = ctx->dev;
+ ctx->dev = NULL;
+ return 0;
+ }
+
sdata = IEEE80211_DEV_TO_SUB_IF(ctx->dev);
local = sdata->local;
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 03171cf00855..07a21dfa8674 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -577,6 +577,12 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
goto unlock_sta;
spin_lock(&cache->walk_lock);
+ if ((READ_ONCE(mpath->flags) & MESH_PATH_DELETED) ||
+ (mppath && (READ_ONCE(mppath->flags) & MESH_PATH_DELETED))) {
+ kfree(entry);
+ goto unlock_cache;
+ }
+
prev = rhashtable_lookup_get_insert_fast(&cache->rht,
&entry->rhash,
fast_tx_rht_params);
@@ -694,8 +700,10 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
return ERR_PTR(-ENOSPC);
new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
- if (!new_mpath)
+ if (!new_mpath) {
+ atomic_dec(&sdata->u.mesh.mpaths);
return ERR_PTR(-ENOMEM);
+ }
tbl = &sdata->u.mesh.mesh_paths;
spin_lock_bh(&tbl->walk_lock);
@@ -708,6 +716,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
if (mpath) {
kfree(new_mpath);
+ atomic_dec(&sdata->u.mesh.mpaths);
if (IS_ERR(mpath))
return mpath;
@@ -733,10 +742,15 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
if (is_multicast_ether_addr(dst))
return -EOPNOTSUPP;
+ if (!atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS))
+ return -ENOSPC;
+
new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
- if (!new_mpath)
+ if (!new_mpath) {
+ atomic_dec(&sdata->u.mesh.mpaths);
return -ENOMEM;
+ }
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
tbl = &sdata->u.mesh.mpp_paths;
@@ -749,10 +763,12 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
hlist_add_head_rcu(&new_mpath->walk_list, &tbl->walk_head);
spin_unlock_bh(&tbl->walk_lock);
- if (ret)
+ if (ret) {
kfree(new_mpath);
- else
+ atomic_dec(&sdata->u.mesh.mpaths);
+ } else {
mesh_fast_tx_flush_addr(sdata, dst);
+ }
sdata->u.mesh.mpp_paths_generation++;
return ret;
@@ -798,7 +814,8 @@ static void mesh_path_free_rcu(struct mesh_table *tbl,
struct ieee80211_sub_if_data *sdata = mpath->sdata;
spin_lock_bh(&mpath->state_lock);
- mpath->flags |= MESH_PATH_RESOLVING | MESH_PATH_DELETED;
+ WRITE_ONCE(mpath->flags,
+ mpath->flags | MESH_PATH_RESOLVING | MESH_PATH_DELETED);
mesh_gate_del(tbl, mpath);
spin_unlock_bh(&mpath->state_lock);
timer_shutdown_sync(&mpath->timer);
@@ -812,6 +829,9 @@ static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
{
hlist_del_rcu(&mpath->walk_list);
rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
+ spin_lock_bh(&mpath->state_lock);
+ WRITE_ONCE(mpath->flags, mpath->flags | MESH_PATH_DELETED);
+ spin_unlock_bh(&mpath->state_lock);
if (tbl == &mpath->sdata->u.mesh.mpp_paths)
mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
else
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index f51167f0fc46..3c31c16860ab 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -5877,6 +5877,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
const struct cfg80211_bss_ies *bss_ies = NULL;
+ struct ieee802_11_elems *bss_elems = NULL;
struct ieee80211_supported_band *sband;
struct ieee802_11_elems *elems;
u16 capab_info;
@@ -6007,7 +6008,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
(is_5ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT &&
(!elems->vht_cap_elem || !elems->vht_operation)))) {
const struct cfg80211_bss_ies *ies;
- struct ieee802_11_elems *bss_elems;
rcu_read_lock();
ies = rcu_dereference(cbss->ies);
@@ -6069,7 +6069,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
"AP bug: VHT operation missing from AssocResp\n");
}
}
- kfree(bss_elems);
}
/*
@@ -6342,6 +6341,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
ret = true;
out:
kfree(elems);
+ kfree(bss_elems);
kfree(bss_ies);
return ret;
}
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index b73ef3adfcc5..45e7a7ea7690 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -7,6 +7,7 @@
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/debugfs.h>
+#include <linux/limits.h>
#include <linux/random.h>
#include <linux/moduleparam.h>
#include <linux/ieee80211.h>
@@ -1193,6 +1194,54 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
mi->sample_time = jiffies;
}
+/*
+ * Check whether an HT/VHT rate from a TX status entry maps to an entry
+ * in the minstrel_ht rate tables. Values that cannot be represented
+ * there must not be used for indexing mi->groups[] and the MCS groups.
+ */
+static bool
+minstrel_ht_txstat_rate_valid(struct ieee80211_tx_rate *rate)
+{
+ unsigned int bw;
+
+ if (!(rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)))
+ return true;
+
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
+ /* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */
+ return rate->idx < MINSTREL_MAX_STREAMS * 8;
+ }
+
+ /* minstrel_ht has no VHT groups for 160 MHz and wider */
+ bw = !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
+ 2 * !!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH);
+ if ((rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) || bw > BW_80)
+ return false;
+
+ return ieee80211_rate_get_vht_nss(rate) <= MINSTREL_MAX_STREAMS &&
+ ieee80211_rate_get_vht_mcs(rate) < MCS_GROUP_RATES;
+}
+
+static bool
+minstrel_ht_ri_txstat_rate_valid(struct rate_info *rate)
+{
+ if (!(rate->flags & (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS)))
+ return true;
+
+ if (rate->flags & RATE_INFO_FLAGS_MCS) {
+ /* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */
+ return rate->mcs < MINSTREL_MAX_STREAMS * 8;
+ }
+
+ /* minstrel_ht has VHT groups only for 20/40/80 MHz */
+ if (rate->bw != RATE_INFO_BW_20 && rate->bw != RATE_INFO_BW_40 &&
+ rate->bw != RATE_INFO_BW_80)
+ return false;
+
+ return rate->nss <= MINSTREL_MAX_STREAMS &&
+ rate->mcs < MCS_GROUP_RATES;
+}
+
static bool
minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_tx_rate *rate)
@@ -1205,9 +1254,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
if (!rate->count)
return false;
- if (rate->flags & IEEE80211_TX_RC_MCS ||
- rate->flags & IEEE80211_TX_RC_VHT_MCS)
- return true;
+ if (rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
+ return minstrel_ht_txstat_rate_valid(rate);
for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++)
if (rate->idx == mp->cck_rates[i])
@@ -1235,9 +1283,9 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp,
if (!rate_status->try_count)
return false;
- if (rate_status->rate_idx.flags & RATE_INFO_FLAGS_MCS ||
- rate_status->rate_idx.flags & RATE_INFO_FLAGS_VHT_MCS)
- return true;
+ if (rate_status->rate_idx.flags &
+ (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS))
+ return minstrel_ht_ri_txstat_rate_valid(&rate_status->rate_idx);
for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) {
if (rate_status->rate_idx.legacy ==
@@ -1947,14 +1995,39 @@ minstrel_ht_alloc(struct ieee80211_hw *hw)
}
#ifdef CONFIG_MAC80211_DEBUGFS
+static int minstrel_ht_fixed_rate_idx_get(void *data, u64 *val)
+{
+ *val = *(u32 *)data;
+ return 0;
+}
+
+static int minstrel_ht_fixed_rate_idx_set(void *data, u64 val)
+{
+ u32 idx = val;
+
+ /* U32_MAX is the default and keeps fixed rate processing disabled */
+ if (val != U32_MAX &&
+ (val > U16_MAX ||
+ MI_RATE_GROUP(idx) >= ARRAY_SIZE(minstrel_mcs_groups) ||
+ MI_RATE_IDX(idx) >= MCS_GROUP_RATES))
+ return -EINVAL;
+
+ *(u32 *)data = idx;
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(minstrel_ht_fixed_rate_idx_fops,
+ minstrel_ht_fixed_rate_idx_get,
+ minstrel_ht_fixed_rate_idx_set, "%llu\n");
+
static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv,
struct dentry *debugfsdir)
{
struct minstrel_priv *mp = priv;
mp->fixed_rate_idx = (u32) -1;
- debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
- &mp->fixed_rate_idx);
+ debugfs_create_file("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
+ &mp->fixed_rate_idx, &minstrel_ht_fixed_rate_idx_fops);
}
#endif
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 5e26be8e27d8..7ead509304eb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2499,6 +2499,12 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
}
skb_pull(rx->skb, ieee80211_hdrlen(fc));
+ if (unlikely((u32)entry->extra_len + rx->skb->len > U16_MAX)) {
+ I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
+ __skb_queue_purge(&entry->skb_list);
+ return RX_DROP_U_DEFRAG_OVERFLOW;
+ }
+
__skb_queue_tail(&entry->skb_list, rx->skb);
entry->last_frag = frag;
entry->extra_len += rx->skb->len;
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 880f4625775d..f5e4970869b6 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -111,8 +111,8 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
switch (chan_width) {
case NL80211_CHAN_WIDTH_320:
- WARN_ON(1);
- break;
+ chandef->chan = NULL;
+ return;
case NL80211_CHAN_WIDTH_160:
vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index fdf00cbf49d8..e81cd155197b 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -137,6 +137,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct ps_data *ps;
+ cancel_work_sync(&sta->drv_deliver_wk);
+
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
@@ -166,8 +168,6 @@ static void __cleanup_single_sta(struct sta_info *sta)
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_sta_cleanup(sta);
- cancel_work_sync(&sta->drv_deliver_wk);
-
/*
* Destroy aggregation state here. It would be nice to wait for the
* driver to finish aggregation stop and then clean up, but for now
@@ -1582,6 +1582,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta, bool recalc)
sta->dead = true;
+ cancel_work_sync(&sta->drv_deliver_wk);
+
local->num_sta--;
local->sta_generation++;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 3d811f652603..414979e2a1d9 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -1164,6 +1164,73 @@ void ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb)
}
EXPORT_SYMBOL(ieee80211_tx_status_skb);
+/*
+ * Check whether the HT/VHT rate information in a TX status entry is
+ * valid for its encoding. This is not specific to any rate control
+ * algorithm; all status consumers rely on the values being sane.
+ */
+static bool ieee80211_tx_status_rate_info_valid(const struct rate_info *rate)
+{
+ if (rate->flags & RATE_INFO_FLAGS_MCS)
+ return rate->mcs < 32;
+
+ if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+ return rate->nss >= 1 && rate->nss <= 8 && rate->mcs <= 11;
+
+ return true;
+}
+
+static bool
+ieee80211_tx_status_tx_rate_valid(const struct ieee80211_tx_rate *rate)
+{
+ if (rate->flags & IEEE80211_TX_RC_MCS)
+ return rate->idx < 32;
+
+ if (rate->flags & IEEE80211_TX_RC_VHT_MCS)
+ return ieee80211_rate_get_vht_mcs(rate) <= 11;
+
+ return true;
+}
+
+/*
+ * Drop TX status rate entries that don't describe a valid rate. The
+ * status information is used by rate control and by other mac80211
+ * code, and a malformed entry must not be able to corrupt state beyond
+ * the driver that reported it.
+ */
+static void
+ieee80211_tx_status_drop_invalid_rates(struct ieee80211_tx_status *status)
+{
+ int i;
+
+ for (i = 0; i < status->n_rates; i++) {
+ struct ieee80211_rate_status *rs = &status->rates[i];
+
+ if (ieee80211_tx_status_rate_info_valid(&rs->rate_idx))
+ continue;
+
+ rs->try_count = 0;
+ memset(&rs->rate_idx, 0, sizeof(rs->rate_idx));
+ }
+
+ if (!status->info)
+ return;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ struct ieee80211_tx_rate *rate;
+
+ rate = &status->info->status.rates[i];
+ if (rate->idx < 0)
+ break;
+
+ if (ieee80211_tx_status_tx_rate_valid(rate))
+ continue;
+
+ rate->idx = -1;
+ rate->count = 0;
+ }
+}
+
void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
struct ieee80211_tx_status *status)
{
@@ -1176,6 +1243,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
bool acked, noack_success, ack_signal_valid;
u16 tx_time_est;
+ ieee80211_tx_status_drop_invalid_rates(status);
+
if (pubsta) {
sta = container_of(pubsta, struct sta_info, sta);
@@ -1300,6 +1369,7 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
.sta = pubsta,
};
+ ieee80211_tx_status_drop_invalid_rates(&status);
rate_control_tx_status(local, &status);
if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 814399989b5e..d51f810dd2c5 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2091,7 +2091,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
(struct ieee80211_radiotap_header *)skb->data;
/* check for not even having the fixed radiotap header part */
- if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
+ if (unlikely(skb_headlen(skb) < sizeof(struct ieee80211_radiotap_header)))
return false; /* too short to be possibly valid */
/* is it a header version we can trust to find length from? */
@@ -2099,7 +2099,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
return false; /* only version 0 is supported */
/* does the skb contain enough to deliver on the alleged length? */
- if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data)))
+ if (unlikely(skb_headlen(skb) < ieee80211_get_radiotap_len(skb->data)))
return false; /* skb too short for claimed rt header extent */
return true;
@@ -2388,13 +2388,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
skb_set_network_header(skb, len_rthdr);
skb_set_transport_header(skb, len_rthdr);
- if (skb->len < len_rthdr + 2)
+ if (skb_headlen(skb) < len_rthdr + 2)
goto fail;
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
hdrlen = ieee80211_hdrlen(hdr->frame_control);
- if (skb->len < len_rthdr + hdrlen)
+ if (skb_headlen(skb) < len_rthdr + hdrlen)
goto fail;
/*
@@ -2402,7 +2402,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
* carrying a rfc1042 header
*/
if (ieee80211_is_data(hdr->frame_control) &&
- skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) {
+ skb_headlen(skb) >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) {
u8 *payload = (u8 *)hdr + hdrlen;
if (ether_addr_equal(payload, rfc1042_header))
@@ -2532,7 +2532,7 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
- struct sta_info **sta_out)
+ struct sta_info **sta_out, bool bss)
{
struct sta_info *sta;
@@ -2553,7 +2553,10 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
*sta_out = ERR_PTR(-ENOENT);
return 0;
}
- sta = sta_info_get_bss(sdata, skb->data);
+ if (bss)
+ sta = sta_info_get_bss(sdata, skb->data);
+ else
+ sta = sta_info_get(sdata, skb->data);
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
@@ -4419,7 +4422,8 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
ieee80211_mesh_xmit_fast(sdata, skb, ctrl_flags))
goto out;
- if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta,
+ skb->protocol == sdata->control_port_protocol))
goto out_free;
if (IS_ERR(sta))
@@ -4731,6 +4735,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_tx_info *info;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
struct tid_ampdu_tx *tid_tx = NULL;
struct sk_buff *seg, *next;
unsigned int skbs = 0, len = 0;
@@ -4780,6 +4785,16 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data, u.ap);
+ /* MLD transmissions must not rely on the band */
+ if (!ieee80211_vif_is_mld(&sdata->vif)) {
+ chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
+ if (unlikely(!chanctx_conf)) {
+ kfree_skb_list(skb);
+ return;
+ }
+ info->band = chanctx_conf->def.chan->band;
+ }
+
info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
info->control.vif = &sdata->vif;
@@ -4848,7 +4863,10 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
rcu_read_lock();
- if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ if (unlikely(sdata->control_port_protocol == ehdr->h_proto))
+ goto skip_offload;
+
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) {
kfree_skb(skb);
goto out;
}
@@ -4869,8 +4887,7 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
link = &sdata->deflink;
key = rcu_dereference(link->default_multicast_key);
} else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
- !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
- sdata->control_port_protocol == ehdr->h_proto)) {
+ !test_sta_flag(sta, WLAN_STA_AUTHORIZED))) {
goto skip_offload;
} else {
key = rcu_dereference(sta->ptk[sta->ptk_idx]);
@@ -4931,7 +4948,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
- if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) {
kfree_skb(skb);
skb = ERR_PTR(-EINVAL);
goto out;
@@ -5002,7 +5019,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
}
result = ieee80211_tx(sdata, NULL, skb, true);
} else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
- if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
+ if (ieee80211_lookup_ra_sta(sdata, skb, &sta, true)) {
dev_kfree_skb(skb);
return true;
}
@@ -6639,7 +6656,7 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
* AF_PACKET
*/
rcu_read_lock();
- err = ieee80211_lookup_ra_sta(sdata, skb, &sta);
+ err = ieee80211_lookup_ra_sta(sdata, skb, &sta, true);
if (err) {
dev_kfree_skb(skb);
rcu_read_unlock();
diff --git a/net/mctp/device.c b/net/mctp/device.c
index 822120e860c8..9f76ff092559 100644
--- a/net/mctp/device.c
+++ b/net/mctp/device.c
@@ -344,13 +344,13 @@ static struct mctp_dev *mctp_add_dev(struct net_device *dev)
mdev->net = mctp_default_net(dev_net(dev));
- /* associate to net_device */
refcount_set(&mdev->refs, 1);
- rcu_assign_pointer(dev->mctp_ptr, mdev);
-
dev_hold(dev);
mdev->dev = dev;
+ /* Associate to net_device, publish the mdev. */
+ rcu_assign_pointer(dev->mctp_ptr, mdev);
+
return mdev;
}
diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
index d6a7e6604542..ae376fa3e7a3 100644
--- a/net/netfilter/ipset/ip_set_bitmap_gen.h
+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -159,7 +159,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
if (SET_WITH_COUNTER(set))
ip_set_init_counter(ext_counter(x, set), ext);
- if (SET_WITH_COMMENT(set))
+ if (SET_WITH_COMMENT(set) && !ext->target)
ip_set_init_comment(set, ext_comment(x, set), ext);
if (SET_WITH_SKBINFO(set))
ip_set_init_skbinfo(ext_skbinfo(x, set), ext);
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index 6fa3e1dc534c..cb009208826f 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -1102,6 +1102,9 @@ ip_vs_bind_dest(struct ip_vs_conn *cp, struct ip_vs_dest *dest)
if (cp->protocol != IPPROTO_UDP)
conn_flags &= ~IP_VS_CONN_F_ONE_PACKET;
flags = cp->flags;
+ /* Only visible templates can control multiple connections */
+ if (flags & IP_VS_CONN_F_TEMPLATE)
+ conn_flags &= ~IP_VS_CONN_F_ONE_PACKET;
/* Bind with the destination and its corresponding transmitter */
if (flags & IP_VS_CONN_F_SYNC) {
/* Synced conns are hashed, so they can not get this flag */
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 693bcc82ccb7..55af77a56929 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -203,7 +203,11 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
if (en->dest == dest)
return en;
ip_vs_lblc_del(en);
+ atomic_dec(&tbl->entries);
}
+ if (atomic_read(&tbl->entries) >= tbl->max_size * 3 / 2)
+ return NULL;
+
en = kmalloc_obj(*en, GFP_ATOMIC);
if (!en)
return NULL;
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index f53f05ceea36..858393b1d2d1 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -363,6 +363,9 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr,
en = ip_vs_lblcr_get(af, tbl, daddr);
if (!en) {
+ if (atomic_read(&tbl->entries) >= tbl->max_size * 3 / 2)
+ return NULL;
+
en = kmalloc_obj(*en, GFP_ATOMIC);
if (!en)
return NULL;
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 5383aeafb0ae..69dc28153ec1 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -949,6 +949,21 @@ static void ip_vs_proc_conn(struct netns_ipvs *ipvs, struct ip_vs_conn_param *pa
ip_vs_conn_put(cp);
}
+/* Check for incompatible flags */
+static bool ip_vs_sync_validate_flags(u32 flags)
+{
+ /* We do not expect NO_CPORT, especially to allow lookups
+ * to hit templates
+ */
+ if (flags & IP_VS_CONN_F_NO_CPORT) {
+ if (flags & IP_VS_CONN_F_TEMPLATE)
+ return false;
+ }
+ if (flags & IP_VS_CONN_F_ONE_PACKET)
+ return false;
+ return true;
+}
+
/*
* Process received multicast message for Version 0
*/
@@ -972,8 +987,7 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer
return;
}
s = (struct ip_vs_sync_conn_v0 *) p;
- flags = ntohs(s->flags) | IP_VS_CONN_F_SYNC;
- flags &= ~IP_VS_CONN_F_HASHED;
+ flags = ntohs(s->flags);
if (flags & IP_VS_CONN_F_SEQ_MASK) {
opt = (struct ip_vs_sync_conn_options *)&s[1];
p += FULL_CONN_SIZE;
@@ -986,6 +1000,13 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer
p += SIMPLE_CONN_SIZE;
}
+ if (!ip_vs_sync_validate_flags(flags)) {
+ IP_VS_DBG(2, "BACKUP v0, Invalid flags 0x%X\n", flags);
+ continue;
+ }
+ flags &= IP_VS_CONN_F_BACKUP_MASK;
+ flags |= IP_VS_CONN_F_SYNC;
+
state = ntohs(s->state);
if (!(flags & IP_VS_CONN_F_TEMPLATE)) {
pp = ip_vs_proto_get(s->protocol);
@@ -1141,7 +1162,13 @@ static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *m
}
/* Get flags and Mask off unsupported */
- flags = ntohl(s->v4.flags) & IP_VS_CONN_F_BACKUP_MASK;
+ flags = ntohl(s->v4.flags);
+ if (!ip_vs_sync_validate_flags(flags)) {
+ IP_VS_DBG(3, "BACKUP, Invalid flags 0x%X\n", flags);
+ retc = 25;
+ goto out;
+ }
+ flags &= IP_VS_CONN_F_BACKUP_MASK;
flags |= IP_VS_CONN_F_SYNC;
state = ntohs(s->v4.state);
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
index 934c6151f558..36bbc7be2f74 100644
--- a/net/netfilter/nf_flow_table_core.c
+++ b/net/netfilter/nf_flow_table_core.c
@@ -575,7 +575,12 @@ static void nf_flow_table_extend_ct_timeout(struct nf_conn *ct)
static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
struct flow_offload *flow, void *data)
{
- bool teardown = test_bit(NF_FLOW_TEARDOWN, &flow->flags);
+ bool teardown;
+
+ if (test_bit(NF_FLOW_PENDING, &flow->flags))
+ return;
+
+ teardown = test_bit(NF_FLOW_TEARDOWN, &flow->flags);
if (nf_flow_has_expired(flow) ||
nf_ct_is_dying(flow->ct) ||
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
index 6757fd89c1f1..4365859220e6 100644
--- a/net/netfilter/nf_flow_table_offload.c
+++ b/net/netfilter/nf_flow_table_offload.c
@@ -995,6 +995,7 @@ static void flow_offload_work_del(struct flow_offload_work *offload)
flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
+ set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
}
static void flow_offload_tuple_stats(struct flow_offload_work *offload,
@@ -1056,13 +1057,8 @@ static void flow_offload_work_handler(struct work_struct *work)
default:
WARN_ON_ONCE(1);
}
-
- clear_bit(NF_FLOW_HW_PENDING, &offload->flow->flags);
- if (offload->cmd == FLOW_CLS_DESTROY) {
- /* Publish after the worker's last flow access. */
- smp_mb__before_atomic();
- set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
- }
+ smp_mb__before_atomic();
+ clear_bit(NF_FLOW_PENDING, &offload->flow->flags);
kfree(offload);
}
@@ -1089,12 +1085,12 @@ nf_flow_offload_work_alloc(struct nf_flowtable *flowtable,
{
struct flow_offload_work *offload;
- if (test_and_set_bit(NF_FLOW_HW_PENDING, &flow->flags))
+ if (test_and_set_bit(NF_FLOW_PENDING, &flow->flags))
return NULL;
offload = kmalloc_obj(struct flow_offload_work, GFP_ATOMIC);
if (!offload) {
- clear_bit(NF_FLOW_HW_PENDING, &flow->flags);
+ clear_bit(NF_FLOW_PENDING, &flow->flags);
return NULL;
}
diff --git a/net/netfilter/nf_flow_table_path.c b/net/netfilter/nf_flow_table_path.c
index 1e55644f2edb..d90013685bf1 100644
--- a/net/netfilter/nf_flow_table_path.c
+++ b/net/netfilter/nf_flow_table_path.c
@@ -53,6 +53,7 @@ static int nft_dev_fill_forward_path(const struct dst_entry *dst_cache,
struct net_device_path_ctx ctx = {
.dev = dev,
.ether_type = ether_type,
+ .ieee80211 = true,
};
struct neighbour *n;
u8 nud_state;
@@ -114,6 +115,7 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,
path = &stack->path[i];
switch (path->type) {
case DEV_PATH_ETHERNET:
+ case DEV_PATH_IEEE80211:
case DEV_PATH_DSA:
case DEV_PATH_VLAN:
case DEV_PATH_PPPOE:
@@ -123,6 +125,7 @@ static int nft_dev_path_info(struct net_device_path_stack *stack,
memcpy(info->h_source, path->dev->dev_addr, ETH_ALEN);
if (path->type == DEV_PATH_ETHERNET ||
+ path->type == DEV_PATH_IEEE80211 ||
path->type == DEV_PATH_DSA)
break;
diff --git a/net/netfilter/nf_nat_bpf.c b/net/netfilter/nf_nat_bpf.c
index f9dd85ccea01..7572b58c448b 100644
--- a/net/netfilter/nf_nat_bpf.c
+++ b/net/netfilter/nf_nat_bpf.c
@@ -39,6 +39,9 @@ __bpf_kfunc int bpf_ct_set_nat_info(struct nf_conn___init *nfct,
if (proto != NFPROTO_IPV4 && proto != NFPROTO_IPV6)
return -EINVAL;
+ if (manip != NF_NAT_MANIP_SRC && manip != NF_NAT_MANIP_DST)
+ return -EINVAL;
+
memset(&range, 0, sizeof(struct nf_nat_range2));
range.flags = NF_NAT_RANGE_MAP_IPS;
range.min_addr = *addr;
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index a4858c2b2d65..cc8e1e81006d 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -767,8 +767,9 @@ nf_nat_setup_info(struct nf_conn *ct,
if (nf_ct_is_confirmed(ct))
return NF_ACCEPT;
- WARN_ON(maniptype != NF_NAT_MANIP_SRC &&
- maniptype != NF_NAT_MANIP_DST);
+ if (WARN_ON(maniptype != NF_NAT_MANIP_SRC &&
+ maniptype != NF_NAT_MANIP_DST))
+ return NF_DROP;
if (WARN_ON(nf_nat_initialized(ct, maniptype)))
return NF_DROP;
diff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c
index 32b4281038dd..d3c5651dd699 100644
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -160,6 +160,7 @@ static int nft_flow_offload_init(const struct nft_ctx *ctx,
struct nft_flow_offload *priv = nft_expr_priv(expr);
u8 genmask = nft_genmask_next(ctx->net);
struct nft_flowtable *flowtable;
+ int err;
if (!tb[NFTA_FLOW_TABLE_NAME])
return -EINVAL;
@@ -174,7 +175,11 @@ static int nft_flow_offload_init(const struct nft_ctx *ctx,
priv->flowtable = flowtable;
- return nf_ct_netns_get(ctx->net, ctx->family);
+ err = nf_ct_netns_get(ctx->net, ctx->family);
+ if (err < 0)
+ nft_use_dec(&flowtable->use);
+
+ return err;
}
static void nft_flow_offload_deactivate(const struct nft_ctx *ctx,
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index 9894832281c4..12431b55752f 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -900,6 +900,8 @@ static void nft_rbtree_gc_scan(struct nft_set *set)
next = rb_next(node);
rbe = rb_entry(node, struct nft_rbtree_elem, node);
+ if (!nft_set_elem_active(&rbe->ext, NFT_GENMASK_ANY))
+ continue;
/* elements are reversed in the rbtree for historical reasons,
* from highest to lowest value, that is why end element is
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 7c83e01526ed..8865c1a42667 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2874,6 +2874,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
vnet_hdr.hdr_len);
has_vnet_hdr = true;
}
+ /* Prevent the byte count from wrapping into a negative errno */
+ if (unlikely(tp_len > INT_MAX - len_sum))
+ break;
copylen = max_t(int, copylen, hard_header_len);
skb = sock_alloc_send_skb(&po->sk,
hlen + tlen + sizeof(struct sockaddr_ll) +
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
index 411e3dd92d07..b48e6d2bb5a7 100644
--- a/net/sched/act_ct.c
+++ b/net/sched/act_ct.c
@@ -289,7 +289,7 @@ static bool tcf_ct_flow_is_outdated(const struct flow_offload *flow)
{
return test_bit(IPS_SEEN_REPLY_BIT, &flow->ct->status) &&
test_bit(IPS_HW_OFFLOAD_BIT, &flow->ct->status) &&
- !test_bit(NF_FLOW_HW_PENDING, &flow->flags) &&
+ !test_bit(NF_FLOW_PENDING, &flow->flags) &&
!test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags);
}
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
index bfec6b668410..26258b353cde 100644
--- a/net/sched/act_skbedit.c
+++ b/net/sched/act_skbedit.c
@@ -72,9 +72,8 @@ TC_INDIRECT_SCOPE int tcf_skbedit_act(struct sk_buff *skb,
}
if (params->flags & SKBEDIT_F_QUEUE_MAPPING &&
skb->dev->real_num_tx_queues > params->queue_mapping) {
-#ifdef CONFIG_NET_EGRESS
- netdev_xmit_skip_txqueue(true);
-#endif
+ if (skb_at_tc_egress(skb))
+ netdev_xmit_skip_txqueue(true);
skb_set_queue_mapping(skb, tcf_skbedit_hash(params, skb));
}
if (params->flags & SKBEDIT_F_MARK) {
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index c47d2ee13641..a9f54988561f 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -2463,7 +2463,20 @@ replay:
}
errout:
- if (err && tp_state == TP_CREATED)
+ if (err && !IS_ERR_OR_NULL(tp) &&
+ (tp_state == TP_CREATED || tp->ops->delete_empty))
+ /*
+ * The request is dropping its reference to tp. If it was
+ * the last user (the idr is empty now), reclaim the proto.
+ * A tp this request created is reclaimed unconditionally:
+ * it is the only owner, so marking it for deletion is
+ * safe. Otherwise only classifiers with a delete_empty
+ * callback are reclaimed -- the callback admits an empty
+ * proto only, so a live shared proto is never unlinked.
+ * Classifiers without one (deleting is set
+ * unconditionally) are rtnl-serialized, so the raced
+ * window this guard closes cannot arise for them.
+ */
tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
errout_tp:
if (chain) {
diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
index 6aa5829d6961..a1ba2e355f2d 100644
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -205,7 +205,7 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt,
codel_params_init(&q->params);
codel_vars_init(&q->vars);
codel_stats_init(&q->stats);
- q->params.mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, 1 << 20);
+ q->params.mtu = min_t(u32, psched_mtu(qdisc_dev(sch)), 1 << 20);
if (opt) {
int err = codel_change(sch, opt, extack);
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 969b2510b0b8..e6c87a32950f 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -517,8 +517,8 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt,
q->flows_cnt = 1024;
q->memory_limit = 32 << 20; /* 32 MBytes */
q->drop_batch_size = 64;
- mtu = clamp_t(u32, psched_mtu(qdisc_dev(sch)), 256, FQ_CODEL_QUANTUM_MAX);
- q->quantum = mtu;
+ mtu = min_t(u32, psched_mtu(qdisc_dev(sch)), FQ_CODEL_QUANTUM_MAX);
+ q->quantum = clamp_t(u32, mtu, 256, FQ_CODEL_QUANTUM_MAX);
INIT_LIST_HEAD(&q->new_flows);
INIT_LIST_HEAD(&q->old_flows);
codel_params_init(&q->cparams);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c7b9e325ec1c..4652fd90d9a6 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -1844,7 +1844,7 @@ static int sctp_sendmsg_to_asoc(struct sctp_association *asoc,
if (asoc->ep->intl_enable) {
timeo = sock_sndtimeo(sk, 0);
err = sctp_wait_for_connect(asoc, &timeo);
- if (err) {
+ if (err || sk->sk_shutdown & RCV_SHUTDOWN) {
err = -ESRCH;
goto err;
}
diff --git a/net/tipc/crypto.c b/net/tipc/crypto.c
index 16f1ed1f6b1b..4409bdb70fe9 100644
--- a/net/tipc/crypto.c
+++ b/net/tipc/crypto.c
@@ -144,7 +144,7 @@ struct tipc_tfm {
* @rcu: struct rcu_head
* @key: the aead key
* @gen: the key's generation
- * @seqno: the key seqno (cluster scope)
+ * @seqno: the per-key TX nonce counter
* @refcnt: the key reference counter
*/
struct tipc_aead {
@@ -190,7 +190,6 @@ struct tipc_crypto_stats {
* @rekeying_intv: rekeying interval (in minutes)
* @stats: the crypto statistics
* @name: the crypto name
- * @sndnxt: the per-peer sndnxt (TX)
* @timer1: general timer 1 (jiffies)
* @timer2: general timer 2 (jiffies)
* @working: the crypto is working or not
@@ -219,7 +218,6 @@ struct tipc_crypto {
struct tipc_crypto_stats __percpu *stats;
char name[48];
- atomic64_t sndnxt ____cacheline_aligned;
unsigned long timer1;
unsigned long timer2;
union {
@@ -1051,14 +1049,11 @@ static int tipc_ehdr_build(struct net *net, struct tipc_aead *aead,
WARN_ON(skb_headroom(skb) < ehsz);
ehdr = (struct tipc_ehdr *)skb_push(skb, ehsz);
- /* Obtain a seqno first:
- * Use the key seqno (= cluster wise) if dest is unknown or we're in
- * cluster key mode, otherwise it's better for a per-peer seqno!
+ /*
+ * Keep the nonce unique for the lifetime of the TX key,
+ * including key state changes and peer reconnection.
*/
- if (!__rx || aead->mode == CLUSTER_KEY)
- seqno = atomic64_inc_return(&aead->seqno);
- else
- seqno = atomic64_inc_return(&__rx->sndnxt);
+ seqno = atomic64_inc_return(&aead->seqno);
/* Revoke the key if seqno is wrapped around */
if (unlikely(!seqno))
@@ -1237,7 +1232,6 @@ void tipc_crypto_key_flush(struct tipc_crypto *c)
tipc_crypto_key_set_state(c, 0, 0, 0);
for (k = KEY_MIN; k <= KEY_MAX; k++)
tipc_crypto_key_detach(c->aead[k], &c->lock);
- atomic64_set(&c->sndnxt, 0);
spin_unlock_bh(&c->lock);
}
@@ -1384,8 +1378,6 @@ done:
* It also considers if peer has no key, then we need to make own master key
* (if any) taking over i.e. starting grace period and also trigger key
* distributing process.
- *
- * The "per-peer" sndnxt is also reset when the peer key has switched.
*/
static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb)
{
@@ -1436,7 +1428,6 @@ static void tipc_crypto_key_synch(struct tipc_crypto *rx, struct sk_buff *skb)
if (cur)
tipc_aead_users_dec(tx->aead[cur], 0);
- atomic64_set(&rx->sndnxt, 0);
/* Mark the point TX key users changed */
tx->timer1 = jiffies;
@@ -1501,7 +1492,6 @@ int tipc_crypto_start(struct tipc_crypto **crypto, struct net *net,
tipc_crypto_key_set_state(c, 0, 0, 0);
atomic_set(&c->key_distr, 0);
atomic_set(&c->peer_rx_active, 0);
- atomic64_set(&c->sndnxt, 0);
c->timer1 = jiffies;
c->timer2 = jiffies;
c->rekeying_intv = TIPC_REKEYING_INTV_DEF;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 9fafd7673d85..ddce61ec016a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -4563,12 +4563,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.retry_long = retry_long;
if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
rdev->wiphy.frag_threshold = frag_threshold;
- if ((changed & WIPHY_PARAM_RTS_THRESHOLD) &&
- old_radio_rts_threshold) {
+ if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
rdev->wiphy.rts_threshold = rts_threshold;
- for (i = 0 ; i < rdev->wiphy.n_radio; i++)
- rdev->wiphy.radio_cfg[i].rts_threshold =
- rdev->wiphy.rts_threshold;
+ if (old_radio_rts_threshold) {
+ for (i = 0; i < rdev->wiphy.n_radio; i++)
+ rdev->wiphy.radio_cfg[i].rts_threshold =
+ rdev->wiphy.rts_threshold;
+ }
}
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
rdev->wiphy.coverage_class = coverage_class;
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index caa9c6495f20..d35c826c4b1a 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -1992,20 +1992,22 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
return false;
}
- old = rcu_access_pointer(known->pub.beacon_ies);
+ if (!new->pub.hidden_beacon_bss) {
+ old = rcu_access_pointer(known->pub.beacon_ies);
- rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
+ rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
- /* Override IEs if they were from a beacon before */
- if (old == rcu_access_pointer(known->pub.ies))
- rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
+ /* Override IEs if they were from a beacon before */
+ if (old == rcu_access_pointer(known->pub.ies))
+ rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
- cfg80211_update_hidden_bsses(known,
- rcu_access_pointer(new->pub.beacon_ies),
- old);
+ cfg80211_update_hidden_bsses(known,
+ rcu_access_pointer(new->pub.beacon_ies),
+ old);
- if (old)
- kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
+ if (old)
+ kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
+ }
}
known->pub.beacon_interval = new->pub.beacon_interval;
diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs
index 22ef3dde36fa..2f8733a694a9 100644
--- a/rust/kernel/net/netlink.rs
+++ b/rust/kernel/net/netlink.rs
@@ -11,6 +11,7 @@
use kernel::{
alloc::{self, AllocError},
error::to_result,
+ num::casts::u16_as_usize,
prelude::*,
transmute::AsBytes,
types::Opaque,
@@ -86,9 +87,17 @@ impl GenlMsg {
where
T: ?Sized + AsBytes,
{
+ // `nla_len` is a 16-bit field that encodes the total attribute length
+ // (header + payload). Subtracting the header size from `u16::MAX` gives
+ // the largest payload that still fits within that field.
+ const MAX_PAYLOAD_LEN: usize = u16_as_usize(u16::MAX) - size_of::<bindings::nlattr>();
+
let skb = self.skb.skb.as_ptr();
let len = size_of_val(value);
let ptr = core::ptr::from_ref(value).cast::<c_void>();
+ if len > MAX_PAYLOAD_LEN {
+ return Err(EMSGSIZE);
+ }
// SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is
// readable and initialized for its `size_of` bytes.
to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) })
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 22679dca9aae..80110501da6f 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -44,7 +44,9 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
return -EFAULT;
data->kernel = NULL;
- err = snd_seq_kernel_client_ctl(client->number, cmd, data);
+ scoped_guard(mutex, &client->ioctl_mutex) {
+ err = snd_seq_kernel_client_ctl(client->number, cmd, data);
+ }
if (err < 0)
return err;
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 5127a111c59c..be5482530aa2 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -4025,6 +4025,7 @@ enum {
ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2,
ALC269_FIXUP_DELL_M101Z,
+ ALC256_FIXUP_ASUS_PM3406CHA,
ALC269_FIXUP_SKU_IGNORE,
ALC269_FIXUP_ASUS_G73JW,
ALC269_FIXUP_ASUS_N7601ZM_PINS,
@@ -4124,6 +4125,7 @@ enum {
ALC285_FIXUP_HP_SPECTRE_X360_EB1,
ALC285_FIXUP_HP_SPECTRE_X360_DF1,
ALC285_FIXUP_HP_ENVY_X360,
+ ALC285_FIXUP_HP_ENVY_13AQ,
ALC288_FIXUP_DELL_HEADSET_MODE,
ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC288_FIXUP_DELL_XPS_13,
@@ -4199,6 +4201,7 @@ enum {
ALC225_FIXUP_DELL_WYSE_AIO_MIC_NO_PRESENCE,
ALC225_FIXUP_WYSE_AUTO_MUTE,
ALC225_FIXUP_WYSE_DISABLE_MIC_VREF,
+ ALC225_FIXUP_HP_AIO_SPK,
ALC286_FIXUP_ACER_AIO_HEADSET_MIC,
ALC256_FIXUP_ASUS_HEADSET_MIC,
ALC256_FIXUP_ASUS_MIC_NO_PRESENCE,
@@ -4384,6 +4387,7 @@ enum {
ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN_HEADSET,
ALC285_LENOVO_DAC_RENAME,
ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1,
+ ALC256_FIXUP_IPASON_SMARTBOOK_S1,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -4481,6 +4485,14 @@ static const struct hda_fixup alc269_fixups[] = {
{}
}
},
+ [ALC256_FIXUP_ASUS_PM3406CHA] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7f20 },
+ {}
+ }
+ },
[ALC269_FIXUP_SKU_IGNORE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_sku_ignore,
@@ -5719,6 +5731,14 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
},
+ [ALC225_FIXUP_HP_AIO_SPK] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0220 },
+ { }
+ },
+ },
[ALC286_FIXUP_ACER_AIO_HEADSET_MIC] = {
.type = HDA_FIXUP_VERBS,
.v.verbs = (const struct hda_verb[]) {
@@ -6400,6 +6420,15 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT,
},
+ [ALC285_FIXUP_HP_ENVY_13AQ] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x90170150 }, /* front speakers */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC285_FIXUP_HP_GPIO_AMP_INIT,
+ },
[ALC287_FIXUP_IDEAPAD_BASS_SPK_AMP] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_ideapad_s740_coef,
@@ -7087,6 +7116,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC236_FIXUP_DELL_HP_POP_NOISE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_invalidate_dacs,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
[ALC274_FIXUP_HP_89E9_GPIO] = {
.type = HDA_FIXUP_FUNC,
@@ -7136,6 +7167,13 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC287_FIXUP_TXNW2781_I2C,
},
+ [ALC256_FIXUP_IPASON_SMARTBOOK_S1] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170110 }, /* the real internal speaker */
+ { }
+ },
+ },
};
static const struct hda_quirk alc269_fixup_tbl[] = {
@@ -7183,6 +7221,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1466, "Acer Aspire A515-56", ALC255_FIXUP_ACER_HEADPHONE_AND_MIC),
+ SND_PCI_QUIRK(0x1025, 0x151e, "Acer Nitro AN515-45", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1534, "Acer Predator PH315-54", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1539, "Acer Nitro 5 AN515-57", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED),
@@ -7196,8 +7235,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED),
+ SND_PCI_QUIRK(0x1025, 0x172c, "Acer Nitro ANV16-41", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1731, "Acer Predator PHN16-72", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED),
+ SND_PCI_QUIRK(0x1025, 0x173b, "Acer Swift SFG16-72", ALC245_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
@@ -7303,6 +7344,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x2164, "HP Pavilion 15 Notebook PC", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -7385,9 +7427,11 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x854d, "HP EliteBook 840 G6", ALC285_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B),
+ HDA_CODEC_QUIRK(0x103c, 0x85ba, "HP Pavilion AiO 24", ALC225_FIXUP_HP_AIO_SPK),
SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11),
SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360),
SND_PCI_QUIRK(0x103c, 0x85f0, "HP Laptop 15-dw0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
+ SND_PCI_QUIRK(0x103c, 0x85f1, "HP Laptop 15-dw1xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x85f3, "HP 250 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x8603, "HP Omen 17-cb0xxx", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x860c, "HP ZBook 17 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
@@ -7395,6 +7439,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
SND_PCI_QUIRK(0x103c, 0x864f, "HP Laptop 15-dy0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x86ad, "HP ENVY 13-aq1xxx", ALC285_FIXUP_HP_ENVY_13AQ),
SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO),
SND_PCI_QUIRK(0x103c, 0x86c8, "HP Laptop 14s-dr1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
@@ -7423,6 +7468,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8786, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8787, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8788, "HP OMEN 15", ALC285_FIXUP_HP_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x87b2, "HP Pavilion Gaming Laptop 15-ec1xxx", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x87b7, "HP Laptop 14-fq0xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x87cb, "HP Pavilion 15-eg0xxx", ALC287_FIXUP_HP_GPIO_LED),
@@ -7483,6 +7529,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8975, "HP EliteBook x360 840 Aero G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x897d, "HP mt440 Mobile Thin Client U74", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8981, "HP Elite Dragonfly G3", ALC245_FIXUP_CS35L41_SPI_4),
+ SND_PCI_QUIRK(0x103c, 0x8987, "HP Pavilion 14-dv1001TU", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x898a, "HP Pavilion 15-eg100", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x898e, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x898f, "HP EliteBook 835 G9", ALC287_FIXUP_CS35L41_I2C_2),
@@ -7520,7 +7567,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a2d, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a2e, "HP Envy 16", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8a30, "HP Envy 17", ALC287_FIXUP_CS35L41_I2C_2),
- SND_PCI_QUIRK(0x103c, 0x8a31, "HP Envy 15", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8a31, "HP ENVY x360 2-in-1 Laptop 15-ey0xxx", ALC287_FIXUP_CS35L41_I2C_2_HP_MUTE_LEDS),
SND_PCI_QUIRK(0x103c, 0x8a34, "HP Pavilion x360 2-in-1 Laptop 14-ek0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a36, "HP Pavilion Plus 14-eh0xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
@@ -7581,6 +7628,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8bb4, "HP Slim OMEN", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8bb6, "HP Laptop 15-fd0039nt", ALC236_FIXUP_HP_15_FD0XXX),
SND_PCI_QUIRK(0x103c, 0x8bbe, "HP Victus 16-r0xxx (MB 8BBE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8bc7, "HP Pavilion 15-eh3174nw", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8bc8, "HP Victus 15-fa1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8bcd, "HP Omen 16-xd0xxx", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8bd4, "HP Victus 16-s0xxx (MB 8BD4)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
@@ -7603,6 +7651,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8c17, "HP Spectre x360 2-in-1 Laptop 16-aa0xxx", ALC245_FIXUP_HP_SPECTRE_X360_16_AA0XXX),
SND_PCI_QUIRK(0x103c, 0x8c21, "HP Pavilion Plus Laptop 14-ey0XXX", ALC245_FIXUP_HP_X360_MUTE_LEDS),
SND_PCI_QUIRK(0x103c, 0x8c2d, "HP Victus 15-fa1xxx (MB 8C2D)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8c2f, "HP Victus 15-fb2xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c30, "HP Victus 15-fb1xxx", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c3f, "HP Victus 15-fa1xxx (MB 8C3F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8c46, "HP EliteBook 830 G11", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
@@ -7712,6 +7761,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8e37, "HP 16 Piston OmniBook X", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e3a, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e3b, "HP Agusta", ALC287_FIXUP_CS35L41_I2C_2),
+ SND_PCI_QUIRK(0x103c, 0x8e43, "HP Laptop 15-fd2xxx", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_GPIO),
SND_PCI_QUIRK(0x103c, 0x8e60, "HP OmniBook 7 Laptop 16-bh0xxx", ALC245_FIXUP_CS35L41_I2C_2_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x8e61, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x103c, 0x8e62, "HP Trekker ", ALC287_FIXUP_CS35L41_I2C_2),
@@ -7814,6 +7864,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x1652, "ASUS ROG Zephyrus Do 15 SE", ALC289_FIXUP_ASUS_ZEPHYRUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x1662, "ASUS GV301QH", ALC294_FIXUP_ASUS_DUAL_SPK),
SND_PCI_QUIRK(0x1043, 0x1663, "ASUS GU603ZI/ZJ/ZQ/ZU/ZV", ALC285_FIXUP_ASUS_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1043, 0x1664, "ASUS ExpertBook PM3406CHA", ALC256_FIXUP_ASUS_PM3406CHA),
SND_PCI_QUIRK(0x1043, 0x1683, "ASUS UM3402YAR", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS UX3402VA", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x16b2, "ASUS GU603", ALC289_FIXUP_ASUS_GA401),
@@ -7916,7 +7967,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1043, 0x3ef0, "ASUS Strix G635LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x1043, 0x3f00, "ASUS Strix G815LH_LM_LP", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x1043, 0x3f10, "ASUS Strix G835LR_LW_LX", ALC287_FIXUP_TXNW2781_I2C),
- SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C),
+ SND_PCI_QUIRK(0x1043, 0x3f20, "ASUS Strix G615LR_LW", ALC287_FIXUP_TXNW2781_I2C_ASUS),
SND_PCI_QUIRK(0x1043, 0x3f30, "ASUS Strix G815LR_LW", ALC287_FIXUP_TXNW2781_I2C),
SND_PCI_QUIRK(0x1043, 0x3fd0, "ASUS B3605CVA", ALC245_FIXUP_CS35L41_SPI_2),
SND_PCI_QUIRK(0x1043, 0x3ff0, "ASUS B5405CVA", ALC245_FIXUP_CS35L41_SPI_2),
@@ -7939,6 +7990,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN),
SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
+ SND_PCI_QUIRK(0x10ec, 0x111e, "Higole F9B", ALC290_FIXUP_MONO_SPEAKERS),
SND_PCI_QUIRK(0x10ec, 0x118c, "Medion EE4254 MD62100", ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE),
SND_PCI_QUIRK(0x10ec, 0x119e, "Positivo SU C1400", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
SND_PCI_QUIRK(0x10ec, 0x11bc, "VAIO VJFE-IL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -8164,7 +8216,6 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3834, "Lenovo IdeaPad Slim 9i 14ITL5", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x383d, "Legion Y9000X 2019", ALC285_FIXUP_LEGION_Y9000X_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3843, "Lenovo Yoga 9i / Yoga Book 9i", ALC287_FIXUP_LENOVO_YOGA_BOOK_9I),
- SND_PCI_QUIRK(0x17aa, 0x3846, "Lenovo Yoga Pro 9 16IAH10", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1),
/* Yoga Pro 7 14IMH9 shares PCI SSID 17aa:3847 with Legion 7 16ACHG6;
* use codec SSID to distinguish them
*/
@@ -8189,6 +8240,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */
SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x3871, "Lenovo ThinkBook 16p Gen 3 ARH", ALC287_FIXUP_YOGA7_14ARB7_I2C),
SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2),
@@ -8239,6 +8291,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x38d5, "Yoga S990-16 Pro IMH YC Quad", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38d6, "Yoga S990-16 Pro IMH VECO Quad", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38d7, "Lenovo Yoga 9 14IMH9", ALC287_FIXUP_YOGA9_14IMH9_BASS_SPK_PIN),
+ SND_PCI_QUIRK(0x17aa, 0x38dc, "Lenovo ThinkBook G8", ALC285_FIXUP_SPEAKER2_TO_DAC1),
SND_PCI_QUIRK(0x17aa, 0x38df, "Yoga Y990 Intel YC Dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C),
SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C),
@@ -8258,7 +8311,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3912, "Lenovo Xiaoxin 14 GT", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
SND_PCI_QUIRK(0x17aa, 0x3913, "Lenovo 145", ALC236_FIXUP_LENOVO_INV_DMIC),
SND_PCI_QUIRK(0x17aa, 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, 0x3920, "Yoga S990-16 pro Quad VECO Quad", ALC287_FIXUP_YOGA9_SPEAKER2_TO_DAC1),
SND_PCI_QUIRK(0x17aa, 0x3929, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x392b, "Thinkbook 13x Gen 5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
HDA_CODEC_QUIRK(0x17aa, 0x3936, "Legion R9000P ADR10H", ALC287_FIXUP_LENOVO_LEGION_AW88399),
@@ -8354,6 +8407,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x2014, 0x800a, "Positivo ARN50", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x2039, 0x0001, "Inspur S14-G1", ALC295_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK(0x2145, 0x0001, "Star Labs StarFighter", ALC233_FIXUP_STARLABS_STARFIGHTER),
+ SND_PCI_QUIRK(0x2782, 0x0206, "IPASON SmartBook S1", ALC256_FIXUP_IPASON_SMARTBOOK_S1),
SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13),
SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO),
diff --git a/sound/hda/codecs/realtek/realtek.c b/sound/hda/codecs/realtek/realtek.c
index db365a746b1a..bc5226fad52b 100644
--- a/sound/hda/codecs/realtek/realtek.c
+++ b/sound/hda/codecs/realtek/realtek.c
@@ -1292,6 +1292,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
}
switch (codec->core.vendor_id) {
+ case 0x10ec0235:
case 0x10ec0255:
alc_process_coef_fw(codec, coef0255);
break;
@@ -1407,6 +1408,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
};
switch (codec->core.vendor_id) {
+ case 0x10ec0235:
case 0x10ec0255:
alc_write_coef_idx(codec, 0x45, 0xc489);
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
@@ -1562,6 +1564,7 @@ static void alc_headset_mode_default(struct hda_codec *codec)
alc_process_coef_fw(codec, coef0225);
alc_hp_enable_unmute(codec, 75);
break;
+ case 0x10ec0235:
case 0x10ec0255:
alc_process_coef_fw(codec, coef0255);
break;
@@ -1663,6 +1666,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
};
switch (codec->core.vendor_id) {
+ case 0x10ec0235:
case 0x10ec0255:
alc_process_coef_fw(codec, coef0255);
break;
@@ -1780,6 +1784,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
};
switch (codec->core.vendor_id) {
+ case 0x10ec0235:
case 0x10ec0255:
alc_process_coef_fw(codec, coef0255);
break;
@@ -1881,6 +1886,7 @@ static void alc_determine_headset_type(struct hda_codec *codec)
}
switch (codec->core.vendor_id) {
+ case 0x10ec0235:
case 0x10ec0255:
alc_process_coef_fw(codec, coef0255);
msleep(300);
diff --git a/sound/hda/controllers/tegra.c b/sound/hda/controllers/tegra.c
index 31c14c4bbe68..674cc0f79c3c 100644
--- a/sound/hda/controllers/tegra.c
+++ b/sound/hda/controllers/tegra.c
@@ -374,7 +374,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
* in powers of 2, next available ratio is 16 which can be
* used as a limiting factor here.
*/
- if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
+ if (of_device_is_compatible(np, "nvidia,tegra30-hda") ||
+ of_device_is_compatible(np, "nvidia,tegra194-hda"))
chip->bus.core.sdo_limit = 16;
/* codec detection */
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 36066ffe8784..26a6fa69f5f5 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1895,6 +1895,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
if (hw->model == CTSB1270) {
/* Set up the PCM4220 ADC on Titanium HD */
+ data = hw_read_20kx(hw, GPIO_DATA);
data &= ~0x0C;
if (1 == info->msr)
data |= 0x00; /* Single Speed Mode 32-50kHz */
@@ -1903,6 +1904,7 @@ static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
else
data |= 0x04; /* Quad Speed Mode 108kHz-216kHz */
hw_write_20kx(hw, GPIO_DATA, data);
+ usleep_range(10000, 11000);
}
hw_adc_start(hw);
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index d6bfd8fb20e6..1fad8c418132 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -2523,7 +2523,7 @@ static int snd_ice1712_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
- struct snd_card *card __free(snd_card_unref) = NULL;
+ struct snd_card *card __free(snd_card_free) = NULL;
struct snd_ice1712 *ice;
int pcm_dev = 0, err;
const struct snd_ice1712_card_info * const *tbl, *c;
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
index 2d50be8190c1..b3b73096ac74 100644
--- a/sound/soc/amd/acp-config.c
+++ b/sound/soc/amd/acp-config.c
@@ -52,6 +52,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = {
},
},
{
+ /* Lenovo Yoga Pro 7 14ASP9 (Strix Point, ACP 7.0) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"),
+ },
+ },
+ {
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"),
DMI_MATCH(DMI_PRODUCT_NAME, "FA401EA"),
@@ -64,6 +71,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = {
},
},
{
+ /* ASUS Zenbook S14 UM5406GA (Strix Point, ACP 7.0) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Zenbook S14 UM5406GA"),
+ },
+ },
+ {
/* HP OmniBook X Flip 14-kc0xxx (Strix Point, ACP 7.2) */
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index a04c301df4d6..e880e44b9763 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -181,6 +181,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
select SND_SOC_RT722_SDCA_SDW
+ select SND_SOC_TAS2783_SDW
help
This option enables Legacy(No DSP) sound card support for SoundWire
enabled AMD platforms along with ACP PDM controller.
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
index 815e088b437d..2a9da6de7bbc 100644
--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -748,8 +748,28 @@ static const struct snd_soc_acpi_link_adr acp70_rt721_only[] = {
{}
};
+static const struct snd_soc_acpi_link_adr acp70_tas2783a_l0_rt712_vb_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(tas2783_2_adr),
+ .adr_d = tas2783_2_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt712_vb_1_group1_adr),
+ .adr_d = rt712_vb_1_group1_adr,
+ },
+ {}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
{
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_tas2783a_l0_rt712_vb_l1,
+ .machine_check = snd_soc_acpi_amd_sdca_is_device_rt712_vb,
+ .drv_name = "amd_sdw",
+ },
+ {
.link_mask = BIT(0),
.links = acp70_tas2783_2,
.drv_name = "amd_sdw",
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index 385fede6d77f..e798bf6a9cd0 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -252,6 +252,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21JN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21M1"),
}
},
@@ -384,6 +391,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83UU"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d88593c2bac8..8e251e1d84b4 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1989,9 +1989,7 @@ config SND_SOC_RT715_SDCA_SDW
config SND_SOC_RT766_SDCA_SDW
tristate "Realtek RT766 SDCA Codec - SDW"
depends on SOUNDWIRE
- depends on SND_SOC_SDCA
- select SND_SOC_SDCA_HID
- select SND_SOC_SDCA_IRQ
+ depends on SND_SOC_SDCA_CLASS
select REGMAP_SOUNDWIRE
select REGMAP_SOUNDWIRE_MBQ
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index cfd2ac0a6cda..38e37908baf9 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -1841,6 +1841,7 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
int event)
{
struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ unsigned int gain;
u16 gain_reg;
u16 reg;
struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
@@ -1882,6 +1883,8 @@ static int wsa_macro_enable_interpolator(struct snd_soc_dapm_widget *w,
CDC_WSA_RX_PGA_HALF_DB_MASK,
CDC_WSA_RX_PGA_HALF_DB_ENABLE);
}
+ gain = snd_soc_component_read(component, gain_reg);
+ snd_soc_component_write(component, gain_reg, gain);
wsa_macro_config_ear_spkr_gain(component, wsa,
event, gain_reg);
break;
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
index 0e32b7a94cb0..4a6d7b163acf 100644
--- a/sound/soc/codecs/max98363.c
+++ b/sound/soc/codecs/max98363.c
@@ -200,7 +200,7 @@ static int max98363_sdw_dai_hw_params(struct snd_pcm_substream *substream,
struct max98363_priv *max98363 =
snd_soc_component_get_drvdata(component);
- struct sdw_stream_config stream_config;
+ struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config;
enum sdw_data_direction direction;
struct sdw_stream_runtime *stream;
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
index caf75e5657ef..01cc2511a7d0 100644
--- a/sound/soc/codecs/rt1017-sdca-sdw.c
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -15,6 +15,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <sound/sdw.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -573,11 +574,10 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_config stream_config;
+ struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config;
- enum sdw_data_direction direction;
struct sdw_stream_runtime *sdw_stream;
- int retval, port, num_channels, ch_mask;
+ int retval, port;
unsigned int sampling_rate;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
@@ -591,28 +591,17 @@ static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
/* SoundWire specific configuration */
/* port 1 for playback */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- direction = SDW_DATA_DIR_RX;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
port = 1;
- } else {
- direction = SDW_DATA_DIR_TX;
+ else
port = 2;
- }
-
- num_channels = params_channels(params);
- ch_mask = (1 << num_channels) - 1;
-
- stream_config.frame_rate = params_rate(params);
- stream_config.ch_count = num_channels;
- stream_config.bps = snd_pcm_format_width(params_format(params));
- stream_config.direction = direction;
- port_config.ch_mask = ch_mask;
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
port_config.num = port;
dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n",
- params_rate(params), num_channels, snd_pcm_format_width(params_format(params)),
- direction, ch_mask, port);
+ stream_config.frame_rate, stream_config.ch_count, stream_config.bps,
+ stream_config.direction, port_config.ch_mask, port);
retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config,
&port_config, 1, sdw_stream);
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index f8c370106504..3c47f1ae8db9 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -619,9 +619,11 @@ static int rt274_hw_params(struct snd_pcm_substream *substream,
unsigned int val = 0;
int d_len_code = 0, c_len_code = 0;
+ /* bit 14 Sample Rate Base, 0: 48kHz 1: 44.1kHz */
switch (params_rate(params)) {
- /* bit 14 0:48K 1:44.1K */
case 44100:
+ val |= BIT(14);
+ break;
case 48000:
break;
default:
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 4217467904d6..bcd9f5abcda8 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -953,7 +953,7 @@ static int rt286_probe(struct snd_soc_component *component)
if (rt286->i2c->irq)
schedule_delayed_work(&rt286->jack_detect_work,
- msecs_to_jiffies(50));
+ msecs_to_jiffies(1250));
return 0;
}
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 156e1e24a388..fbf388cf966d 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -676,7 +676,7 @@ SOC_ENUM("DRC Decay Rate", drc_decay),
SOC_ENUM("DRC FF Delay", drc_ff_delay),
SOC_SINGLE("DRC Anticlip Switch", WM8903_DRC_0, 1, 1, 0),
SOC_SINGLE("DRC QR Switch", WM8903_DRC_0, 2, 1, 0),
-SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_0, 6, 3, 0, drc_tlv_max),
+SOC_SINGLE_TLV("DRC QR Threshold Volume", WM8903_DRC_1, 6, 3, 0, drc_tlv_max),
SOC_ENUM("DRC QR Decay Rate", drc_qr_decay),
SOC_SINGLE("DRC Smoothing Switch", WM8903_DRC_0, 3, 1, 0),
SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8903_DRC_0, 0, 1, 0),
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 62cb4856c797..c9b1053205de 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -411,6 +411,23 @@ static const struct dmi_system_id byt_rt5651_quirk_table[] = {
BYT_RT5651_MONO_SPEAKER),
},
{
+ /* Chuwi Hi8 (CWI513) with a BIOS using generic DMI strings */
+ .callback = byt_rt5651_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
+ DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
+ /* Above matches are too generic, add bios-date match */
+ DMI_MATCH(DMI_BIOS_DATE, "05/21/2016"),
+ },
+ .driver_data = (void *)(BYT_RT5651_MCLK_EN |
+ BYT_RT5651_JD1_1 |
+ BYT_RT5651_OVCD_TH_2000UA |
+ BYT_RT5651_OVCD_SF_1P0 |
+ BYT_RT5651_IN2_MAP |
+ BYT_RT5651_HP_LR_SWAPPED |
+ BYT_RT5651_MONO_SPEAKER),
+ },
+ {
/* Chuwi Vi8 Plus (CWI519) */
.callback = byt_rt5651_quirk_cb,
.matches = {
diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
index 3b9758f73238..62b8811395cb 100644
--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
@@ -745,6 +745,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.links = lnl_cs42l43_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg",
+ .machine_check = snd_soc_acpi_intel_force_function_topology,
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c
index 9fdeb6ad96ac..e62ef81214ff 100644
--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c
+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.c
@@ -53,6 +53,22 @@ bool snd_soc_acpi_intel_no_function_topology(void *arg)
}
EXPORT_SYMBOL_NS(snd_soc_acpi_intel_no_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS");
+static const struct dmi_system_id force_function_topology_quirk_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83R0"),
+ },
+ },
+ {}
+};
+
+bool snd_soc_acpi_intel_force_function_topology(void *arg)
+{
+ return !dmi_check_system(force_function_topology_quirk_table);
+}
+EXPORT_SYMBOL_NS(snd_soc_acpi_intel_force_function_topology, "SND_SOC_ACPI_INTEL_SDCA_QUIRKS");
+
bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg)
{
return snd_soc_acpi_intel_sdca_is_device_rt712_vb(arg) &&
diff --git a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h
index 60b665ab5924..10179004c86a 100644
--- a/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h
+++ b/sound/soc/intel/common/soc-acpi-intel-sdca-quirks.h
@@ -11,6 +11,7 @@
bool snd_soc_acpi_intel_sdca_is_device_rt712_vb(void *arg);
bool snd_soc_acpi_intel_no_function_topology(void *arg);
+bool snd_soc_acpi_intel_force_function_topology(void *arg);
bool snd_soc_acpi_intel_rt712_vb_no_function_topology(void *arg);
#endif
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 242bc16da36d..079177a6d81e 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -1234,8 +1234,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
}
if (drvdata->mi2s_playback_sd_mode[dai_id] ==
LPAIF_I2SCTL_MODE_QUAD01) {
- variant->dai_driver[dai_id].playback.channels_min = 4;
- variant->dai_driver[dai_id].playback.channels_max = 4;
+ variant->dai_driver[i].playback.channels_min = 4;
+ variant->dai_driver[i].playback.channels_max = 4;
}
}
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index cc7045dc26e6..610931ae5531 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -388,20 +388,27 @@ static int class_function_probe(struct auxiliary_device *auxdev,
ret = devm_pm_runtime_enable(dev);
if (ret)
- return ret;
+ goto err_pm;
ret = class_function_boot(drv);
if (ret)
- return ret;
+ goto err_pm;
ret = devm_snd_soc_register_component(dev, cmp_drv, dais, num_dais);
- if (ret)
- return dev_err_probe(dev, ret, "failed to register component\n");
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register component\n");
+ goto err_pm;
+ }
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
+
+err_pm:
+ pm_runtime_put_sync(dev);
+
+ return ret;
}
static void class_function_remove(struct auxiliary_device *auxdev)
@@ -490,8 +497,6 @@ static int class_function_suspend(struct device *dev)
struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
int ret;
- drv->suspended = true;
-
/* Ensure runtime resume runs on resume */
ret = pm_runtime_resume_and_get(dev);
if (ret) {
@@ -499,6 +504,8 @@ static int class_function_suspend(struct device *dev)
return ret;
}
+ drv->suspended = true;
+
sdca_irq_disable(drv->function, drv->core->irq_info);
ret = pm_runtime_force_suspend(dev);
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
index 7f360dfaf8b1..d8ae5d997615 100644
--- a/sound/soc/tegra/tegra186_asrc.c
+++ b/sound/soc/tegra/tegra186_asrc.c
@@ -828,7 +828,7 @@ static const struct snd_kcontrol_new tegra186_asrc_controls[] = {
tegra186_asrc_put_input_threshold),
SOC_SINGLE_EXT("Stream6 Input Threshold",
- ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 4), 0, 3, 0,
+ ASRC_STREAM_REG(TEGRA186_ASRC_RX_THRESHOLD, 5), 0, 3, 0,
tegra186_asrc_get_input_threshold,
tegra186_asrc_put_input_threshold),
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index 3d821fde4582..f604846ec6e6 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -562,6 +562,9 @@ static int snd_probe(struct usb_interface *intf,
ret = init_card(caiaqdev(card));
if (ret < 0) {
dev_err(&usb_dev->dev, "unable to init card! (ret=%d)\n", ret);
+#ifdef CONFIG_SND_USB_CAIAQ_INPUT
+ snd_usb_caiaq_input_disconnect(caiaqdev(card));
+#endif
snd_card_free(card);
return ret;
}
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 7ebaf125f969..aa6ddf8746a0 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -182,6 +182,13 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
fsize *= bytes_per_frame;
+ if (fsize > line6pcm->max_packet_size_out) {
+ dev_err(line6pcm->line6->ifcdev,
+ "playback packet too large: %d > %d\n",
+ fsize, line6pcm->max_packet_size_out);
+ return -EMSGSIZE;
+ }
+
fout->offset = urb_size;
fout->length = fsize;
urb_size += fsize;
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index 860a62a3d74b..58d8a3cdc8b9 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -1007,6 +1007,15 @@ static int detect_usb_format(struct ua101 *ua)
}
ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd));
ua->playback.max_packet_bytes = usb_endpoint_maxp(epd);
+
+ if (ua->capture.max_packet_bytes / ua->capture.frame_bytes *
+ ua->playback.frame_bytes > ua->playback.max_packet_bytes) {
+ dev_err(&ua->dev->dev,
+ "playback packet size %u too small for %u capture frames\n",
+ ua->playback.max_packet_bytes,
+ ua->capture.max_packet_bytes / ua->capture.frame_bytes);
+ return -ENXIO;
+ }
return 0;
}
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 41454cb403d0..f8e1f78039e1 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -749,6 +749,10 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.id = USB_ID(0x0db0, 0xa073),
.map = msi_mpg_x570s_carbon_max_wifi_alc4080_map,
},
+ { /* MSI MAG B850M MORTAR WIFI */
+ .id = USB_ID(0x0db0, 0xcc78),
+ .map = msi_mpg_x570s_carbon_max_wifi_alc4080_map,
+ },
{ /* MSI TRX40 */
.id = USB_ID(0x0db0, 0x543d),
.map = trx40_mobo_map,
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c5870901a8d4..3afc49565963 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1702,10 +1702,12 @@ int snd_usb_apply_boot_quirk_once(struct usb_device *dev,
switch (id) {
case USB_ID(0x07fd, 0x0008): /* MOTU M Series, 1st hardware version */
return snd_usb_motu_m_series_boot_quirk(dev);
- case USB_ID(0x1397, 0x1234): /* Behringer CM1A */
- return snd_usb_cm1a_boot_quirk(dev);
}
+ /* Behringer devices may need explicit device descriptor read at boot */
+ if (USB_ID_VENDOR(id) == 0x1397)
+ return snd_usb_cm1a_boot_quirk(dev);
+
return 0;
}
@@ -2366,6 +2368,10 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_CTL_MSG_DELAY_1M),
DEVICE_FLG(0x0a73, 0x003a, /* Mackie DLZ Creator XS */
QUIRK_FLAG_ALWAYS_SET_RATE),
+ DEVICE_FLG(0x0b05, 0x1826, /* ASUS SupremeFX Hi-Fi */
+ QUIRK_FLAG_DISABLE_AUTOSUSPEND),
+ DEVICE_FLG(0x0b05, 0x1827, /* ASUS SupremeFX Hi-Fi */
+ QUIRK_FLAG_DISABLE_AUTOSUSPEND),
DEVICE_FLG(0x0b05, 0x18a6, /* ASUSTek Computer, Inc. */
QUIRK_FLAG_MIXER_CAPTURE_MIN_MUTE),
DEVICE_FLG(0x0b0e, 0x0349, /* Jabra 550a */
@@ -2510,8 +2516,6 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x262a, 0x9302, /* ddHiFi TC44C */
QUIRK_FLAG_DSD_RAW),
- DEVICE_FLG(0x2708, 0x0002, /* Audient iD14 */
- QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x2772, 0x0502, /* Musical Fidelity M6s DAC */
0), /* for avoiding QUIRK_FLAG_DSD_RAW with vendor match */
DEVICE_FLG(0x2912, 0x30c8, /* Audioengine D1 */
@@ -2542,6 +2546,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_IGNORE_CTL_ERROR),
DEVICE_FLG(0x3255, 0x0000, /* Luxman D-10X */
QUIRK_FLAG_ITF_USB_DSD_DAC | QUIRK_FLAG_CTL_MSG_DELAY),
+ DEVICE_FLG(0x32bb, 0x0004, /* HiBy FC4 */
+ QUIRK_FLAG_DSD_RAW),
DEVICE_FLG(0x3302, 0x17c2, /* TTGK Technology USB-C Audio */
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x339b, 0x3a07, /* Synaptics HONOR USB-C HEADSET */
@@ -2608,6 +2614,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x2622, /* IAG Limited devices */
QUIRK_FLAG_DSD_RAW),
+ VENDOR_FLG(0x2708, /* Audient devices */
+ QUIRK_FLAG_IGNORE_CTL_ERROR),
VENDOR_FLG(0x2772, /* Musical Fidelity devices */
QUIRK_FLAG_DSD_RAW),
VENDOR_FLG(0x278b, /* Rotel? */
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_ma_ttrace.c b/tools/testing/selftests/bpf/prog_tests/bpf_ma_ttrace.c
new file mode 100644
index 000000000000..a1d41b109940
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_ma_ttrace.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "bpf_ma_ttrace.skel.h"
+
+#define NR_ELEMS 4096
+
+/*
+ * The first free_bulk() starts RCU tasks trace GP. The rest of the elements are
+ * deleted while it's in flight. They should be freed without further alloc or
+ * free from this map.
+ */
+void test_bpf_ma_ttrace(void)
+{
+ LIBBPF_OPTS(bpf_test_run_opts, opts);
+ struct bpf_ma_ttrace *skel;
+ __u32 cnt = NR_ELEMS;
+ long *vals = NULL;
+ int *keys = NULL;
+ int i, err, fd, nr_cpus;
+
+ skel = bpf_ma_ttrace__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+ nr_cpus = libbpf_num_possible_cpus();
+ if (!ASSERT_GT(nr_cpus, 0, "nr_cpus"))
+ goto out;
+ skel->bss->nr_cpus = nr_cpus;
+
+ keys = calloc(NR_ELEMS, sizeof(*keys));
+ vals = calloc(NR_ELEMS, sizeof(*vals));
+ if (!ASSERT_OK_PTR(keys, "keys") || !ASSERT_OK_PTR(vals, "vals"))
+ goto out;
+ for (i = 0; i < NR_ELEMS; i++)
+ keys[i] = i;
+
+ fd = bpf_map__fd(skel->maps.htab);
+ err = bpf_map_update_batch(fd, keys, vals, &cnt, NULL);
+ if (!ASSERT_OK(err, "update_batch") || !ASSERT_EQ(cnt, NR_ELEMS, "update_cnt"))
+ goto out;
+ err = bpf_map_delete_batch(fd, keys, &cnt, NULL);
+ if (!ASSERT_OK(err, "delete_batch") || !ASSERT_EQ(cnt, NR_ELEMS, "delete_cnt"))
+ goto out;
+
+ /* Wait for all __free_rcu() callbacks to finish */
+ for (i = 0; i < 300; i++) {
+ err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.check_ttrace), &opts);
+ if (!ASSERT_OK(err, "test_run") || !ASSERT_OK(opts.retval, "retval"))
+ goto out;
+ if (!skel->bss->in_progress)
+ break;
+ usleep(100000);
+ }
+ ASSERT_EQ(skel->bss->nr_caches, nr_cpus, "nr_caches");
+ ASSERT_EQ(skel->bss->in_progress, 0, "in_progress");
+ ASSERT_EQ(skel->bss->not_freed, 0, "not_freed");
+out:
+ free(keys);
+ free(vals);
+ bpf_ma_ttrace__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c
index ecc3d47919ad..f8497e764beb 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c
@@ -55,38 +55,6 @@ static void *load_module_thread(void *p)
return p;
}
-static int sys_userfaultfd(int flags)
-{
- return syscall(__NR_userfaultfd, flags);
-}
-
-static int test_setup_uffd(void *fault_addr)
-{
- struct uffdio_register uffd_register = {};
- struct uffdio_api uffd_api = {};
- int uffd;
-
- uffd = sys_userfaultfd(O_CLOEXEC);
- if (uffd < 0)
- return -errno;
-
- uffd_api.api = UFFD_API;
- uffd_api.features = 0;
- if (ioctl(uffd, UFFDIO_API, &uffd_api)) {
- close(uffd);
- return -1;
- }
-
- uffd_register.range.start = (unsigned long)fault_addr;
- uffd_register.range.len = getpagesize();
- uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
- if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {
- close(uffd);
- return -1;
- }
- return uffd;
-}
-
static void test_bpf_mod_race_config(const struct test_config *config)
{
void *fault_addr, *skel_fail;
@@ -117,7 +85,7 @@ static void test_bpf_mod_race_config(const struct test_config *config)
if (!ASSERT_OK(bpf_mod_race__attach(skel), "bpf_mod_kfunc_race__attach"))
goto end_destroy;
- uffd = test_setup_uffd(fault_addr);
+ uffd = uffd_block_page(fault_addr);
if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address"))
goto end_destroy;
diff --git a/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c b/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c
new file mode 100644
index 000000000000..1eb0d7237605
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/tramp_prog_detach.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <pthread.h>
+#include <poll.h>
+#include <sys/mman.h>
+#include <linux/userfaultfd.h>
+#include "tramp_prog_detach.skel.h"
+#include "testing_helpers.h"
+
+/*
+ * Detach and free progs while a task sleeps in the prog that runs before them
+ * in the same trampoline image, then let that task continue through the
+ * image. It must not call into the freed progs.
+ *
+ * The task is held in a sleepable prog with userfaultfd, like bpf_mod_race
+ * does.
+ */
+
+static struct bpf_program *pick_prog(struct tramp_prog_detach *skel,
+ bool fexit, bool sleepable)
+{
+ if (fexit)
+ return sleepable ? skel->progs.fexit_sleepable :
+ skel->progs.fexit_victim;
+ return sleepable ? skel->progs.fentry_sleepable :
+ skel->progs.fentry_victim;
+}
+
+static struct bpf_program *sleepable_prog;
+
+static void *run_sleepable(void *arg)
+{
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+
+ /* calls bpf_fentry_test1() */
+ return (void *)(long)bpf_prog_test_run_opts(bpf_program__fd(sleepable_prog),
+ &topts);
+}
+
+static struct tramp_prog_detach *load_one(bool fexit, bool sleepable)
+{
+ struct tramp_prog_detach *skel;
+ int err;
+
+ skel = tramp_prog_detach__open();
+ if (!ASSERT_OK_PTR(skel, "open"))
+ return NULL;
+
+ bpf_program__set_autoload(pick_prog(skel, fexit, sleepable), true);
+ err = tramp_prog_detach__load(skel);
+ if (!ASSERT_OK(err, "load"))
+ goto err;
+ skel->bss->pid = getpid();
+ err = tramp_prog_detach__attach(skel);
+ if (!ASSERT_OK(err, "attach"))
+ goto err;
+ return skel;
+err:
+ tramp_prog_detach__destroy(skel);
+ return NULL;
+}
+
+/* The .bss map of a destroyed skeleton goes away when its prog is freed */
+static bool wait_for_map_free(__u32 map_id)
+{
+ int i, fd;
+
+ for (i = 0; i < 100; i++) {
+ fd = bpf_map_get_fd_by_id(map_id);
+ if (fd < 0)
+ return true;
+ close(fd);
+ usleep(100 * 1000);
+ }
+ return false;
+}
+
+static void test_detach(bool sleepable_fexit, bool victim_fexit)
+{
+ struct tramp_prog_detach *sleepable = NULL, *victims[2] = {};
+ struct pollfd pfd = { .events = POLLIN };
+ struct uffdio_copy uffd_copy = {};
+ struct bpf_map_info map_info = {};
+ __u32 map_info_len = sizeof(map_info);
+ struct uffd_msg uffd_msg;
+ void *fault_page, *src_page = MAP_FAILED;
+ long page_size = getpagesize();
+ bool started = false;
+ void *thread_ret;
+ pthread_t thread;
+ int i, uffd = -1;
+
+ fault_page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (!ASSERT_NEQ(fault_page, MAP_FAILED, "mmap fault_page"))
+ return;
+ src_page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (!ASSERT_NEQ(src_page, MAP_FAILED, "mmap src_page"))
+ goto out;
+
+ /* The most recently attached prog runs first */
+ for (i = 0; i < ARRAY_SIZE(victims); i++) {
+ victims[i] = load_one(victim_fexit, false);
+ if (!victims[i])
+ goto out;
+ }
+ sleepable = load_one(sleepable_fexit, true);
+ if (!sleepable)
+ goto out;
+ sleepable_prog = pick_prog(sleepable, sleepable_fexit, true);
+
+ /* Not armed yet so this doesn't block, make sure sleepable runs first */
+ if (!ASSERT_OK((long)run_sleepable(NULL), "dry run"))
+ goto out;
+ for (i = 0; i < ARRAY_SIZE(victims); i++)
+ if (!ASSERT_LT(sleepable->bss->ts, victims[i]->bss->ts, "prog order"))
+ goto out;
+
+ uffd = uffd_block_page(fault_page);
+ if (!ASSERT_GE(uffd, 0, "userfaultfd open + register address"))
+ goto out;
+ sleepable->bss->fault_addr = fault_page;
+
+ if (!ASSERT_OK(pthread_create(&thread, NULL, run_sleepable, NULL),
+ "pthread_create"))
+ goto out;
+ started = true;
+
+ /* Wait for the thread to sleep in bpf_copy_from_user() */
+ pfd.fd = uffd;
+ if (!ASSERT_EQ(poll(&pfd, 1, 10000), 1, "poll uffd"))
+ goto out;
+ if (!ASSERT_EQ(read(uffd, &uffd_msg, sizeof(uffd_msg)), sizeof(uffd_msg),
+ "read uffd"))
+ goto out;
+ if (!ASSERT_EQ(uffd_msg.event, UFFD_EVENT_PAGEFAULT, "uffd pagefault"))
+ goto out;
+
+ /*
+ * Detach and unload the victim progs and wait for them to be freed.
+ * After the first one, the task is in an image that isn't the
+ * trampoline's current one anymore.
+ */
+ for (i = 0; i < ARRAY_SIZE(victims); i++) {
+ if (!ASSERT_OK(bpf_map_get_info_by_fd(bpf_map__fd(victims[i]->maps.bss),
+ &map_info, &map_info_len),
+ "victim bss info"))
+ goto out;
+ tramp_prog_detach__destroy(victims[i]);
+ victims[i] = NULL;
+ if (!ASSERT_TRUE(wait_for_map_free(map_info.id), "victim freed"))
+ goto out;
+ }
+
+out:
+ /* Let the thread proceed with the rest of the trampoline */
+ if (uffd >= 0) {
+ uffd_copy.dst = (unsigned long)fault_page;
+ uffd_copy.src = (unsigned long)src_page;
+ uffd_copy.len = page_size;
+ ASSERT_OK(ioctl(uffd, UFFDIO_COPY, &uffd_copy), "uffd copy");
+ close(uffd);
+ }
+ if (started &&
+ ASSERT_OK(pthread_join(thread, &thread_ret), "pthread_join"))
+ ASSERT_NULL(thread_ret, "blocking run");
+
+ for (i = 0; i < ARRAY_SIZE(victims); i++)
+ tramp_prog_detach__destroy(victims[i]);
+ tramp_prog_detach__destroy(sleepable);
+ if (src_page != MAP_FAILED)
+ munmap(src_page, page_size);
+ munmap(fault_page, page_size);
+}
+
+void serial_test_tramp_prog_detach(void)
+{
+ /* a task sleeping before the original function is called */
+ if (test__start_subtest("fentry"))
+ test_detach(false, false);
+ /* a task sleeping after the original function returned */
+ if (test__start_subtest("fexit"))
+ test_detach(true, true);
+ /* the original function runs in between and must still be called */
+ if (test__start_subtest("fentry_fexit"))
+ test_detach(false, true);
+}
diff --git a/tools/testing/selftests/bpf/progs/bpf_ma_ttrace.c b/tools/testing/selftests/bpf/progs/bpf_ma_ttrace.c
new file mode 100644
index 000000000000..31b2a962e339
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/bpf_ma_ttrace.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_kfuncs.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __uint(max_entries, 4096);
+ __type(key, int);
+ __type(value, long);
+} htab SEC(".maps");
+
+extern const void __per_cpu_offset __ksym;
+
+int nr_cpus;
+int nr_caches;
+int in_progress;
+int not_freed;
+
+/* Look at bpf_mem_cache of every cpu that htab allocates its elements from */
+SEC("syscall")
+int check_ttrace(void *ctx)
+{
+ struct bpf_htab *h = bpf_core_cast(&htab, struct bpf_htab);
+ unsigned long cache = (unsigned long)BPF_CORE_READ(h, ma.cache);
+ const unsigned long *offsets = &__per_cpu_offset;
+ struct bpf_mem_cache *c;
+ unsigned long off;
+ int cpu;
+
+ nr_caches = 0;
+ in_progress = 0;
+ not_freed = 0;
+ bpf_for(cpu, 0, nr_cpus) {
+ if (bpf_probe_read_kernel(&off, sizeof(off), offsets + cpu))
+ return -1;
+ c = bpf_core_cast((void *)(cache + off), struct bpf_mem_cache);
+ if (c->unit_size)
+ nr_caches++;
+ if (c->call_rcu_ttrace_in_progress.counter)
+ in_progress++;
+ if (c->free_by_rcu_ttrace.first || c->waiting_for_gp_ttrace.first)
+ not_freed++;
+ }
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/tramp_prog_detach.c b/tools/testing/selftests/bpf/progs/tramp_prog_detach.c
new file mode 100644
index 000000000000..507d372167ec
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/tramp_prog_detach.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+char _license[] SEC("license") = "GPL";
+
+int pid;
+void *fault_addr;
+__u64 ts;
+
+static int do_sleepable(void)
+{
+ char dst;
+
+ if (bpf_get_current_pid_tgid() >> 32 != pid)
+ return 0;
+
+ ts = bpf_ktime_get_ns();
+ /* blocks for as long as user space wants when fault_addr is armed */
+ bpf_copy_from_user(&dst, sizeof(dst), fault_addr);
+ return 0;
+}
+
+static int do_victim(void)
+{
+ if (bpf_get_current_pid_tgid() >> 32 != pid)
+ return 0;
+
+ ts = bpf_ktime_get_ns();
+ return 0;
+}
+
+SEC("?fentry.s/bpf_fentry_test1")
+int BPF_PROG(fentry_sleepable, int a)
+{
+ return do_sleepable();
+}
+
+SEC("?fentry/bpf_fentry_test1")
+int BPF_PROG(fentry_victim, int a)
+{
+ return do_victim();
+}
+
+SEC("?fexit.s/bpf_fentry_test1")
+int BPF_PROG(fexit_sleepable, int a, int ret)
+{
+ return do_sleepable();
+}
+
+SEC("?fexit/bpf_fentry_test1")
+int BPF_PROG(fexit_victim, int a, int ret)
+{
+ return do_victim();
+}
diff --git a/tools/testing/selftests/bpf/progs/verifier_align.c b/tools/testing/selftests/bpf/progs/verifier_align.c
index 3e52686515ca..0083ef8b8ba7 100644
--- a/tools/testing/selftests/bpf/progs/verifier_align.c
+++ b/tools/testing/selftests/bpf/progs/verifier_align.c
@@ -284,7 +284,7 @@ __msg("26: {{.*}} R5=pkt(r=8,imm=14)")
*/
__msg("28: {{.*}} R4={{[^)]*}}var_off=(0x2; 0x7fc){{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
/* Constant is added to R5 again, setting reg->off to 18. */
-__msg("29: {{.*}} R5=pkt(id=3,{{[^)]*}}var_off=(0x2; 0x7fc)")
+__msg("29: {{.*}} R5=pkt(id=3,{{[^)]*}}var_off=(0x2; 0xffc)")
/* And once more we add a variable; resulting {{[^)]*}}var_off
* is still (4n), fixed offset is not changed.
* Also, we create a new reg->id.
@@ -359,7 +359,7 @@ __msg("7: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3fc)")
__msg("8: {{.*}} R6={{[^)]*}}var_off=(0x2; 0x7fc)")
/* Packet pointer has (4n+2) offset */
__msg("11: {{.*}} R5={{[^)]*}}var_off=(0x2; 0x7fc)")
-__msg("12: {{.*}} R4={{[^)]*}}var_off=(0x2; 0x7fc)")
+__msg("12: {{.*}} R4={{[^)]*}}var_off=(0x2; 0xffc)")
/* At the time the word size load is performed from R5,
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
* which is 2. Then the variable offset is (4n+2), so
@@ -375,7 +375,7 @@ __msg("17: {{.*}} R6={{[^)]*}}var_off=(0x0; 0x3fc)")
* another (4n+2).
*/
__msg("19: {{.*}} R5={{[^)]*}}var_off=(0x2; 0xffc)")
-__msg("20: {{.*}} R4={{[^)]*}}var_off=(0x2; 0xffc)")
+__msg("20: {{.*}} R4={{[^)]*}}var_off=(0x2; 0x1ffc)")
/* At the time the word size load is performed from R5,
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
* which is 2. Then the variable offset is (4n+2), so
diff --git a/tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c b/tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c
index 915a9707298b..139ff019d87d 100644
--- a/tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c
+++ b/tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c
@@ -920,4 +920,182 @@ l1_%=: r0 = *(u8*)(r9 + 0); \
: __clobber_all);
}
+SEC("tc")
+__description("direct packet access: 8-aligned offset, check p + 8, load 8 bytes at p")
+__success __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
+__naked void pkt_same_id_check_copy_load_base(void)
+{
+ asm volatile (" \
+ r2 = *(u32*)(r1 + %[__sk_buff_data]); \
+ r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
+ r4 = *(u32*)(r1 + %[__sk_buff_mark]); \
+ r4 &= 0x38; \
+ if r4 > 50 goto l0_%=; \
+ /* r4 is a multiple of 8, at most 48 */ \
+ r5 = r2; \
+ r5 += r4; \
+ r6 = r5; \
+ r6 += 8; \
+ if r6 > r3 goto l0_%=; \
+ /* [r5, r5 + 8) is in the packet */ \
+ r0 = *(u64*)(r5 + 0); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
+ __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
+ __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
+ : __clobber_all);
+}
+
+SEC("tc")
+__description("direct packet access: 8-aligned offset, check p, load past it via p + 8")
+__failure __msg("invalid access to packet, off=51 size=1")
+__naked void pkt_same_id_check_base_load_via_copy(void)
+{
+ asm volatile (" \
+ r2 = *(u32*)(r1 + %[__sk_buff_data]); \
+ r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
+ r4 = *(u32*)(r1 + %[__sk_buff_mark]); \
+ r4 &= 0x38; \
+ if r4 > 50 goto l0_%=; \
+ /* r4 is a multiple of 8, at most 48 */ \
+ r5 = r2; \
+ r5 += r4; \
+ r6 = r5; \
+ r6 += 8; \
+ if r5 > r3 goto l0_%=; \
+ /* r6 - 7 is r5 + 1 */ \
+ r0 = *(u8*)(r6 - 7); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
+ __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
+ __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
+ : __clobber_all);
+}
+
+SEC("tc")
+__description("direct packet access: 8-aligned offset, check p + 4, 4-byte load at p + 2")
+__failure __msg("invalid access to packet, off=52 size=4")
+__naked void pkt_same_id_check_base_4_load_via_copy(void)
+{
+ asm volatile (" \
+ r2 = *(u32*)(r1 + %[__sk_buff_data]); \
+ r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
+ r4 = *(u32*)(r1 + %[__sk_buff_mark]); \
+ r4 &= 0x38; \
+ if r4 > 50 goto l0_%=; \
+ /* r4 is a multiple of 8, at most 48 */ \
+ r5 = r2; \
+ r5 += r4; \
+ r6 = r5; \
+ r6 += 8; \
+ r7 = r5; \
+ r7 += 4; \
+ if r7 > r3 goto l0_%=; \
+ /* [r5, r5 + 4) is in the packet, [r5 + 2, r5 + 6) may not be */ \
+ r0 = *(u32*)(r6 - 6); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
+ __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
+ __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
+ : __clobber_all);
+}
+
+SEC("tc")
+__description("direct packet access: checked pointer minus non-negative unknown keeps range")
+__success __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
+__naked void pkt_sub_unknown_keeps_range(void)
+{
+ asm volatile (" \
+ r2 = *(u32*)(r1 + %[__sk_buff_data]); \
+ r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
+ r4 = *(u32*)(r1 + %[__sk_buff_mark]); \
+ r4 &= 0x1f; \
+ r4 += 8; \
+ r5 = r2; \
+ r5 += 40; \
+ if r5 > r3 goto l0_%=; \
+ /* r5 is 8 to 39 bytes below the checked pointer */ \
+ r5 -= r4; \
+ r0 = *(u64*)(r5 + 0); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
+ __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
+ __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
+ : __clobber_all);
+}
+
+SEC("tc")
+__description("direct packet access: no pruning of a path whose checks prove fewer bytes")
+__failure __msg("invalid access to packet, off=255 size=8")
+__flag(BPF_F_ANY_ALIGNMENT) __flag(BPF_F_TEST_STATE_FREQ)
+__naked void pkt_same_id_pruning(void)
+{
+ asm volatile (" \
+ r2 = *(u32*)(r1 + %[__sk_buff_data]); \
+ r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
+ r4 = *(u32*)(r1 + %[__sk_buff_mark]); \
+ r0 = *(u32*)(r1 + %[__sk_buff_priority]); \
+ r4 &= 0xff; \
+ r5 = r2; \
+ r5 += r4; \
+ if r0 != 0 goto l1_%=; \
+ /* this path proves [r5, r5 + 8) */ \
+ r6 = r5; \
+ r6 += 8; \
+ if r6 > r3 goto l0_%=; \
+ goto l2_%=; \
+l1_%=: /* this path proves [r5, r5 + 7) */ \
+ if r5 > r3 goto l0_%=; \
+ r6 = r5; \
+ r6 += 7; \
+ if r6 > r3 goto l0_%=; \
+l2_%=: r0 = *(u64*)(r5 + 0); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
+ __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
+ __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)),
+ __imm_const(__sk_buff_priority, offsetof(struct __sk_buff, priority))
+ : __clobber_all);
+}
+
+SEC("tc")
+__description("direct packet access: spilled copy of checked pointer, load past range")
+__failure __msg("invalid access to packet, off=262 size=1, R7(id={{[0-9]+}},off=262,r=262)")
+__naked void pkt_spilled_copy_gets_range(void)
+{
+ asm volatile (" \
+ r2 = *(u32*)(r1 + %[__sk_buff_data]); \
+ r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
+ r4 = *(u32*)(r1 + %[__sk_buff_mark]); \
+ r4 &= 0xff; \
+ r5 = r2; \
+ r5 += r4; \
+ *(u64*)(r10 - 8) = r5; \
+ /* proves only bytes before r5 */ \
+ if r5 > r3 goto l0_%=; \
+ r6 = r5; \
+ r6 += 7; \
+ if r6 > r3 goto l0_%=; \
+ /* [r5, r5 + 7) is in the packet */ \
+ r7 = *(u64*)(r10 - 8); \
+ r0 = *(u8*)(r7 + 7); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
+ __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
+ __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_meta_access.c b/tools/testing/selftests/bpf/progs/verifier_meta_access.c
index 62235f032ffe..c87e0be4b2ff 100644
--- a/tools/testing/selftests/bpf/progs/verifier_meta_access.c
+++ b/tools/testing/selftests/bpf/progs/verifier_meta_access.c
@@ -281,4 +281,34 @@ l0_%=: r0 = 0; \
: __clobber_all);
}
+SEC("xdp")
+__description("meta access, 8-aligned offset, check p, load a byte at p + 1 via p + 8")
+__failure __msg("invalid access to packet, off=51 size=1")
+__naked void meta_access_check_base_load_via_copy(void)
+{
+ asm volatile (" \
+ r9 = r1; \
+ call %[bpf_get_prandom_u32]; \
+ r4 = r0; \
+ r4 &= 0x38; \
+ if r4 > 50 goto l0_%=; \
+ /* r4 is a multiple of 8, at most 48 */ \
+ r2 = *(u32*)(r9 + %[xdp_md_data_meta]); \
+ r3 = *(u32*)(r9 + %[xdp_md_data]); \
+ r5 = r2; \
+ r5 += r4; \
+ r6 = r5; \
+ r6 += 8; \
+ if r5 > r3 goto l0_%=; \
+ /* r6 - 7 is r5 + 1 */ \
+ r0 = *(u8*)(r6 - 7); \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32),
+ __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
+ __imm_const(xdp_md_data_meta, offsetof(struct xdp_md, data_meta))
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c
index c970e7793dfc..9f672d0c9dda 100644
--- a/tools/testing/selftests/bpf/testing_helpers.c
+++ b/tools/testing/selftests/bpf/testing_helpers.c
@@ -13,6 +13,7 @@
#include "test_progs.h"
#include "testing_helpers.h"
#include <linux/membarrier.h>
+#include <linux/userfaultfd.h>
int parse_num_list(const char *s, bool **num_set, int *num_set_len)
{
@@ -459,6 +460,33 @@ int kern_sync_rcu(void)
return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0);
}
+int uffd_block_page(void *fault_addr)
+{
+ struct uffdio_register uffd_register = {};
+ struct uffdio_api uffd_api = {};
+ int uffd;
+
+ uffd = syscall(__NR_userfaultfd, O_CLOEXEC);
+ if (uffd < 0)
+ return -errno;
+
+ uffd_api.api = UFFD_API;
+ uffd_api.features = 0;
+ if (ioctl(uffd, UFFDIO_API, &uffd_api)) {
+ close(uffd);
+ return -1;
+ }
+
+ uffd_register.range.start = (unsigned long)fault_addr;
+ uffd_register.range.len = getpagesize();
+ uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {
+ close(uffd);
+ return -1;
+ }
+ return uffd;
+}
+
int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt)
{
__u32 buf_element_size = sizeof(struct bpf_insn);
diff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h
index 2edc6fb7fc52..1f83b6080716 100644
--- a/tools/testing/selftests/bpf/testing_helpers.h
+++ b/tools/testing/selftests/bpf/testing_helpers.h
@@ -36,6 +36,8 @@ __u64 read_perf_max_sample_freq(void);
int load_bpf_testmod(bool verbose);
int unload_bpf_testmod(bool verbose);
int kern_sync_rcu(void);
+/* returns a userfaultfd that makes accesses to fault_addr's page block */
+int uffd_block_page(void *fault_addr);
int finit_module(int fd, const char *param_values, int flags);
int delete_module(const char *name, int flags);
int load_module(const char *path, bool verbose);
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index 32d81ba15a25..8c41ebfb85e4 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -548,6 +548,37 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
/*
* Call hid_hw_raw_request against the given uhid device,
+ * check that the program is called and does the expected.
+ */
+TEST_F(hid_bpf, test_hid_user_raw_request_call_eio)
+{
+ struct hid_hw_request_syscall_args args = {
+ .retval = -1,
+ .type = HID_FEATURE_REPORT,
+ .request_type = HID_REQ_GET_PROTOCOL,
+ .size = MAX_BUF_SIZE,
+ };
+ DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
+ .ctx_in = &args,
+ .ctx_size_in = sizeof(args),
+ );
+ int err, prog_fd;
+
+ LOAD_BPF;
+
+ args.hid = self->hid.hid_id;
+ args.data[0] = 1; /* report ID */
+
+ prog_fd = bpf_program__fd(self->skel->progs.hid_user_raw_request);
+
+ err = bpf_prog_test_run_opts(prog_fd, &tattrs);
+ ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
+
+ ASSERT_EQ(args.retval, -EIO);
+}
+
+/*
+ * Call hid_hw_raw_request against the given uhid device,
* check that the program is called and prevents the
* call to uhid.
*/
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 6a1482e3a286..f43549a56f0b 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -103,6 +103,7 @@ TEST_GEN_PROGS_x86 += x86/nested_tdp_fault_test
TEST_GEN_PROGS_x86 += x86/nested_tsc_adjust_test
TEST_GEN_PROGS_x86 += x86/nested_tsc_scaling_test
TEST_GEN_PROGS_x86 += x86/nested_vmsave_vmload_test
+TEST_GEN_PROGS_x86 += x86/nested_x2apic_test
TEST_GEN_PROGS_x86 += x86/platform_info_test
TEST_GEN_PROGS_x86 += x86/pmu_counters_test
TEST_GEN_PROGS_x86 += x86/pmu_event_filter_test
@@ -195,6 +196,7 @@ TEST_GEN_PROGS_arm64 += arm64/vgic_v5
TEST_GEN_PROGS_arm64 += arm64/vpmu_counter_access
TEST_GEN_PROGS_arm64 += arm64/no-vgic
TEST_GEN_PROGS_arm64 += arm64/idreg-idst
+TEST_GEN_PROGS_arm64 += arm64/hidden_features
TEST_GEN_PROGS_arm64 += arm64/kvm-uuid
TEST_GEN_PROGS_arm64 += access_tracking_perf_test
TEST_GEN_PROGS_arm64 += arch_timer
diff --git a/tools/testing/selftests/kvm/arm64/hidden_features.c b/tools/testing/selftests/kvm/arm64/hidden_features.c
new file mode 100644
index 000000000000..194d746e7605
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/hidden_features.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hidden_features - Check that a feature's instruction runs in the guest when
+ * its ID register field is advertised, and is UNDEFINED when userspace clears
+ * the field.
+ *
+ * Copyright (c) 2026 Google LLC
+ * Author: Fuad Tabba <fuad.tabba@linux.dev>
+ */
+#include "kvm_util.h"
+#include "processor.h"
+#include "test_util.h"
+
+static volatile bool undef;
+
+static void guest_tlbi_os(void)
+{
+ /* tlbi vmalle1os */
+ asm volatile("sys #0, c8, c1, #0\n\tdsb ish\n\tisb" ::: "memory");
+}
+
+static void guest_mops(void)
+{
+ register u64 *d asm("x0");
+ register u64 n asm("x1");
+ register u64 s asm("x2");
+ u64 buf[8];
+
+ d = buf;
+ n = sizeof(buf);
+ s = 0;
+ /* setp [x0]!, x1!, x2; setm; sete */
+ asm volatile(".inst 0x19c20420\n\t.inst 0x19c24420\n\t.inst 0x19c28420"
+ : "+r"(d), "+r"(n) : "r"(s) : "cc", "memory");
+}
+
+static void guest_tcr2(void)
+{
+ read_sysreg_s(SYS_TCR2_EL1);
+}
+
+static void guest_fpmr(void)
+{
+ read_sysreg_s(SYS_FPMR);
+}
+
+struct feature {
+ const char *name;
+ u64 id_reg;
+ u64 mask;
+ u8 shift;
+ u64 min;
+ void (*insn)(void);
+ bool (*trappable)(struct kvm_vcpu *vcpu);
+};
+
+/* Without FGT, KVM traps a hidden TLBI OS only through HCR_EL2.TTLBOS (FEAT_EVT2). */
+static bool tlbi_os_trappable(struct kvm_vcpu *vcpu)
+{
+ u64 mmfr0 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR0_EL1));
+ u64 mmfr2 = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64MMFR2_EL1));
+
+ return SYS_FIELD_GET(ID_AA64MMFR0_EL1, FGT, mmfr0) >= ID_AA64MMFR0_EL1_FGT_IMP ||
+ SYS_FIELD_GET(ID_AA64MMFR2_EL1, EVT, mmfr2) >= ID_AA64MMFR2_EL1_EVT_TTLBxS;
+}
+
+#define FEATURE(n, reg, field, min_val, fn, trap) \
+{ \
+ .name = n, \
+ .id_reg = SYS_##reg, \
+ .mask = reg##_##field##_MASK, \
+ .shift = reg##_##field##_SHIFT, \
+ .min = reg##_##field##_##min_val, \
+ .insn = fn, \
+ .trappable = trap, \
+}
+
+static const struct feature features[] = {
+ FEATURE("TLBI OS", ID_AA64ISAR0_EL1, TLB, OS, guest_tlbi_os, tlbi_os_trappable),
+ FEATURE("MOPS", ID_AA64ISAR2_EL1, MOPS, IMP, guest_mops, NULL),
+ FEATURE("TCR2_EL1", ID_AA64MMFR3_EL1, TCRX, IMP, guest_tcr2, NULL),
+ FEATURE("FPMR", ID_AA64PFR2_EL1, FPMR, IMP, guest_fpmr, NULL),
+};
+
+static void guest_code(const struct feature *feat)
+{
+ undef = false;
+ feat->insn();
+ GUEST_SYNC(undef);
+ GUEST_DONE();
+}
+
+static void guest_undef_handler(struct ex_regs *regs)
+{
+ undef = true;
+ regs->pc += 4;
+}
+
+static bool run(const struct feature *feat, bool hide)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+ bool got = false;
+ u64 val;
+
+ vm = vm_create_with_one_vcpu(&vcpu, (void *)guest_code);
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
+ vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, ESR_ELx_EC_UNKNOWN, guest_undef_handler);
+ vcpu_args_set(vcpu, 1, feat);
+
+ if (hide) {
+ val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(feat->id_reg));
+ vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(feat->id_reg), val & ~feat->mask);
+ }
+
+ for (;;) {
+ vcpu_run(vcpu);
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_SYNC:
+ got = uc.args[1];
+ break;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_DONE:
+ kvm_vm_free(vm);
+ return got;
+ default:
+ TEST_FAIL("Unknown ucall %lu", uc.cmd);
+ }
+ }
+}
+
+static void probe_feature(const struct feature *feat, bool *present, bool *trappable)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ u64 val;
+
+ vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ val = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(feat->id_reg));
+ *present = ((val & feat->mask) >> feat->shift) >= feat->min;
+ *trappable = !feat->trappable || feat->trappable(vcpu);
+ kvm_vm_free(vm);
+}
+
+int main(void)
+{
+ const struct feature *feat;
+ bool present, trappable;
+ int i;
+
+ test_disable_default_vgic();
+
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(features) * 2);
+
+ for (i = 0; i < ARRAY_SIZE(features); i++) {
+ feat = &features[i];
+
+ probe_feature(feat, &present, &trappable);
+ if (!present) {
+ ksft_test_result_skip("%s advertised, not supported\n", feat->name);
+ ksft_test_result_skip("%s hidden, not supported\n", feat->name);
+ continue;
+ }
+
+ if (run(feat, false))
+ ksft_test_result_fail("%s advertised, UNDEF\n", feat->name);
+ else
+ ksft_test_result_pass("%s advertised\n", feat->name);
+
+ if (!trappable)
+ ksft_test_result_skip("%s hidden, not trappable\n", feat->name);
+ else if (run(feat, true))
+ ksft_test_result_pass("%s hidden\n", feat->name);
+ else
+ ksft_test_result_fail("%s hidden, no UNDEF\n", feat->name);
+ }
+
+ ksft_finished();
+}
diff --git a/tools/testing/selftests/kvm/x86/nested_x2apic_test.c b/tools/testing/selftests/kvm/x86/nested_x2apic_test.c
new file mode 100644
index 000000000000..ce204ce29a9c
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/nested_x2apic_test.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "vmx.h"
+#include "svm_util.h"
+
+/*
+ * Use the kernel's posted interrupt vectors to minimize the risk of crashing
+ * the host if KVM is buggy. Note, the vectors aren't set in stone, ideally
+ * these will be kept up-to-date if the kernel vectors change, but it's "fine"
+ * if they are stale.
+ */
+#define POSTED_INTR_VECTOR 0xf2
+#define POSTED_INTR_WAKEUP_VECTOR 0xf1
+#define POSTED_INTR_NESTED_VECTOR 0xf0
+
+static bool inhibit_apicv;
+static volatile unsigned int nr_irqs;
+
+static void guest_irq_handler(struct ex_regs *regs)
+{
+ nr_irqs++;
+ x2apic_write_reg(APIC_EOI, 0);
+}
+
+static void l2_guest_code(void)
+{
+ if (inhibit_apicv)
+ wrmsr(MSR_IA32_APICBASE, rdmsr(MSR_IA32_APICBASE) & GENMASK_ULL(11, 0));
+
+ for (;;) {
+ x2apic_write_reg(APIC_TASKPRI, 0xf0);
+ GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xf0);
+
+ asm volatile("cpuid" ::: "eax", "ebx", "ecx", "edx");
+ }
+}
+
+static void l1_svm_code(struct svm_test_data *svm)
+{
+ struct vmcb_control_area *ctrl = &svm->vmcb->control;
+
+ generic_svm_setup(svm, l2_guest_code);
+ ctrl->intercept |= BIT_ULL(INTERCEPT_CPUID) | BIT_ULL(INTERCEPT_MSR_PROT);
+
+ run_guest(svm->vmcb, svm->vmcb_gpa);
+ GUEST_ASSERT_EQ(ctrl->exit_code, SVM_EXIT_CPUID);
+
+ stgi();
+ x2apic_write_reg(APIC_TASKPRI, 0);
+}
+
+static void l1_vmx_code(struct vmx_pages *vmx, struct hyperv_test_pages *hv_pages)
+{
+ u64 control;
+
+ if (hv_pages) {
+ wrmsr(HV_X64_MSR_GUEST_OS_ID, HYPERV_LINUX_OS_ID);
+ enable_vp_assist(hv_pages->vp_assist_gpa, hv_pages->vp_assist);
+ evmcs_enable();
+ }
+
+ GUEST_ASSERT_EQ(prepare_for_vmx_operation(vmx), true);
+
+ if (hv_pages) {
+ GUEST_ASSERT(load_evmcs(hv_pages));
+ current_evmcs->hv_enlightenments_control.msr_bitmap = 1;
+ } else {
+ GUEST_ASSERT(load_vmcs(vmx));
+ }
+
+ prepare_vmcs(vmx, NULL);
+ GUEST_ASSERT_EQ(vmwrite(GUEST_RIP, (unsigned long)l2_guest_code), 0);
+
+ control = vmreadz(PIN_BASED_VM_EXEC_CONTROL);
+ control |= PIN_BASED_EXT_INTR_MASK;
+ vmwrite(PIN_BASED_VM_EXEC_CONTROL, control);
+
+ control = vmreadz(CPU_BASED_VM_EXEC_CONTROL);
+ control |= CPU_BASED_USE_MSR_BITMAPS | CPU_BASED_TPR_SHADOW;
+ GUEST_ASSERT_EQ(vmwrite(CPU_BASED_VM_EXEC_CONTROL, control), 0);
+
+ control = vmreadz(SECONDARY_VM_EXEC_CONTROL);
+ control |= SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
+ SECONDARY_EXEC_APIC_REGISTER_VIRT |
+ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY;
+ control &= (rdmsr(MSR_IA32_VMX_PROCBASED_CTLS2) >> 32);
+ GUEST_ASSERT_EQ(vmwrite(SECONDARY_VM_EXEC_CONTROL, control), 0);
+
+ GUEST_ASSERT(!vmlaunch());
+ GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_CPUID);
+ GUEST_ASSERT_EQ(vmwrite(GUEST_RIP,
+ vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)), 0);
+}
+
+static void l1_vmx_code_part2(void)
+{
+ u64 control;
+
+ control = vmreadz(CPU_BASED_VM_EXEC_CONTROL);
+ control &= ~CPU_BASED_TPR_SHADOW;
+ GUEST_ASSERT_EQ(vmwrite(CPU_BASED_VM_EXEC_CONTROL, control), 0);
+
+ control = vmreadz(SECONDARY_VM_EXEC_CONTROL);
+ control &= ~(SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE |
+ SECONDARY_EXEC_APIC_REGISTER_VIRT |
+ SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY);
+ GUEST_ASSERT_EQ(vmwrite(SECONDARY_VM_EXEC_CONTROL, control), 0);
+
+ GUEST_ASSERT(!vmresume());
+ GUEST_ASSERT_EQ(vmreadz(VM_EXIT_REASON), EXIT_REASON_CPUID);
+ GUEST_ASSERT_EQ(vmwrite(GUEST_RIP,
+ vmreadz(GUEST_RIP) + vmreadz(VM_EXIT_INSTRUCTION_LEN)), 0);
+}
+
+static void l1_test_x2apic_intercepts(void)
+{
+ GUEST_ASSERT_EQ(nr_irqs, 0);
+
+ sti_nop();
+
+ GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xf0);
+
+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_VECTOR);
+ GUEST_ASSERT_EQ(nr_irqs, 0);
+
+ x2apic_write_reg(APIC_TASKPRI, 0xff);
+ GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0xff);
+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_WAKEUP_VECTOR);
+ GUEST_ASSERT_EQ(nr_irqs, 0);
+
+ x2apic_write_reg(APIC_TASKPRI, 0);
+ GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0);
+
+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | POSTED_INTR_NESTED_VECTOR);
+ GUEST_ASSERT_EQ(nr_irqs, 3);
+
+ nr_irqs = 0;
+}
+
+static void l1_guest_code(void *test_data, void *hv_pages)
+{
+ x2apic_enable();
+
+ if (this_cpu_has(X86_FEATURE_SVM))
+ l1_svm_code(test_data);
+ else
+ l1_vmx_code(test_data, hv_pages);
+
+ GUEST_ASSERT_EQ(x2apic_read_reg(APIC_TASKPRI), 0);
+ x2apic_write_reg(APIC_TASKPRI, 0xf0);
+
+ l1_test_x2apic_intercepts();
+
+ if (this_cpu_has(X86_FEATURE_VMX)) {
+ l1_vmx_code_part2();
+ l1_test_x2apic_intercepts();
+ }
+
+ GUEST_DONE();
+}
+
+static void test_x2apic_intercepts(bool with_inhibit_apicv, bool use_evmcs)
+{
+ gva_t nested_test_data_gva, hv_pages_gva = 0;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+
+ inhibit_apicv = with_inhibit_apicv;
+
+ vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code);
+ vm_install_exception_handler(vm, POSTED_INTR_VECTOR, guest_irq_handler);
+ vm_install_exception_handler(vm, POSTED_INTR_WAKEUP_VECTOR, guest_irq_handler);
+ vm_install_exception_handler(vm, POSTED_INTR_NESTED_VECTOR, guest_irq_handler);
+
+ sync_global_to_guest(vm, inhibit_apicv);
+
+ if (kvm_cpu_has(X86_FEATURE_SVM))
+ vcpu_alloc_svm(vm, &nested_test_data_gva);
+ else
+ vcpu_alloc_vmx(vm, &nested_test_data_gva);
+
+ if (use_evmcs) {
+ vcpu_set_hv_cpuid(vcpu);
+ vcpu_enable_evmcs(vcpu);
+
+ vcpu_alloc_hyperv_test_pages(vm, &hv_pages_gva);
+ }
+
+ vcpu_args_set(vcpu, 2, nested_test_data_gva, hv_pages_gva);
+
+ vcpu_run(vcpu);
+
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_DONE:
+ break;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ default:
+ TEST_FAIL("Expected DONE, got unexpected ucall %lu", uc.cmd);
+ }
+
+ kvm_vm_free(vm);
+}
+
+int main(int argc, char *argv[])
+{
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM) || kvm_cpu_has(X86_FEATURE_VMX));
+
+ test_x2apic_intercepts(true, false);
+ test_x2apic_intercepts(false, false);
+
+ if (kvm_has_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) {
+ test_x2apic_intercepts(true, true);
+ test_x2apic_intercepts(false, true);
+ }
+}
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index c9f46031ac73..dacd36ed8455 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -55,6 +55,7 @@ tools
tun
txring_overwrite
txtimestamp
+udp_splice_checksum
udpgso
udpgso_bench_rx
udpgso_bench_tx
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 3ee3378f8b26..590b33e16d9f 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -199,6 +199,7 @@ TEST_GEN_PROGS := \
tap \
tcp_port_share \
tls \
+ udp_splice_checksum \
# end of TEST_GEN_PROGS
TEST_FILES := \
diff --git a/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt b/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt
new file mode 100644
index 000000000000..f072e5e4c449
--- /dev/null
+++ b/tools/testing/selftests/net/packetdrill/tcp_old_ack_ts.pkt
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+// An out-of-order segment advances SND.UNA. The gap filler still carries
+// its original, older ACK. The reply must echo the gap filler's timestamp.
+--tcp_ts_tick_usecs=1000
+
+`./defaults.sh`
+
+0.000 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
++0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
++0 bind(3, ..., ...) = 0
++0 listen(3, 1) = 0
+0.100 < S 0:0(0) win 20000 <mss 1000,sackOK,TS val 900 ecr 0>
++0 > S. 0:0(0) ack 1 <mss 1460,sackOK,TS val 100 ecr 900>
+0.200 < . 1:1(0) ack 1 win 20000 <nop,nop,TS val 1000 ecr 100>
++0 accept(3, ..., ...) = 4
+0.300 write(4, ..., 17) = 17
++0 > P. 1:18(17) ack 1 <nop,nop,TS val 300 ecr 1000>
+0.310 < P. 18:35(17) ack 18 win 20000 <nop,nop,TS val 301001 ecr 300>
++0 > . 18:18(0) ack 1 <nop,nop,TS val 310 ecr 1000,nop,nop,sack 18:35>
+0.320 < P. 1:18(17) ack 1 win 20000 <nop,nop,TS val 301000 ecr 100>
++0 > . 18:18(0) ack 35 <nop,nop,TS val 320 ecr 301000>
++0 read(4, ..., 34) = 34
diff --git a/tools/testing/selftests/net/udp_splice_checksum.c b/tools/testing/selftests/net/udp_splice_checksum.c
new file mode 100644
index 000000000000..dc61444e6481
--- /dev/null
+++ b/tools/testing/selftests/net/udp_splice_checksum.c
@@ -0,0 +1,378 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Exercise UDP software checksums over multiple spliced pipe fragments.
+ * All traffic stays on loopback; no privileges or external services are needed.
+ * Exit status: 0 = pass, 1 = failure, 4 = all cases skipped.
+ */
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "kselftest.h"
+
+#define TIMEOUT_MS 200
+
+struct test_case {
+ const char *name;
+ const char *body;
+ const char *prefix;
+ size_t fragments[3];
+ bool ordinary;
+ bool page_tail;
+};
+
+static const struct test_case cases[] = {
+ { "ordinary-control", "abcDEFGH", "", { 3, 5 }, true, false },
+ { "uniform-odd-fragments", "BBBBBBBB", "", { 3, 5 }, false, false },
+ { "even-fragments", "abcdEFGH", "", { 4, 4 }, false, false },
+ { "odd-fragments", "abcDEFGH", "", { 3, 5 }, false, false },
+ { "odd-prefix-odd-fragments", "abcDEFGH", "P", { 3, 5 }, false, false },
+ { "even-prefix-odd-fragments", "abcDEFGH", "PQ", { 3, 5 }, false, false },
+ { "page-tail-odd-fragments", "abcDEFGH", "", { 3, 5 }, false, true },
+ { "three-fragments", "abcDEFGhijkl", "", { 3, 4, 5 }, false, false },
+ { "odd-prefix-three-fragments",
+ "abcDEFGhijkl",
+ "P",
+ { 3, 4, 5 },
+ false,
+ false },
+};
+
+static const char *const families[] = { "IPv4", "IPv6", "IPv4-mapped" };
+
+static int error(const char *what)
+{
+ ksft_perror(what);
+ return 1;
+}
+
+static bool unavailable(void)
+{
+ return errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT ||
+ errno == EADDRNOTAVAIL;
+}
+
+static long long now_ms(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts))
+ ksft_exit_fail_perror("clock_gettime");
+ return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+}
+
+static int receive_one(int fd, const char *expected, size_t length)
+{
+ struct pollfd pfd = { .fd = fd, .events = POLLIN };
+ long long deadline = now_ms() + TIMEOUT_MS;
+ int remaining, ret;
+ char received[64];
+ ssize_t count;
+
+ for (;;) {
+ remaining = deadline - now_ms();
+ if (remaining < 0)
+ remaining = 0;
+ ret = poll(&pfd, 1, remaining);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ return error("poll");
+ }
+ if (!ret) {
+ ksft_print_msg("no datagram within %d ms after send\n",
+ TIMEOUT_MS);
+ return 1;
+ }
+ count = recv(fd, received, sizeof(received),
+ MSG_DONTWAIT | MSG_TRUNC);
+ if (count < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return error("recv");
+ }
+ break;
+ }
+ if (count != (ssize_t)length || memcmp(received, expected, length)) {
+ ksft_print_msg("received %zd bytes; expected %zu matching\n",
+ count, length);
+ return 1;
+ }
+ count = recv(fd, received, sizeof(received), MSG_DONTWAIT | MSG_TRUNC);
+ if (count >= 0) {
+ ksft_print_msg("unexpected extra datagram (%zd bytes)\n",
+ count);
+ return 1;
+ }
+ if (errno != EAGAIN)
+ return error("extra-datagram check");
+ return 0;
+}
+
+/* Separate source pipes prevent adjacent small writes merging into one buffer. */
+static int append_fragment(int dest, const char *data, size_t length)
+{
+ int source[2], ret = 1;
+
+ if (pipe2(source, O_NONBLOCK | O_CLOEXEC))
+ return error("source pipe");
+ if (write(source[1], data, length) != (ssize_t)length) {
+ error("source write");
+ goto out;
+ }
+ if (splice(source[0], NULL, dest, NULL, length, SPLICE_F_NONBLOCK) !=
+ (ssize_t)length) {
+ error("pipe-to-pipe splice");
+ goto out;
+ }
+ ret = 0;
+out:
+ close(source[0]);
+ close(source[1]);
+ return ret;
+}
+
+static int pipe_capacity(int fd, unsigned int slots, const char **skip_reason)
+{
+ long required = slots * sysconf(_SC_PAGESIZE);
+ int capacity = fcntl(fd, F_GETPIPE_SZ);
+
+ if (capacity < 0)
+ return error("query pipe capacity");
+ if (capacity >= required ||
+ fcntl(fd, F_SETPIPE_SZ, required) >= required)
+ return 0;
+ ksft_print_msg("cannot reserve %u pipe slots: %s\n", slots,
+ strerror(errno));
+ *skip_reason = "required pipe capacity unavailable";
+ return KSFT_SKIP;
+}
+
+/* Leave three bytes at a page's end, followed by five bytes on the next page. */
+static int append_page_tail(int dest, const char *body,
+ const char **skip_reason)
+{
+ long page = sysconf(_SC_PAGESIZE);
+ int source[2], ret = 1;
+ char *data;
+
+ if (page < 8) {
+ ksft_print_msg("invalid page size %ld\n", page);
+ return 1;
+ }
+ data = malloc(page + 5);
+ if (!data)
+ return error("malloc");
+ memset(data, 'x', page - 3);
+ memcpy(data + page - 3, body, 8);
+ if (pipe2(source, O_NONBLOCK | O_CLOEXEC)) {
+ error("page-tail source pipe");
+ goto free_data;
+ }
+ ret = pipe_capacity(source[1], 2, skip_reason);
+ if (ret)
+ goto out;
+ ret = 1;
+ if (write(source[1], data, page + 5) != page + 5) {
+ error("page-tail write");
+ goto out;
+ }
+ if (read(source[0], data, page - 3) != page - 3) {
+ error("page-tail discard");
+ goto out;
+ }
+ if (splice(source[0], NULL, dest, NULL, 8, SPLICE_F_NONBLOCK) != 8) {
+ error("page-tail pipe-to-pipe splice");
+ goto out;
+ }
+ ret = 0;
+out:
+ close(source[0]);
+ close(source[1]);
+free_data:
+ free(data);
+ return ret;
+}
+
+static int run_case(unsigned int family, bool connected,
+ const struct test_case *test, const char **skip_reason)
+{
+ size_t body_length = strlen(test->body), offset = 0;
+ int tx_family = family == 0 ? AF_INET : AF_INET6;
+ int rx_family = family == 1 ? AF_INET6 : AF_INET;
+ int rx = -1, tx = -1, pipefd[2] = { -1, -1 };
+ size_t prefix_length = strlen(test->prefix);
+ struct sockaddr_storage address = { 0 };
+ socklen_t address_length;
+ struct sockaddr_in6 *v6;
+ struct sockaddr_in *v4;
+ int ret = 1, zero = 0;
+ char expected[64];
+ unsigned int i;
+ ssize_t count;
+
+ v4 = (struct sockaddr_in *)&address;
+ v6 = (struct sockaddr_in6 *)&address;
+ *skip_reason = "address family unavailable";
+ memcpy(expected, test->prefix, prefix_length);
+ memcpy(expected + prefix_length, test->body, body_length);
+ rx = socket(rx_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (rx < 0) {
+ ret = unavailable() ? KSFT_SKIP : error("receive socket");
+ goto out;
+ }
+ if (rx_family == AF_INET) {
+ v4->sin_family = AF_INET;
+ v4->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ address_length = sizeof(*v4);
+ } else {
+ v6->sin6_family = AF_INET6;
+ v6->sin6_addr = in6addr_loopback;
+ address_length = sizeof(*v6);
+ }
+ if (bind(rx, (struct sockaddr *)&address, address_length)) {
+ ret = unavailable() ? KSFT_SKIP : error("bind loopback");
+ goto out;
+ }
+ if (getsockname(rx, (struct sockaddr *)&address, &address_length)) {
+ error("getsockname");
+ goto out;
+ }
+ if (family == 2) {
+ unsigned short port = v4->sin_port;
+
+ memset(&address, 0, sizeof(address));
+ v6->sin6_family = AF_INET6;
+ v6->sin6_port = port;
+ inet_pton(AF_INET6, "::ffff:127.0.0.1", &v6->sin6_addr);
+ address_length = sizeof(*v6);
+ }
+ tx = socket(tx_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (tx < 0) {
+ ret = unavailable() ? KSFT_SKIP : error("send socket");
+ goto out;
+ }
+ if (family == 2 &&
+ setsockopt(tx, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero))) {
+ error("disable IPV6_V6ONLY");
+ goto out;
+ }
+ if (connected &&
+ connect(tx, (struct sockaddr *)&address, address_length)) {
+ error("connect");
+ goto out;
+ }
+ if (test->ordinary) {
+ count = sendto(tx, expected, prefix_length + body_length, 0,
+ (struct sockaddr *)&address, address_length);
+ if (count != (ssize_t)(prefix_length + body_length)) {
+ error("ordinary sendto");
+ goto out;
+ }
+ } else {
+ if (pipe2(pipefd, O_NONBLOCK | O_CLOEXEC)) {
+ error("assembled pipe");
+ goto out;
+ }
+ ret = pipe_capacity(pipefd[1], test->fragments[2] ? 3 : 2,
+ skip_reason);
+ if (ret)
+ goto out;
+ ret = 1;
+ if (test->page_tail) {
+ ret = append_page_tail(pipefd[1], test->body,
+ skip_reason);
+ if (ret)
+ goto out;
+ ret = 1;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(test->fragments); i++) {
+ if (!test->fragments[i])
+ break;
+ if (append_fragment(pipefd[1],
+ test->body + offset,
+ test->fragments[i]))
+ goto out;
+ offset += test->fragments[i];
+ }
+ }
+ /* Even an empty prefix establishes the destination and UDP cork. */
+ count = sendto(tx, test->prefix, prefix_length, MSG_MORE,
+ (struct sockaddr *)&address, address_length);
+ if (count != (ssize_t)prefix_length) {
+ error("prime sendto(MSG_MORE)");
+ goto out;
+ }
+ count = splice(pipefd[0], NULL, tx, NULL, body_length,
+ SPLICE_F_NONBLOCK | SPLICE_F_MORE);
+ if (count != (ssize_t)body_length) {
+ ksft_print_msg("socket splice: %zd, expected %zu: %s\n",
+ count, body_length,
+ count < 0 ? strerror(errno) :
+ "short transfer");
+ goto out;
+ }
+ if (sendto(tx, "", 0, 0, (struct sockaddr *)&address,
+ address_length)) {
+ error("commit sendto");
+ goto out;
+ }
+ }
+ ret = receive_one(rx, expected, prefix_length + body_length);
+out:
+ if (pipefd[0] >= 0) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ }
+ if (tx >= 0)
+ close(tx);
+ if (rx >= 0)
+ close(rx);
+ return ret;
+}
+
+int main(void)
+{
+ unsigned int family, connected, i;
+ const char *connection, *skip;
+ int ret;
+
+ ksft_print_header();
+ ksft_set_plan(ARRAY_SIZE(families) * 2 * ARRAY_SIZE(cases));
+ for (family = 0; family < ARRAY_SIZE(families); family++) {
+ for (connected = 0; connected < 2; connected++) {
+ connection = connected ? "connected" : "unconnected";
+ for (i = 0; i < ARRAY_SIZE(cases); i++) {
+ ret = run_case(family, connected, &cases[i],
+ &skip);
+ if (ret == KSFT_SKIP)
+ ksft_test_result_skip("%s %s %s: %s\n",
+ families[family],
+ connection,
+ cases[i].name,
+ skip);
+ else
+ ksft_test_result(!ret, "%s %s %s\n",
+ families[family],
+ connection,
+ cases[i].name);
+ }
+ }
+ }
+ if (!ksft_get_pass_cnt() && !ksft_get_fail_cnt()) {
+ /* ksft_exit_skip() would add a result beyond the plan. */
+ ksft_print_cnts();
+ return KSFT_SKIP;
+ }
+ ksft_finished();
+}
diff --git a/tools/testing/selftests/ublk/kublk.c b/tools/testing/selftests/ublk/kublk.c
index 2400b4615766..5b19bfcfa8d7 100644
--- a/tools/testing/selftests/ublk/kublk.c
+++ b/tools/testing/selftests/ublk/kublk.c
@@ -1589,7 +1589,7 @@ fail_start:
/* Signal listener thread to stop and wait for it */
if (linfo.stop_efd >= 0) {
- write(linfo.stop_efd, &stop_val, sizeof(stop_val));
+ (void)!write(linfo.stop_efd, &stop_val, sizeof(stop_val));
pthread_join(listener, NULL);
close(linfo.stop_efd);
ublk_shmem_sock_destroy(dinfo->dev_id, linfo.sock_fd);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 85f42289748d..5a782849521c 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -1363,6 +1363,9 @@ int kvm_trylock_all_vcpus(struct kvm *kvm)
lockdep_assert_held(&kvm->lock);
+ if (WARN_ON_ONCE(kvm_is_vcpu_creation_in_progress(kvm)))
+ return -EBUSY;
+
kvm_for_each_vcpu(i, vcpu, kvm)
if (!mutex_trylock_nest_lock(&vcpu->mutex, &kvm->lock))
goto out_unlock;
@@ -1386,6 +1389,9 @@ int kvm_lock_all_vcpus(struct kvm *kvm)
lockdep_assert_held(&kvm->lock);
+ if (WARN_ON_ONCE(kvm_is_vcpu_creation_in_progress(kvm)))
+ return -EBUSY;
+
kvm_for_each_vcpu(i, vcpu, kvm) {
r = mutex_lock_killable_nest_lock(&vcpu->mutex, &kvm->lock);
if (r)
@@ -4182,6 +4188,8 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
struct kvm_vcpu *vcpu;
struct page *page;
+ guard(mutex)(&kvm->lock);
+
/*
* KVM tracks vCPU IDs as 'int', be kind to userspace and reject
* too-large values instead of silently truncating.
@@ -4194,26 +4202,17 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
if (id >= KVM_MAX_VCPU_IDS)
return -EINVAL;
- mutex_lock(&kvm->lock);
- if (kvm->created_vcpus >= kvm->max_vcpus) {
- mutex_unlock(&kvm->lock);
+ if (kvm->created_vcpus >= kvm->max_vcpus)
return -EINVAL;
- }
- if (test_bit(id, kvm->vcpu_ids)) {
- mutex_unlock(&kvm->lock);
+ if (kvm_get_vcpu_by_id(kvm, id))
return -EEXIST;
- }
r = kvm_arch_vcpu_precreate(kvm, id);
- if (r) {
- mutex_unlock(&kvm->lock);
+ if (r)
return r;
- }
kvm->created_vcpus++;
- __set_bit(id, kvm->vcpu_ids);
- mutex_unlock(&kvm->lock);
vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL_ACCOUNT);
if (!vcpu) {
@@ -4244,13 +4243,6 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
goto arch_vcpu_destroy;
}
- mutex_lock(&kvm->lock);
-
- if (WARN_ON_ONCE(kvm_get_vcpu_by_id(kvm, id))) {
- r = -EEXIST;
- goto unlock_vcpu_destroy;
- }
-
/*
* Set the vCPU's index *before* the vCPU is reachable by other tasks.
* Unwind the index back to -1 on failure so that KVM can use the index
@@ -4284,7 +4276,6 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, unsigned long id)
atomic_inc(&kvm->online_vcpus);
mutex_unlock(&vcpu->mutex);
- mutex_unlock(&kvm->lock);
kvm_arch_vcpu_postcreate(vcpu);
kvm_create_vcpu_debugfs(vcpu);
return r;
@@ -4295,7 +4286,6 @@ kvm_put_xa_erase:
xa_erase(&kvm->vcpu_array, vcpu->vcpu_idx);
unlock_vcpu_destroy:
vcpu->vcpu_idx = -1;
- mutex_unlock(&kvm->lock);
kvm_dirty_ring_free(&vcpu->dirty_ring);
arch_vcpu_destroy:
kvm_arch_vcpu_destroy(vcpu);
@@ -4304,10 +4294,7 @@ vcpu_free_run_page:
vcpu_free:
kmem_cache_free(kvm_vcpu_cache, vcpu);
vcpu_decrement:
- mutex_lock(&kvm->lock);
kvm->created_vcpus--;
- __clear_bit(id, kvm->vcpu_ids);
- mutex_unlock(&kvm->lock);
return r;
}