summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap3
-rw-r--r--Documentation/ABI/testing/sysfs-class-power-bd718281
-rw-r--r--Documentation/admin-guide/cgroup-v2.rst50
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt5640.yaml1
-rw-r--r--Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml9
-rw-r--r--MAINTAINERS20
-rw-r--r--Makefile10
-rw-r--r--arch/arm/boot/dts/broadcom/bcm-ns.dtsi2
-rw-r--r--arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts2
-rw-r--r--arch/arm/mach-npcm/platsmp.c2
-rw-r--r--arch/arm64/boot/dts/broadcom/bcm2712.dtsi2
-rw-r--r--arch/arm64/boot/dts/qcom/eliza.dtsi2
-rw-r--r--arch/arm64/boot/dts/qcom/glymur.dtsi32
-rw-r--r--arch/arm64/boot/dts/qcom/monaco.dtsi1
-rw-r--r--arch/arm64/boot/dts/qcom/purwa.dtsi2
-rw-r--r--arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts2
-rw-r--r--arch/arm64/boot/dts/qcom/sc8280xp.dtsi4
-rw-r--r--arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts4
-rw-r--r--arch/arm64/boot/dts/qcom/sm8650.dtsi4
-rw-r--r--arch/arm64/kernel/pi/relocate.c2
-rw-r--r--arch/riscv/Makefile2
-rw-r--r--arch/riscv/errata/sifive/errata.c4
-rw-r--r--arch/riscv/include/asm/timex.h4
-rw-r--r--arch/riscv/kernel/unaligned_access_speed.c4
-rw-r--r--arch/riscv/mm/init.c24
-rw-r--r--arch/s390/pci/pci_mmio.c1
-rw-r--r--arch/um/drivers/vector_kern.c3
-rw-r--r--arch/x86/boot/string.c11
-rw-r--r--arch/x86/entry/entry_64.S8
-rw-r--r--arch/x86/include/asm/nospec-branch.h57
-rw-r--r--arch/x86/kernel/cpu/amd.c4
-rw-r--r--arch/x86/kernel/cpu/bugs.c39
-rw-r--r--arch/x86/lib/retpoline.S20
-rw-r--r--block/genhd.c12
-rw-r--r--drivers/accel/qaic/qaic_control.c2
-rw-r--r--drivers/acpi/cppc_acpi.c29
-rw-r--r--drivers/ata/ahci_ceva.c18
-rw-r--r--drivers/ata/libata-core.c9
-rw-r--r--drivers/ata/libata-eh.c37
-rw-r--r--drivers/ata/libata-sata.c2
-rw-r--r--drivers/ata/libata-scsi.c119
-rw-r--r--drivers/ata/libata.h3
-rw-r--r--drivers/ata/sata_mv.c2
-rw-r--r--drivers/block/ublk_drv.c9
-rw-r--r--drivers/cpufreq/amd-pstate.c8
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c33
-rw-r--r--drivers/cpufreq/powernow-k8.c1
-rw-r--r--drivers/dma/idxd/cdev.c4
-rw-r--r--drivers/dma/idxd/init.c36
-rw-r--r--drivers/dma/sun6i-dma.c11
-rw-r--r--drivers/dma/switchtec_dma.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c13
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c146
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v15_0.c93
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc21.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c26
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c10
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_crat.c9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.c23
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c78
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c15
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c31
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c21
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c16
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.c16
-rw-r--r--drivers/gpu/drm/amd/include/kgd_pp_interface.h6
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c39
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c29
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h6
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c5
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h38
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h6
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c6
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c59
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c3
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c223
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c3
-rw-r--r--drivers/gpu/drm/bridge/display-connector.c11
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c26
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.h2
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c7
-rw-r--r--drivers/gpu/drm/imagination/pvr_trace.h16
-rw-r--r--drivers/gpu/drm/mediatek/mtk_crtc.c6
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c7
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dsi.c5
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.c11
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_v2.c8
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c47
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate.c29
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_blit.c39
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c49
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c20
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c13
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_shader.c13
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_validation.c3
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c17
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c13
-rw-r--r--drivers/hv/connection.c47
-rw-r--r--drivers/hv/hyperv_vmbus.h2
-rw-r--r--drivers/hv/mshv_eventfd.c39
-rw-r--r--drivers/hv/mshv_irq.c2
-rw-r--r--drivers/hv/mshv_portid_table.c9
-rw-r--r--drivers/hv/mshv_root_main.c41
-rw-r--r--drivers/hv/mshv_vtl_main.c13
-rw-r--r--drivers/hv/vmbus_drv.c28
-rw-r--r--drivers/hwmon/adt7470.c129
-rw-r--r--drivers/hwmon/ina2xx.c61
-rw-r--r--drivers/hwmon/lm63.c5
-rw-r--r--drivers/hwmon/lm90.c4
-rw-r--r--drivers/hwmon/ltc4282.c4
-rw-r--r--drivers/hwmon/nct6775-core.c20
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c11
-rw-r--r--drivers/hwmon/nzxt-smart2.c2
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c7
-rw-r--r--drivers/hwmon/sht3x.c9
-rw-r--r--drivers/i2c/busses/i2c-amd-mp2-plat.c4
-rw-r--r--drivers/i2c/busses/i2c-bcm-iproc.c11
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c80
-rw-r--r--drivers/i2c/busses/i2c-imx.c54
-rw-r--r--drivers/i2c/busses/i2c-jz4780.c5
-rw-r--r--drivers/i2c/busses/i2c-k1.c10
-rw-r--r--drivers/i2c/busses/i2c-qcom-cci.c18
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c15
-rw-r--r--drivers/iommu/io-pgfault.c24
-rw-r--r--drivers/iommu/iommufd/device.c2
-rw-r--r--drivers/iommu/iommufd/eventq.c2
-rw-r--r--drivers/iommu/iommufd/pages.c3
-rw-r--r--drivers/iommu/iommufd/viommu.c22
-rw-r--r--drivers/of/address.c22
-rw-r--r--drivers/of/of_reserved_mem.c14
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c52
-rw-r--r--drivers/phy/qualcomm/phy-qcom-m31-eusb2.c2
-rw-r--r--drivers/phy/rockchip/phy-rockchip-naneng-combphy.c6
-rw-r--r--drivers/phy/xilinx/phy-zynqmp.c62
-rw-r--r--drivers/power/supply/bq25890_charger.c2
-rw-r--r--drivers/power/supply/macsmc-power.c52
-rw-r--r--drivers/power/supply/max17040_battery.c6
-rw-r--r--drivers/s390/block/dasd.c14
-rw-r--r--drivers/s390/block/dasd_eckd.c11
-rw-r--r--drivers/s390/block/dasd_ioctl.c2
-rw-r--r--drivers/s390/crypto/pkey_cca.c15
-rw-r--r--drivers/s390/crypto/zcrypt_api.c4
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.c89
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.h6
-rw-r--r--drivers/s390/scsi/zfcp_aux.c1
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c10
-rw-r--r--drivers/scsi/libiscsi.c2
-rw-r--r--drivers/scsi/libiscsi_tcp.c8
-rw-r--r--drivers/scsi/libsas/sas_init.c37
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c15
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c9
-rw-r--r--drivers/scsi/scsi_debug.c8
-rw-r--r--drivers/soc/aspeed/Makefile1
-rw-r--r--drivers/soc/aspeed/aspeed-lpc-snoop.c38
-rw-r--r--drivers/spi/spi-cadence.c26
-rw-r--r--drivers/spi/spi-nxp-fspi.c129
-rw-r--r--drivers/spi/spi-qcom-qspi.c3
-rw-r--r--drivers/spi/spi-qpic-snand.c22
-rw-r--r--drivers/spi/spi-spacemit-k1.c22
-rw-r--r--drivers/target/target_core_iblock.c6
-rw-r--r--drivers/target/target_core_transport.c12
-rw-r--r--drivers/ufs/core/ufs-rpmb.c2
-rw-r--r--drivers/ufs/core/ufshcd.c46
-rw-r--r--drivers/vdpa/mlx5/core/mlx5_vdpa.h2
-rw-r--r--drivers/vdpa/mlx5/core/mr.c8
-rw-r--r--drivers/vdpa/mlx5/core/resources.c11
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim.c10
-rw-r--r--drivers/vdpa/vdpa_user/iova_domain.c11
-rw-r--r--drivers/vhost/iotlb.c47
-rw-r--r--drivers/vhost/scsi.c20
-rw-r--r--drivers/vhost/vdpa.c18
-rw-r--r--drivers/vhost/vhost.c16
-rw-r--r--fs/afs/fsclient.c5
-rw-r--r--fs/afs/internal.h3
-rw-r--r--fs/attr.c2
-rw-r--r--fs/binfmt_misc.c54
-rw-r--r--fs/btrfs/Kconfig3
-rw-r--r--fs/btrfs/btrfs_inode.h1
-rw-r--r--fs/btrfs/disk-io.c38
-rw-r--r--fs/btrfs/extent_io.c113
-rw-r--r--fs/btrfs/fs.c12
-rw-r--r--fs/btrfs/fs.h12
-rw-r--r--fs/btrfs/inode.c221
-rw-r--r--fs/btrfs/lzo.c5
-rw-r--r--fs/btrfs/subpage.c216
-rw-r--r--fs/btrfs/subpage.h41
-rw-r--r--fs/crypto/policy.c2
-rw-r--r--fs/iomap/ioend.c21
-rw-r--r--fs/netfs/buffered_read.c10
-rw-r--r--fs/netfs/internal.h1
-rw-r--r--fs/netfs/main.c7
-rw-r--r--fs/netfs/objects.c30
-rw-r--r--fs/netfs/read_pgpriv2.c3
-rw-r--r--fs/netfs/rolling_buffer.c22
-rw-r--r--fs/netfs/write_issue.c15
-rw-r--r--fs/nfs/nfs4proc.c27
-rw-r--r--fs/ntfs/attrib.c10
-rw-r--r--fs/ntfs/namei.c3
-rw-r--r--fs/ntfs/runlist.c50
-rw-r--r--fs/smb/client/cifs_swn.c6
-rw-r--r--fs/smb/client/cifsacl.c3
-rw-r--r--fs/smb/client/cifssmb.c12
-rw-r--r--fs/smb/client/file.c37
-rw-r--r--fs/smb/client/fscache.c6
-rw-r--r--fs/smb/client/inode.c27
-rw-r--r--fs/smb/client/misc.c51
-rw-r--r--fs/smb/client/smb1maperror.c6
-rw-r--r--fs/smb/client/smb2ops.c2
-rw-r--r--fs/smb/server/connection.h5
-rw-r--r--fs/smb/server/smb2pdu.c14
-rw-r--r--fs/smb/server/smb_common.c37
-rw-r--r--fs/smb/server/vfs_cache.c2
-rw-r--r--fs/xfs/libxfs/xfs_exchmaps.c10
-rw-r--r--fs/xfs/libxfs/xfs_rtrefcount_btree.c4
-rw-r--r--fs/xfs/libxfs/xfs_sb.c4
-rw-r--r--fs/xfs/scrub/agheader.c98
-rw-r--r--fs/xfs/scrub/agheader_repair.c169
-rw-r--r--fs/xfs/scrub/alloc.c5
-rw-r--r--fs/xfs/scrub/attr_repair.c3
-rw-r--r--fs/xfs/scrub/bmap.c5
-rw-r--r--fs/xfs/scrub/dirtree_repair.c22
-rw-r--r--fs/xfs/scrub/inode_repair.c6
-rw-r--r--fs/xfs/scrub/nlinks.c13
-rw-r--r--fs/xfs/scrub/nlinks_repair.c7
-rw-r--r--fs/xfs/scrub/parent.c2
-rw-r--r--fs/xfs/scrub/rtbitmap_repair.c46
-rw-r--r--fs/xfs/scrub/rtsummary.c2
-rw-r--r--fs/xfs/scrub/tempfile.c1
-rw-r--r--fs/xfs/scrub/trace.h2
-rw-r--r--fs/xfs/xfs_buf.c2
-rw-r--r--fs/xfs/xfs_buf_item_recover.c57
-rw-r--r--fs/xfs/xfs_dquot.c3
-rw-r--r--fs/xfs/xfs_dquot_item_recover.c2
-rw-r--r--fs/xfs/xfs_mount.h7
-rw-r--r--fs/xfs/xfs_rtalloc.c2
-rw-r--r--fs/xfs/xfs_super.c53
-rw-r--r--fs/xfs/xfs_zone_gc.c13
-rw-r--r--include/acpi/cppc_acpi.h5
-rw-r--r--include/hyperv/hvhdk_mini.h3
-rw-r--r--include/linux/ata.h6
-rw-r--r--include/linux/dma/qcom_bam_dma.h21
-rw-r--r--include/linux/iommu.h5
-rw-r--r--include/linux/libata.h6
-rw-r--r--include/linux/netfs.h1
-rw-r--r--include/linux/psi.h4
-rw-r--r--include/linux/rolling_buffer.h6
-rw-r--r--include/linux/sched/ext.h10
-rw-r--r--include/scsi/libsas.h3
-rw-r--r--include/sound/sdca_fdl.h2
-rw-r--r--include/sound/sdca_interrupts.h14
-rw-r--r--include/sound/sdca_jack.h3
-rw-r--r--include/sound/soc-component.h2
-rw-r--r--include/trace/events/btrfs.h35
-rw-r--r--io_uring/cancel.c2
-rw-r--r--io_uring/epoll.c3
-rw-r--r--io_uring/kbuf.c5
-rw-r--r--io_uring/net.c1
-rw-r--r--io_uring/tctx.c7
-rw-r--r--io_uring/tctx.h1
-rw-r--r--io_uring/zcrx.c3
-rw-r--r--kernel/audit.c11
-rw-r--r--kernel/auditfilter.c6
-rw-r--r--kernel/cgroup/cgroup.c23
-rw-r--r--kernel/events/uprobes.c2
-rw-r--r--kernel/futex/core.c85
-rw-r--r--kernel/liveupdate/kexec_handover.c22
-rw-r--r--kernel/sched/cpufreq_schedutil.c11
-rw-r--r--kernel/sched/deadline.c3
-rw-r--r--kernel/sched/ext/ext.c47
-rw-r--r--kernel/sched/ext/idle.c4
-rw-r--r--kernel/sched/psi.c75
-rw-r--r--kernel/trace/ring_buffer.c2
-rw-r--r--kernel/trace/trace_events.c4
-rw-r--r--kernel/trace/trace_events_filter.c3
-rw-r--r--kernel/trace/trace_mmiotrace.c13
-rw-r--r--mm/huge_memory.c12
-rw-r--r--mm/memcontrol-v1.h6
-rw-r--r--mm/memory.c2
-rw-r--r--mm/memremap.c1
-rw-r--r--mm/migrate.c3
-rw-r--r--mm/page_reporting.c6
-rw-r--r--mm/vmscan.c12
-rw-r--r--scripts/package/kernel.spec11
-rw-r--r--security/integrity/ima/ima_appraise.c9
-rw-r--r--security/integrity/ima/ima_main.c39
-rw-r--r--security/selinux/hooks.c8
-rw-r--r--security/selinux/ss/mls.c24
-rw-r--r--security/selinux/ss/policydb.c108
-rw-r--r--security/selinux/ss/services.c28
-rw-r--r--sound/core/pcm_native.c7
-rw-r--r--sound/core/seq/seq_timer.c9
-rw-r--r--sound/core/timer.c2
-rw-r--r--sound/core/ump.c1
-rw-r--r--sound/hda/codecs/realtek/alc269.c57
-rw-r--r--sound/hda/controllers/intel.c2
-rw-r--r--sound/pci/lx6464es/lx6464es.c5
-rw-r--r--sound/pci/lx6464es/lx_core.c5
-rw-r--r--sound/soc/amd/acp-config.c7
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c22
-rw-r--r--sound/soc/codecs/max98090.c5
-rw-r--r--sound/soc/codecs/max98095.c5
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt722-sdca.c14
-rw-r--r--sound/soc/codecs/tas2562.c46
-rw-r--r--sound/soc/codecs/tas2781-i2c.c4
-rw-r--r--sound/soc/fsl/fsl_asrc.c2
-rw-r--r--sound/soc/fsl/fsl_easrc.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-nvl-match.c13
-rw-r--r--sound/soc/sdca/sdca_class.c4
-rw-r--r--sound/soc/sdca/sdca_class_function.c6
-rw-r--r--sound/soc/sdca/sdca_device.c2
-rw-r--r--sound/soc/sdca/sdca_fdl.c18
-rw-r--r--sound/soc/sdca/sdca_functions.c2
-rw-r--r--sound/soc/sdca/sdca_interrupts.c174
-rw-r--r--sound/soc/sdca/sdca_jack.c88
-rw-r--r--sound/soc/sdca/sdca_ump.c4
-rw-r--r--sound/soc/soc-component.c10
-rw-r--r--sound/soc/soc-core.c5
-rw-r--r--sound/soc/sophgo/cv1800b-sound-adc.c10
-rw-r--r--sound/usb/6fire/chip.c4
-rw-r--r--sound/usb/endpoint.c14
-rw-r--r--sound/usb/midi.c2
-rw-r--r--sound/usb/midi2.c4
-rw-r--r--sound/usb/mixer_maps.c25
-rw-r--r--sound/usb/mixer_quirks.c2
-rw-r--r--sound/usb/quirks.c7
-rw-r--r--tools/testing/selftests/clone3/clone3_set_tid.c2
-rw-r--r--tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc16
-rw-r--r--tools/testing/selftests/mm/mlock-random-test.c2
-rw-r--r--tools/testing/selftests/sched_ext/numa.bpf.c13
-rw-r--r--tools/tracing/rtla/src/timerlat_top.c2
376 files changed, 5177 insertions, 1666 deletions
diff --git a/.mailmap b/.mailmap
index fd5e272bca9a..01453c5e1ab4 100644
--- a/.mailmap
+++ b/.mailmap
@@ -177,6 +177,7 @@ Brian Cain <bcain@kernel.org> <bcain@quicinc.com>
Brian King <brking@us.ibm.com>
Brian Silverman <bsilver16384@gmail.com> <brian.silverman@bluerivertech.com>
Bryan Tan <bryan-bt.tan@broadcom.com> <bryantan@vmware.com>
+Burak Emir <burak.emir@gmail.com> <bqe@google.com>
Cai Huoqing <cai.huoqing@linux.dev> <caihuoqing@baidu.com>
Casey Connolly <casey.connolly@linaro.org> <caleb.connolly@linaro.org>
Casey Connolly <casey.connolly@linaro.org> <caleb@connolly.tech>
@@ -650,6 +651,7 @@ Nicholas Piggin <npiggin@gmail.com> <npiggin@suse.de>
Nicholas Piggin <npiggin@gmail.com> <nickpiggin@yahoo.com.au>
Nicholas Piggin <npiggin@gmail.com> <piggin@cyberone.com.au>
Nicolas Ferre <nicolas.ferre@microchip.com> <nicolas.ferre@atmel.com>
+Nico Pache <nico.pache@linux.dev> <npache@redhat.com>
Nicolas Pitre <nico@fluxnic.net> <nicolas.pitre@linaro.org>
Nicolas Pitre <nico@fluxnic.net> <nico@linaro.org>
Nicolas Saenz Julienne <nsaenz@kernel.org> <nsaenzjulienne@suse.de>
@@ -697,6 +699,7 @@ Paulo Alcantara <pc@manguebit.org> <palcantara@suse.com>
Paulo Alcantara <pc@manguebit.org> <pc@manguebit.com>
Pavankumar Kondeti <quic_pkondeti@quicinc.com> <pkondeti@codeaurora.org>
Peter A Jonsson <pj@ludd.ltu.se>
+Peter Collingbourne <peter@pcc.me.uk> <pcc@google.com>
Peter Hilber <peter.hilber@oss.qualcomm.com> <quic_philber@quicinc.com>
Peter Oruba <peter.oruba@amd.com>
Peter Oruba <peter@oruba.de>
diff --git a/Documentation/ABI/testing/sysfs-class-power-bd71828 b/Documentation/ABI/testing/sysfs-class-power-bd71828
index 2d451e1c8336..f9a325e005f3 100644
--- a/Documentation/ABI/testing/sysfs-class-power-bd71828
+++ b/Documentation/ABI/testing/sysfs-class-power-bd71828
@@ -10,3 +10,4 @@ Description:
0 no adjustment of input current limit. This
helps for more unusual power sources like
solar modules.
+ ============ ===========================================
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 14b8c571c0d1..aed195a71cbf 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -2239,9 +2239,12 @@ IO Latency
~~~~~~~~~~
This is a cgroup v2 controller for IO workload protection. You provide a group
-with a latency target, and if the average latency exceeds that target the
-controller will throttle any peers that have a lower latency target than the
-protected workload.
+with a latency target, and if the group misses its target the controller will
+throttle any peers that have a lower latency target than the protected
+workload. How a miss is detected depends on the device: on rotational devices
+the average latency over the window must exceed the target, while on
+non-rotational devices a miss is counted once enough of the IOs in the window
+individually exceed the target.
The limits are only applied at the peer level in the hierarchy. This means that
in the diagram below, only groups A, B, and C will influence each other, and
@@ -2258,10 +2261,12 @@ So the ideal way to configure this is to set io.latency in groups A, B, and C.
Generally you do not want to set a value lower than the latency your device
supports. Experiment to find the value that works best for your workload.
Start at higher than the expected latency for your device and, with
-blkcg_debug_stats enabled, watch the avg_lat value in io.stat for your
-workload group to get an idea of the latency you see during normal operation.
-Use the avg_lat value as a basis for your real setting, setting at 10-15%
-higher than the value in io.stat.
+blkcg_debug_stats enabled, observe io.stat for your workload group to get an
+idea of the latency you see during normal operation. On rotational devices,
+use the avg_lat value as a basis for your real setting, setting it 10-15%
+higher. On non-rotational devices io.stat reports no average latency; set
+the target based on your device and use the missed/total fields to verify it
+is being met.
How IO Latency Throttling Works
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2303,19 +2308,36 @@ IO Latency Interface Files
the blkcg_debug_stats module parameter is enabled (it is disabled by
default).
+ The reported latency fields depend on the device. Rotational devices
+ report avg_lat and win; non-rotational devices report missed and total
+ instead. missed and total are live counters for the current window and
+ may change between reads.
+
depth
This is the current queue depth for the group.
avg_lat
- This is an exponential moving average with a decay rate of 1/exp
- bound by the sampling interval. The decay rate interval can be
- calculated by multiplying the win value in io.stat by the
- corresponding number of samples based on the win value.
+ (Rotational devices only.) This is an exponential moving
+ average with a decay rate of 1/exp bound by the sampling
+ interval. The decay rate interval can be calculated by
+ multiplying the win value in io.stat by the corresponding number
+ of samples based on the win value.
win
- The sampling window size in milliseconds. This is the minimum
- duration of time between evaluation events. Windows only elapse
- with IO activity. Idle periods extend the most recent window.
+ (Rotational devices only.) The sampling window size in
+ milliseconds. This is the minimum duration of time between
+ evaluation events. Windows only elapse with IO activity. Idle
+ periods extend the most recent window.
+
+ missed
+ (Non-rotational devices only.) The number of IOs in the
+ current window whose latency exceeded the target. A group is
+ considered to be missing its target once missed reaches a
+ certain ratio of total.
+
+ total
+ (Non-rotational devices only.) The total number of IOs
+ accounted in the current window.
IO Priority
~~~~~~~~~~~
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml
index 2eb631950963..922fe161f4d7 100644
--- a/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml
+++ b/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml
@@ -135,7 +135,6 @@ properties:
required:
- compatible
- reg
- - interrupts
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml
index db165a235cb6..66d73e9025d6 100644
--- a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml
+++ b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml
@@ -18,7 +18,14 @@ properties:
- const: qcom,sa8255p-ufshc
reg:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
+
+ reg-names:
+ minItems: 1
+ items:
+ - const: std
+ - const: mcq
interrupts:
maxItems: 1
diff --git a/MAINTAINERS b/MAINTAINERS
index 9de29cdcd8c6..cfee5d47b214 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2676,6 +2676,8 @@ F: drivers/irqchip/irq-aspeed-i2c-ic.c
ARM/ASPEED MACHINE SUPPORT
M: Joel Stanley <joel@jms.id.au>
M: Andrew Jeffery <andrew@codeconstruct.com.au>
+R: Ryan Chen <ryan_chen@aspeedtech.com>
+R: Billy Tsai <billy_tsai@aspeedtech.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
S: Supported
@@ -2881,7 +2883,7 @@ W: http://www.armlinux.org.uk/
F: arch/arm/include/asm/hardware/dec21285.h
F: arch/arm/mach-footbridge/
-ARM/FREESCALE IMX / MXC ARM ARCHITECTURE
+ARM/FREESCALE IMX / MXC / LAYERSCAPE ARM ARCHITECTURE
M: Frank Li <Frank.Li@nxp.com>
M: Sascha Hauer <s.hauer@pengutronix.de>
R: Pengutronix Kernel Team <kernel@pengutronix.de>
@@ -2895,22 +2897,11 @@ F: Documentation/devicetree/bindings/firmware/nxp*
F: arch/arm/boot/dts/nxp/
F: arch/arm64/boot/dts/freescale/
X: Documentation/devicetree/bindings/media/i2c/
-X: arch/arm64/boot/dts/freescale/fsl-*
-X: arch/arm64/boot/dts/freescale/qoriq-*
X: drivers/media/i2c/
N: imx
N: mxs
N: \bmxc[^\d]
-ARM/FREESCALE LAYERSCAPE ARM ARCHITECTURE
-M: Frank Li <Frank.Li@nxp.com>
-L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/frank.li/linux.git
-F: arch/arm/boot/dts/nxp/ls/
-F: arch/arm64/boot/dts/freescale/fsl-*
-F: arch/arm64/boot/dts/freescale/qoriq-*
-
ARM/FREESCALE VYBRID ARM ARCHITECTURE
M: Frank Li <Frank.Li@nxp.com>
M: Sascha Hauer <s.hauer@pengutronix.de>
@@ -4634,7 +4625,7 @@ F: rust/helpers/cpumask.c
BITMAP API [RUST]
M: Alice Ryhl <aliceryhl@google.com>
-M: Burak Emir <bqe@google.com>
+M: Burak Emir <burak.emir@gmail.com>
R: Yury Norov <yury.norov@gmail.com>
S: Maintained
F: lib/find_bit_benchmark_rust.rs
@@ -17254,7 +17245,7 @@ M: Lorenzo Stoakes <ljs@kernel.org>
R: Zi Yan <ziy@nvidia.com>
R: Baolin Wang <baolin.wang@linux.alibaba.com>
R: Liam R. Howlett <liam@infradead.org>
-R: Nico Pache <npache@redhat.com>
+R: Nico Pache <nico.pache@linux.dev>
R: Ryan Roberts <ryan.roberts@arm.com>
R: Dev Jain <dev.jain@arm.com>
R: Barry Song <baohua@kernel.org>
@@ -20693,7 +20684,6 @@ F: include/linux/switchtec.h
F: include/uapi/linux/switchtec_ioctl.h
PCI DRIVER FOR MOBIVEIL PCIE IP
-M: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in>
M: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
L: linux-pci@vger.kernel.org
S: Supported
diff --git a/Makefile b/Makefile
index 2f5bf24e3389..902f3f3d54b7 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 7
PATCHLEVEL = 2
SUBLEVEL = 0
-EXTRAVERSION = -rc5
+EXTRAVERSION = -rc6
NAME = Baby Opossum Posse
# *DOCUMENTATION*
@@ -700,13 +700,11 @@ filechk_makefile = { \
echo "include $(abs_srctree)/Makefile"; \
}
-$(objtree)/Makefile: FORCE
+PHONY += $(CURDIR)/Makefile
+$(CURDIR)/Makefile: FORCE
$(call filechk,makefile)
-# Prevent $(srcroot)/Makefile from inhibiting the rule to run.
-PHONY += $(objtree)/Makefile
-
-outputmakefile: $(objtree)/Makefile
+outputmakefile: $(CURDIR)/Makefile
ifeq ($(KBUILD_EXTMOD),)
@if [ -f $(srctree)/.config -o \
-d $(srctree)/include/config -o \
diff --git a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
index 392a25713669..bd52de0faa3e 100644
--- a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi
@@ -141,7 +141,7 @@
/* PCIe Controller 2 */
<0x00014000 0 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
- <0x00014000 1 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>,
+ <0x00014000 1 &gic GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>,
<0x00014000 2 &gic GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>,
<0x00014000 3 &gic GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
<0x00014000 4 &gic GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
diff --git a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts
index 5bbc2ba0f959..87569408bb69 100644
--- a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts
+++ b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts
@@ -26,7 +26,7 @@
nvram@1c080000 {
compatible = "brcm,nvram";
- reg = <0x1c080000 0x180000>;
+ reg = <0x1c080000 0x100000>;
et2macaddr: et2macaddr {
#nvmem-cell-cells = <1>;
diff --git a/arch/arm/mach-npcm/platsmp.c b/arch/arm/mach-npcm/platsmp.c
index 41891d3aa124..4c1fc9983746 100644
--- a/arch/arm/mach-npcm/platsmp.c
+++ b/arch/arm/mach-npcm/platsmp.c
@@ -32,6 +32,7 @@ static int npcm7xx_smp_boot_secondary(unsigned int cpu,
goto out;
}
gcr_base = of_iomap(gcr_np, 0);
+ of_node_put(gcr_np);
if (!gcr_base) {
pr_err("could not iomap gcr");
ret = -ENOMEM;
@@ -60,6 +61,7 @@ static void __init npcm7xx_smp_prepare_cpus(unsigned int max_cpus)
return;
}
scu_base = of_iomap(scu_np, 0);
+ of_node_put(scu_np);
if (!scu_base) {
pr_err("could not iomap scu");
return;
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
index 761c59d90ffc..09ff5e9959d3 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
@@ -678,8 +678,6 @@
<GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
- IRQ_TYPE_LEVEL_LOW)>,
- <GIC_PPI 12 (GIC_CPU_MASK_SIMPLE(4) |
IRQ_TYPE_LEVEL_LOW)>;
};
diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi
index 977de44b816e..72b786fec195 100644
--- a/arch/arm64/boot/dts/qcom/eliza.dtsi
+++ b/arch/arm64/boot/dts/qcom/eliza.dtsi
@@ -2559,7 +2559,7 @@
"dsi_pll";
clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>,
- <&rpmhcc RPMH_CXO_CLK>;
+ <&bi_tcxo_div2>;
clock-names = "iface",
"ref";
diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
index 20b49af7298e..129be417ac42 100644
--- a/arch/arm64/boot/dts/qcom/glymur.dtsi
+++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
@@ -1876,7 +1876,7 @@
spi0: spi@b80000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b80000 0x0 0x4000>;
- interrupts = <GIC_SPI 1052 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 92 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1903,7 +1903,7 @@
i2c1: i2c@b84000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b84000 0x0 0x4000>;
- interrupts = <GIC_SPI 1053 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1930,7 +1930,7 @@
spi1: spi@b84000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b84000 0x0 0x4000>;
- interrupts = <GIC_SPI 1053 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1957,7 +1957,7 @@
i2c2: i2c@b88000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b88000 0x0 0x4000>;
- interrupts = <GIC_SPI 1054 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 94 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -1984,7 +1984,7 @@
spi2: spi@b88000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b88000 0x0 0x4000>;
- interrupts = <GIC_SPI 1054 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 94 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2011,7 +2011,7 @@
uart2: serial@b88000 {
compatible = "qcom,geni-uart";
reg = <0x0 0x00b88000 0x0 0x4000>;
- interrupts = <GIC_SPI 1054 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 94 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2056,7 +2056,7 @@
spi3: spi@b8c000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b8c000 0x0 0x4000>;
- interrupts = <GIC_SPI 1055 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 95 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2110,7 +2110,7 @@
spi4: spi@b90000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b90000 0x0 0x4000>;
- interrupts = <GIC_SPI 1056 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 96 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2164,7 +2164,7 @@
spi5: spi@b94000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b94000 0x0 0x4000>;
- interrupts = <GIC_SPI 1057 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 97 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2191,7 +2191,7 @@
i2c6: i2c@b98000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b98000 0x0 0x4000>;
- interrupts = <GIC_SPI 1058 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2218,7 +2218,7 @@
spi6: spi@b98000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b98000 0x0 0x4000>;
- interrupts = <GIC_SPI 1058 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2245,7 +2245,7 @@
i2c7: i2c@b9c000 {
compatible = "qcom,geni-i2c";
reg = <0x0 0x00b9c000 0x0 0x4000>;
- interrupts = <GIC_SPI 1059 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 99 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -2272,7 +2272,7 @@
spi7: spi@b9c000 {
compatible = "qcom,geni-spi";
reg = <0x0 0x00b9c000 0x0 0x4000>;
- interrupts = <GIC_SPI 1059 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 99 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>;
clock-names = "se";
interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS
@@ -6874,9 +6874,9 @@
pcie_smmu: iommu@15480000 {
compatible = "arm,smmu-v3";
reg = <0x0 0x15480000 0x0 0x20000>;
- interrupts = <GIC_SPI 964 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 962 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 960 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_ESPI 4 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 2 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_ESPI 0 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "eventq", "cmdq-sync", "gerror";
dma-coherent;
#iommu-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi
index e4c8466f941b..a350a8ba48fa 100644
--- a/arch/arm64/boot/dts/qcom/monaco.dtsi
+++ b/arch/arm64/boot/dts/qcom/monaco.dtsi
@@ -7384,6 +7384,7 @@
interrupt-controller;
#redistributor-regions = <1>;
redistributor-stride = <0x0 0x20000>;
+ #address-cells = <0>;
};
watchdog@17c10000 {
diff --git a/arch/arm64/boot/dts/qcom/purwa.dtsi b/arch/arm64/boot/dts/qcom/purwa.dtsi
index 9ab4f26b35f2..5b17840fb62f 100644
--- a/arch/arm64/boot/dts/qcom/purwa.dtsi
+++ b/arch/arm64/boot/dts/qcom/purwa.dtsi
@@ -47,6 +47,8 @@
&gpu {
compatible = "qcom,adreno-43030c00", "qcom,adreno";
+ iommus = <&adreno_smmu 0 0x0>;
+
nvmem-cells = <&gpu_speed_bin>;
nvmem-cell-names = "speed_bin";
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts
index f3c00be67081..cd575e20d90a 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts
+++ b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts
@@ -637,7 +637,7 @@
compatible = "huawei,gaokun3-ec";
reg = <0x38>;
- interrupts-extended = <&tlmm 107 IRQ_TYPE_LEVEL_LOW>;
+ interrupts-extended = <&tlmm 103 IRQ_TYPE_LEVEL_LOW>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
index a2bd6b10e475..b0de9e262f29 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
@@ -5356,7 +5356,7 @@
<211 699 1>,
<212 705 1>,
<213 450 1>,
- <214 643 1>,
+ <214 643 2>,
<216 646 5>,
<221 390 5>,
<226 700 3>,
@@ -5379,7 +5379,7 @@
<252 798 1>,
<253 765 1>,
<254 763 1>,
- <255 454 1>,
+ <255 454 3>,
<258 139 1>,
<259 786 2>,
<261 370 2>,
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index e41200839dbe..8cdaef5360d1 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -347,6 +347,10 @@
status = "okay";
};
+&cluster_sleep_0 {
+ arm,psci-suspend-param = <0x41008244>;
+};
+
&gcc {
protected-clocks = <GCC_QSPI_CORE_CLK>,
<GCC_QSPI_CORE_CLK_SRC>,
diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi
index 160ead25ecf7..65c4a2b46d21 100644
--- a/arch/arm64/boot/dts/qcom/sm8650.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi
@@ -7091,8 +7091,8 @@
ranges = <0 0 0x14680000 0x2c000>;
- ipa_modem_tables: modem-tables@8000 {
- reg = <0x8000 0x2000>;
+ ipa_modem_tables: modem-tables@3000 {
+ reg = <0x3000 0x2000>;
};
};
diff --git a/arch/arm64/kernel/pi/relocate.c b/arch/arm64/kernel/pi/relocate.c
index 2407d2696398..82592f3a5c1c 100644
--- a/arch/arm64/kernel/pi/relocate.c
+++ b/arch/arm64/kernel/pi/relocate.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright 2023 Google LLC
// Authors: Ard Biesheuvel <ardb@google.com>
-// Peter Collingbourne <pcc@google.com>
+// Peter Collingbourne <peter@pcc.me.uk>
#include <linux/elf.h>
#include <linux/init.h>
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index ce0cc737f870..1363e5bef35c 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -168,7 +168,7 @@ vdso_prepare: prepare0
endif
endif
-vdso-install-y += arch/riscv/kernel/vdso/vdso.so.dbg
+vdso-install-$(CONFIG_MMU) += arch/riscv/kernel/vdso/vdso.so.dbg
vdso-install-$(CONFIG_RISCV_USER_CFI) += arch/riscv/kernel/vdso_cfi/vdso-cfi.so.dbg
vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg
diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c
index d0c61f86cba3..df80c9614df1 100644
--- a/arch/riscv/errata/sifive/errata.c
+++ b/arch/riscv/errata/sifive/errata.c
@@ -93,10 +93,8 @@ void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end,
for (alt = begin; alt < end; alt++) {
if (alt->vendor_id != SIFIVE_VENDOR_ID)
continue;
- if (alt->patch_id >= ERRATA_SIFIVE_NUMBER) {
- WARN(1, "This errata id:%d is not in kernel errata list", alt->patch_id);
+ if (alt->patch_id >= ERRATA_SIFIVE_NUMBER)
continue;
- }
tmp = (1U << alt->patch_id);
if (cpu_req_errata & tmp) {
diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h
index a06697846e69..d41acfb3959d 100644
--- a/arch/riscv/include/asm/timex.h
+++ b/arch/riscv/include/asm/timex.h
@@ -22,13 +22,13 @@ static inline cycles_t get_cycles(void)
#else /* !CONFIG_64BIT */
static inline u32 get_cycles(void)
{
- return readl_relaxed(((u32 *)clint_time_val));
+ return readl_relaxed(((u32 __iomem *)clint_time_val));
}
#define get_cycles get_cycles
static inline u32 get_cycles_hi(void)
{
- return readl_relaxed(((u32 *)clint_time_val) + 1);
+ return readl_relaxed(((u32 __iomem *)clint_time_val) + 1);
}
#define get_cycles_hi get_cycles_hi
#endif /* CONFIG_64BIT */
diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c
index bb57eb5d19df..5a5aa22124e7 100644
--- a/arch/riscv/kernel/unaligned_access_speed.c
+++ b/arch/riscv/kernel/unaligned_access_speed.c
@@ -289,7 +289,7 @@ free:
}
/* Measure unaligned access speed on all CPUs present at boot in parallel. */
-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
{
schedule_on_each_cpu(check_vector_unaligned_access);
riscv_hwprobe_complete_async_probe();
@@ -297,7 +297,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway
return 0;
}
#else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */
-static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
+static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
{
return 0;
}
diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c
index 3e450890be07..01d252c741d2 100644
--- a/arch/riscv/mm/init.c
+++ b/arch/riscv/mm/init.c
@@ -63,7 +63,8 @@ EXPORT_SYMBOL(phys_ram_base);
#ifdef CONFIG_SPARSEMEM_VMEMMAP
#define VMEMMAP_ADDR_ALIGN max(1ULL << SECTION_SIZE_BITS, \
- MAX_FOLIO_VMEMMAP_ALIGN)
+ PFN_PHYS(MAX_FOLIO_VMEMMAP_ALIGN / \
+ sizeof(struct page)))
unsigned long vmemmap_start_pfn __ro_after_init;
EXPORT_SYMBOL(vmemmap_start_pfn);
@@ -164,7 +165,9 @@ static void print_vm_layout(void) { }
void __init arch_mm_preinit(void)
{
- bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit);
+ bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit) &&
+ memblock_start_of_DRAM() < dma32_phys_limit;
+ unsigned int swiotlb_flags = SWIOTLB_VERBOSE;
#ifdef CONFIG_FLATMEM
BUG_ON(!mem_map);
#endif /* CONFIG_FLATMEM */
@@ -172,17 +175,22 @@ void __init arch_mm_preinit(void)
if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb &&
dma_cache_alignment != 1) {
/*
- * If no bouncing needed for ZONE_DMA, allocate 1MB swiotlb
- * buffer per 1GB of RAM for kmalloc() bouncing on
- * non-coherent platforms.
+ * No 32-bit DMA bouncing needed (either all DRAM is within
+ * the 32-bit limit, or it all starts above it), but
+ * kmalloc() buffers whose sizes are not cache-line-aligned
+ * still require bouncing for non-coherent DMA. Use
+ * SWIOTLB_ANY so that the buffer can be allocated from high
+ * memory when DRAM starts above dma32_phys_limit. Allocate
+ * ~1 MB per 1 GB of RAM.
*/
unsigned long size =
DIV_ROUND_UP(memblock_phys_mem_size(), 1024);
swiotlb_adjust_size(min(swiotlb_size_or_default(), size));
swiotlb = true;
+ swiotlb_flags |= SWIOTLB_ANY;
}
- swiotlb_init(swiotlb, SWIOTLB_VERBOSE);
+ swiotlb_init(swiotlb, swiotlb_flags);
print_vm_layout();
}
@@ -1618,7 +1626,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un
for (; addr < end; addr = next) {
next = pud_addr_end(addr, end);
- pudp = pud_base + pud_index(addr);
+ pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base;
pud = pudp_get(pudp);
if (!pud_present(pud))
continue;
@@ -1649,7 +1657,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un
for (; addr < end; addr = next) {
next = p4d_addr_end(addr, end);
- p4dp = p4d_base + p4d_index(addr);
+ p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base;
p4d = p4dp_get(p4dp);
if (!p4d_present(p4d))
continue;
diff --git a/arch/s390/pci/pci_mmio.c b/arch/s390/pci/pci_mmio.c
index 51e7a28af899..f3f79ba78410 100644
--- a/arch/s390/pci/pci_mmio.c
+++ b/arch/s390/pci/pci_mmio.c
@@ -188,6 +188,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr,
goto out_unlock_mmap;
}
+ ret = -EFAULT;
io_addr = (void __iomem *)((args.pfn << PAGE_SHIFT) |
(mmio_addr & ~PAGE_MASK));
diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index 2cc90055499a..8a70b3a62553 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -995,6 +995,9 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
*/
dev_kfree_skb_irq(skb);
vp->estats.rx_encaps_errors++;
+ (*skbuff_vector) = NULL;
+ mmsg_vector++;
+ skbuff_vector++;
continue;
}
if (header_check > 0) {
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
index ac0f900ebc47..1632d40e1f54 100644
--- a/arch/x86/boot/string.c
+++ b/arch/x86/boot/string.c
@@ -32,8 +32,15 @@
int memcmp(const void *s1, const void *s2, size_t len)
{
bool diff;
- asm("repe cmpsb"
- : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len));
+
+ /*
+ * Make sure ZF is properly set in the len==0 case because in it,
+ * RCX==0 and the REPE; CMPSB won't get executed.
+ */
+ asm volatile("test %3, %3\n\t"
+ "repe cmpsb"
+ : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)
+ : : "cc", "memory");
return diff;
}
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index c6d996593f32..253f0a585927 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -937,6 +937,8 @@ SYM_CODE_START(paranoid_entry)
IBRS_ENTER save_reg=%r15
UNTRAIN_RET_FROM_CALL
+ HANDLE_INTR_SAFERET 8(%rsp)
+
RET
SYM_CODE_END(paranoid_entry)
@@ -1039,6 +1041,11 @@ SYM_CODE_START(error_entry)
movl %ecx, %eax /* zero extend */
cmpq %rax, RIP+8(%rsp)
je .Lbstep_iret
+
+ VALIDATE_UNRET_END
+
+ HANDLE_INTR_SAFERET 8(%rsp)
+
cmpq $.Lgs_change, RIP+8(%rsp)
jne .Lerror_entry_done_lfence
@@ -1057,7 +1064,6 @@ SYM_CODE_START(error_entry)
FENCE_SWAPGS_KERNEL_ENTRY
CALL_DEPTH_ACCOUNT
leaq 8(%rsp), %rax /* return pt_regs pointer */
- VALIDATE_UNRET_END
RET
.Lbstep_iret:
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
index b68892e6d58c..2ea6591bc7b9 100644
--- a/arch/x86/include/asm/nospec-branch.h
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -12,6 +12,7 @@
#include <asm/msr-index.h>
#include <asm/unwind_hints.h>
#include <asm/percpu.h>
+#include <asm/ptrace-abi.h>
/*
* Call depth tracking for Intel SKL CPUs to address the RSB underflow
@@ -176,6 +177,50 @@
add $(BITS_PER_LONG/8), %_ASM_SP; \
lfence;
+/*
+ * Helper for detecting if an interrupt occurred at an unsafe location within
+ * Safe-RET. If Safe-RET is interrupted after the CALL or LEA the RSB may get
+ * poisoned by the interrupt handler.
+ *
+ * The Safe-RET sequence is:
+ *
+ * CALL
+ * LEA 8(%RSP), %RSP
+ * RET
+ *
+ * The two CMPs below check whether RIP points to after the CALL or after the
+ * LEA.
+ *
+ * The LFENCE below is to address this particular speculation case:
+ *
+ * 1. Userspace runs and poisons the BTB around the safe-RET routine
+ *
+ * 2. Userspace triggers some kind of exception
+ *
+ * 3. Kernel executes error_entry() and mis-speculates the branch into thinking
+ * it actually came from kernel space
+ *
+ * 4. The kernel then further mis-speculates that the exception occurred due
+ * to an interrupted safe-RET
+ *
+ * 5. The handle_interrupted_saferet() routine speculatively executes and
+ * speculatively does a safe-RET. But this is unsafe since it was never
+ * untrained.
+ *
+ * The LFENCE fixes this by ensuring step 5 is never reached speculatively.
+ * Note that this LFENCE only occurs if safe-RET was actually interrupted (so
+ * it's outside of the normal path).
+ */
+#define __HANDLE_INTR_SAFERET(name, pt_regs) \
+ cmpq $(name), RIP+pt_regs; \
+ jb 1f; \
+ cmpq $(name)+5, RIP+pt_regs; \
+ ja 1f; \
+ lfence; \
+ leaq pt_regs, %rdi; \
+ call handle_interrupted_saferet; \
+ 1:
+
#ifdef __ASSEMBLER__
/*
@@ -293,6 +338,14 @@
#define UNTRAIN_RET_FROM_CALL \
__UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL)
+.macro HANDLE_INTR_SAFERET pt_regs
+#ifdef CONFIG_MITIGATION_SRSO
+ ALTERNATIVE_2 "", \
+ __stringify(__HANDLE_INTR_SAFERET(srso_safe_ret, \pt_regs)), X86_FEATURE_SRSO, \
+ __stringify(__HANDLE_INTR_SAFERET(srso_alias_safe_ret, \pt_regs)), X86_FEATURE_SRSO_ALIAS
+
+#endif
+.endm
.macro CALL_DEPTH_ACCOUNT
#ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING
@@ -625,6 +678,10 @@ static __always_inline void x86_idle_clear_cpu_buffers(void)
x86_clear_cpu_buffers();
}
+void srso_safe_ret(void);
+void srso_alias_safe_ret(void);
+void handle_interrupted_saferet(struct pt_regs *regs);
+
#endif /* __ASSEMBLER__ */
#endif /* _ASM_X86_NOSPEC_BRANCH_H_ */
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 487ac147e11f..d61df70d6875 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -515,11 +515,13 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
case 0x00 ... 0x2f:
case 0x40 ... 0x4f:
case 0x60 ... 0x7f:
+ case 0xd0 ... 0xd7:
setup_force_cpu_cap(X86_FEATURE_ZEN5);
break;
case 0x50 ... 0x5f:
case 0x80 ... 0xaf:
- case 0xc0 ... 0xef:
+ case 0xc0 ... 0xcf:
+ case 0xd8 ... 0xef:
setup_force_cpu_cap(X86_FEATURE_ZEN6);
break;
default:
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index d9af230c0512..82436b3534fa 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -3775,3 +3775,42 @@ void __warn_thunk(void)
{
WARN_ONCE(1, "Unpatched return thunk in use. This should not happen!\n");
}
+
+#ifdef CONFIG_MITIGATION_SRSO
+/*
+ * Called during exception/interrupt entry if interrupted during the
+ * safe-RET sequence. The safe-RET sequence consists of 3 instructions:
+ *
+ * CALL
+ * LEA 8(%RSP), %RSP
+ * RET
+ *
+ * An interrupt after the CALL or after the LEA could potentially lead
+ * to branch predictor poisoning and results in the sequence not being
+ * able to be safely resumed.
+ *
+ * Therefore, modify the regs state as if the remaining part of the
+ * safe-RET sequence executed so the interrupt returns back to the
+ * desired return target, instead of the to the safe-RET sequence.
+ */
+void noinstr handle_interrupted_saferet(struct pt_regs *regs)
+{
+ unsigned long rip = regs->ip;
+
+ if (rip == (unsigned long) srso_safe_ret ||
+ rip == (unsigned long) srso_alias_safe_ret) {
+ /* Modify stack pointer as if LEA executed: */
+ regs->sp += 8;
+ }
+
+ /*
+ * Adjust registers as if RET executed:
+ *
+ * 1. Read the return address off the stack and into rIP:
+ */
+ regs->ip = *(unsigned long *)(regs->sp);
+
+ /* 2. Pop rIP off the stack: */
+ regs->sp += 8;
+}
+#endif /* CONFIG_MITIGATION_SRSO */
diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
index 8f1fed0c3b83..f9ca1d8eabca 100644
--- a/arch/x86/lib/retpoline.S
+++ b/arch/x86/lib/retpoline.S
@@ -207,10 +207,24 @@ __EXPORT_THUNK(srso_alias_untrain_ret)
.pushsection .text..__x86.rethunk_safe
SYM_CODE_START_NOALIGN(srso_alias_safe_ret)
+
+ /*
+ * Tell objtool that those are not function pointers referenced by
+ * __HANDLE_INTR_SAFERET(). Below too.
+ */
+ ANNOTATE_NOENDBR
+
+ /*
+ * Safe-RET sequence. If you need to change it, adjust
+ * handle_interrupted_saferet() too.
+ */
lea 8(%_ASM_SP), %_ASM_SP
UNWIND_HINT_FUNC
+
+ ANNOTATE_NOENDBR
ANNOTATE_UNRET_SAFE
ret
+ /* End of Safe-RET sequence */
int3
SYM_FUNC_END(srso_alias_safe_ret)
@@ -245,8 +259,14 @@ SYM_CODE_START_LOCAL_NOALIGN(srso_untrain_ret)
* the stack.
*/
SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL)
+ /*
+ * Safe-RET sequence. If you need to change it, adjust
+ * handle_interrupted_saferet() too.
+ */
lea 8(%_ASM_SP), %_ASM_SP
ret
+ /* End of Safe-RET sequence */
+
int3
int3
/* end of movabs */
diff --git a/block/genhd.c b/block/genhd.c
index df2c3c69b467..e8ce0cabf392 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1281,14 +1281,18 @@ static void disk_release(struct device *dev)
/*
* To undo the all initialization from blk_mq_init_allocated_queue in
* case of a probe failure where add_disk is never called we have to
- * call blk_mq_exit_queue here. We can't do this for the more common
- * teardown case (yet) as the tagset can be gone by the time the disk
- * is released once it was added.
+ * call blk_mq_exit_queue here, after stopping the timer and work items
+ * that I/O issued before add_disk may have left pending. We can't do
+ * this for the more common teardown case (yet) as the tagset can be
+ * gone by the time the disk is released once it was added.
*/
if (queue_is_mq(disk->queue) &&
test_bit(GD_OWNS_QUEUE, &disk->state) &&
- !test_bit(GD_ADDED, &disk->state))
+ !test_bit(GD_ADDED, &disk->state)) {
+ blk_sync_queue(disk->queue);
+ blk_mq_cancel_work_sync(disk->queue);
blk_mq_exit_queue(disk->queue);
+ }
blkcg_exit_disk(disk);
diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
index bb94d3556904..50bf3340e49c 100644
--- a/drivers/accel/qaic/qaic_control.c
+++ b/drivers/accel/qaic/qaic_control.c
@@ -786,7 +786,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
break;
}
trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len);
- if (trans_hdr->len < sizeof(trans_hdr) ||
+ if (trans_hdr->len < sizeof(*trans_hdr) ||
size_add(user_len, trans_hdr->len) > user_msg->len) {
ret = -EINVAL;
break;
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index 9f572f481241..53d09ca98f06 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -475,17 +475,29 @@ bool acpi_cpc_valid(void)
}
EXPORT_SYMBOL_GPL(acpi_cpc_valid);
-bool cppc_allow_fast_switch(void)
+bool cppc_allow_fast_switch(const struct cpumask *cpus)
{
- struct cpc_register_resource *desired_reg;
+ struct cpc_register_resource *desired_reg, *min_reg, *max_reg;
struct cpc_desc *cpc_ptr;
int cpu;
- for_each_online_cpu(cpu) {
+ for_each_cpu(cpu, cpus) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
+ if (!cpc_ptr)
+ return false;
desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF];
- if (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
- !CPC_IN_SYSTEM_IO(desired_reg))
+ min_reg = &cpc_ptr->cpc_regs[MIN_PERF];
+ max_reg = &cpc_ptr->cpc_regs[MAX_PERF];
+
+ if (!CPC_SUPPORTED(desired_reg) ||
+ (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
+ !CPC_IN_SYSTEM_IO(desired_reg)) ||
+ (CPC_SUPPORTED(min_reg) &&
+ !CPC_IN_SYSTEM_MEMORY(min_reg) &&
+ !CPC_IN_SYSTEM_IO(min_reg)) ||
+ (CPC_SUPPORTED(max_reg) &&
+ !CPC_IN_SYSTEM_MEMORY(max_reg) &&
+ !CPC_IN_SYSTEM_IO(max_reg)))
return false;
}
@@ -1951,16 +1963,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
cpc_desc->write_cmd_status = 0;
}
- cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
+ if (CPC_SUPPORTED(desired_reg))
+ cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
/*
* Only write if min_perf and max_perf not zero. Some drivers pass zero
* value to min and max perf, but they don't mean to set the zero value,
* they just don't want to write to those registers.
*/
- if (perf_ctrls->min_perf)
+ if (perf_ctrls->min_perf && CPC_SUPPORTED(min_perf_reg))
cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf);
- if (perf_ctrls->max_perf)
+ if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg))
cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf);
if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg))
diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c
index 2d6a08c23d6a..2961e53288f4 100644
--- a/drivers/ata/ahci_ceva.c
+++ b/drivers/ata/ahci_ceva.c
@@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
rc = phy_init(hpriv->phys[i]);
if (rc)
- goto disable_rsts;
+ goto exit_phys;
}
/* De-assert the controller reset */
@@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv)
return 0;
-disable_rsts:
- ahci_platform_deassert_rsts(hpriv);
-
disable_phys:
while (--i >= 0) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
phy_power_off(hpriv->phys[i]);
phy_exit(hpriv->phys[i]);
}
+ ahci_platform_assert_rsts(hpriv);
+ goto disable_clks;
+
+exit_phys:
+ while (--i >= 0) {
+ if (ahci_ignore_port(hpriv, i))
+ continue;
+
+ phy_exit(hpriv->phys[i]);
+ }
disable_clks:
ahci_platform_disable_clks(hpriv);
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index c43bd28b20b1..961d03fbc4d9 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -4414,6 +4414,15 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = {
{ "WDC WD3200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM },
/*
+ * WD drives with LPM issues (irrespective of supported SATA speeds).
+ * (Unlike ATA_QUIRK_WD_BROKEN_LPM, which is only applied if the drive
+ * exposes SATA Gen1 speed support, and SATA Gen1 speed support only.)
+ */
+ { "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM },
+ { "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM },
+ { "WD Green 2.5 480GB", NULL, ATA_QUIRK_NOLPM },
+
+ /*
* This sata dom device goes on a walkabout when the ATA_LOG_DIRECTORY
* log page is accessed. Ensure we never ask for this log page with
* these devices.
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 05df7ea6954a..c154103d892c 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -106,6 +106,12 @@ static const unsigned int ata_eh_flush_timeouts[] = {
UINT_MAX,
};
+static const unsigned int ata_eh_standby_timeouts[] = {
+ 15000, /* Some drives may be slow to standby */
+ /* but don't hold up a suspend too long waiting for them */
+ UINT_MAX,
+};
+
static const unsigned int ata_eh_other_timeouts[] = {
5000, /* same rationale as identify timeout */
10000, /* ditto */
@@ -147,6 +153,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = {
.timeouts = ata_eh_other_timeouts, },
{ .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT),
.timeouts = ata_eh_flush_timeouts },
+ { .commands = CMDS(ATA_CMD_STANDBYNOW1),
+ .timeouts = ata_eh_standby_timeouts },
{ .commands = CMDS(ATA_CMD_VERIFY),
.timeouts = ata_eh_reset_timeouts },
};
@@ -650,29 +658,12 @@ int ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
set_host_byte(scmd, DID_OK);
ata_qc_for_each_raw(ap, qc, i) {
- if (qc->scsicmd != scmd)
- continue;
- if ((qc->flags & ATA_QCFLAG_ACTIVE) ||
- qc == qc->dev->link->deferred_qc)
+ if (qc->scsicmd == scmd &&
+ qc->flags & ATA_QCFLAG_ACTIVE)
break;
}
- if (i < ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) {
- /*
- * This is a deferred command that timed out while
- * waiting for the command queue to drain. Since the qc
- * is not active yet (deferred_qc is still set, so the
- * deferred qc work has not issued the command yet),
- * simply signal the timeout by finishing the SCSI
- * command and clear the deferred qc to prevent the
- * deferred qc work from issuing this qc.
- */
- WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE);
- qc->dev->link->deferred_qc = NULL;
- cancel_work(&qc->dev->link->deferred_qc_work);
- set_host_byte(scmd, DID_TIME_OUT);
- scsi_eh_finish_cmd(scmd, &ap->eh_done_q);
- } else if (i < ATA_MAX_QUEUE) {
+ if (i < ATA_MAX_QUEUE) {
/* the scmd has an associated qc */
if (!(qc->flags & ATA_QCFLAG_EH)) {
/* which hasn't failed yet, timeout */
@@ -948,10 +939,10 @@ static void ata_eh_set_pending(struct ata_port *ap, bool fastdrain)
ap->pflags |= ATA_PFLAG_EH_PENDING;
/*
- * If we have a deferred qc, requeue it so that it is retried once EH
- * completes.
+ * If we have deferred QCs, requeue them so that the SCSI EH task can
+ * run.
*/
- ata_scsi_requeue_deferred_qc(ap);
+ ata_scsi_requeue_deferred_qc(ap, NULL);
if (!fastdrain)
return;
diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c
index b0706c30da05..ad40f516c6d4 100644
--- a/drivers/ata/libata-sata.c
+++ b/drivers/ata/libata-sata.c
@@ -913,7 +913,7 @@ static bool ata_scsi_lpm_supported(struct ata_port *ap)
return false;
ata_for_each_link(link, ap, EDGE) {
- ata_for_each_dev(dev, &ap->link, ENABLED) {
+ ata_for_each_dev(dev, link, ENABLED) {
if (dev->quirks & ATA_QUIRK_NOLPM)
return false;
}
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 5868526301a2..d2160ee7ca7d 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1685,26 +1685,80 @@ void ata_scsi_deferred_qc_work(struct work_struct *work)
spin_unlock_irqrestore(ap->lock, flags);
}
-void ata_scsi_requeue_deferred_qc(struct ata_port *ap)
+enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap,
+ struct scsi_cmnd *timedout_scmd)
{
+ enum scsi_timeout_action action = SCSI_EH_NOT_HANDLED;
+ struct ata_queued_cmd *qc;
struct ata_link *link;
+ u32 host_byte;
lockdep_assert_held(ap->lock);
/*
- * If we have a deferred qc when a reset occurs or NCQ commands fail,
- * do not try to be smart about what to do with this deferred command
- * and simply requeue it by completing it with DID_REQUEUE.
+ * If we have deferred QCs when a reset, a timeout or an NCQ command
+ * fails, do not try to be smart about what to do with the deferred
+ * commands and simply terminate them and let the SCSI layer decide
+ * what to do.
*/
ata_for_each_link(link, ap, PMP_FIRST) {
- struct ata_queued_cmd *qc = link->deferred_qc;
+ qc = link->deferred_qc;
+ if (!qc)
+ continue;
- if (qc) {
- link->deferred_qc = NULL;
- cancel_work(&link->deferred_qc_work);
- ata_scsi_qc_done(qc, true, DID_REQUEUE << 16);
+ /*
+ * Clear the deferred QC so that the deferred work does not try
+ * to issue it.
+ */
+ link->deferred_qc = NULL;
+ cancel_work(&link->deferred_qc_work);
+
+ /*
+ * We are going to complete some scsi command, either with
+ * DID_TIME_OUT if the command timed out while waiting for being
+ * issued, or with DID_REQUEUE if another command timed out or
+ * we had a failed command. However, the block layer may re-issue
+ * these commands immediately, keeping the scsi host busy and
+ * thus preventing the SCSI EH task from running.
+ * So schedule EH on the port to prevent accepting new commands
+ * until everything is sorted out with the error or timeout that
+ * got us here in the first place. Note that we set EH pending
+ * on the port before calling ata_port_schedule_eh() so that we
+ * do not reenter this function from ata_eh_set_pending() with
+ * timedout_scmd being NULL and erroneously retry deferred QCs
+ * that have timed out on other links.
+ */
+ if (!ata_port_eh_scheduled(ap)) {
+ ap->pflags |= ATA_PFLAG_EH_PENDING;
+ ata_port_schedule_eh(ap);
}
+
+ /*
+ * If we are being called from scsi_timeout(), then we have a
+ * non-NULL timedout_scmd. If the timed out command is for a
+ * deferred QC, terminate that deferred QC with DID_TIME_OUT and
+ * requeue all other deferred QCs. In this case we need to
+ * return SCSI_EH_DONE, because the timed out command was
+ * handled.
+ * If the timed out command is not for a deferred QC, we need to
+ * requeue all deferred QCs, and return SCSI_EH_NOT_HANDLED so
+ * that the timed out command gets added to the EH work queue
+ * with scsi_eh_scmd_add(), for later handling with libata EH
+ * ata_scsi_cmd_error_handler().
+ * If timedout_scmd is NULL, we simply need to requeue all
+ * deferred QCs and the return value does not matter as we were
+ * not called from scsi_timeout().
+ */
+ if (timedout_scmd && qc->scsicmd == timedout_scmd) {
+ host_byte = DID_TIME_OUT;
+ action = SCSI_EH_DONE;
+ } else {
+ host_byte = DID_REQUEUE;
+ }
+ ata_scsi_qc_done(qc, true, host_byte << 16);
}
+
+ return action;
}
static void ata_scsi_schedule_deferred_qc(struct ata_link *link)
@@ -1723,13 +1777,42 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link)
return;
if (ata_port_eh_scheduled(ap)) {
- ata_scsi_requeue_deferred_qc(ap);
+ ata_scsi_requeue_deferred_qc(ap, NULL);
return;
}
if (!ap->ops->qc_defer(qc))
queue_work(system_highpri_wq, &link->deferred_qc_work);
}
+enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap,
+ struct scsi_cmnd *scmd)
+{
+ enum scsi_timeout_action action;
+ unsigned long flags;
+
+ spin_lock_irqsave(ap->lock, flags);
+ action = ata_scsi_requeue_deferred_qc(ap, scmd);
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ return action;
+}
+EXPORT_SYMBOL_GPL(ata_scsi_retry_deferred_qc);
+
+enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd)
+{
+ struct ata_port *ap = ata_shost_to_port(scmd->device->host);
+
+ /*
+ * ata_scsi_cmd_error_handler() takes care of commands that timed out
+ * while executing. However, if we have deferred QCs while a timeout
+ * triggers, we must requeue these commands for retry so that we do not
+ * unnecessarily delay starting the SCSI EH task until these deferred
+ * commands also time out.
+ */
+ return ata_scsi_retry_deferred_qc(ap, scmd);
+}
+EXPORT_SYMBOL_GPL(ata_scsi_eh_timed_out);
+
static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_link *link = qc->dev->link;
@@ -2911,6 +2994,7 @@ static void atapi_fixup_inquiry(struct scsi_cmnd *cmd)
static void atapi_qc_complete(struct ata_queued_cmd *qc)
{
+ struct ata_link *link = qc->dev->link;
struct scsi_cmnd *cmd = qc->scsicmd;
unsigned int err_mask = qc->err_mask;
@@ -2936,8 +3020,16 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev)
qc->dev->sdev->locked = 0;
- ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION);
- return;
+ if (cmd->result)
+ ata_scsi_qc_done(qc, false, 0);
+ else
+ ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION);
+ goto schedule_deferred;
+ }
+
+ if (cmd->result) {
+ ata_scsi_qc_done(qc, false, 0);
+ goto schedule_deferred;
}
/* successful completion path */
@@ -2945,6 +3037,9 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc)
atapi_fixup_inquiry(cmd);
ata_scsi_qc_done(qc, true, SAM_STAT_GOOD);
+
+schedule_deferred:
+ ata_scsi_schedule_deferred_qc(link);
}
/**
* atapi_xlat - Initialize PACKET taskfile
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 700627596ce1..8dacc1daabf0 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -180,7 +180,8 @@ enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd,
struct ata_port *ap)
__must_hold(ap->lock);
void ata_scsi_deferred_qc_work(struct work_struct *work);
-void ata_scsi_requeue_deferred_qc(struct ata_port *ap);
+enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap,
+ struct scsi_cmnd *scmd);
/* libata-eh.c */
extern unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd);
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 41647a56a9f4..365454390d7e 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -4027,7 +4027,7 @@ static int mv_platform_probe(struct platform_device *pdev)
/*
* Simple resource validation ..
*/
- if (unlikely(pdev->num_resources != 1)) {
+ if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) {
dev_err(&pdev->dev, "invalid number of resources\n");
return -EINVAL;
}
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 4ca6ec738c93..2a22f9dc1f2f 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -4765,6 +4765,15 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
ub->dev_info.dev_id = ub->ub_number;
/*
+ * ->state and ->ublksrv_pid are owned by the driver and only read back
+ * by userspace, but they come from the copied-in dev_info, so reset
+ * them. Otherwise a device added with ->state != DEAD looks live while
+ * ->ub_disk is still NULL.
+ */
+ ub->dev_info.state = UBLK_S_DEV_DEAD;
+ ub->dev_info.ublksrv_pid = -1;
+
+ /*
* 64bit flags will be copied back to userspace as feature
* negotiation result, so have to clear flags which driver
* doesn't support yet, then userspace can get correct flags
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index a74a4cf99d22..3a6b4b224a66 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -1031,7 +1031,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
return -EINVAL;
}
- if (lowest_nonlinear_freq <= min_freq || lowest_nonlinear_freq > nominal_freq) {
+ if (lowest_nonlinear_freq < min_freq || lowest_nonlinear_freq > nominal_freq) {
pr_err("lowest_nonlinear_freq(%d) value is out of range [min_freq(%d), nominal_freq(%d)]\n",
lowest_nonlinear_freq, min_freq, nominal_freq);
return -EINVAL;
@@ -2167,6 +2167,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = {
};
/*
+ * Processors without frequency scaling support can't do CPPC.
* CPPC function is not supported for family ID 17H with model_ID ranging from 0x10 to 0x2F.
* show the debug message that helps to check if the CPU has CPPC support for loading issue.
*/
@@ -2175,6 +2176,11 @@ static bool amd_cppc_supported(void)
struct cpuinfo_x86 *c = &cpu_data(0);
bool warn = false;
+ if (!cpu_feature_enabled(X86_FEATURE_HW_PSTATE)) {
+ pr_debug_once("frequency scaling is not supported by the processor\n");
+ return false;
+ }
+
if ((boot_cpu_data.x86 == 0x17) && (boot_cpu_data.x86_model < 0x30)) {
pr_debug_once("CPPC feature is not supported by the processor\n");
return false;
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index f6cea0c54dd9..6fe0e972952a 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void)
}
#endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */
-static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data,
- struct cpufreq_policy *policy)
+static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data,
+ struct cpufreq_policy *policy,
+ u32 *min_perf, u32 *max_perf)
{
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
- u32 min_perf, max_perf;
+ unsigned int min_freq, max_freq;
+ u32 min, max;
+
+ min_freq = READ_ONCE(policy->min);
+ max_freq = READ_ONCE(policy->max);
+ if (unlikely(min_freq > max_freq))
+ min_freq = max_freq;
+
+ min = cppc_khz_to_perf(caps, min_freq);
+ max = cppc_khz_to_perf(caps, max_freq);
- min_perf = cppc_khz_to_perf(caps, policy->min);
- max_perf = cppc_khz_to_perf(caps, policy->max);
+ *min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf);
+ *max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf);
+}
- cpu_data->perf_ctrls.min_perf =
- clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf);
- cpu_data->perf_ctrls.max_perf =
- clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf);
+static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data,
+ struct cpufreq_policy *policy)
+{
+ cppc_cpufreq_get_perf_limits(cpu_data, policy,
+ &cpu_data->perf_ctrls.min_perf,
+ &cpu_data->perf_ctrls.max_perf);
}
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
@@ -693,7 +706,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
goto out;
}
- policy->fast_switch_possible = cppc_allow_fast_switch();
+ policy->fast_switch_possible = cppc_allow_fast_switch(policy->cpus);
policy->dvfs_possible_from_any_cpu = true;
/*
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index 2b791f1ec51b..909a3f0598a9 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -1084,6 +1084,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
err_out_exit_acpi:
powernow_k8_cpu_exit_acpi(data);
+ kfree(data->powernow_table);
err_out:
kfree(data);
diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c
index 0366c7cf3502..82b07cf942ef 100644
--- a/drivers/dma/idxd/cdev.c
+++ b/drivers/dma/idxd/cdev.c
@@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
fdev->parent = cdev_dev(idxd_cdev);
fdev->bus = &dsa_bus_type;
fdev->type = &idxd_cdev_file_type;
+ idxd_wq_get(wq);
rc = dev_set_name(fdev, "file%d", ctx->id);
if (rc < 0) {
@@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
goto failed_dev_add;
}
- idxd_wq_get(wq);
mutex_unlock(&wq->wq_lock);
return 0;
failed_dev_add:
failed_dev_name:
+ mutex_unlock(&wq->wq_lock);
put_device(fdev);
+ return rc;
failed_ida:
failed_set_pasid:
if (device_user_pasid_enabled(idxd))
diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
index f1cfc7790d95..4b827a329756 100644
--- a/drivers/dma/idxd/init.c
+++ b/drivers/dma/idxd/init.c
@@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd)
static void idxd_clean_wqs(struct idxd_device *idxd)
{
- struct idxd_wq *wq;
struct device *conf_dev;
int i;
for (i = 0; i < idxd->max_wqs; i++) {
- wq = idxd->wqs[i];
- if (idxd->hw.wq_cap.op_config)
- bitmap_free(wq->opcap_bmap);
- kfree(wq->wqcfg);
- conf_dev = wq_confdev(wq);
+ conf_dev = wq_confdev(idxd->wqs[i]);
put_device(conf_dev);
- kfree(wq);
}
bitmap_free(idxd->wq_enable_map);
kfree(idxd->wqs);
@@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
if (rc < 0) {
put_device(conf_dev);
- kfree(wq);
goto err_unwind;
}
@@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
if (!wq->wqcfg) {
put_device(conf_dev);
- kfree(wq);
rc = -ENOMEM;
goto err_unwind;
}
@@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
if (idxd->hw.wq_cap.op_config) {
wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
if (!wq->opcap_bmap) {
- kfree(wq->wqcfg);
put_device(conf_dev);
- kfree(wq);
rc = -ENOMEM;
goto err_unwind;
}
@@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
err_unwind:
while (--i >= 0) {
- wq = idxd->wqs[i];
- if (idxd->hw.wq_cap.op_config)
- bitmap_free(wq->opcap_bmap);
- kfree(wq->wqcfg);
- conf_dev = wq_confdev(wq);
+ conf_dev = wq_confdev(idxd->wqs[i]);
put_device(conf_dev);
- kfree(wq);
}
bitmap_free(idxd->wq_enable_map);
@@ -270,15 +255,12 @@ err_free_wqs:
static void idxd_clean_engines(struct idxd_device *idxd)
{
- struct idxd_engine *engine;
struct device *conf_dev;
int i;
for (i = 0; i < idxd->max_engines; i++) {
- engine = idxd->engines[i];
- conf_dev = engine_confdev(engine);
+ conf_dev = engine_confdev(idxd->engines[i]);
put_device(conf_dev);
- kfree(engine);
}
kfree(idxd->engines);
}
@@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd)
rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id);
if (rc < 0) {
put_device(conf_dev);
- kfree(engine);
goto err;
}
@@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd)
err:
while (--i >= 0) {
- engine = idxd->engines[i];
- conf_dev = engine_confdev(engine);
+ conf_dev = engine_confdev(idxd->engines[i]);
put_device(conf_dev);
- kfree(engine);
}
kfree(idxd->engines);
@@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd)
static void idxd_clean_groups(struct idxd_device *idxd)
{
- struct idxd_group *group;
int i;
for (i = 0; i < idxd->max_groups; i++) {
- group = idxd->groups[i];
- put_device(group_confdev(group));
- kfree(group);
+ put_device(group_confdev(idxd->groups[i]));
}
kfree(idxd->groups);
}
@@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id);
if (rc < 0) {
put_device(conf_dev);
- kfree(group);
goto err;
}
@@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
while (--i >= 0) {
group = idxd->groups[i];
put_device(group_confdev(group));
- kfree(group);
}
kfree(idxd->groups);
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index a9a254dbf8cb..f47a326dd7ff 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -945,16 +945,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave(&vchan->vc.lock, flags);
- if (vchan->cyclic) {
- vchan->cyclic = false;
- if (pchan && pchan->desc) {
- struct virt_dma_desc *vd = &pchan->desc->vd;
- struct virt_dma_chan *vc = &vchan->vc;
+ if (pchan && pchan->desc && pchan->desc != pchan->done) {
+ struct virt_dma_desc *vd = &pchan->desc->vd;
- list_add_tail(&vd->node, &vc->desc_completed);
- }
+ vchan_terminate_vdesc(vd);
}
+ vchan->cyclic = false;
vchan_get_all_descriptors(&vchan->vc, &head);
if (pchan) {
diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c
index 3ef928640615..71d9868ce613 100644
--- a/drivers/dma/switchtec_dma.c
+++ b/drivers/dma/switchtec_dma.c
@@ -1099,7 +1099,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev,
dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len);
thresh = se_buf_len / 2;
- valid_en_se |= FIELD_GET(SE_THRESH_MASK, thresh);
+ valid_en_se |= FIELD_PREP(SE_THRESH_MASK, thresh);
writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se);
/* request irqs */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c
index 6a364357522b..b0299d861903 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c
@@ -92,7 +92,7 @@ struct amdgpu_amdkfd_fence *to_amdgpu_amdkfd_fence(struct dma_fence *f)
return NULL;
fence = container_of(f, struct amdgpu_amdkfd_fence, base);
- if (f->ops == &amdkfd_fence_ops)
+ if (rcu_access_pointer(f->ops) == &amdkfd_fence_ops)
return fence;
return NULL;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 35fe2c974699..f0f516a79424 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -3096,7 +3096,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu *
process_info->eviction_fence = new_fence;
replace_eviction_fence(ef, dma_fence_get(&new_fence->base));
} else {
- WARN_ONCE(*ef != &process_info->eviction_fence->base,
+ WARN_ONCE(rcu_access_pointer(*ef) != &process_info->eviction_fence->base,
"KFD eviction fence doesn't match KGD process_info");
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index e714cee2997a..5445f75741b5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -1177,19 +1177,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo);
}
- if (adev->debug_vm) {
- /* Invalidate all BOs to test for userspace bugs */
- amdgpu_bo_list_for_each_entry(e, p->bo_list) {
- struct amdgpu_bo *bo = e->bo;
-
- /* ignore duplicates */
- if (!bo)
- continue;
-
- amdgpu_vm_bo_invalidate(bo, false);
- }
- }
-
return 0;
}
@@ -1378,6 +1365,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
/* Cleanup the parser structure */
static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
{
+ struct amdgpu_device *adev = parser->adev;
+ struct amdgpu_bo_list_entry *e;
unsigned int i;
amdgpu_sync_free(&parser->sync);
@@ -1393,8 +1382,21 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
if (parser->ctx)
amdgpu_ctx_put(parser->ctx);
- if (parser->bo_list)
+ if (parser->bo_list) {
+ if (adev->debug_vm) {
+ /* Invalidate all BOs to test for userspace bugs */
+ amdgpu_bo_list_for_each_entry(e, parser->bo_list) {
+ struct amdgpu_bo *bo = e->bo;
+
+ /* ignore duplicates */
+ if (!bo)
+ continue;
+
+ amdgpu_vm_bo_invalidate(bo, false);
+ }
+ }
amdgpu_bo_list_put(parser->bo_list);
+ }
for (i = 0; i < parser->nchunks; i++)
kvfree(parser->chunks[i].kdata);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
index 389bad724273..df2588642817 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -43,6 +43,11 @@
#if defined(CONFIG_DEBUG_FS)
+/* Encode milliwatts in the raw Q24.8 sensor report format used by UMR. */
+#define AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(power_mw) \
+ DIV_ROUND_CLOSEST_ULL((u64)(power_mw) * BIT(8), \
+ MILLIWATT_PER_WATT)
+
/**
* amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes
*
@@ -1104,6 +1109,10 @@ static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf,
return r;
}
+ if (idx == AMDGPU_PP_SENSOR_GPU_AVG_POWER ||
+ idx == AMDGPU_PP_SENSOR_GPU_INPUT_POWER)
+ values[0] = AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(values[0]);
+
if (size > valuesize) {
amdgpu_virt_disable_access_debugfs(adev);
return -EINVAL;
@@ -1319,8 +1328,8 @@ err:
* @size: Number of bytes to read
* @pos: Offset to seek to
*
- * Read the last residency value logged. It doesn't auto update, one needs to
- * stop logging before getting the current value.
+ * Read a live GFXOFF residency sample from firmware. One needs to start logging
+ * before getting the current value.
*/
static ssize_t amdgpu_debugfs_gfxoff_residency_read(struct file *f, char __user *buf,
size_t size, loff_t *pos)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index 2860f12915c0..5e83edbd313b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -2304,6 +2304,7 @@ static int amdgpu_discovery_set_psp_ip_blocks(struct amdgpu_device *adev)
amdgpu_device_ip_block_add(adev, &psp_v14_0_ip_block);
break;
case IP_VERSION(15, 0, 0):
+ case IP_VERSION(15, 0, 5):
case IP_VERSION(15, 0, 9):
amdgpu_device_ip_block_add(adev, &psp_v15_0_ip_block);
break;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 4c0c77eafbd1..1aed121f4ddb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2908,6 +2908,19 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
return 0;
}
+static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev)
+{
+ enum amd_dpm_forced_level level;
+ uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
+ AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
+
+ level = amdgpu_dpm_get_performance_level(adev);
+ if (level & profile_mode_mask)
+ amdgpu_asic_update_umd_stable_pstate(adev, true);
+}
+
static int amdgpu_pmops_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2952,6 +2965,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
+
+ amdgpu_restore_umd_profile_pstate_after_runpm(adev);
adev->in_runpm = false;
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 71272f40feef..8bb9c198ddec 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -1210,7 +1210,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
}
}
- ui32 >>= 8;
+ ui32 /= MILLIWATT_PER_WATT;
break;
case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER:
/* get input GPU power */
@@ -1219,7 +1219,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
(void *)&ui32, &ui32_size)) {
return -EINVAL;
}
- ui32 >>= 8;
+ ui32 /= MILLIWATT_PER_WATT;
break;
case AMDGPU_INFO_SENSOR_VDDNB:
/* get VDDNB in millivolts */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index e0c0d7872e45..7139cb3b2db5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@ -275,6 +275,7 @@ static int psp_early_init(struct amdgpu_ip_block *ip_block)
psp->boot_time_tmr = false;
break;
case IP_VERSION(15, 0, 0):
+ case IP_VERSION(15, 0, 5):
case IP_VERSION(15, 0, 9):
psp_v15_0_0_set_psp_funcs(psp);
psp->boot_time_tmr = false;
@@ -1222,6 +1223,33 @@ int psp_memory_partition(struct psp_context *psp, int mode)
return ret;
}
+int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev)
+{
+ int ret;
+ struct psp_context *psp = &adev->psp;
+ struct psp_gfx_cmd_resp *cmd = acquire_psp_cmd_buf(psp);
+
+ cmd->cmd_id = GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL;
+
+ ret = psp_cmd_submit_buf(psp, NULL, cmd, psp->fence_buf_mc_addr);
+ if (ret) {
+ dev_err(psp->adev->dev,
+ "PSP request failed to set mmuhub eco sec level with ret=%d\n", ret);
+ release_psp_cmd_buf(psp);
+ return ret;
+ }
+
+ if (cmd->resp.status) {
+ dev_err(psp->adev->dev,
+ "MMHUB ECO SEC LEVEL command 0x%x failed, PSP response status: 0x%X\n",
+ cmd->cmd_id, cmd->resp.status);
+ ret = -EIO;
+ }
+ release_psp_cmd_buf(psp);
+
+ return ret;
+}
+
static int psp_ptl_fmt_verify(struct psp_context *psp, enum amdgpu_ptl_fmt fmt,
uint32_t *ptl_fmt)
{
@@ -3476,8 +3504,10 @@ static int psp_load_non_psp_fw(struct psp_context *psp)
amdgpu_ip_version(adev, MP0_HWIP, 0) ==
IP_VERSION(15, 0, 0) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) ==
- IP_VERSION(15, 0, 8) ||
+ IP_VERSION(15, 0, 5) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) ==
+ IP_VERSION(15, 0, 8) ||
+ amdgpu_ip_version(adev, MP0_HWIP, 0) ==
IP_VERSION(15, 0, 9)) &&
(ucode->ucode_id == AMDGPU_UCODE_ID_SDMA1 ||
ucode->ucode_id == AMDGPU_UCODE_ID_SDMA2 ||
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h
index 2d838b1b2b11..3d7744d60fac 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h
@@ -656,5 +656,6 @@ int amdgpu_psp_reg_program_no_ring(struct psp_context *psp, uint32_t val,
void amdgpu_psp_debugfs_init(struct amdgpu_device *adev);
int amdgpu_psp_get_fw_type(struct amdgpu_firmware_info *ucode,
enum psp_gfx_fw_type *type);
+int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index eb8bbfc7e6d9..5fc91f45db5b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -2173,6 +2173,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
gtt_size = configured_size;
}
+ /* Cap GTT so that it does not exceed total physical RAM. */
+ if (adev->flags & AMD_IS_APU) {
+ u64 phys_ram = (u64)totalram_pages() << PAGE_SHIFT;
+
+ if (gtt_size > phys_ram) {
+ gtt_size = phys_ram;
+ dev_info(adev->dev,
+ "Capping GTT to %uM to not exceed available system memory\n",
+ (unsigned int)(gtt_size / (1024 * 1024)));
+ }
+ }
+
/* Initialize GTT memory pool */
r = amdgpu_gtt_mgr_init(adev, gtt_size);
if (r) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 572f2949cb64..3efe1ae27386 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -965,6 +965,7 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
struct amdgpu_vm *vm = &fpriv->vm;
unsigned long key, tmp_key;
struct amdgpu_bo_va *bo_va;
+ struct amdgpu_usermode_queue *queue;
struct amdgpu_bo *bo;
struct drm_exec exec;
struct xarray xa;
@@ -1080,6 +1081,24 @@ retry_lock:
dma_fence_wait(bo_va->last_pt_update, false);
dma_fence_wait(vm->last_update, false);
+ xa_for_each(&uq_mgr->userq_xa, tmp_key, queue) {
+ bo = queue->wptr_obj.obj;
+ if (!bo) {
+ ret = -EINVAL;
+ goto unlock_all;
+ }
+
+ ret = amdgpu_ttm_alloc_gart(&bo->tbo);
+ if (unlikely(ret)) {
+ drm_file_err(uq_mgr->file,
+ "failed to bind wptr bo to gart on resume, qid=%lu ret=%d\n",
+ tmp_key, ret);
+ goto unlock_all;
+ }
+
+ queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(bo);
+ }
+
ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec);
if (ret)
drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n");
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index f224d33c4bc4..dc6a9d7dd0b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -1553,6 +1553,8 @@ int amdgpu_vm_clear_freed(struct amdgpu_device *adev,
struct amdgpu_sync sync;
int r;
+ if (list_empty(&vm->freed))
+ return 0;
/*
* Implicitly sync to command submissions in the same VM before
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h
index d80f01c0e754..b02561f41b58 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h
@@ -379,7 +379,7 @@ struct amd_sriov_msg_vf2pf_info {
struct {
uint8_t id;
uint32_t version;
- } ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE];
+ } __packed ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE];
uint64_t dummy_page_addr;
/* FB allocated for guest MES to record UQ info */
uint64_t mes_info_addr;
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
index e7546816baba..2c3e6d9f52a6 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c
@@ -399,6 +399,25 @@ static void jpeg_v5_3_0_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx)
}
/**
+ * jpeg_v5_3_0_set_mmhub_eco_sec_level - set jpeg sec lvl reg
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * request psp to set secure lvl
+ */
+static int jpeg_v5_3_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev)
+{
+ int r = 0;
+
+ if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+ /* Request to PSP to program JPEG secure lvl */
+ r = psp_set_mmhub_eco_sec_level(adev);
+ }
+
+ return r;
+}
+
+/**
* jpeg_v5_3_0_start - start JPEG block
*
* @adev: amdgpu_device pointer
@@ -423,6 +442,11 @@ static int jpeg_v5_3_0_start(struct amdgpu_device *adev)
if (r)
return r;
+ /* program JPEG secure lvl register */
+ r = jpeg_v5_3_0_set_mmhub_eco_sec_level(adev);
+ if (r)
+ return r;
+
/* JPEG disable CGC */
jpeg_v5_3_0_disable_clock_gating(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index 16625c31bfd3..4e44a581a78a 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -70,27 +70,23 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev,
ret = -EINVAL;
goto fail_map;
}
-
- /* TODO use eviction fence instead of pinning. */
- ret = amdgpu_bo_pin(wptr_obj->obj, AMDGPU_GEM_DOMAIN_GTT);
+ /* Keep WPTR BO under eviction-fence control instead of pinning. */
+ ret = amdgpu_evf_mgr_attach_fence(&uq_mgr_to_fpriv(uq_mgr)->evf_mgr, wptr_obj->obj);
if (ret) {
- DRM_ERROR("Failed to pin wptr bo. ret %d\n", ret);
+ DRM_ERROR("Failed to attach eviction fence to wptr bo. ret %d\n", ret);
goto fail_map;
}
ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo);
if (ret) {
- DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret);
- goto fail_alloc_gart;
+ DRM_ERROR("Failed to bind wptr bo to GART. ret %d\n", ret);
+ goto fail_map;
}
queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj);
drm_exec_fini(&exec);
return 0;
-
-fail_alloc_gart:
- amdgpu_bo_unpin(wptr_obj->obj);
fail_map:
amdgpu_bo_unref(&wptr_obj->obj);
fail_lock:
@@ -468,9 +464,6 @@ static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue)
amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr,
&queue->mqd.cpu_ptr);
- amdgpu_bo_reserve(queue->wptr_obj.obj, true);
- amdgpu_bo_unpin(queue->wptr_obj.obj);
- amdgpu_bo_unreserve(queue->wptr_obj.obj);
amdgpu_bo_unref(&queue->wptr_obj.obj);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c
index c78f0598637f..000516b5845a 100644
--- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c
@@ -30,70 +30,76 @@
#include "ivsrcid/nbio/irqsrcs_nbif_7_4.h"
#include <uapi/linux/kfd_ioctl.h>
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6
-#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3
-#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021
-#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2
-
-#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13
-#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5
-#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14
-#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5
-#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15
-#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5
-
-#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x8e4d
-#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5
-#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x8e4e
-#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5
-
-#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0xd000
-#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 5
-
-#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5 0x8e20
-#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5_BASE_IDX 5
-
-#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5 0x8e11
-#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 5
-#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5 0x8e12
-#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 5
-
-#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x8e26
-#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 5
-#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x8e27
-#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 5
-
-#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x8e17
-#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6
+#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3
+#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021
+#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2
+
+#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13
+#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5
+#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14
+#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5
+#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15
+#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5
+
+#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x012d
+#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2
+#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x012e
+#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2
+
+#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0x0021
+#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 2
+
+#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5 0x0100
+#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5_BASE_IDX 2
+
+#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5 0x00f1
+#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 2
+#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5 0x00f2
+#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 2
+
+#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x0106
+#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 2
+#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x0107
+#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 2
+
+#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x00f7
+#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2
+
+//BIF_BX1_BIF_FB_EN
+#define BIF_BX1_BIF_FB_EN__FB_READ_EN__SHIFT_nbio_7_11_5 0x0
+#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN__SHIFT_nbio_7_11_5 0x1
+#define BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 0x00000001L
+#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5 0x00000002L
static void nbif_v6_3_1_remap_hdp_registers(struct amdgpu_device *adev)
{
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) {
- WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5,
+ WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5,
adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_MEM_FLUSH_CNTL);
- WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5,
+ WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5,
adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_REG_FLUSH_CNTL);
} else {
WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL,
@@ -110,7 +116,7 @@ static u32 nbif_v6_3_1_get_rev_id(struct amdgpu_device *adev)
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 4))
tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10);
else if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5))
- tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5);
+ tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5);
else
tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0);
@@ -124,11 +130,11 @@ static void nbif_v6_3_1_mc_access_enable(struct amdgpu_device *adev, bool enable
{
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) {
if (enable)
- WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5,
- BIF_BX0_BIF_FB_EN__FB_READ_EN_MASK |
- BIF_BX0_BIF_FB_EN__FB_WRITE_EN_MASK);
+ WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5,
+ BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 |
+ BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5);
else
- WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, 0);
+ WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, 0);
} else {
if (enable)
WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN,
@@ -383,12 +389,12 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev)
/* setup interrupt control */
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5))
- WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5,
+ WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5,
adev->dummy_page_addr >> 8);
else
WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2, adev->dummy_page_addr >> 8);
- interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5);
+ interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5);
/*
* BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
* BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
@@ -401,7 +407,7 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev)
IH_REQ_NONSNOOP_EN, 0);
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5))
- WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl);
+ WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl);
else
WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL, interrupt_cntl);
}
@@ -427,7 +433,7 @@ nbif_v6_3_1_get_clockgating_state(struct amdgpu_device *adev,
static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev)
{
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5))
- return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5);
+ return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5);
else
return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ);
}
@@ -435,7 +441,7 @@ static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev)
static u32 nbif_v6_3_1_get_hdp_flush_done_offset(struct amdgpu_device *adev)
{
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5))
- return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5);
+ return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5);
else
return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE);
}
@@ -622,7 +628,7 @@ static void nbif_v6_3_1_set_reg_remap(struct amdgpu_device *adev)
} else {
if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5))
adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0,
- regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2;
+ regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2;
else
adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0,
regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2;
diff --git a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h
index ac34bac3c839..ad618b78685f 100644
--- a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h
+++ b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h
@@ -110,6 +110,7 @@ enum psp_gfx_cmd_id
GFX_CMD_ID_PERF_HW = 0x0000004C, /* performance monitor */
GFX_CMD_ID_FB_FW_RESERV_ADDR = 0x00000050, /* Query FW reservation addr */
GFX_CMD_ID_FB_FW_RESERV_EXT_ADDR = 0x00000051, /* Query FW reservation extended addr */
+ GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL = 0x0000005D, /* Set MMHUB ECO sec lvls on VCN block */
};
/* PSP boot config sub-commands */
diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c
index 2a4d91368ac6..abaa40b6def9 100644
--- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c
@@ -33,9 +33,22 @@
MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin");
MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin");
+MODULE_FIRMWARE("amdgpu/psp_15_0_5_toc.bin");
+MODULE_FIRMWARE("amdgpu/psp_15_0_5_ta.bin");
MODULE_FIRMWARE("amdgpu/psp_15_0_9_toc.bin");
MODULE_FIRMWARE("amdgpu/psp_15_0_9_ta.bin");
+#define regMPASP_PCRU0_MPASP_C2PMSG_64 0x4280
+#define regMPASP_PCRU0_MPASP_C2PMSG_64_BASE_IDX 2
+#define regMPASP_PCRU0_MPASP_C2PMSG_67 0x4283
+#define regMPASP_PCRU0_MPASP_C2PMSG_67_BASE_IDX 2
+#define regMPASP_PCRU0_MPASP_C2PMSG_69 0x4285
+#define regMPASP_PCRU0_MPASP_C2PMSG_69_BASE_IDX 2
+#define regMPASP_PCRU0_MPASP_C2PMSG_70 0x4286
+#define regMPASP_PCRU0_MPASP_C2PMSG_70_BASE_IDX 2
+#define regMPASP_PCRU0_MPASP_C2PMSG_71 0x4287
+#define regMPASP_PCRU0_MPASP_C2PMSG_71_BASE_IDX 2
+
static int psp_v15_0_0_init_microcode(struct psp_context *psp)
{
struct amdgpu_device *adev = psp->adev;
@@ -71,14 +84,25 @@ static int psp_v15_0_0_ring_stop(struct psp_context *psp,
ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_SMN_C2PMSG_101),
0x80000000, 0x80000000, false);
} else {
- /* Write the ring destroy command*/
- WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64,
- GFX_CTRL_CMD_ID_DESTROY_RINGS);
- /* there might be handshake issue with hardware which needs delay */
- mdelay(20);
- /* Wait for response flag (bit 31) */
- ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64),
- 0x80000000, 0x80000000, false);
+ if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) {
+ /* Write the ring destroy command*/
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64,
+ GFX_CTRL_CMD_ID_DESTROY_RINGS);
+ /* there might be handshake issue with hardware which needs delay */
+ mdelay(20);
+ /* Wait for response flag (bit 31) */
+ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64),
+ 0x80000000, 0x80000000, false);
+ } else {
+ /* Write the ring destroy command*/
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64,
+ GFX_CTRL_CMD_ID_DESTROY_RINGS);
+ /* there might be handshake issue with hardware which needs delay */
+ mdelay(20);
+ /* Wait for response flag (bit 31) */
+ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64),
+ 0x80000000, 0x80000000, false);
+ }
}
return ret;
@@ -118,9 +142,10 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp,
0x80000000, 0x8000FFFF, false);
} else {
- /* Wait for sOS ready for ring creation */
- ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64),
- 0x80000000, 0x80000000, false);
+ if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) {
+ /* Wait for sOS ready for ring creation */
+ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64),
+ 0x80000000, 0x80000000, false);
if (ret) {
DRM_ERROR("Failed to wait for trust OS ready for ring creation\n");
return ret;
@@ -128,6 +153,35 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp,
/* Write low address of the ring to C2PMSG_69 */
psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr);
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_69, psp_ring_reg);
+ /* Write high address of the ring to C2PMSG_70 */
+ psp_ring_reg = upper_32_bits(ring->ring_mem_mc_addr);
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_70, psp_ring_reg);
+ /* Write size of ring to C2PMSG_71 */
+ psp_ring_reg = ring->ring_size;
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_71, psp_ring_reg);
+ /* Write the ring initialization command to C2PMSG_64 */
+ psp_ring_reg = ring_type;
+ psp_ring_reg = psp_ring_reg << 16;
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, psp_ring_reg);
+
+ /* there might be handshake issue with hardware which needs delay */
+ mdelay(20);
+
+ /* Wait for response flag (bit 31) in C2PMSG_64 */
+ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64),
+ 0x80000000, 0x8000FFFF, false);
+ } else {
+ /* Wait for sOS ready for ring creation */
+ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64),
+ 0x80000000, 0x80000000, false);
+ if (ret) {
+ DRM_ERROR("Failed to wait for trust OS ready for ring creation\n");
+ return ret;
+ }
+
+ /* Write low address of the ring to C2PMSG_69 */
+ psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr);
WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_69, psp_ring_reg);
/* Write high address of the ring to C2PMSG_70 */
psp_ring_reg = upper_32_bits(ring->ring_mem_mc_addr);
@@ -146,6 +200,7 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp,
/* Wait for response flag (bit 31) in C2PMSG_64 */
ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64),
0x80000000, 0x8000FFFF, false);
+ }
}
return ret;
@@ -176,8 +231,12 @@ static uint32_t psp_v15_0_0_ring_get_wptr(struct psp_context *psp)
if (amdgpu_sriov_vf(adev))
data = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102);
- else
- data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67);
+ else {
+ if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5))
+ data = RREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67);
+ else
+ data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67);
+ }
return data;
}
@@ -190,8 +249,12 @@ static void psp_v15_0_0_ring_set_wptr(struct psp_context *psp, uint32_t value)
WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102, value);
WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_101,
GFX_CTRL_CMD_ID_CONSUME_CMD);
- } else
- WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value);
+ } else {
+ if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5))
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67, value);
+ else
+ WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value);
+ }
}
static const struct psp_funcs psp_v15_0_0_funcs = {
diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c
index e0b80abcd075..e9d51e992a8c 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc21.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc21.c
@@ -406,6 +406,7 @@ soc21_asic_reset_method(struct amdgpu_device *adev)
case IP_VERSION(14, 0, 4):
case IP_VERSION(14, 0, 5):
case IP_VERSION(15, 0, 0):
+ case IP_VERSION(15, 0, 5):
case IP_VERSION(15, 0, 9):
return AMD_RESET_METHOD_MODE2;
default:
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
index 45580e9c4e0c..886e2c2074a9 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c
@@ -796,6 +796,27 @@ static int vcn_v5_0_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst,
}
/**
+ * vcn_v5_0_0_set_mmhub_eco_sec_level - set vcn sec lvl reg
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * request psp to set sec lvl
+ */
+static int vcn_v5_0_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev)
+{
+ int r = 0;
+
+ if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(5, 3, 0)) {
+ if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
+ /* Request to PSP to program VCN secure lvl */
+ r = psp_set_mmhub_eco_sec_level(adev);
+ }
+ }
+
+ return r;
+}
+
+/**
* vcn_v5_0_0_start - VCN start
*
* @vinst: VCN instance
@@ -819,6 +840,11 @@ static int vcn_v5_0_0_start(struct amdgpu_vcn_inst *vinst)
fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr;
+ /* program VCN secure lvl register */
+ r = vcn_v5_0_0_set_mmhub_eco_sec_level(adev);
+ if (r)
+ return r;
+
if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG)
return vcn_v5_0_0_start_dpg_mode(vinst, adev->vcn.inst[i].indirect_sram);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index c7edebd2fd8a..e1689b3d2add 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -3109,10 +3109,14 @@ static int kfd_ioctl_set_debug_trap(struct file *filep, struct kfd_process *p, v
goto out;
}
- /* Check if target is still PTRACED. */
+ /*
+ * Verify debugger has permission to debug target process.
+ * For cross-process debugging, require active ptrace relationship.
+ * This applies to ALL operations to prevent unauthorized interference.
+ */
rcu_read_lock();
- if (target != p && args->op != KFD_IOC_DBG_TRAP_DISABLE
- && ptrace_parent(target->lead_thread) != current) {
+ if (target != p && ptrace_parent(target->lead_thread) != current
+ && target->debugger_process != p) {
pr_err("PID %i is not PTRACED and cannot be debugged\n", args->pid);
r = -EPERM;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
index 2a239f45fc24..6e0df685503d 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
@@ -1412,6 +1412,15 @@ int kfd_parse_crat_table(void *crat_image, struct list_head *device_list,
break;
}
+ /* Validate subtype fits within remaining image */
+ if ((char *)sub_type_hdr + sub_type_hdr->length >
+ (char *)crat_image + image_len) {
+ pr_warn("CRAT subtype length %u exceeds image bounds\n",
+ sub_type_hdr->length);
+ ret = -EINVAL;
+ break;
+ }
+
if (sub_type_hdr->flags & CRAT_SUBTYPE_FLAGS_ENABLED) {
ret = kfd_parse_subtype(sub_type_hdr, device_list);
if (ret)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index 2e97da597b3d..f705c61bdb1d 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -524,6 +524,9 @@ int kfd_criu_restore_event(struct file *devkfd,
ret = create_other_event(p, ev, &ev_priv->event_id);
break;
+ default:
+ ret = -EINVAL;
+ break;
}
mutex_unlock(&p->event_mutex);
@@ -545,15 +548,27 @@ int kfd_criu_checkpoint_events(struct kfd_process *p,
int ret = 0;
struct kfd_event *ev;
uint32_t ev_id;
+ uint32_t num_events;
- uint32_t num_events = kfd_get_num_events(p);
+ /* Serialize the count and the walk below against concurrent event
+ * create/destroy. Those paths take only p->event_mutex, not the
+ * p->mutex held by the CRIU checkpoint caller, so without this the
+ * event_idr can grow between kfd_get_num_events() and the loop and the
+ * walk writes past the ev_privs allocation.
+ */
+ mutex_lock(&p->event_mutex);
- if (!num_events)
+ num_events = kfd_get_num_events(p);
+ if (!num_events) {
+ mutex_unlock(&p->event_mutex);
return 0;
+ }
ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL);
- if (!ev_privs)
+ if (!ev_privs) {
+ mutex_unlock(&p->event_mutex);
return -ENOMEM;
+ }
idr_for_each_entry(&p->event_idr, ev, ev_id) {
@@ -594,6 +609,8 @@ int kfd_criu_checkpoint_events(struct kfd_process *p,
i++;
}
+ mutex_unlock(&p->event_mutex);
+
ret = copy_to_user(user_priv_data + *priv_data_offset,
ev_privs, num_events * sizeof(*ev_privs));
if (ret) {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index 9ccbc6e5b27b..503176f00204 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -383,7 +383,7 @@ int pqm_create_queue(struct process_queue_manager *pqm,
false);
if (retval) {
dev_err(dev->adev->dev, "failed to allocate process context bo\n");
- return retval;
+ goto err_allocate_pqn;
}
memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE);
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
index 98a5512b701b..b249e7d1af48 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
@@ -288,7 +288,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
}
err = kfd_queue_buffer_get(vm, (void *)properties->eop_ring_buffer_address,
&properties->eop_buf_bo,
- ALIGN(properties->eop_ring_buffer_size, PAGE_SIZE));
+ ALIGN((u64)properties->eop_ring_buffer_size, PAGE_SIZE));
if (err)
goto out_err_unreserve;
}
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 eaf19ec843e8..1820547b1dde 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -9903,25 +9903,6 @@ static void remove_stream(struct amdgpu_device *adev,
acrtc->enabled = false;
}
-static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
-{
-
- assert_spin_locked(&acrtc->base.dev->event_lock);
- WARN_ON(acrtc->event);
-
- acrtc->event = acrtc->base.state->event;
-
- /* Set the flip status */
- acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
-
- /* Mark this event as consumed */
- acrtc->base.state->event = NULL;
-
- drm_dbg_state(acrtc->base.dev,
- "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
- acrtc->crtc_id);
-}
-
static void update_freesync_state_on_stream(
struct amdgpu_display_manager *dm,
struct dm_crtc_state *new_crtc_state,
@@ -10274,17 +10255,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
return;
if (pflip_update) {
- drm_crtc_vblank_get(&acrtc->base);
WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
- /* Arm flip completion handling and event delivery after programming. */
- prepare_flip_isr(acrtc);
+ WARN_ON(acrtc->event);
+
+ acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
+ acrtc->event = acrtc->base.state->event;
+ acrtc->base.state->event = NULL;
+
+ drm_dbg_state(acrtc->base.dev,
+ "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
+ acrtc->crtc_id);
} else if (cursor_update) {
- drm_crtc_vblank_get(&acrtc->base);
acrtc->event = acrtc->base.state->event;
acrtc->base.state->event = NULL;
}
}
+/**
+ * dm_arm_vblank_event_pre_programming - Prepare for programming
+ * @acrtc: The amdgpu CRTC to prepare
+ * @acrtc_state: The new CRTC state
+ * @pflip_update: Whether a page flip is being programmed
+ * @cursor_update: Whether a cursor update is being programmed
+ *
+ * Grab a reference on the vblank counter if a page flip or cursor update is to
+ * be programmed. Do this before programming so the HW is not in any
+ * idle-optimized state (such as PSR).
+ */
+static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
+ struct dm_crtc_state *acrtc_state,
+ bool pflip_update,
+ bool cursor_update)
+{
+ assert_spin_locked(&acrtc->base.dev->event_lock);
+
+ if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
+ return;
+
+ if (pflip_update || cursor_update)
+ drm_crtc_vblank_get(&acrtc->base);
+}
+
static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
struct drm_device *dev,
struct amdgpu_display_manager *dm,
@@ -10550,16 +10561,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
}
}
- /*
- * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
- * event delivery. Only GRPH_FLIP handler can send pflip events, and it
- * only fires if HW latched to the flip. Maintain legacy behavior by
- * arming event before programming.
- */
- if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
- scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
+ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
+ dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state,
+ pflip_present,
+ cursor_update);
+ /*
+ * DCE depends on a combination of GRPH_FLIP, VLINE0, and
+ * VUPDATE for event delivery. Only GRPH_FLIP handler can send
+ * pflip events, and it only fires if HW latched to the flip.
+ * Maintain legacy behavior by arming event before programming.
+ */
+ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
dm_arm_vblank_event(acrtc_attach, acrtc_state,
- pflip_present, cursor_update);
+ pflip_present, cursor_update);
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
index ecb8493ec523..8e3862f6dac2 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c
@@ -45,9 +45,7 @@
clk_src->base.ctx
#define DC_LOGGER \
- calc_pll_cs->ctx->logger
-#define DC_LOGGER_INIT() \
- struct calc_pll_clock_source *calc_pll_cs = &clk_src->calc_pll
+ CTX->logger
#undef FN
#define FN(reg_name, field_name) \
@@ -287,6 +285,7 @@ static bool calc_pll_dividers_in_range(
}
static uint32_t calculate_pixel_clock_pll_dividers(
+ struct dce110_clk_src *clk_src,
struct calc_pll_clock_source *calc_pll_cs,
struct pll_settings *pll_settings)
{
@@ -474,7 +473,7 @@ static uint32_t dce110_get_pix_clk_dividers_helper (
{
uint32_t field = 0;
uint32_t pll_calc_error = MAX_PLL_CALC_ERROR;
- DC_LOGGER_INIT();
+
/* Check if reference clock is external (not pcie/xtalin)
* HW Dce80 spec:
* 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB
@@ -517,12 +516,14 @@ static uint32_t dce110_get_pix_clk_dividers_helper (
/*Calculate Dividers by HDMI object, no SS case or SS case */
pll_calc_error =
calculate_pixel_clock_pll_dividers(
+ clk_src,
&clk_src->calc_pll_hdmi,
pll_settings);
else
/*Calculate Dividers by default object, no SS case or SS case */
pll_calc_error =
calculate_pixel_clock_pll_dividers(
+ clk_src,
&clk_src->calc_pll,
pll_settings);
@@ -568,7 +569,6 @@ static uint32_t dce110_get_pix_clk_dividers(
{
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs);
uint32_t pll_calc_error = MAX_PLL_CALC_ERROR;
- DC_LOGGER_INIT();
if (pix_clk_params == NULL || pll_settings == NULL
|| pix_clk_params->requested_pix_clk_100hz == 0) {
@@ -600,7 +600,6 @@ static uint32_t dce112_get_pix_clk_dividers(
struct pll_settings *pll_settings)
{
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs);
- DC_LOGGER_INIT();
if (pix_clk_params == NULL || pll_settings == NULL
|| pix_clk_params->requested_pix_clk_100hz == 0) {
@@ -1442,8 +1441,6 @@ static uint32_t dcn3_get_pix_clk_dividers(
unsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz : 0;
struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs);
- DC_LOGGER_INIT();
-
if (pix_clk_params == NULL || pll_settings == NULL
|| pix_clk_params->requested_pix_clk_100hz == 0) {
DC_LOG_ERROR(
@@ -1513,7 +1510,6 @@ static const struct clock_source_funcs dce110_clk_src_funcs = {
.get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz
};
-
static void get_ss_info_from_atombios(
struct dce110_clk_src *clk_src,
enum as_signal_type as_signal,
@@ -1526,7 +1522,7 @@ static void get_ss_info_from_atombios(
struct spread_spectrum_info *ss_info_cur;
struct spread_spectrum_data *ss_data_cur;
uint32_t i;
- DC_LOGGER_INIT();
+
if (ss_entries_num == NULL) {
DC_LOG_SYNC(
"Invalid entry !!!\n");
@@ -1657,6 +1653,7 @@ static void ss_info_from_atombios_create(
}
static bool calc_pll_max_vco_construct(
+ struct dce110_clk_src *clk_src,
struct calc_pll_clock_source *calc_pll_cs,
struct calc_pll_clock_source_init_data *init_data)
{
@@ -1808,6 +1805,7 @@ bool dce110_clk_src_construct(
ss_info_from_atombios_create(clk_src);
if (!calc_pll_max_vco_construct(
+ clk_src,
&clk_src->calc_pll,
&calc_pll_cs_init_data)) {
ASSERT_CRITICAL(false);
@@ -1822,7 +1820,7 @@ bool dce110_clk_src_construct(
if (!calc_pll_max_vco_construct(
- &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) {
+ clk_src, &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) {
ASSERT_CRITICAL(false);
goto unexpected_failure;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c
index 835fece1d46a..50e445275b14 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c
@@ -359,14 +359,13 @@ void dml21_handle_phantom_streams_planes(const struct dc *dc, struct dc_state *c
main_plane = main_stream_status->plane_states[dc_plane_index];
/* create phantom planes for subvp enabled plane */
- dml21_add_phantom_plane(dml_ctx,
- dc,
- context,
- phantom_stream,
- main_plane,
- &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]);
-
- phantoms_added = true;
+ if (dml21_add_phantom_plane(dml_ctx,
+ dc,
+ context,
+ phantom_stream,
+ main_plane,
+ &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]))
+ phantoms_added = true;
}
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index f338e733318e..b4e2264ef408 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -2452,7 +2452,7 @@ static void calculate_mcache_row_bytes(
DML_ASSERT(*p->num_mcaches > 0);
}
-static void calculate_mcache_setting(
+static bool calculate_mcache_setting(
struct dml2_core_internal_scratch *scratch,
struct dml2_core_calcs_calculate_mcache_setting_params *p)
{
@@ -2478,7 +2478,7 @@ static void calculate_mcache_setting(
*p->lc_comb_mcache = 0;
if (!p->dcc_enable)
- return;
+ return true;
l->is_dual_plane = dml_is_420(p->source_format) || p->source_format == dml2_rgbe_alpha;
@@ -2515,7 +2515,14 @@ static void calculate_mcache_setting(
l->l_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_l;
calculate_mcache_row_bytes(scratch, &l->l_p);
- DML_ASSERT(*p->num_mcaches_l > 0);
+ if (*p->num_mcaches_l == 0 ||
+ (p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l) == 0) {
+ DML_LOG_VERBOSE("DML::%s: degenerate luma viewport (num_mcaches_l=%u mvmpg_%s_l=%u) — mode not supported\n",
+ __func__, *p->num_mcaches_l,
+ p->surf_vert ? "height" : "width",
+ p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l);
+ return false;
+ }
if (l->is_dual_plane) {
l->c_p.num_chans = p->num_chans;
@@ -2551,7 +2558,14 @@ static void calculate_mcache_setting(
l->c_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_c;
calculate_mcache_row_bytes(scratch, &l->c_p);
- DML_ASSERT(*p->num_mcaches_c > 0);
+ if (*p->num_mcaches_c == 0 ||
+ (p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c) == 0) {
+ DML_LOG_VERBOSE("DML::%s: degenerate chroma viewport (num_mcaches_c=%u mvmpg_%s_c=%u) — mode not supported\n",
+ __func__, *p->num_mcaches_c,
+ p->surf_vert ? "height" : "width",
+ p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c);
+ return false;
+ }
}
// Sharing for iMALL access
@@ -2661,6 +2675,7 @@ static void calculate_mcache_setting(
*p->mcache_shift_granularity_l = l->mvmpg_access_width_l;
*p->mcache_shift_granularity_c = l->mvmpg_access_width_c;
+ return true;
}
static void calculate_mall_bw_overhead_factor(
@@ -9457,7 +9472,10 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->ms.mall_comb_mcache_c[k];
calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->ms.lc_comb_mcache[k];
- calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params);
+ if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) {
+ mode_lib->ms.support.ModeSupport = false;
+ return false;
+ }
}
calculate_mall_bw_overhead_factor(
@@ -10933,7 +10951,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
calculate_mcache_setting_params->mall_comb_mcache_l = &mode_lib->mp.mall_comb_mcache_l[k];
calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->mp.mall_comb_mcache_c[k];
calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->mp.lc_comb_mcache[k];
- calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params);
+ if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params))
+ return false;
}
calculate_mall_bw_overhead_factor(
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index 042602c50e35..53fffe58602f 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@ -1337,8 +1337,27 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
{
- if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL)
+ if (pipe_ctx == NULL || pipe_ctx->stream_res.stream_enc == NULL)
+ return;
+
+ if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) {
pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable);
+
+ /* Wait for three frames to make sure AV mute is sent out.
+ * Some HDMI sinks need additional GCP packets to properly
+ * process the mute state, especially after link re-establishment
+ * with HDMI 2.0 scrambling enabled.
+ */
+ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+ int i;
+
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+ for (i = 0; i < 3; i++) {
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+ }
+ }
+ }
}
enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id)
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
index a7c85a2302ab..2d3587a31bc8 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
@@ -847,13 +847,19 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable)
pipe_ctx->stream_res.stream_enc,
enable);
- /* Wait for two frame to make sure AV mute is sent out */
+ /* Wait for three frames to make sure AV mute is sent out.
+ * Some HDMI sinks need additional GCP packets to properly
+ * process the mute state, especially after link re-establishment
+ * with HDMI 2.0 scrambling enabled.
+ */
if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+ int i;
+
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
- pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
- pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
- pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
- pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+ for (i = 0; i < 3; i++) {
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK);
+ pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+ }
}
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
index 42e7f0e9b3ce..ca5997748799 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -384,7 +384,7 @@ static bool write_i2c_retimer_vga(
for (size_t i = 0; i < ARRAY_SIZE(vga_data); i++) {
if (!write_i2c_retimer_offset_value(link, address, vga_data[i][0], vga_data[i][1])) {
- DC_LOG_ERROR("Set retimer failed, vga index: %zu\n", i);
+ DC_LOG_DEBUG("Set retimer failed, vga index: %zu\n", i);
return false;
}
}
@@ -405,7 +405,7 @@ static bool write_i2c_retimer_byte(
return true;
if (!write_i2c_retimer_offset_value(link, address, index, value)) {
- DC_LOG_ERROR("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value);
+ DC_LOG_DEBUG("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value);
return false;
}
@@ -421,14 +421,14 @@ static bool write_i2c_retimer_byte(
if (!link_query_ddc_data(
link->ddc, address, &offset, 1, &value, 1
)) {
- DC_LOG_ERROR("Set retimer failed, link_query_ddc_data\n");
+ DC_LOG_DEBUG("Set retimer failed, link_query_ddc_data\n");
return false;
}
}
value |= apply_rx_tx_change;
if (!write_i2c_retimer_offset_value(link, address, offset, value)) {
- DC_LOG_ERROR("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value);
+ DC_LOG_DEBUG("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value);
return false;
}
}
@@ -449,7 +449,7 @@ static bool write_i2c_retimer_setting(
uint8_t value = settings->reg_settings[i].i2c_reg_val;
if (!write_i2c_retimer_byte(link, address, index, value)) {
- DC_LOG_ERROR("Set retimer failed, index: %zu\n", i);
+ DC_LOG_DEBUG("Set retimer failed, index: %zu\n", i);
return false;
}
}
@@ -460,7 +460,7 @@ static bool write_i2c_retimer_setting(
uint8_t value = settings->reg_settings_6g[i].i2c_reg_val;
if (!write_i2c_retimer_byte(link, address, index, value)) {
- DC_LOG_ERROR("Set retimer failed, 6g index: %zu\n", i);
+ DC_LOG_DEBUG("Set retimer failed, 6g index: %zu\n", i);
return false;
}
}
@@ -492,7 +492,7 @@ static bool write_i2c_default_retimer_setting(
for (size_t i = 0; i < ARRAY_SIZE(data); i++) {
if (!write_i2c_retimer_offset_value(link, address, data[i][0], data[i][1])) {
- DC_LOG_ERROR("Set default retimer failed, index: %zu\n", i);
+ DC_LOG_DEBUG("Set default retimer failed, index: %zu\n", i);
return false;
}
}
@@ -524,7 +524,7 @@ static bool write_i2c_redriver_setting(
);
if (!success)
- DC_LOG_ERROR("Set redriver failed");
+ DC_LOG_DEBUG("Set redriver failed");
return success;
}
diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 6371f292f2d8..606d48115301 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -24,6 +24,8 @@
#ifndef __KGD_PP_INTERFACE_H__
#define __KGD_PP_INTERFACE_H__
+#include <linux/units.h>
+
extern const struct amdgpu_ip_block_version pp_smu_ip_block;
extern const struct amdgpu_ip_block_version smu_v11_0_ip_block;
extern const struct amdgpu_ip_block_version smu_v12_0_ip_block;
@@ -150,8 +152,8 @@ enum amd_pp_sensors {
AMDGPU_PP_SENSOR_MEM_TEMP,
AMDGPU_PP_SENSOR_VCE_POWER,
AMDGPU_PP_SENSOR_UVD_POWER,
- AMDGPU_PP_SENSOR_GPU_AVG_POWER,
- AMDGPU_PP_SENSOR_GPU_INPUT_POWER,
+ AMDGPU_PP_SENSOR_GPU_AVG_POWER, /* milliwatts */
+ AMDGPU_PP_SENSOR_GPU_INPUT_POWER, /* milliwatts */
AMDGPU_PP_SENSOR_SS_APU_SHARE,
AMDGPU_PP_SENSOR_SS_DGPU_SHARE,
AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK,
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index f76ba6753551..e5e89294958a 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -1183,20 +1183,36 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
return ret;
}
-int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table)
+static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev)
+{
+ return !amdgpu_sriov_vf(adev) &&
+ !(adev->flags & AMD_IS_APU) &&
+ !adev->scpm_enabled;
+}
+
+int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
+ size_t size)
{
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+ char *pptable = NULL;
int ret = 0;
- if (!table)
+ if ((!table && size) || (table && !size))
return -EINVAL;
- if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
+ if (!amdgpu_dpm_is_pp_table_allowed(adev) ||
+ !pp_funcs->get_pp_table)
return -EOPNOTSUPP;
mutex_lock(&adev->pm.mutex);
ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle,
- table);
+ &pptable);
+ if (ret > 0 && !pptable) {
+ ret = -EINVAL;
+ } else if (ret > 0 && table) {
+ ret = min_t(size_t, ret, size);
+ memcpy(table, pptable, ret);
+ }
mutex_unlock(&adev->pm.mutex);
return ret;
@@ -1426,17 +1442,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev,
return ret;
}
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table)
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+ size_t size)
{
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
- int ret = 0;
+ void *table;
+ ssize_t ret;
if (!pp_funcs->get_gpu_metrics)
return 0;
mutex_lock(&adev->pm.mutex);
ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle,
- table);
+ &table);
+ if (ret > 0) {
+ ret = min_t(ssize_t, ret, size);
+ memcpy(buf, table, ret);
+ }
mutex_unlock(&adev->pm.mutex);
return ret;
@@ -1703,7 +1725,8 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev,
if (!buf || !size)
return -EINVAL;
- if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled)
+ if (!amdgpu_dpm_is_pp_table_allowed(adev) ||
+ !pp_funcs->set_pp_table)
return -EOPNOTSUPP;
mutex_lock(&adev->pm.mutex);
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index 97da01aff76c..85b089a3dc41 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -41,8 +41,6 @@
#define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name)
-#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff))
-
struct od_attribute {
struct kobj_attribute attribute;
struct list_head entry;
@@ -564,25 +562,19 @@ static ssize_t amdgpu_get_pp_table(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(ddev);
- char *table = NULL;
int size, ret;
ret = amdgpu_pm_get_access_if_active(adev);
if (ret)
return ret;
- size = amdgpu_dpm_get_pp_table(adev, &table);
+ size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1);
amdgpu_pm_put_access(adev);
if (size <= 0)
return size;
- if (size >= PAGE_SIZE)
- size = PAGE_SIZE - 1;
-
- memcpy(buf, table, size);
-
return size;
}
@@ -1780,7 +1772,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(ddev);
- void *gpu_metrics;
ssize_t size = 0;
int ret;
@@ -1788,15 +1779,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev,
if (ret)
return ret;
- size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics);
+ size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1);
if (size <= 0)
goto out;
- if (size >= PAGE_SIZE)
- size = PAGE_SIZE - 1;
-
- memcpy(buf, gpu_metrics, size);
-
out:
amdgpu_pm_put_access(adev);
@@ -2726,10 +2712,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
*states = ATTR_STATE_UNSUPPORTED;
} else if (DEVICE_ATTR_IS(pp_table)) {
int ret;
- char *tmp = NULL;
- ret = amdgpu_dpm_get_pp_table(adev, &tmp);
- if (ret == -EOPNOTSUPP || !tmp)
+ ret = amdgpu_dpm_get_pp_table(adev, NULL, 0);
+ if (ret <= 0)
*states = ATTR_STATE_UNSUPPORTED;
else
*states = ATTR_STATE_SUPPORTED;
@@ -3364,7 +3349,7 @@ static int amdgpu_hwmon_get_power(struct device *dev,
return r;
/* convert to microwatts */
- return power_2_mwatt(query) * 1000;
+ return query * 1000;
}
static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev,
@@ -4932,7 +4917,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
seq_printf(m, "\t%u mV (VDDNB)\n", value);
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) {
- mwatt = power_2_mwatt(query);
+ mwatt = query;
centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
@@ -4941,7 +4926,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a
}
size = sizeof(uint32_t);
if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) {
- mwatt = power_2_mwatt(query);
+ mwatt = query;
centiwatt = DIV_ROUND_CLOSEST(mwatt, 10);
if (adev->flags & AMD_IS_APU)
seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100);
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index aa3f427819a0..8d1b097a3c3c 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev,
int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
enum amd_pp_task task_id,
enum amd_pm_state_type *user_state);
-int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table);
+int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
+ size_t size);
int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev,
uint32_t type,
long *input,
@@ -517,7 +518,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev,
char *buf);
int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev,
long *input, uint32_t size);
-int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table);
+ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf,
+ size_t size);
ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id,
void *table);
ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev,
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
index bc82ba2e4c9b..f8a5648095d1 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c
@@ -4062,7 +4062,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
(adev->asic_type != CHIP_FIJI) &&
(adev->asic_type != CHIP_TONGA)) {
smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp);
- *query = tmp;
+ *query = PP_PWR_Q24_8_TO_MW(tmp);
if (tmp != 0)
return 0;
@@ -4081,7 +4081,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query)
if (tmp != 0)
break;
}
- *query = tmp;
+ *query = PP_PWR_Q24_8_TO_MW(tmp);
return 0;
}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
index 4b92b52aba2b..c283309efe87 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c
@@ -3934,8 +3934,8 @@ static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr,
if (ret)
return ret;
- /* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */
- *query = value << 8;
+ /* SMC returns whole Watts, while power sensors use milliwatts. */
+ *query = value * MILLIWATT_PER_WATT;
return 0;
}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c
index 69a9074058ce..b466b08e4a67 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c
@@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query)
if (ret)
return ret;
- *query = metrics_table.CurrSocketPower << 8;
+ *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT;
return ret;
}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c
index 7b8f4c1b80eb..e9c194056e32 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c
@@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, int idx,
switch (idx) {
case AMDGPU_PP_SENSOR_GPU_AVG_POWER:
if (hwmgr->smu_version == 0x282e00)
- *query = metrics_table.AverageSocketPower << 8;
+ *query = metrics_table.AverageSocketPower *
+ MILLIWATT_PER_WATT;
else
ret = -EOPNOTSUPP;
break;
case AMDGPU_PP_SENSOR_GPU_INPUT_POWER:
- *query = metrics_table.CurrSocketPower << 8;
+ *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT;
break;
}
diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h
index ca71efaa1656..368200f7cf16 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h
+++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h
@@ -35,6 +35,11 @@ struct pp_hwmgr;
struct phm_fan_speed_info;
struct pp_atomctrl_voltage_table;
+/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */
+#define PP_PWR_Q24_8_TO_MW(power) \
+ DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \
+ BIT(8))
+
#define VOLTAGE_SCALE 4
#define VOLTAGE_VID_OFFSET_SCALE1 625
#define VOLTAGE_VID_OFFSET_SCALE2 100
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index e3a89e9a9df4..64a64973fb86 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -1348,7 +1348,7 @@ struct pptable_funcs {
u32 (*set_gfx_off_residency)(struct smu_context *smu, bool start);
/**
- * @get_gfx_off_residency: Average GFXOFF residency % during the logging interval
+ * @get_gfx_off_residency: Live GFXOFF residency percentage
*/
u32 (*get_gfx_off_residency)(struct smu_context *smu, uint32_t *residency);
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h
index 47ddf533b08f..a5d3e8d88860 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h
@@ -144,6 +144,44 @@ typedef struct {
uint32_t MaxGfxClk;
} DpmClocks_t;
+//Freq in MHz
+//Voltage in milli volts with 2 fractional bits
+typedef struct {
+ uint32_t DcfClocks[NUM_DCFCLK_DPM_LEVELS];
+ uint32_t DispClocks[NUM_DISPCLK_DPM_LEVELS];
+ uint32_t DppClocks[NUM_DPPCLK_DPM_LEVELS];
+ uint32_t SocClocks[NUM_SOCCLK_DPM_LEVELS];
+
+ uint32_t VPEClocks[NUM_VPE_DPM_LEVELS];
+ uint32_t FclkClocks_Freq[NUM_FCLK_DPM_LEVELS];
+ uint32_t FclkClocks_Voltage[NUM_FCLK_DPM_LEVELS];
+ uint32_t SocVoltage[NUM_SOC_VOLTAGE_LEVELS];
+ MemPstateTable_t MemPstateTable[NUM_MEM_PSTATE_LEVELS];
+
+ uint8_t NumDcfClkLevelsEnabled;
+ uint8_t NumDispClkLevelsEnabled; //Applies to both Dispclk and Dppclk
+ uint8_t NumSocClkLevelsEnabled;
+
+ uint8_t VpeClkLevelsEnabled;
+
+ uint8_t NumMemPstatesEnabled;
+ uint8_t NumFclkLevelsEnabled;
+ uint8_t Spare1;
+ uint8_t Spare2;
+
+ uint8_t Spare3;
+ uint8_t Spare4;
+ uint8_t Spare5[2];
+
+ uint32_t MinGfxClk;
+ uint32_t MaxGfxClk;
+
+ uint32_t Spare6[8];
+ uint32_t Spare7[8];
+ uint32_t Spare8[8];
+ uint32_t Spare9[8];
+} DpmClocks_t_v15_0_5;
+
typedef struct {
uint16_t CoreFrequency[16]; //Target core frequency [MHz]
uint16_t CorePower[16]; //CAC calculated core power [mW]
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h
index 7471e2df2828..4206514765cd 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h
@@ -110,8 +110,10 @@
#define PPSMC_MSG_GetSlowPPTLimit 0x4C
#define PPSMC_MSG_GetGfxOffStatus 0x50
#define PPSMC_MSG_GetGfxOffEntryCount 0x51
-#define PPSMC_MSG_LogGfxOffResidency 0x52
-#define PPSMC_Message_Count 0x53
+#define PPSMC_MSG_GfxOffResidencyLogReadSample 0x52
+#define PPSMC_MSG_StopGfxOffResidencyLogging 0x53
+#define PPSMC_MSG_StartGfxOffResidencyLogging 0x56
+#define PPSMC_Message_Count 0x57
//Argument for PPSMC_MSG_GfxDeviceDriverReset
enum {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
index 636ff90923d9..acf03838d49d 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
@@ -252,7 +252,9 @@
__SMU_DUMMY_MAP(DriverMode2Reset), \
__SMU_DUMMY_MAP(GetGfxOffStatus), \
__SMU_DUMMY_MAP(GetGfxOffEntryCount), \
- __SMU_DUMMY_MAP(LogGfxOffResidency), \
+ __SMU_DUMMY_MAP(StartGfxOffResidencyLogging), \
+ __SMU_DUMMY_MAP(GfxOffResidencyLogReadSample), \
+ __SMU_DUMMY_MAP(StopGfxOffResidencyLogging), \
__SMU_DUMMY_MAP(SetNumBadMemoryPagesRetired), \
__SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), \
__SMU_DUMMY_MAP(AllowGpo), \
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
index e6fd8be2cc4a..0fa32fd041da 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
@@ -26,7 +26,7 @@
#include "amdgpu_smu.h"
#define SMU15_DRIVER_IF_VERSION_INV 0xFFFFFFFF
-#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x7
+#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x9
#define FEATURE_MASK(feature) (1ULL << feature)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c
index 54d3dba7d354..cfad8cd74773 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c
@@ -658,7 +658,7 @@ static int arcturus_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->VcnActivityPercentage;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c
index e6e009df9840..bb9620666dae 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c
@@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->Current.MemclkFrequency;
break;
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->Current.CurrentSocketPower << 8) /
- 1000;
+ *value = metrics->Current.CurrentSocketPower;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = (metrics->Average.CurrentSocketPower << 8) /
- 1000;
+ *value = metrics->Average.CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->Current.GfxTemperature / 100 *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
index cd0457e13f54..6dfc3b585c6b 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
@@ -602,7 +602,7 @@ static int navi10_get_legacy_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
@@ -691,7 +691,7 @@ static int navi10_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
@@ -777,7 +777,7 @@ static int navi12_get_legacy_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
@@ -866,7 +866,7 @@ static int navi12_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->TemperatureEdge *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
index f799e489b481..dccca6ddd5ad 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
@@ -855,9 +855,10 @@ static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu,
metrics->AverageUclkActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 :
- use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 :
- metrics->AverageSocketPower << 8;
+ *value = use_metrics_v3 ? metrics_v3->AverageSocketPower :
+ use_metrics_v2 ? metrics_v2->AverageSocketPower :
+ metrics->AverageSocketPower;
+ *value *= MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = (use_metrics_v3 ? metrics_v3->TemperatureEdge :
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
index 016a5c893fee..3e3c68448ba9 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
@@ -72,6 +72,12 @@ static const struct smu_feature_bits vangogh_dpm_features = {
}
};
+/*
+ * SMU support new GFXOFF residency log interface since version 4.63.62.00,
+ * use this to get live readings of GFXOFF residency
+ */
+#define SUPPORT_LIVE_RESIDENCY_MSG_VERSION 0x043f3e00
+
static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = {
MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0),
MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 0),
@@ -142,7 +148,9 @@ static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = {
MSG_MAP(GetSlowPPTLimit, PPSMC_MSG_GetSlowPPTLimit, 0),
MSG_MAP(GetGfxOffStatus, PPSMC_MSG_GetGfxOffStatus, 0),
MSG_MAP(GetGfxOffEntryCount, PPSMC_MSG_GetGfxOffEntryCount, 0),
- MSG_MAP(LogGfxOffResidency, PPSMC_MSG_LogGfxOffResidency, 0),
+ MSG_MAP(StartGfxOffResidencyLogging, PPSMC_MSG_StartGfxOffResidencyLogging, 0),
+ MSG_MAP(GfxOffResidencyLogReadSample, PPSMC_MSG_GfxOffResidencyLogReadSample, 0),
+ MSG_MAP(StopGfxOffResidencyLogging, PPSMC_MSG_StopGfxOffResidencyLogging, 0),
};
static struct cmn2asic_mapping vangogh_feature_mask_map[SMU_FEATURE_COUNT] = {
@@ -310,8 +318,7 @@ static int vangogh_get_legacy_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = (metrics->CurrentSocketPower << 8) /
- 1000 ;
+ *value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
@@ -379,12 +386,10 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->Current.UvdActivity;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = (metrics->Average.CurrentSocketPower << 8) /
- 1000;
+ *value = metrics->Average.CurrentSocketPower;
break;
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->Current.CurrentSocketPower << 8) /
- 1000;
+ *value = metrics->Current.CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->Current.GfxTemperature / 100 *
@@ -2449,19 +2454,32 @@ static int vangogh_set_power_limit(struct smu_context *smu,
static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start)
{
int ret = 0;
- u32 residency;
struct amdgpu_device *adev = smu->adev;
if (!(adev->pm.pp_feature & PP_GFXOFF_MASK))
return 0;
- ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_LogGfxOffResidency,
- start, &residency);
- if (ret)
- return ret;
+ if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) {
+ u32 residency;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxOffResidencyLogReadSample,
+ start, &residency);
+ if (ret)
+ return ret;
- if (!start)
- adev->gfx.gfx_off_residency = residency;
+ if (!start)
+ adev->gfx.gfx_off_residency = residency;
+ } else {
+ if (start) {
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StartGfxOffResidencyLogging, NULL);
+ if (ret)
+ return ret;
+ } else {
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StopGfxOffResidencyLogging, NULL);
+ if (ret)
+ return ret;
+ }
+ }
return ret;
}
@@ -2478,11 +2496,20 @@ static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start)
*/
static u32 vangogh_get_gfxoff_residency(struct smu_context *smu, uint32_t *residency)
{
+ int ret = 0;
struct amdgpu_device *adev = smu->adev;
- *residency = adev->gfx.gfx_off_residency;
+ if (!(adev->pm.pp_feature & PP_GFXOFF_MASK))
+ return 0;
+
+ if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) {
+ *residency = adev->gfx.gfx_off_residency;
+ } else {
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GfxOffResidencyLogReadSample,
+ residency);
+ }
- return 0;
+ return ret;
}
/**
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
index 75335da224c7..175c44190caa 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
@@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu,
((amdgpu_ip_version(adev, MP1_HWIP, 0) ==
IP_VERSION(12, 0, 0)) &&
(adev->pm.fw_version >= 0x373200)))
- *value = metrics->CurrentSocketPower << 8;
+ *value = metrics->CurrentSocketPower *
+ MILLIWATT_PER_WATT;
else
- *value = (metrics->CurrentSocketPower << 8) / 1000;
+ *value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = (metrics->GfxTemperature / 100) *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
index 9d8b1227388f..18c0b43b93b9 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
@@ -677,7 +677,8 @@ static int aldebaran_get_smu_metrics_data(struct smu_context *smu,
case METRICS_AVERAGE_SOCKETPOWER:
/* Valid power data is available only from primary die */
if (aldebaran_is_primary(smu))
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower *
+ MILLIWATT_PER_WATT;
else
ret = -EOPNOTSUPP;
break;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index acbd7046d8a5..0be34fbdac26 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -807,7 +807,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu,
metrics->Vcn1ActivityPercentage);
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->AvgTemperature[TEMP_EDGE] *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
index fe929bd89058..8d476a60d62f 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
@@ -434,7 +434,8 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu,
*value = SMUQ10_ROUND(metrics->DramBandwidthUtilization);
break;
case METRICS_CURR_SOCKETPOWER:
- *value = SMUQ10_ROUND(metrics->SocketPower) << 8;
+ *value = SMUQ10_ROUND(metrics->SocketPower) *
+ MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_HOTSPOT:
*value = SMUQ10_ROUND(metrics->MaxSocketTemperature) *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
index ba91bf590eed..a9d558f5b739 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
@@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = (metrics->AverageSocketPower << 8) / 1000;
+ *value = metrics->AverageSocketPower;
break;
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->CurrentSocketPower << 8) / 1000;
+ *value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
index 27372a60e83d..f95b15af436d 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
@@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->CurrentSocketPower << 8) / 1000;
+ *value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
index b12388134489..41608574bc61 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
@@ -1321,7 +1321,8 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu,
*value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version));
break;
case METRICS_CURR_SOCKETPOWER:
- *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8;
+ *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) *
+ MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_HOTSPOT:
*value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index 42c9ceeb4f7d..5d11ed8be4c3 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -817,7 +817,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu,
*value = smu_safe_u16_nn(metrics->AverageUclkActivity);
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->AvgTemperature[TEMP_EDGE] *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
index 7bf88ffd311b..edc3fedca9f8 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
@@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->UvdActivity / 100;
break;
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->CurrentSocketPower << 8) / 1000;
+ *value = metrics->CurrentSocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
index 3d73f2050bbe..568665265a13 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
@@ -312,8 +312,7 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu,
break;
case METRICS_AVERAGE_SOCKETPOWER:
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->SocketPower / 1000 << 8) +
- (metrics->SocketPower % 1000 / 10);
+ *value = metrics->SocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index a6a88e7b2668..bd054cbfbae0 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -695,7 +695,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu,
metrics->Vcn1ActivityPercentage);
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = metrics->AverageSocketPower << 8;
+ *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->AvgTemperature[TEMP_EDGE] *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
index bb8d09e73c7d..0da1ffb3a567 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c
@@ -168,7 +168,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu)
SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t),
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
- SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t),
+ SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t_v15_0_5),
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t),
PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM);
@@ -178,7 +178,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu)
goto err0_out;
smu_table->metrics_time = 0;
- smu_table->clocks_table = kzalloc_obj(DpmClocks_t);
+ smu_table->clocks_table = kzalloc_obj(DpmClocks_t_v15_0_5);
if (!smu_table->clocks_table)
goto err1_out;
@@ -378,8 +378,7 @@ static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu,
break;
case METRICS_AVERAGE_SOCKETPOWER:
case METRICS_CURR_SOCKETPOWER:
- *value = (metrics->SocketPower / 1000 << 8) +
- (metrics->SocketPower % 1000 / 10);
+ *value = metrics->SocketPower;
break;
case METRICS_TEMPERATURE_EDGE:
*value = metrics->GfxTemperature / 100 *
@@ -752,11 +751,48 @@ static int smu_v15_0_0_get_dpm_freq_by_index(struct smu_context *smu,
return 0;
}
+static int smu_v15_0_5_get_dpm_freq_by_index(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t dpm_level,
+ uint32_t *freq)
+{
+ DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table;
+
+ if (!clk_table || clk_type >= SMU_CLK_COUNT)
+ return -EINVAL;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ if (dpm_level >= clk_table->NumSocClkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->SocClocks[dpm_level];
+ break;
+ case SMU_UCLK:
+ case SMU_MCLK:
+ if (dpm_level >= clk_table->NumMemPstatesEnabled)
+ return -EINVAL;
+ *freq = clk_table->MemPstateTable[dpm_level].MemClk;
+ break;
+ case SMU_FCLK:
+ if (dpm_level >= clk_table->NumFclkLevelsEnabled)
+ return -EINVAL;
+ *freq = clk_table->FclkClocks_Freq[dpm_level];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int smu_v15_0_common_get_dpm_freq_by_index(struct smu_context *smu,
enum smu_clk_type clk_type,
uint32_t dpm_level,
uint32_t *freq)
{
+ if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5))
+ smu_v15_0_5_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq);
+ else
smu_v15_0_0_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq);
return 0;
@@ -907,12 +943,116 @@ failed:
return ret;
}
+static int smu_v15_0_5_get_dpm_ultimate_freq(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *min,
+ uint32_t *max)
+{
+ DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table;
+ uint32_t clock_limit;
+ uint32_t max_dpm_level, min_dpm_level;
+ int ret = 0;
+
+ if (!smu_v15_0_0_clk_dpm_is_enabled(smu, clk_type)) {
+ switch (clk_type) {
+ case SMU_MCLK:
+ case SMU_UCLK:
+ clock_limit = smu->smu_table.boot_values.uclk;
+ break;
+ case SMU_FCLK:
+ clock_limit = smu->smu_table.boot_values.fclk;
+ break;
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ clock_limit = smu->smu_table.boot_values.gfxclk;
+ break;
+ case SMU_SOCCLK:
+ clock_limit = smu->smu_table.boot_values.socclk;
+ break;
+ default:
+ clock_limit = 0;
+ break;
+ }
+
+ /* clock in Mhz unit */
+ if (min)
+ *min = clock_limit / 100;
+ if (max)
+ *max = clock_limit / 100;
+
+ return 0;
+ }
+
+ if (max) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *max = clk_table->MaxGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ max_dpm_level = 0;
+ break;
+ case SMU_FCLK:
+ max_dpm_level = clk_table->NumFclkLevelsEnabled - 1;
+ break;
+ case SMU_SOCCLK:
+ max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type,
+ max_dpm_level, max);
+ if (ret)
+ goto failed;
+ }
+ }
+
+ if (min) {
+ switch (clk_type) {
+ case SMU_GFXCLK:
+ case SMU_SCLK:
+ *min = clk_table->MinGfxClk;
+ break;
+ case SMU_MCLK:
+ case SMU_UCLK:
+ min_dpm_level = clk_table->NumMemPstatesEnabled - 1;
+ break;
+ case SMU_FCLK:
+ min_dpm_level = 0;
+ break;
+ case SMU_SOCCLK:
+ min_dpm_level = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) {
+ ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type,
+ min_dpm_level, min);
+ if (ret)
+ goto failed;
+ }
+ }
+
+failed:
+ return ret;
+}
+
static int smu_v15_0_common_get_dpm_ultimate_freq(struct smu_context *smu,
enum smu_clk_type clk_type,
uint32_t *min,
uint32_t *max)
{
- if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1)
+ if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5))
+ smu_v15_0_5_get_dpm_ultimate_freq(smu, clk_type, min, max);
+ else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1)
smu_v15_0_0_get_dpm_ultimate_freq(smu, clk_type, min, max);
return 0;
@@ -986,11 +1126,36 @@ static int smu_v15_0_0_get_dpm_level_count(struct smu_context *smu,
return 0;
}
+static int smu_v15_0_5_get_dpm_level_count(struct smu_context *smu,
+ enum smu_clk_type clk_type,
+ uint32_t *count)
+{
+ DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table;
+
+ switch (clk_type) {
+ case SMU_SOCCLK:
+ *count = clk_table->NumSocClkLevelsEnabled;
+ break;
+ case SMU_MCLK:
+ *count = clk_table->NumMemPstatesEnabled;
+ break;
+ case SMU_FCLK:
+ *count = clk_table->NumFclkLevelsEnabled;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int smu_v15_0_common_get_dpm_level_count(struct smu_context *smu,
enum smu_clk_type clk_type,
uint32_t *count)
{
- if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1)
+ if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5))
+ smu_v15_0_5_get_dpm_level_count(smu, clk_type, count);
+ else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1)
smu_v15_0_0_get_dpm_level_count(smu, clk_type, count);
return 0;
@@ -1363,9 +1528,24 @@ static int smu_v15_0_0_set_fine_grain_gfx_freq_parameters(struct smu_context *sm
return 0;
}
+static int smu_v15_0_5_set_fine_grain_gfx_freq_parameters(struct smu_context *smu)
+{
+ DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table;
+
+ smu->gfx_default_hard_min_freq = clk_table->MinGfxClk;
+ smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk;
+ smu->gfx_actual_hard_min_freq = 0;
+ smu->gfx_actual_soft_max_freq = 0;
+
+ return 0;
+}
+
static int smu_v15_0_common_set_fine_grain_gfx_freq_parameters(struct smu_context *smu)
{
- smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu);
+ if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5))
+ smu_v15_0_5_set_fine_grain_gfx_freq_parameters(smu);
+ else
+ smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu);
return 0;
}
@@ -1405,9 +1585,36 @@ static int smu_v15_0_0_get_dpm_table(struct smu_context *smu, struct dpm_clocks
return 0;
}
+static int smu_v15_0_5_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table)
+{
+ DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table;
+ uint8_t idx;
+
+ /*
+ * Only the Clock information of SOC and
+ * VPE is copied to provide VPE DPM settings for use.
+ */
+ for (idx = 0; idx < NUM_SOCCLK_DPM_LEVELS; idx++) {
+ clock_table->SocClocks[idx].Freq =
+ (idx < clk_table->NumSocClkLevelsEnabled) ? clk_table->SocClocks[idx]:0;
+ clock_table->SocClocks[idx].Vol = 0;
+ }
+
+ for (idx = 0; idx < NUM_VPE_DPM_LEVELS; idx++) {
+ clock_table->VPEClocks[idx].Freq =
+ (idx < clk_table->VpeClkLevelsEnabled) ? clk_table->VPEClocks[idx]:0;
+ clock_table->VPEClocks[idx].Vol = 0;
+ }
+
+ return 0;
+}
+
static int smu_v15_0_common_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table)
{
- smu_v15_0_0_get_dpm_table(smu, clock_table);
+ if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5))
+ smu_v15_0_5_get_dpm_table(smu, clock_table);
+ else
+ smu_v15_0_0_get_dpm_table(smu, clock_table);
return 0;
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c
index 2105a1d7bb34..118eda5429ff 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c
@@ -411,7 +411,8 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu,
*value = SMUQ10_ROUND(metrics->DramBandwidthUtilization);
break;
case METRICS_CURR_SOCKETPOWER:
- *value = SMUQ10_ROUND(metrics->SocketPower) << 8;
+ *value = SMUQ10_ROUND(metrics->SocketPower) *
+ MILLIWATT_PER_WATT;
break;
case METRICS_TEMPERATURE_HOTSPOT:
*value = SMUQ10_ROUND(metrics->MaxSocketTemperature) *
diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c
index 16c0631adeb1..6b128fabe3a9 100644
--- a/drivers/gpu/drm/bridge/display-connector.c
+++ b/drivers/gpu/drm/bridge/display-connector.c
@@ -40,6 +40,13 @@ static int display_connector_attach(struct drm_bridge *bridge,
return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;
}
+static void display_connector_destroy(struct drm_bridge *bridge)
+{
+ struct display_connector *conn = to_display_connector(bridge);
+
+ i2c_put_adapter(conn->bridge.ddc);
+}
+
static enum drm_connector_status display_connector_detect(struct drm_bridge *bridge)
{
struct display_connector *conn = to_display_connector(bridge);
@@ -177,6 +184,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge,
static const struct drm_bridge_funcs display_connector_bridge_funcs = {
.attach = display_connector_attach,
+ .destroy = display_connector_destroy,
.detect = display_connector_bridge_detect,
.edid_read = display_connector_edid_read,
.atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts,
@@ -403,9 +411,6 @@ static void display_connector_remove(struct platform_device *pdev)
regulator_disable(conn->supply);
drm_bridge_remove(&conn->bridge);
-
- if (!IS_ERR(conn->bridge.ddc))
- i2c_put_adapter(conn->bridge.ddc);
}
static const struct of_device_id display_connector_match[] = {
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index 9c31e14cc413..e623ccb4c1d8 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -3686,6 +3686,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
int bw;
u8 buf;
+ if (!drm_dp_is_branch(dpcd))
+ return 0;
+
+ if (dpcd[DP_DPCD_REV] < 0x11)
+ return 0;
+
+ if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0)
+ return 0;
+
+ if ((port_cap[0] & DP_DS_PORT_TYPE_MASK) != DP_DS_PORT_TYPE_HDMI)
+ return 0;
+
buf = port_cap[2];
bw = buf & DP_PCON_MAX_FRL_BW;
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index 6296635c4e79..95008e0c0eb8 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state,
}
intel_ddi_buf_enable(encoder, buf_ctl);
+
+ intel_hdmi_poll_for_scrambling_enable(crtc_state, connector);
}
static void intel_ddi_enable(struct intel_atomic_state *state,
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 7ff5712f8b19..5733d2e7ac7f 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -4242,7 +4242,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp)
rate_per_lane = info->hdmi.max_frl_rate_per_lane;
max_frl_rate = max_lanes * rate_per_lane;
- if (info->hdmi.dsc_cap.v_1p2) {
+ /*
+ * The sink's DSC max FRL rate only applies to compressed video
+ * transport, which requires a DSC 1.2 encoder in the PCON. Without
+ * one the HDMI link always carries uncompressed video, for which
+ * the regular max FRL rate is the limit.
+ */
+ if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) &&
+ info->hdmi.dsc_cap.v_1p2) {
max_dsc_lanes = info->hdmi.dsc_cap.max_lanes;
dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane;
if (max_dsc_lanes && dsc_rate_per_lane)
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 9076c2b176ec..b9d11fb8559d 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -2729,6 +2729,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_
}
/*
+ * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after
+ * enabling Scrambling_Enable and starting scrambled video
+ * transmission, poll Scrambling_Status for up to 200 ms.
+ */
+void
+intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
+ struct drm_connector *_connector)
+{
+ struct intel_connector *connector = to_intel_connector(_connector);
+ struct intel_display *display = to_intel_display(crtc_state);
+ bool scrambling_enabled = false;
+ int ret;
+
+ if (!crtc_state->hdmi_scrambling)
+ return;
+
+ /* Poll for a max of 200 msec as per HDMI spec */
+ ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base),
+ scrambling_enabled, 1000, 200 * 1000, false);
+ if (ret)
+ drm_dbg_kms(display->drm,
+ "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n",
+ connector->base.base.id, connector->base.name);
+}
+
+/*
* intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup
* @encoder: intel_encoder
* @connector: drm_connector
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
index be2fad57e4ad..0fa3661568e8 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
@@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
unsigned int type,
void *frame, ssize_t len);
+void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state,
+ struct drm_connector *_connector);
#endif /* __INTEL_HDMI_H__ */
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index 941c017399fc..d13726401d92 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -726,8 +726,6 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1;
}
- trace_pvr_job_submit_fw(job);
-
/* Submit job to FW */
pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd,
job->id, job->id);
@@ -802,6 +800,9 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
job->hwrt,
frag_job->fw_ccb_cmd_type ==
ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR);
+
+ trace_pvr_job_submit_fw(geom_job);
+ trace_pvr_job_submit_fw(frag_job);
} else {
struct pvr_queue *queue = container_of(job->base.sched,
struct pvr_queue, scheduler);
@@ -809,6 +810,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
pvr_cccb_send_kccb_kick(pvr_dev, &queue->cccb,
pvr_context_get_fw_addr(job->ctx) + queue->ctx_offset,
job->hwrt);
+
+ trace_pvr_job_submit_fw(job);
}
return dma_fence_get(job->done_fence);
diff --git a/drivers/gpu/drm/imagination/pvr_trace.h b/drivers/gpu/drm/imagination/pvr_trace.h
index ffbbde3b2bcc..943d6b2418cb 100644
--- a/drivers/gpu/drm/imagination/pvr_trace.h
+++ b/drivers/gpu/drm/imagination/pvr_trace.h
@@ -41,6 +41,17 @@ TRACE_EVENT(pvr_job_submit_ioctl,
__entry->count)
);
+#define PVR_JOB_GET_HWRT_FW_ADDR(job) \
+ ({ \
+ struct pvr_job *_job = (job); \
+ u32 _hwrt_fw_addr = 0; \
+ \
+ if (_job && _job->hwrt) \
+ pvr_fw_object_get_fw_addr(_job->hwrt->fw_obj, &_hwrt_fw_addr); \
+ \
+ _hwrt_fw_addr; \
+ })
+
#define PVR_JOB_TYPE_TO_STR(val) \
__print_symbolic(val, \
{ DRM_PVR_JOB_TYPE_GEOMETRY, "geometry" }, \
@@ -64,9 +75,7 @@ TRACE_EVENT(pvr_job_create,
__entry->ctx = job->ctx;
__entry->fw_obj = job->ctx->fw_obj;
pvr_fw_object_get_fw_addr(job->ctx->fw_obj, &__entry->fw_addr);
- __entry->hwrt_addr = job->hwrt ?
- job->hwrt->fw_obj->fw_addr_offset :
- 0;
+ __entry->hwrt_addr = PVR_JOB_GET_HWRT_FW_ADDR(job);
__entry->job = job;
__entry->job_type = job->type;
__entry->sync_op_count = sync_op_count;),
@@ -82,6 +91,7 @@ TRACE_EVENT(pvr_job_create,
);
#undef PVR_JOB_TYPE_TO_STR
+#undef PVR_JOB_GET_HWRT_FW_ADDR
TRACE_EVENT(pvr_job_submit_fw,
TP_PROTO(struct pvr_job *job),
diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index 8e552cdc3b53..97e3ff412e6e 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -154,10 +154,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc)
{
struct mtk_crtc_state *state;
- if (crtc->state)
+ if (crtc->state) {
__drm_atomic_helper_crtc_destroy_state(crtc->state);
-
- kfree(to_mtk_crtc_state(crtc->state));
+ kfree(to_mtk_crtc_state(crtc->state));
+ }
crtc->state = NULL;
state = kzalloc_obj(*state);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index c0af3e3b51d5..6a259872b0a9 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -625,6 +625,7 @@ static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev)
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
priv->children_bound = false;
+ component_unbind_all(dev, priv->mmsys_dev);
}
static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = {
@@ -651,12 +652,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
priv->mmsys_dev = pdev->dev.platform_data;
- component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
+ ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add component master\n");
pm_runtime_enable(dev);
ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops);
if (ret != 0) {
+ component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(dev);
return dev_err_probe(dev, ret, "Failed to add component\n");
}
@@ -666,6 +670,7 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev)
static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev)
{
+ component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops);
component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops);
pm_runtime_disable(&pdev->dev);
}
diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index eb325e68aa59..49dfc0f825cb 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -743,8 +743,6 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
mtk_dsi_set_vm_cmd(dsi);
mtk_dsi_config_vdo_timing(dsi);
mtk_dsi_set_interrupt_enable(dsi);
- mtk_dsi_lane_ready(dsi);
- mtk_dsi_clk_hs_mode(dsi, 1);
return 0;
err_disable_engine_clk:
@@ -858,6 +856,9 @@ static void mtk_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
ret = mtk_dsi_poweron(dsi);
if (ret < 0)
drm_err(drm, "failed to power on dsi\n");
+
+ mtk_dsi_lane_ready(dsi);
+ mtk_dsi_clk_hs_mode(dsi, 1);
}
static void mtk_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge,
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
index a049489daa2d..042b98e5a519 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
@@ -290,6 +290,13 @@ static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struc
return 0;
}
+static void mtk_hdmi_put_adapter(void *_adap)
+{
+ struct i2c_adapter *adap = _adap;
+
+ i2c_put_adapter(adap);
+}
+
static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device *pdev,
const char * const *clk_names, size_t num_clocks)
{
@@ -328,12 +335,12 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device
if (!i2c_np)
return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n");
- hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
+ hdmi->ddc_adpt = of_get_i2c_adapter_by_node(i2c_np);
of_node_put(i2c_np);
if (!hdmi->ddc_adpt)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get ddc i2c adapter by node\n");
- ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &hdmi->ddc_adpt->dev);
+ ret = devm_add_action_or_reset(dev, mtk_hdmi_put_adapter, hdmi->ddc_adpt);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
index 7bbf463056c9..ffe456238a2b 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
@@ -1499,13 +1499,6 @@ static int mtk_hdmi_v2_probe(struct platform_device *pdev)
return 0;
}
-static void mtk_hdmi_v2_remove(struct platform_device *pdev)
-{
- struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
-
- i2c_put_adapter(hdmi->ddc_adpt);
-}
-
static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = {
{ .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 },
{ .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 },
@@ -1515,7 +1508,6 @@ MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_of_ids);
static struct platform_driver mtk_hdmi_v2_driver = {
.probe = mtk_hdmi_v2_probe,
- .remove = mtk_hdmi_v2_remove,
.driver = {
.name = "mediatek-drm-hdmi-v2",
.of_match_table = mtk_drm_hdmi_v2_of_ids,
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 90f59d782a80..e45933e333b5 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -545,6 +545,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
struct panthor_fw_binary_section_entry_hdr hdr;
struct panthor_fw_section *section;
u32 section_size;
+ u32 data_size;
u32 name_len;
int ret;
@@ -595,6 +596,13 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
return -EINVAL;
}
+ section_size = hdr.va.end - hdr.va.start;
+ data_size = hdr.data.end - hdr.data.start;
+ if (data_size > section_size) {
+ drm_err(&ptdev->base, "Firmware corrupted, section data exceeds section size\n");
+ return -EINVAL;
+ }
+
name_len = iter->size - iter->offset;
section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL);
@@ -603,7 +611,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
list_add_tail(&section->node, &ptdev->fw->sections);
section->flags = hdr.flags;
- section->data.size = hdr.data.end - hdr.data.start;
+ section->data.size = data_size;
if (section->data.size > 0) {
void *data = drmm_kmalloc(&ptdev->base, section->data.size, GFP_KERNEL);
@@ -626,7 +634,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
section->name = name;
}
- section_size = hdr.va.end - hdr.va.start;
if (section_size) {
u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
struct panthor_gem_object *bo;
@@ -857,18 +864,24 @@ out:
* iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address
* @ptdev: Device.
* @mcu_va: MCU address.
+ * @size: Size of the object pointed to by @mcu_va.
*
- * Return: NULL if the address is not part of the shared section, non-NULL otherwise.
+ * Return: NULL if the object is not part of the shared section, non-NULL otherwise.
*/
-static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va)
+static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va, size_t size)
{
u64 shared_mem_start = panthor_kernel_bo_gpuva(ptdev->fw->shared_section->mem);
- u64 shared_mem_end = shared_mem_start +
- panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
- if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end)
+ size_t shared_mem_size = panthor_kernel_bo_size(ptdev->fw->shared_section->mem);
+ u64 offset;
+
+ if (mcu_va < shared_mem_start)
+ return NULL;
+
+ offset = mcu_va - shared_mem_start;
+ if (offset > shared_mem_size || size > shared_mem_size - offset)
return NULL;
- return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start);
+ return ptdev->fw->shared_section->mem->kmap + offset;
}
static int panthor_init_cs_iface(struct panthor_device *ptdev,
@@ -890,8 +903,10 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev,
spin_lock_init(&cs_iface->lock);
cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset;
- cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va);
- cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va);
+ cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va,
+ sizeof(*cs_iface->input));
+ cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va,
+ sizeof(*cs_iface->output));
if (!cs_iface->input || !cs_iface->output) {
drm_err(&ptdev->base, "Invalid stream control interface input/output VA");
@@ -941,8 +956,10 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev,
spin_lock_init(&csg_iface->lock);
csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset;
- csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va);
- csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va);
+ csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va,
+ sizeof(*csg_iface->input));
+ csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va,
+ sizeof(*csg_iface->output));
if (csg_iface->control->stream_num < MIN_CS_PER_CSG ||
csg_iface->control->stream_num > MAX_CS_PER_CSG)
@@ -999,8 +1016,10 @@ static int panthor_fw_init_ifaces(struct panthor_device *ptdev)
return -EINVAL;
}
- glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va);
- glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va);
+ glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va,
+ sizeof(*glb_iface->input));
+ glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va,
+ sizeof(*glb_iface->output));
if (!glb_iface->input || !glb_iface->output) {
drm_err(&ptdev->base, "Invalid global control interface input/output VA");
return -EINVAL;
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index 8e5141bb5075..7877d493d80e 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct *work)
vc4->bin_alloc_overflow = BIT(bin_bo_slot);
V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size);
- V3D_WRITE(V3D_BPOS, bo->base.base.size);
+ V3D_WRITE(V3D_BPOS, vc4->bin_alloc_size);
V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
spin_unlock_irqrestore(&vc4->job_lock, irqflags);
diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c
index 7f2fadfde7a8..d2a65c968b1f 100644
--- a/drivers/gpu/drm/vc4/vc4_validate.c
+++ b/drivers/gpu/drm/vc4/vc4_validate.c
@@ -385,6 +385,23 @@ validate_tile_binning_config(VALIDATE_ARGS)
return -EINVAL;
}
+ /* The tile state data array is 48 bytes per tile, and we put it at
+ * the start of a BO containing both it and the tile alloc.
+ */
+ tile_state_size = 48 * tile_count;
+
+ /* Since the tile alloc array will follow us, align. */
+ tile_state_size = roundup(tile_state_size, 4096);
+
+ /* Reject configurations whose tile state would leave no room for
+ * the tile alloc pool that follows it in the slot.
+ */
+ if (tile_state_size >= vc4->bin_alloc_size) {
+ DRM_DEBUG("Tile binning config of %dx%d too large\n",
+ exec->bin_tiles_x, exec->bin_tiles_y);
+ return -EINVAL;
+ }
+
bin_slot = vc4_v3d_get_bin_slot(vc4);
if (bin_slot < 0) {
if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
@@ -400,13 +417,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
exec->bin_slots |= BIT(bin_slot);
bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size;
- /* The tile state data array is 48 bytes per tile, and we put it at
- * the start of a BO containing both it and the tile alloc.
- */
- tile_state_size = 48 * tile_count;
+ exec->tile_alloc_offset = bin_addr + tile_state_size;
- /* Since the tile alloc array will follow us, align. */
- exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
+ /* The TSDA area must be zeroed out before use, otherwise the PTB might
+ * consume a stale tile state.
+ */
+ memset(vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size, 0,
+ tile_state_size);
*(uint8_t *)(validated + 14) =
((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c
index 2421b0dd057c..f9042bafdc93 100644
--- a/drivers/gpu/drm/vmwgfx/ttm_object.c
+++ b/drivers/gpu/drm/vmwgfx/ttm_object.c
@@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile,
if (IS_ERR(dma_buf))
return PTR_ERR(dma_buf);
- if (dma_buf->ops != &tdev->ops)
- return -ENOSYS;
+ if (dma_buf->ops != &tdev->ops) {
+ ret = -ENOSYS;
+ goto out;
+ }
prime = (struct ttm_prime_object *) dma_buf->priv;
base = &prime->base;
*handle = base->handle;
ret = ttm_ref_object_add(tfile, base, NULL, false);
+out:
dma_buf_put(dma_buf);
return ret;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
index 135b75a3e013..56f965ec99dc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
@@ -30,6 +30,7 @@
#include "vmwgfx_bo.h"
#include <linux/highmem.h>
+#include <linux/overflow.h>
/*
* Template that implements find_first_diff() for a generic
@@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
container_of(dst->tbo.bdev, struct vmw_private, bdev);
size_t dst_size = dst->tbo.resource->size;
size_t src_size = src->tbo.resource->size;
+ size_t dst_end, src_end;
struct iosys_map dst_map = {0};
struct iosys_map src_map = {0};
+ bool dst_mapped = false;
+ bool src_mapped = false;
int ret, i;
int x_in_bytes;
u8 *vsrc;
u8 *vdst;
+ if (!height || !width_in_bytes)
+ return 0;
+
+ if (!dst_stride || !src_stride)
+ return -EINVAL;
+ if (dst_stride < width_in_bytes || src_stride < width_in_bytes)
+ return -EINVAL;
+ if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) ||
+ check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) ||
+ check_add_overflow((size_t)dst_offset, dst_end, &dst_end) ||
+ dst_end > dst_size ||
+ check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) ||
+ check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) ||
+ check_add_overflow((size_t)src_offset, src_end, &src_end) ||
+ src_end > src_size) {
+ drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n");
+ return -EINVAL;
+ }
+
vsrc = map_external(src, &src_map);
if (!vsrc) {
drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n");
ret = -ENOMEM;
goto out;
}
+ src_mapped = true;
vdst = map_external(dst, &dst_map);
if (!vdst) {
@@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
ret = -ENOMEM;
goto out;
}
+ dst_mapped = true;
vsrc += src_offset;
vdst += dst_offset;
- if (src_stride == dst_stride) {
- dst_size -= dst_offset;
- src_size -= src_offset;
- memcpy(vdst, vsrc,
- min(dst_stride * height, min(dst_size, src_size)));
+ if (src_stride == dst_stride && width_in_bytes == dst_stride) {
+ memcpy(vdst, vsrc, dst_stride * (size_t)height);
} else {
- WARN_ON(dst_stride < width_in_bytes);
for (i = 0; i < height; ++i) {
memcpy(vdst, vsrc, width_in_bytes);
vsrc += src_stride;
@@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset,
ret = 0;
out:
- unmap_external(src, &src_map);
- unmap_external(dst, &dst_map);
+ if (src_mapped)
+ unmap_external(src, &src_map);
+ if (dst_mapped)
+ unmap_external(dst, &dst_map);
return ret;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
index b010fc7ca68e..d1e7df500190 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
@@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps)
u32 size = vmw_cursor_mob_size(vps->cursor.update_type,
vps->base.crtc_w, vps->base.crtc_h);
struct vmw_bo *vbo = vps->cursor.mob;
+ void *map;
if (!vbo)
return -EINVAL;
@@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps)
if (unlikely(ret != 0))
return -ENOMEM;
- vmw_bo_map_and_cache(vbo);
+ map = vmw_bo_map_and_cache(vbo);
+ if (!map) {
+ vmw_bo_unmap(vbo);
+ ret = -ENOMEM;
+ }
ttm_bo_unreserve(&vbo->tbo);
- return 0;
+ return ret;
}
/**
@@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane,
!vmw_cursor_buffer_changed(vps, old_vps)) {
vps->cursor.update_type =
VMW_CURSOR_UPDATE_NONE;
- } else {
- vmw_cursor_mob_get(vcp, vps);
- vmw_cursor_mob_map(vps);
+ } else if (vps->cursor.update_type ==
+ VMW_CURSOR_UPDATE_MOB &&
+ (vmw_cursor_mob_get(vcp, vps) ||
+ vmw_cursor_mob_map(vps))) {
+ /*
+ * Reset the cursor to avoid crashes later.
+ */
+ vps->cursor.update_type =
+ VMW_CURSOR_UPDATE_NONE;
}
}
}
@@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane,
"surface not suitable for cursor\n");
return -EINVAL;
}
+ } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY ||
+ update_type == VMW_CURSOR_UPDATE_MOB) {
+ u32 cursor_max_dim =
+ vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION);
+
+ if (new_state->crtc_w > cursor_max_dim ||
+ new_state->crtc_h > cursor_max_dim) {
+ drm_warn(&vmw->drm,
+ "Cursor dimensions (%d, %d) exceed device max %u\n",
+ new_state->crtc_w, new_state->crtc_h,
+ cursor_max_dim);
+ return -EINVAL;
+ }
+
+ if (update_type == VMW_CURSOR_UPDATE_MOB) {
+ u32 mob_max_size =
+ vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE);
+ u64 mob_size = (u64)new_state->crtc_w *
+ new_state->crtc_h * sizeof(u32) +
+ sizeof(SVGAGBCursorHeader);
+
+ if (mob_size > mob_max_size) {
+ drm_warn(&vmw->drm,
+ "Cursor MOB size %llu exceeds device max %u\n",
+ mob_size, mob_max_size);
+ return -EINVAL;
+ }
+ }
}
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index e1f18020170a..a9136a6523cb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -1272,9 +1272,13 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv,
SVGA3dCmdHeader *header)
{
VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery);
+ struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context);
struct vmw_bo *vmw_bo;
int ret;
+ if (!ctx_node)
+ return -EINVAL;
+
cmd = container_of(header, typeof(*cmd), header);
/*
@@ -1288,7 +1292,7 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv,
return ret;
sw_context->dx_query_mob = vmw_bo;
- sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx;
+ sw_context->dx_query_ctx = ctx_node->ctx;
return 0;
}
@@ -1506,6 +1510,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
bool dirty;
cmd = container_of(header, typeof(*cmd), header);
+
+ if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
+ VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n");
+ return -EINVAL;
+ }
+
suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body +
header->size - sizeof(*suffix));
@@ -1567,11 +1577,17 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,
uint32_t maxnum;
int ret;
+ cmd = container_of(header, typeof(*cmd), header);
+
+ if (unlikely(header->size < sizeof(cmd->body))) {
+ VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n");
+ return -EINVAL;
+ }
+
ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
if (unlikely(ret != 0))
return ret;
- cmd = container_of(header, typeof(*cmd), header);
maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
if (unlikely(cmd->body.numVertexDecls > maxnum)) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 4ef84ff9b638..384c6736cf6b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -367,13 +367,24 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman)
ret = vmw_fence_obj_wait(fence, false, false,
VMW_FENCE_WAIT_TIMEOUT);
+ spin_lock(&fman->lock);
if (unlikely(ret != 0)) {
+ bool cookie = dma_fence_begin_signalling();
+
list_del_init(&fence->head);
- dma_fence_signal(&fence->base);
+ if (fence->waiter_added) {
+ vmw_seqno_waiter_remove(fman->dev_priv);
+ fence->waiter_added = false;
+ }
+ dma_fence_signal_locked(&fence->base);
+ dma_fence_end_signalling(cookie);
}
BUG_ON(!list_empty(&fence->head));
+ spin_unlock(&fman->lock);
+
dma_fence_put(&fence->base);
+
spin_lock(&fman->lock);
}
spin_unlock(&fman->lock);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
index 45561bc1c9ef..8ab88f388652 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
@@ -311,7 +311,7 @@ void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res)
return;
cur = max(res_start, dirty->start);
- res_end = max(res_end, dirty->end);
+ res_end = min(res_end, dirty->end);
while (cur < res_end) {
unsigned long num;
@@ -347,7 +347,7 @@ void vmw_bo_dirty_clear(struct vmw_bo *vbo)
return;
cur = max(res_start, dirty->start);
- res_end = max(res_end, dirty->end);
+ res_end = min(res_end, dirty->end);
while (cur < res_end) {
unsigned long num;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index 388011696941..e3a187a2c7a1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -136,7 +136,7 @@ static void vmw_resource_release(struct kref *kref)
val_buf.num_shared = 0;
res->func->unbind(res, false, &val_buf);
}
- res->guest_memory_size = false;
+ res->guest_memory_dirty = false;
vmw_resource_mob_detach(res);
if (res->dirty)
res->func->dirty_free(res);
@@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmw_bo *vbo)
if (!WARN_ON_ONCE(!res->func->unbind))
(void) res->func->unbind(res, res->res_dirty, &val_buf);
- res->guest_memory_size = true;
+ res->guest_memory_dirty = true;
res->res_dirty = false;
vmw_resource_mob_detach(res);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
index eca4e3e97eb4..39811cf19db1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -25,6 +25,8 @@
*
**************************************************************************/
+#include <linux/overflow.h>
+
#include <drm/ttm/ttm_placement.h>
#include "vmwgfx_binding.h"
@@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data,
static int vmw_user_shader_alloc(struct vmw_private *dev_priv,
struct vmw_bo *buffer,
size_t shader_size,
- size_t offset,
+ u64 offset,
SVGA3dShaderType shader_type,
uint8_t num_input_sig,
uint8_t num_output_sig,
@@ -739,7 +741,7 @@ out:
static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv,
struct vmw_bo *buffer,
size_t shader_size,
- size_t offset,
+ u64 offset,
SVGA3dShaderType shader_type)
{
struct vmw_shader *shader;
@@ -768,7 +770,7 @@ out_err:
static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv,
enum drm_vmw_shader_type shader_type_drm,
- u32 buffer_handle, size_t size, size_t offset,
+ u32 buffer_handle, size_t size, u64 offset,
uint8_t num_input_sig, uint8_t num_output_sig,
uint32_t *shader_handle)
{
@@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv,
int ret;
if (buffer_handle != SVGA3D_INVALID_ID) {
+ u64 end;
+
ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer);
if (unlikely(ret != 0)) {
VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n");
return ret;
}
- if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) {
+ if (check_add_overflow((u64)size, (u64)offset, &end) ||
+ end > buffer->tbo.base.size) {
VMW_DEBUG_USER("Illegal buffer- or shader size.\n");
ret = -EINVAL;
goto out_bad_arg;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
index 35dc94c3db39..45fde7ec514f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c
@@ -309,7 +309,8 @@ int vmw_validation_add_resource(struct vmw_validation_context *ctx,
}
node->res = vmw_resource_reference_unless_doomed(res);
if (!node->res) {
- hash_del_rcu(&node->hash.head);
+ if (ctx->sw_context)
+ hash_del_rcu(&node->hash.head);
return -ESRCH;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
index 7b8163b5e501..3d0d5dfa869f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
@@ -206,14 +206,14 @@ vmw_vkms_init(struct vmw_private *vmw)
vmw->vkms_enabled = false;
ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len);
- if (ret || buf_len > max_buf_len)
- return;
- buffer[buf_len] = '\0';
+ if (!ret && buf_len <= max_buf_len) {
+ buffer[buf_len] = '\0';
- ret = kstrtobool(buffer, &vmw->vkms_enabled);
- if (!ret && vmw->vkms_enabled) {
- ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
- vmw->vkms_enabled = (ret == 0);
+ ret = kstrtobool(buffer, &vmw->vkms_enabled);
+ if (!ret && vmw->vkms_enabled) {
+ ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
+ vmw->vkms_enabled = (ret == 0);
+ }
}
vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0);
@@ -228,7 +228,8 @@ vmw_vkms_init(struct vmw_private *vmw)
void
vmw_vkms_cleanup(struct vmw_private *vmw)
{
- destroy_workqueue(vmw->crc_workq);
+ if (vmw->crc_workq)
+ destroy_workqueue(vmw->crc_workq);
}
bool
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index 39c9c8f0ea2d..d0a1e6c2b55f 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -443,10 +443,6 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
if (!xe_pt_covers(addr, next, level, &xe_walk->base))
return false;
- /* Does the DMA segment cover the whole pte? */
- if (next - xe_walk->va_curs_start > xe_walk->curs->size)
- return false;
-
/* null VMA's and purged BO's do not have dma addresses */
if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo)))
return true;
@@ -455,6 +451,10 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level,
if (xe_walk->clear_pt)
return true;
+ /* Does the DMA segment cover the whole pte? */
+ if (next - xe_walk->va_curs_start > xe_walk->curs->size)
+ return false;
+
/* Is the DMA address huge PTE size aligned? */
size = next - addr;
dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs);
@@ -775,8 +775,11 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
}
xe_walk.needs_64K = (vm->flags & XE_VM_FLAG_64K);
- if (clear_pt)
+ if (clear_pt) {
+ xe_assert(xe, !range);
+ curs.size = xe_vma_size(vma);
goto walk_pt;
+ }
if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) {
xe_walk.default_vram_pte = xe_atomic_for_vram(vm, vma) ? XE_USM_PPGTT_PTE_AE : 0;
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index b5b322ce16df..0fd50d4cb573 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO);
MODULE_PARM_DESC(max_version,
"Maximal VMBus protocol version which can be negotiated");
-int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
+static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo,
+ u32 version, u32 connection_id)
{
int ret = 0;
struct vmbus_channel_initiate_contact *msg;
@@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
msg->vmbus_version_requested = version;
/*
- * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
- * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
- * and for subsequent messages, we must use the Message Connection ID
- * field in the host-returned Version Response Message. And, with
- * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
- * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for
- * compatibility.
+ * For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the
+ * caller-supplied connection_id for the Initiate Contact message so
+ * the caller can implement the required retry scheme. For subsequent
+ * messages, use the Message Connection ID field in the host-returned
+ * Version Response message. With VERSION_WIN10_V5 and higher, we don't
+ * use msg->interrupt_page, but tell the host explicitly that we still
+ * use VMBUS_MESSAGE_SINT(2) for compatibility.
*
* On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
*/
if (version >= VERSION_WIN10_V5) {
msg->msg_sint = VMBUS_MESSAGE_SINT;
msg->msg_vtl = ms_hyperv.vtl;
- vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
+ vmbus_connection.msg_conn_id = connection_id;
} else {
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
@@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
return ret;
}
+int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
+{
+ int ret;
+
+ /* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */
+ if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) {
+ ret = vmbus_try_connection_id(msginfo, version,
+ VMBUS_MESSAGE_CONNECTION_ID_REDIRECT);
+ if (ret != -ENXIO)
+ return ret;
+ }
+
+ return vmbus_try_connection_id(msginfo, version,
+ VMBUS_MESSAGE_CONNECTION_ID_4);
+}
+
/*
* vmbus_connect - Sends a connect request on the partition service connection
*/
@@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
switch (ret) {
case HV_STATUS_INVALID_CONNECTION_ID:
- /*
- * See vmbus_negotiate_version(): VMBus protocol 5.0
- * and higher require that we must use
- * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate
- * Contact message, but on old hosts that only
- * support VMBus protocol 4.0 or lower, here we get
- * HV_STATUS_INVALID_CONNECTION_ID and we should
- * return an error immediately without retrying.
- */
+ /* Allow INITIATE_CONTACT to try another connection ID. */
hdr = buffer;
if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
- return -EINVAL;
+ return -ENXIO;
/*
* We could get this if we send messages too
* frequently.
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index eb8bdd8bb1f5..33923621a5a3 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -110,6 +110,8 @@ struct hv_input_post_message {
enum {
VMBUS_MESSAGE_CONNECTION_ID = 1,
VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
+ /* VTL2 redirect connection ID for INITIATE_CONTACT. */
+ VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
VMBUS_MESSAGE_PORT_ID = 1,
VMBUS_EVENT_CONNECTION_ID = 2,
VMBUS_EVENT_PORT_ID = 2,
diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c
index 90959f639dc3..06aef99c8298 100644
--- a/drivers/hv/mshv_eventfd.c
+++ b/drivers/hv/mshv_eventfd.c
@@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd)
return -EOPNOTSUPP;
#endif
- vp = partition->pt_vp_array[irq->lapic_apic_id];
+ /*
+ * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp().
+ * MSHV_IRQFD does not require the target lapic_apic_id to refer to an
+ * existing VP, so this read can race a concurrent VP creation; the
+ * acquire ensures that a non-NULL pointer implies the VP's
+ * initialising stores are visible.
+ */
+ vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]);
if (!vp->vp_register_page)
return -EOPNOTSUPP;
@@ -284,7 +291,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd)
if (!mshv_irqfd_is_active(irqfd))
return;
- hlist_del(&irqfd->irqfd_hnode);
+ hlist_del_init(&irqfd->irqfd_hnode);
queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown);
}
@@ -473,6 +480,19 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc);
spin_lock_irq(&pt->pt_irqfds_lock);
+ ret = 0;
+ hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
+ if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
+ continue;
+ /* This fd is used for another irq already. */
+ ret = -EBUSY;
+ spin_unlock_irq(&pt->pt_irqfds_lock);
+ goto fail;
+ }
+
+ idx = srcu_read_lock(&pt->pt_irq_srcu);
+ mshv_irqfd_update(pt, irqfd);
+
#if IS_ENABLED(CONFIG_X86)
if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) &&
!irqfd->irqfd_lapic_irq.lapic_control.level_triggered) {
@@ -481,22 +501,12 @@ static int mshv_irqfd_assign(struct mshv_partition *pt,
* Otherwise return with failure
*/
spin_unlock_irq(&pt->pt_irqfds_lock);
+ srcu_read_unlock(&pt->pt_irq_srcu, idx);
ret = -EINVAL;
goto fail;
}
#endif
- ret = 0;
- hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) {
- if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx)
- continue;
- /* This fd is used for another irq already. */
- ret = -EBUSY;
- spin_unlock_irq(&pt->pt_irqfds_lock);
- goto fail;
- }
- idx = srcu_read_lock(&pt->pt_irq_srcu);
- mshv_irqfd_update(pt, irqfd);
hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list);
spin_unlock_irq(&pt->pt_irqfds_lock);
@@ -541,13 +551,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt,
if (IS_ERR(eventfd))
return PTR_ERR(eventfd);
+ spin_lock_irq(&pt->pt_irqfds_lock);
hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list,
irqfd_hnode) {
if (irqfd->irqfd_eventfd_ctx == eventfd &&
irqfd->irqfd_irqnum == args->gsi)
-
mshv_irqfd_deactivate(irqfd);
}
+ spin_unlock_irq(&pt->pt_irqfds_lock);
eventfd_ctx_put(eventfd);
diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c
index b3142c84dcbc..65a4ffc82d56 100644
--- a/drivers/hv/mshv_irq.c
+++ b/drivers/hv/mshv_irq.c
@@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition,
/*
* Allow only one to one mapping between GSI and MSI routing.
*/
- if (girq->guest_irq_num != 0) {
+ if (girq->girq_entry_valid) {
r = -EINVAL;
goto out;
}
diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c
index c349af1f0aaa..0d632507ed21 100644
--- a/drivers/hv/mshv_portid_table.c
+++ b/drivers/hv/mshv_portid_table.c
@@ -40,12 +40,14 @@ mshv_port_table_fini(void)
int
mshv_portid_alloc(struct port_table_info *info)
{
- int ret = 0;
+ int ret;
+ idr_preload(GFP_KERNEL);
idr_lock(&port_table_idr);
ret = idr_alloc(&port_table_idr, info, PORTID_MIN,
- PORTID_MAX, GFP_KERNEL);
+ PORTID_MAX, GFP_NOWAIT);
idr_unlock(&port_table_idr);
+ idr_preload_end();
return ret;
}
@@ -60,8 +62,7 @@ mshv_portid_free(int port_id)
WARN_ON(!info);
idr_unlock(&port_table_idr);
- synchronize_rcu();
- kfree(info);
+ kfree_rcu(info, portbl_rcu);
}
int
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 146726cc4e9b..cc2cfce2aefd 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1072,6 +1072,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
struct mshv_vp *vp;
struct page *intercept_msg_page, *register_page, *ghcb_page;
struct hv_stats_page *stats_pages[2];
+ struct file *file;
+ int fd;
long ret;
if (copy_from_user(&args, arg, sizeof(args)))
@@ -1117,8 +1119,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
goto unmap_ghcb_page;
vp = kzalloc_obj(*vp);
- if (!vp)
+ if (!vp) {
+ ret = -ENOMEM;
goto unmap_stats_pages;
+ }
vp->vp_partition = mshv_partition_get(partition);
if (!vp->vp_partition) {
@@ -1144,21 +1148,40 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
if (ret)
goto put_partition;
- /*
- * Keep anon_inode_getfd last: it installs fd in the file struct and
- * thus makes the state accessible in user space.
- */
- ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp,
- O_RDWR | O_CLOEXEC);
- if (ret < 0)
+ fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
goto remove_debugfs_vp;
+ }
+
+ file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp,
+ O_RDWR | O_CLOEXEC);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto put_unused_vp_fd;
+ }
/* already exclusive with the partition mutex for all ioctls */
partition->pt_vp_count++;
- partition->pt_vp_array[args.vp_index] = vp;
+ /*
+ * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which
+ * can run concurrently from an irqfd waker without holding pt_mutex.
+ * The release ensures the VP's initialising stores are visible to any
+ * reader that observes a non-NULL pointer in pt_vp_array.
+ */
+ smp_store_release(&partition->pt_vp_array[args.vp_index], vp);
+
+ /*
+ * fd_install() is the userspace-visibility commit point. Must be the
+ * last operation that can fail or be observed.
+ */
+ fd_install(fd, file);
+ ret = fd;
goto out;
+put_unused_vp_fd:
+ put_unused_fd(fd);
remove_debugfs_vp:
mshv_debugfs_vp_remove(vp);
put_partition:
diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c
index 0d3d4161974f..6e3c11c68171 100644
--- a/drivers/hv/mshv_vtl_main.c
+++ b/drivers/hv/mshv_vtl_main.c
@@ -129,6 +129,7 @@ mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev)
file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops,
vtl, O_RDWR);
if (IS_ERR(file)) {
+ put_unused_fd(fd);
kfree(vtl);
return PTR_ERR(file);
}
@@ -801,7 +802,7 @@ static vm_fault_t mshv_vtl_fault(struct vm_fault *vmf)
int cpu = vmf->pgoff & MSHV_PG_OFF_CPU_MASK;
int real_off = vmf->pgoff >> MSHV_REAL_OFF_SHIFT;
- if (!cpu_online(cpu))
+ if (cpu >= nr_cpu_ids || !cpu_online(cpu))
return VM_FAULT_SIGBUS;
/*
* CPU Hotplug is not supported in VTL2 in OpenHCL, where this kernel driver exists.
@@ -1148,12 +1149,22 @@ static int mshv_vtl_hvcall_call(struct mshv_vtl_hvcall_fd *fd,
*/
in = (void *)__get_free_page(GFP_KERNEL);
out = (void *)__get_free_page(GFP_KERNEL);
+ if (!in || !out) {
+ ret = -ENOMEM;
+ goto free_pages;
+ }
if (copy_from_user(in, (void __user *)hvcall.input_ptr, hvcall.input_size)) {
ret = -EFAULT;
goto free_pages;
}
+ /*
+ * The caller supplies output_size, so clear the range copied back to
+ * userspace in case the hypercall writes fewer bytes than requested.
+ */
+ memset(out, 0, hvcall.output_size);
+
hvcall.status = hv_do_hypercall(hvcall.control, in, out);
if (copy_to_user((void __user *)hvcall.output_ptr, out, hvcall.output_size)) {
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 23206640c613..6824bd7cb3c4 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -1369,8 +1369,6 @@ static void run_vmbus_irqd(unsigned int cpu)
__vmbus_isr();
}
-static bool vmbus_irq_initialized;
-
static struct smp_hotplug_thread vmbus_irq_threads = {
.store = &vmbus_irqd,
.setup = vmbus_irqd_setup,
@@ -1384,8 +1382,19 @@ void vmbus_isr(void)
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
vmbus_irqd_wake();
} else {
- lockdep_hardirq_threaded();
+ static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG);
+
+ /*
+ * vmbus_isr is never force-threaded and always invoked at hard
+ * IRQ level. __vmbus_isr() below can acquire a spinlock_t
+ * which becomes a sleeping lock and must not be acquired in
+ * this context. Therefore on PREEMPT_RT this will be threaded
+ * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets
+ * lockdep know that acquiring a spinlock_t is not an issue.
+ */
+ lock_map_acquire_try(&vmbus_map);
__vmbus_isr();
+ lock_map_release(&vmbus_map);
}
}
EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl");
@@ -1486,11 +1495,10 @@ static int vmbus_bus_init(void)
* the VMbus interrupt handler.
*/
- if (IS_ENABLED(CONFIG_PREEMPT_RT) && !vmbus_irq_initialized) {
+ if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
ret = smpboot_register_percpu_thread(&vmbus_irq_threads);
if (ret)
goto err_kthread;
- vmbus_irq_initialized = true;
}
if (vmbus_irq == -1) {
@@ -1534,10 +1542,8 @@ err_connect:
else
free_percpu_irq(vmbus_irq, &vmbus_evt);
err_setup:
- if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
+ if (IS_ENABLED(CONFIG_PREEMPT_RT))
smpboot_unregister_percpu_thread(&vmbus_irq_threads);
- vmbus_irq_initialized = false;
- }
err_kthread:
bus_unregister(&hv_bus);
return ret;
@@ -2169,6 +2175,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
child_device_obj->device.dma_parms = &child_device_obj->dma_parms;
child_device_obj->device.dma_mask = &child_device_obj->dma_mask;
dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64));
+ dma_set_coherent_mask(&child_device_obj->device, DMA_BIT_MASK(64));
/*
* Register with the LDM. This will kick off the driver/device
@@ -3034,10 +3041,9 @@ static void __exit vmbus_exit(void)
hv_remove_vmbus_handler();
else
free_percpu_irq(vmbus_irq, &vmbus_evt);
- if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) {
+ if (IS_ENABLED(CONFIG_PREEMPT_RT))
smpboot_unregister_percpu_thread(&vmbus_irq_threads);
- vmbus_irq_initialized = false;
- }
+
for_each_online_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 664349756dc2..c45b984c02e6 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_PWM1_AUTO_MASK 0x80
#define ADT7470_PWM_AUTO_MASK 0xC0
#define ADT7470_REG_PWM34_CFG 0x69
-#define ADT7470_PWM3_AUTO_MASK 0x40
-#define ADT7470_PWM4_AUTO_MASK 0x80
+#define ADT7470_PWM4_AUTO_MASK 0x40
+#define ADT7470_PWM3_AUTO_MASK 0x80
#define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
@@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ALARM2(x) ((x) << 8)
+/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and
+ * 0x72, or BIT(0)..BIT(6) of data->alarm.
+ * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and
+ * 0x73, or BIT(8)..BIT(10) of data->alarm.
+ */
+#define TEMP_ALARM_BIT(ch) ({ \
+ typeof(ch) _ch = (ch); \
+ (1 << (_ch < 7 ? _ch : _ch + 1)); \
+})
+
+/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in
+ * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm.
+ */
+#define FAN_ALARM_BIT(ch) (1 << (12 + (ch)))
+
#define ADT7470_VENDOR 0x41
#define ADT7470_DEVICE 0x70
/* datasheet only mentions a revision 2 */
@@ -167,6 +182,7 @@ struct adt7470_data {
u8 pwm_min[ADT7470_PWM_COUNT];
s8 pwm_tmin[ADT7470_PWM_COUNT];
u8 pwm_auto_temp[ADT7470_PWM_COUNT];
+ u32 pwm_freq;
struct task_struct *auto_update;
unsigned int auto_update_interval;
@@ -205,11 +221,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in
/* Probe for temperature sensors. Assumes lock is held */
static int adt7470_read_temperatures(struct adt7470_data *data)
{
- unsigned long res;
+ struct device *dev = regmap_get_device(data->regmap);
+ u8 pwm[ADT7470_FAN_COUNT];
unsigned int pwm_cfg[2];
- int err;
+ unsigned long res;
+ int err, err2;
int i;
- u8 pwm[ADT7470_FAN_COUNT];
/* save pwm[1-4] config register */
err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]);
@@ -233,19 +250,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2),
ADT7470_PWM_AUTO_MASK, 0);
if (err < 0)
- return err;
+ goto out_restore;
/* write pwm control to whatever it was */
err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0],
ADT7470_PWM_COUNT);
if (err < 0)
- return err;
+ goto out_restore;
/* start reading temperature sensors */
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK);
if (err < 0)
- return err;
+ goto out_restore;
/* Delay is 200ms * number of temp sensors. */
res = msleep_interruptible((data->num_temp_sensors >= 0 ?
@@ -256,13 +273,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data)
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG,
ADT7470_T05_STB_MASK, 0);
if (err < 0)
- return err;
+ goto out_restore;
+out_restore:
/* restore pwm[1-4] config registers */
- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
- if (err < 0)
- return err;
- err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
+ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]);
+ if (err2 < 0) {
+ dev_warn_ratelimited(dev,
+ "failed to restore PWM{1,2} config (%d)\n",
+ err2);
+
+ if (!err)
+ err = err2;
+ }
+
+ err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]);
+ if (err2 < 0) {
+ dev_warn_ratelimited(dev,
+ "failed to restore PWM{3,4} config (%d)\n",
+ err2);
+
+ if (!err)
+ err = err2;
+ }
+
if (err < 0)
return err;
@@ -491,7 +525,7 @@ static ssize_t auto_update_interval_store(struct device *dev,
if (kstrtol(buf, 10, &temp))
return -EINVAL;
- temp = clamp_val(temp, 0, 60000);
+ temp = clamp_val(temp, 500, 60000);
mutex_lock(&data->lock);
data->auto_update_interval = temp;
@@ -551,7 +585,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va
*val = 1000 * data->temp_max[channel];
break;
case hwmon_temp_alarm:
- *val = !!(data->alarm & channel);
+ *val = !!(data->alarm & TEMP_ALARM_BIT(channel));
break;
default:
return -EOPNOTSUPP;
@@ -571,14 +605,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va
switch (attr) {
case hwmon_temp_min:
mutex_lock(&data->lock);
- data->temp_min[channel] = val;
err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val);
+ if (!err)
+ data->temp_min[channel] = val;
mutex_unlock(&data->lock);
break;
case hwmon_temp_max:
mutex_lock(&data->lock);
- data->temp_max[channel] = val;
err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val);
+ if (!err)
+ data->temp_max[channel] = val;
mutex_unlock(&data->lock);
break;
default:
@@ -624,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev,
static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val)
{
struct adt7470_data *data = adt7470_update_device(dev);
+ u16 fan_data;
if (IS_ERR(data))
return PTR_ERR(data);
switch (attr) {
case hwmon_fan_input:
- if (FAN_DATA_VALID(data->fan[channel]))
- *val = FAN_PERIOD_TO_RPM(data->fan[channel]);
- else
- *val = 0;
+ fan_data = READ_ONCE(data->fan[channel]);
break;
case hwmon_fan_min:
- if (FAN_DATA_VALID(data->fan_min[channel]))
- *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]);
- else
- *val = 0;
+ fan_data = READ_ONCE(data->fan_min[channel]);
break;
case hwmon_fan_max:
- if (FAN_DATA_VALID(data->fan_max[channel]))
- *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]);
- else
- *val = 0;
+ fan_data = READ_ONCE(data->fan_max[channel]);
break;
case hwmon_fan_alarm:
- *val = !!(data->alarm & (1 << (12 + channel)));
- break;
+ *val = !!(data->alarm & FAN_ALARM_BIT(channel));
+ return 0;
default:
return -EOPNOTSUPP;
}
+ if (FAN_DATA_VALID(fan_data))
+ *val = FAN_PERIOD_TO_RPM(fan_data);
+ else
+ *val = 0;
+
return 0;
}
@@ -721,7 +754,7 @@ static ssize_t force_pwm_max_store(struct device *dev,
}
/* These are the valid PWM frequencies to the nearest Hz */
-static const int adt7470_freq_map[] = {
+static const u32 adt7470_freq_map[] = {
11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
};
@@ -761,7 +794,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val
*val = 1 + data->pwm_automatic[channel];
break;
case hwmon_pwm_freq:
- *val = pwm1_freq_get(dev);
+ *val = data->pwm_freq;
break;
default:
return -EOPNOTSUPP;
@@ -774,12 +807,14 @@ static int pwm1_freq_set(struct device *dev, long freq)
{
struct adt7470_data *data = dev_get_drvdata(dev);
unsigned int low_freq = ADT7470_CFG_LF;
+ u32 closest_freq;
int index;
int err;
/* Round the user value given to the closest available frequency */
index = find_closest(freq, adt7470_freq_map,
ARRAY_SIZE(adt7470_freq_map));
+ closest_freq = adt7470_freq_map[index];
if (index >= 8) {
index -= 8;
@@ -797,6 +832,10 @@ static int pwm1_freq_set(struct device *dev, long freq)
err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2,
ADT7470_FREQ_MASK,
index << ADT7470_FREQ_SHIFT);
+ if (err < 0)
+ goto out;
+
+ data->pwm_freq = closest_freq;
out:
mutex_unlock(&data->lock);
@@ -813,9 +852,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
case hwmon_pwm_input:
val = clamp_val(val, 0, 255);
mutex_lock(&data->lock);
- data->pwm[channel] = val;
err = regmap_write(data->regmap, ADT7470_REG_PWM(channel),
- data->pwm[channel]);
+ val);
+ if (!err)
+ data->pwm[channel] = val;
mutex_unlock(&data->lock);
break;
case hwmon_pwm_enable:
@@ -829,10 +869,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val
val--;
mutex_lock(&data->lock);
- data->pwm_automatic[channel] = val;
err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel),
pwm_auto_reg_mask,
val ? pwm_auto_reg_mask : 0);
+ if (!err)
+ data->pwm_automatic[channel] = val;
mutex_unlock(&data->lock);
break;
case hwmon_pwm_freq:
@@ -1008,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
if (temp < 0)
return temp;
+ if (temp > 0xF)
+ return -EINVAL;
+
mutex_lock(&data->lock);
- data->pwm_automatic[attr->index] = temp;
if (!(attr->index % 2)) {
mask = 0xF0;
@@ -1020,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev,
}
err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val);
+ if (!err)
+ data->pwm_auto_temp[attr->index] = temp;
+
mutex_unlock(&data->lock);
return err < 0 ? err : count;
@@ -1248,6 +1294,7 @@ static int adt7470_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct adt7470_data *data;
struct device *hwmon_dev;
+ int freq_val;
int err;
data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL);
@@ -1272,6 +1319,14 @@ static int adt7470_probe(struct i2c_client *client)
if (err < 0)
return err;
+ freq_val = pwm1_freq_get(dev);
+ if (freq_val <= 0) {
+ err = freq_val < 0 ? freq_val : -EINVAL;
+ return err;
+ }
+
+ data->pwm_freq = (u32)freq_val;
+
/* Register sysfs hooks */
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&adt7470_chip_info,
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index c4742e84b999..449a72c6b40b 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -16,6 +16,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/limits.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -266,30 +267,34 @@ static u16 ina226_interval_to_reg(long interval)
return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits);
}
-static int ina2xx_get_value(struct ina2xx_data *data, u8 reg,
- unsigned int regval)
+static long ina2xx_get_value(struct ina2xx_data *data, u8 reg,
+ unsigned int regval)
{
- int val;
+ s64 val64;
+ long val;
switch (reg) {
case INA2XX_SHUNT_VOLTAGE:
/* signed register */
- val = (s16)regval >> data->config->shunt_voltage_shift;
- val = DIV_ROUND_CLOSEST(val, data->config->shunt_div);
+ val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift,
+ data->config->shunt_div);
break;
case INA2XX_BUS_VOLTAGE:
- val = (regval >> data->config->bus_voltage_shift) *
- data->config->bus_voltage_lsb;
- val = DIV_ROUND_CLOSEST(val, 1000);
+ val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) *
+ data->config->bus_voltage_lsb, 1000);
break;
case INA2XX_POWER:
- val = regval * data->power_lsb_uW;
+ val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX);
break;
case INA2XX_CURRENT:
/* signed register, result in mA */
- val = ((s16)regval >> data->config->current_shift) *
+ val64 = (s64)((s16)regval >> data->config->current_shift) *
data->current_lsb_uA;
- val = DIV_ROUND_CLOSEST(val, 1000);
+ if (val64 < 0)
+ val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000);
+ else
+ val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000);
+ val = clamp_val(val64, LONG_MIN, LONG_MAX);
break;
case INA2XX_CALIBRATION:
val = regval;
@@ -378,27 +383,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val)
*/
static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
{
+ long limit;
+
switch (reg) {
case INA2XX_SHUNT_VOLTAGE:
- val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div);
- val *= data->config->shunt_div;
- val <<= data->config->shunt_voltage_shift;
- return clamp_val(val, 0, SHRT_MAX);
+ val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div));
+ return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift,
+ SHRT_MAX);
case INA2XX_BUS_VOLTAGE:
- val = clamp_val(val, 0, 200000);
- val = (val * 1000) << data->config->bus_voltage_shift;
- val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb);
- return clamp_val(val, 0, USHRT_MAX);
+ val = min_t(long, val, 130000);
+ return min_t(long,
+ DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift,
+ data->config->bus_voltage_lsb),
+ USHRT_MAX);
case INA2XX_POWER:
- val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW);
- val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW);
- return clamp_val(val, 0, USHRT_MAX);
+ val = min_t(long, val, LONG_MAX - data->power_lsb_uW);
+ return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX);
case INA2XX_CURRENT:
- val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000);
+ limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000;
+ val = min_t(long, val, limit);
/* signed register, result in mA */
val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA);
- val <<= data->config->current_shift;
- return clamp_val(val, SHRT_MIN, SHRT_MAX);
+ limit = SHRT_MAX >> data->config->current_shift;
+ return (u16)(min_t(long, val, limit) << data->config->current_shift);
default:
/* programmer goofed */
WARN_ON_ONCE(1);
@@ -537,6 +544,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
u8 template[6];
int ret;
long accumulator_24, sample_count;
+ u64 val64;
/* 48-bit register read */
ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
@@ -555,7 +563,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va
return 0;
}
- *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
+ val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
+ *val = min_t(u64, val64, LONG_MAX);
return 0;
}
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index e2a429e579ac..da09770c05e7 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -247,8 +247,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
LM63_REG_TACH_LIMIT_MSB) << 8);
}
- data->pwm1_freq = i2c_smbus_read_byte_data(client,
- LM63_REG_PWM_FREQ);
+ data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f;
if (data->pwm1_freq == 0)
data->pwm1_freq = 1;
data->pwm1[0] = i2c_smbus_read_byte_data(client,
@@ -1187,7 +1186,7 @@ static void lm63_init_client(struct lm63_data *data)
data->config |= 0x04;
/* We may need pwm1_freq before ever updating the client data */
- data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
+ data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f;
if (data->pwm1_freq == 0)
data->pwm1_freq = 1;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 4b9c0ccdf260..c6186505661f 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1194,7 +1194,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
check_enable = (client->irq || !(data->config_orig & 0x80)) &&
(data->config & 0x80);
- if (force || check_enable)
+ if (data->hwmon_dev && (force || check_enable))
schedule_work(&data->report_work);
/*
@@ -1202,7 +1202,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force)
* alarms are all clear, and alerts are currently disabled.
* Otherwise (re)schedule worker if needed.
*/
- if (check_enable) {
+ if (check_enable && data->hwmon_dev) {
if (!(data->current_alarms & data->alert_alarms)) {
dev_dbg(&client->dev, "Re-enabling ALERT#\n");
lm90_update_confreg(data, data->config & ~0x80);
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index 39b9d3abca99..cc698803f8bf 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -374,8 +374,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val,
channel, val);
case hwmon_in_min_alarm:
if (channel == LTC4282_CHAN_VGPIO)
- ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
- LTC4282_GPIO_ALARM_L_MASK, val);
+ return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG,
+ LTC4282_GPIO_ALARM_L_MASK, val);
return ltc4282_vdd_source_read_alm(st,
LTC4282_VSOURCE_ALARM_L_MASK,
diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
index d668dc390def..94482c8092cd 100644
--- a/drivers/hwmon/nct6775-core.c
+++ b/drivers/hwmon/nct6775-core.c
@@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 };
static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 };
-static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 };
-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 };
-static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a };
-static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b };
-static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c };
-static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d };
+static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 };
+static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 };
+static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 };
+static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 };
static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 };
static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 };
@@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 };
static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 };
static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 };
static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 };
-static const u16 NCT6116_REG_TEMP_SOURCE[] = {
- 0xb0, 0xb1, 0xb2 };
static const u16 NCT6116_REG_CRITICAL_TEMP[] = {
0x11a, 0x12a, 0x13a, 0x19a, 0x1aa };
@@ -3652,7 +3650,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
= NCT6106_CRITICAL_PWM_ENABLE_MASK;
data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
- data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE;
+ data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
@@ -3666,13 +3664,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
reg_temp = NCT6106_REG_TEMP;
reg_temp_mon = NCT6106_REG_TEMP_MON;
- num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP);
+ num_reg_temp = 3;
num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
reg_temp_over = NCT6106_REG_TEMP_OVER;
reg_temp_hyst = NCT6106_REG_TEMP_HYST;
reg_temp_config = NCT6106_REG_TEMP_CONFIG;
- num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG);
+ num_reg_temp_config = 3;
reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
reg_temp_crit = NCT6106_REG_TEMP_CRIT;
reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
index aea0b8659f5f..6ff2a6bc06c1 100644
--- a/drivers/hwmon/npcm750-pwm-fan.c
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -358,6 +358,11 @@ static void npcm7xx_fan_polling(struct timer_list *t)
add_timer(&data->fan_timer);
}
+static void npcm7xx_fan_cleanup(void *timer)
+{
+ timer_shutdown_sync(timer);
+}
+
static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data,
u8 fan, u8 cmp, u8 fan_id, u8 flag_int,
u8 flag_mode, u8 flag_clear)
@@ -1020,6 +1025,12 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS);
timer_setup(&data->fan_timer,
npcm7xx_fan_polling, 0);
+ ret = devm_add_action_or_reset(dev,
+ npcm7xx_fan_cleanup,
+ &data->fan_timer);
+ if (ret)
+ return ret;
+
add_timer(&data->fan_timer);
break;
}
diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c
index e2316c46629d..ff0c0bee0e83 100644
--- a/drivers/hwmon/nzxt-smart2.c
+++ b/drivers/hwmon/nzxt-smart2.c
@@ -203,7 +203,7 @@ struct drvdata {
*/
struct mutex mutex;
long update_interval;
- u8 output_buffer[OUTPUT_REPORT_SIZE];
+ u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN);
};
static long scale_pwm_value(long val, long orig_max, long new_max)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 3143b9e0316c..5567d37f13fe 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -513,7 +513,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
if (tmp != rv)
rv = _pmbus_write_byte_data(client, page, reg, tmp);
- return rv;
+ return rv < 0 ? rv : 0;
}
EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS");
@@ -2985,8 +2985,9 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
if (reg == sreg && page == spage && (smask & flags)) {
dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
- sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
- kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
+ sysfs_notify(&data->hwmon_dev->kobj, NULL,
+ da->attr.name);
+ kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
flags &= ~smask;
}
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
index c2f6b73aa7f3..4d90f89a9929 100644
--- a/drivers/hwmon/sht3x.c
+++ b/drivers/hwmon/sht3x.c
@@ -21,6 +21,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
+#include <linux/unaligned.h>
/* commands (high repeatability mode) */
static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 };
@@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev)
if (ret)
goto out;
- val = be16_to_cpup((__be16 *)buf);
+ val = get_unaligned_be16(buf);
data->temperature = sht3x_extract_temperature(val);
- val = be16_to_cpup((__be16 *)(buf + 3));
+ val = get_unaligned_be16(buf + 3);
data->humidity = sht3x_extract_humidity(val);
data->last_update = jiffies;
}
@@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data)
if (ret)
return ret;
- raw = be16_to_cpup((__be16 *)buffer);
+ raw = get_unaligned_be16(buffer);
temperature = sht3x_extract_temperature((raw & 0x01ff) << 7);
humidity = sht3x_extract_humidity(raw & 0xfe00);
data->temperature_limits[index] = temperature;
@@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev,
raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7);
raw |= ((humidity * 42950) >> 16) & 0xfe00;
- *((__be16 *)position) = cpu_to_be16(raw);
+ put_unaligned_be16(raw, position);
position += SHT3X_WORD_LEN;
*position = crc8(sht3x_crc8_table,
position - SHT3X_WORD_LEN,
diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c
index 188e24cc4d35..9fdd6a5fb8b6 100644
--- a/drivers/i2c/busses/i2c-amd-mp2-plat.c
+++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c
@@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platform_device *pdev)
amd_mp2_pm_runtime_put(mp2_dev);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret);
+ amd_mp2_unregister_cb(&i2c_dev->common);
+ }
return ret;
}
diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
index b5629cffe99b..86ca4c2221c4 100644
--- a/drivers/i2c/busses/i2c-bcm-iproc.c
+++ b/drivers/i2c/busses/i2c-bcm-iproc.c
@@ -803,6 +803,17 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
}
if (!time_left && !iproc_i2c->xfer_is_done) {
+ /*
+ * The controller may fail to clear START_BUSY after a timeout,
+ * reset the controller to recover in that case.
+ */
+ if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) &
+ BIT(M_CMD_START_BUSY_SHIFT))) {
+ bcm_iproc_i2c_enable_disable(iproc_i2c, false);
+ bcm_iproc_i2c_init(iproc_i2c);
+ bcm_iproc_i2c_enable_disable(iproc_i2c, true);
+ }
+
/* flush both TX/RX FIFOs */
val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val);
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 6d6e81242f74..c8a203fff4d1 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -8,12 +8,14 @@
* Copyright (C) 2007 MontaVista Software Inc.
* Copyright (C) 2009 Provigent Ltd.
*/
+#include <linux/acpi.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dmi.h>
#include <linux/err.h>
#include <linux/errno.h>
+#include <linux/gpio/driver.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -130,6 +132,80 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
return 0;
}
+#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB)
+/*
+ * Check whether an ACPI GpioInt resource's referenced GPIO controller
+ * has finished probing. Resources with no named controller (resource
+ * source string) are skipped, since they can't be resolved to a
+ * struct device.
+ */
+static int check_gpioint_resource(struct acpi_resource *ares, void *data)
+{
+ struct acpi_resource_gpio *agpio;
+ struct acpi_device *gpio_adev;
+ struct device *gpio_dev;
+ acpi_handle handle;
+ acpi_status status;
+
+ if (!acpi_gpio_get_irq_resource(ares, &agpio))
+ return 1; /* not a GpioInt resource, skip */
+
+ if (!agpio->resource_source.string_length)
+ return 1; /* no named controller, skip */
+
+ status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle);
+ if (ACPI_FAILURE(status))
+ return 1;
+
+ gpio_adev = acpi_fetch_acpi_dev(handle);
+ if (!gpio_adev)
+ return 1;
+
+ struct gpio_device *gdev __free(gpio_device_put) =
+ gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev));
+ if (!gdev)
+ return -EPROBE_DEFER; /* controller not registered yet: abort walk */
+
+ gpio_dev = gpio_device_to_device(gdev)->parent;
+
+ guard(device)(gpio_dev);
+ if (!device_is_bound(gpio_dev))
+ return -EPROBE_DEFER; /* controller not bound yet: abort walk */
+
+ return 1; /* bound, skip adding to resource list, continue walk */
+}
+
+static int check_child_gpioint(struct acpi_device *adev, void *data)
+{
+ LIST_HEAD(res_list);
+ int ret;
+
+ ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&res_list);
+
+ return 0;
+}
+
+static int i2c_dw_check_gpio_dependencies(struct device *dev)
+{
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return 0;
+
+ return acpi_dev_for_each_child(adev, check_child_gpioint, NULL);
+}
+#else
+static int i2c_dw_check_gpio_dependencies(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */
+
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
u32 flags = (uintptr_t)device_get_match_data(&pdev->dev);
@@ -138,6 +214,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
struct dw_i2c_dev *dev;
int irq, ret;
+ ret = i2c_dw_check_gpio_dependencies(device);
+ if (ret)
+ return ret;
+
irq = platform_get_irq_optional(pdev, 0);
if (irq == -ENXIO)
flags |= ACCESS_POLLING;
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index d5e6e2eca3b3..9477d814fde9 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
if (i2c_imx->slave)
return -EBUSY;
- i2c_imx->slave = client;
- i2c_imx->last_slave_event = I2C_SLAVE_STOP;
-
/* Resume */
ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
if (ret < 0) {
@@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
return ret;
}
+ scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) {
+ i2c_imx->slave = client;
+ i2c_imx->last_slave_event = I2C_SLAVE_STOP;
+ }
+
i2c_imx_slave_init(i2c_imx);
return 0;
@@ -958,6 +960,7 @@ static int i2c_imx_unreg_slave(struct i2c_client *client)
i2c_imx_reset_regs(i2c_imx);
+ hrtimer_cancel(&i2c_imx->slave_timer);
i2c_imx->slave = NULL;
/* Suspend */
@@ -1952,6 +1955,47 @@ static int i2c_imx_runtime_resume(struct device *dev)
return 0;
}
+static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev)
+{
+ struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ i2c_mark_adapter_suspended(&i2c_imx->adapter);
+
+ /*
+ * Cancel the slave timer before powering down to prevent
+ * i2c_imx_slave_timeout() from accessing hardware registers
+ * while the clock is disabled.
+ */
+ hrtimer_cancel(&i2c_imx->slave_timer);
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret) {
+ i2c_mark_adapter_resumed(&i2c_imx->adapter);
+ if (i2c_imx->slave) {
+ hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY);
+ hrtimer_restart(&i2c_imx->slave_timer);
+ }
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused i2c_imx_resume_noirq(struct device *dev)
+{
+ struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ i2c_mark_adapter_resumed(&i2c_imx->adapter);
+
+ return 0;
+}
+
static int i2c_imx_suspend(struct device *dev)
{
/*
@@ -1985,8 +2029,8 @@ static int i2c_imx_resume(struct device *dev)
}
static const struct dev_pm_ops i2c_imx_pm_ops = {
- NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq,
+ i2c_imx_resume_noirq)
SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume)
RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL)
};
diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c
index 664a5471d933..695be3b21460 100644
--- a/drivers/i2c/busses/i2c-jz4780.c
+++ b/drivers/i2c/busses/i2c-jz4780.c
@@ -141,6 +141,7 @@ struct jz4780_i2c {
void __iomem *iomem;
int irq;
struct clk *clk;
+ unsigned long clk_rate_khz;
struct i2c_adapter adap;
const struct ingenic_i2c_config *cdata;
@@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address)
static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c)
{
- int dev_clk_khz = clk_get_rate(i2c->clk) / 1000;
+ int dev_clk_khz = i2c->clk_rate_khz;
int cnt_high = 0; /* HIGH period count of the SCL clock */
int cnt_low = 0; /* LOW period count of the SCL clock */
int cnt_period = 0; /* period count of the SCL clock */
@@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platform_device *pdev)
if (IS_ERR(i2c->clk))
return PTR_ERR(i2c->clk);
+ i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000;
+
ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
&clk_freq);
if (ret) {
diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c
index 51a0c3d80fc9..3fe716cc153d 100644
--- a/drivers/i2c/busses/i2c-k1.c
+++ b/drivers/i2c/busses/i2c-k1.c
@@ -723,11 +723,6 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
if (i2c->irq < 0)
return dev_err_probe(dev, i2c->irq, "failed to get irq resource");
- ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
- IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
- if (ret)
- return dev_err_probe(dev, ret, "failed to request irq");
-
clk = devm_clk_get_enabled(dev, "func");
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock");
@@ -755,6 +750,11 @@ static int spacemit_i2c_probe(struct platform_device *pdev)
init_completion(&i2c->complete);
+ ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler,
+ IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq");
+
platform_set_drvdata(pdev, i2c);
ret = i2c_add_numbered_adapter(&i2c->adapt);
diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c
index 4d64895a9e9e..bdeda3979c48 100644
--- a/drivers/i2c/busses/i2c-qcom-cci.c
+++ b/drivers/i2c/busses/i2c-qcom-cci.c
@@ -492,24 +492,8 @@ static int __maybe_unused cci_resume_runtime(struct device *dev)
return 0;
}
-static int __maybe_unused cci_suspend(struct device *dev)
-{
- if (!pm_runtime_suspended(dev))
- return cci_suspend_runtime(dev);
-
- return 0;
-}
-
-static int __maybe_unused cci_resume(struct device *dev)
-{
- cci_resume_runtime(dev);
- pm_request_autosuspend(dev);
-
- return 0;
-}
-
static const struct dev_pm_ops qcom_cci_pm = {
- SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL)
};
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index 1e9f7d2de344..85ebfdb3d2a7 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -297,6 +297,20 @@ unlock:
return ret;
}
+static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev)
+{
+ struct device *dev = iommufd_vdevice_to_device(vdev);
+ struct arm_smmu_master *master = dev_iommu_priv_get(dev);
+
+ /*
+ * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so
+ * more streams would leave the rest stale and none reads out of bounds.
+ */
+ if (master->num_streams != 1)
+ return -EOPNOTSUPP;
+ return 0;
+}
+
/* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */
struct arm_vsmmu_invalidation_cmd {
union {
@@ -403,6 +417,7 @@ out:
static const struct iommufd_viommu_ops arm_vsmmu_ops = {
.alloc_domain_nested = arm_vsmmu_alloc_domain_nested,
.cache_invalidate = arm_vsmmu_cache_invalidate,
+ .vdevice_init = arm_vsmmu_vdevice_init,
};
size_t arm_smmu_get_viommu_size(struct device *dev,
diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c
index cca52a34d0ed..c16ff1fc4b95 100644
--- a/drivers/iommu/io-pgfault.c
+++ b/drivers/iommu/io-pgfault.c
@@ -332,17 +332,31 @@ void iopf_group_response(struct iopf_group *group,
.code = status,
};
- /* Only send response if there is a fault report pending */
mutex_lock(&fault_param->lock);
- if (!list_empty(&group->pending_node)) {
- ops->page_response(dev, &group->last_fault, &resp);
- list_del_init(&group->pending_node);
- }
+ ops->page_response(dev, &group->last_fault, &resp);
+ list_del_init(&group->pending_node);
mutex_unlock(&fault_param->lock);
}
EXPORT_SYMBOL_GPL(iopf_group_response);
/**
+ * iopf_group_dequeue - Dequeue a page fault group from the pending list
+ * @group: the group to dequeue
+ *
+ * The fault handler is responsible for responding to the group after
+ * this function returns.
+ */
+void iopf_group_dequeue(struct iopf_group *group)
+{
+ struct iommu_fault_param *fault_param = group->fault_param;
+
+ mutex_lock(&fault_param->lock);
+ list_del_init(&group->pending_node);
+ mutex_unlock(&fault_param->lock);
+}
+EXPORT_SYMBOL_GPL(iopf_group_dequeue);
+
+/**
* iopf_queue_discard_partial - Remove all pending partial fault
* @queue: the queue whose partial faults need to be discarded
*
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 170a7005f0bc..2895e5370910 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -589,7 +589,7 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev,
if (rc)
goto out_free_handle;
- iommufd_auto_response_faults(hwpt, old_handle);
+ iommufd_auto_response_faults(old, old_handle);
kfree(old_handle);
return 0;
diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c
index 5129e3bf5461..747dd5155121 100644
--- a/drivers/iommu/iommufd/eventq.c
+++ b/drivers/iommu/iommufd/eventq.c
@@ -484,6 +484,8 @@ int iommufd_fault_iopf_handler(struct iopf_group *group)
hwpt = group->attach_handle->domain->iommufd_hwpt;
fault = hwpt->fault;
+ iopf_group_dequeue(group);
+
spin_lock(&fault->common.lock);
list_add_tail(&group->node, &fault->common.deliver);
spin_unlock(&fault->common.lock);
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index 03c8379bbc34..404f31d8f729 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -2451,6 +2451,9 @@ int iopt_area_add_access(struct iopt_area *area, unsigned long start_index,
if ((flags & IOMMUFD_ACCESS_RW_WRITE) && !pages->writable)
return -EPERM;
+ if (iopt_is_dmabuf(pages))
+ return -EINVAL;
+
mutex_lock(&pages->mutex);
access = iopt_pages_get_exact_access(pages, start_index, last_index);
if (access) {
diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c
index 4081deda9b33..fc13cf4737ea 100644
--- a/drivers/iommu/iommufd/viommu.c
+++ b/drivers/iommu/iommufd/viommu.c
@@ -143,7 +143,7 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj)
int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd)
{
struct iommu_vdevice_alloc *cmd = ucmd->cmd;
- struct iommufd_vdevice *vdev, *curr;
+ struct iommufd_vdevice *vdev;
size_t vdev_size = sizeof(*vdev);
struct iommufd_viommu *viommu;
struct iommufd_device *idev;
@@ -189,7 +189,7 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd)
if (WARN_ON_ONCE(viommu->ops->vdevice_size < vdev_size ||
!viommu->ops->vdevice_init)) {
rc = -EOPNOTSUPP;
- goto out_put_idev;
+ goto out_unlock_igroup;
}
vdev_size = viommu->ops->vdevice_size;
}
@@ -218,18 +218,28 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd)
*/
idev->vdev = vdev;
- curr = xa_cmpxchg(&viommu->vdevs, virt_id, NULL, vdev, GFP_KERNEL);
- if (curr) {
- rc = xa_err(curr) ?: -EEXIST;
+ /*
+ * Reserve the slot with a zero entry (reads back as NULL) until the
+ * vdevice_init() op accepts the vDEVICE. Only the xa_* helpers hide a
+ * reserved entry, so never use a raw xas_* iterator on this xarray.
+ */
+ rc = xa_insert(&viommu->vdevs, virt_id, NULL, GFP_KERNEL);
+ if (rc) {
+ if (rc == -EBUSY)
+ rc = -EEXIST;
goto out_abort;
}
if (viommu->ops && viommu->ops->vdevice_init) {
rc = viommu->ops->vdevice_init(vdev);
- if (rc)
+ if (rc) {
+ xa_release(&viommu->vdevs, virt_id);
goto out_abort;
+ }
}
+ xa_store(&viommu->vdevs, virt_id, vdev, GFP_KERNEL);
+
cmd->out_vdevice_id = vdev->obj.id;
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
if (rc)
diff --git a/drivers/of/address.c b/drivers/of/address.c
index cf4aab11e9b1..499d37ceae21 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -753,6 +753,7 @@ EXPORT_SYMBOL(of_property_read_reg);
static int parser_init(struct of_pci_range_parser *parser,
struct device_node *node, const char *name)
{
+ const __be32 *range;
int rlen;
parser->node = node;
@@ -761,12 +762,20 @@ static int parser_init(struct of_pci_range_parser *parser,
parser->ns = of_bus_n_size_cells(node);
parser->dma = !strcmp(name, "dma-ranges");
parser->bus = of_match_bus(node);
+ parser->range = NULL;
+ parser->end = NULL;
- parser->range = of_get_property(node, name, &rlen);
- if (parser->range == NULL)
+ range = of_get_property(node, name, &rlen);
+ if (!range)
return -ENOENT;
- parser->end = parser->range + rlen / sizeof(__be32);
+ if (!parser->bus ||
+ !OF_CHECK_COUNTS(parser->na, parser->ns) ||
+ !OF_CHECK_ADDR_COUNT(parser->pna))
+ return -EINVAL;
+
+ parser->range = range;
+ parser->end = range + rlen / sizeof(__be32);
return 0;
}
@@ -792,7 +801,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
int na = parser->na;
int ns = parser->ns;
int np = parser->pna + na + ns;
- int busflag_na = parser->bus->flag_cells;
+ int busflag_na;
if (!range)
return NULL;
@@ -800,6 +809,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
if (!parser->range || parser->range + np > parser->end)
return NULL;
+ busflag_na = parser->bus->flag_cells;
+
range->flags = parser->bus->get_flags(parser->range);
range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na);
@@ -976,8 +987,7 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np)
np = of_root;
ranges = of_get_property(np, "dma-ranges", &len);
- if (ranges && len) {
- of_dma_range_parser_init(&parser, np);
+ if (ranges && len && !of_dma_range_parser_init(&parser, np)) {
for_each_of_range(&parser, &range)
if (range.cpu_addr + range.size > cpu_end)
cpu_end = range.cpu_addr + range.size - 1;
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 82222bd45ac6..42e3e2d8a2b8 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -359,6 +359,7 @@ int __init fdt_scan_reserved_mem(void)
err = __reserved_mem_reserve_reg(child, uname);
if (!err)
count++;
+
/*
* Save the nodes for the dynamically-placed regions
* into an array which will be used for allocation right
@@ -366,10 +367,17 @@ int __init fdt_scan_reserved_mem(void)
* or marked as no-map. This is done to avoid dynamically
* allocating from one of the statically-placed regions.
*/
- if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
- dynamic_nodes[dynamic_nodes_cnt] = child;
- dynamic_nodes_cnt++;
+ if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL))
+ continue;
+
+ if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) {
+ pr_err("too many defined dynamic regions, skip '%s'\n",
+ uname);
+ continue;
}
+
+ dynamic_nodes[dynamic_nodes_cnt] = child;
+ dynamic_nodes_cnt++;
}
for (int i = 0; i < dynamic_nodes_cnt; i++) {
const char *uname;
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 98e1db751132..f7389b5437df 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -680,21 +680,12 @@ static int imx_pcie_attach_pd(struct device *dev)
static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
- if (enable) {
- /* power up core phy and enable ref clock */
- regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
- /*
- * The async reset input need ref clock to sync internally,
- * when the ref clock comes after reset, internal synced
- * reset time is too short, cannot meet the requirement.
- * Add a ~10us delay here.
- */
- usleep_range(10, 100);
- regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
- } else {
- regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
- regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
- }
+ if (enable)
+ regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN);
+ else
+ regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_REF_CLK_EN);
return 0;
}
@@ -825,8 +816,16 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
{
+ if (assert)
+ regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD);
+ else
+ regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD);
+
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST,
assert ? IMX6Q_GPR1_PCIE_SW_RST : 0);
+
if (!assert)
usleep_range(200, 500);
@@ -835,11 +834,15 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
{
- if (!assert)
- return 0;
+ if (assert)
+ regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD);
+ else
+ regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
+ IMX6Q_GPR1_PCIE_TEST_PD);
- regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD);
- regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN);
+ if (!assert)
+ usleep_range(200, 500);
return 0;
}
@@ -1445,6 +1448,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
return 0;
err_phy_off:
+ imx_pcie_assert_core_reset(imx_pcie);
phy_power_off(imx_pcie->phy);
err_phy_exit:
phy_exit(imx_pcie->phy);
@@ -1466,6 +1470,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct imx_pcie *imx_pcie = to_imx_pcie(pci);
+ imx_pcie_assert_core_reset(imx_pcie);
if (imx_pcie->phy) {
if (phy_power_off(imx_pcie->phy))
dev_err(pci->dev, "unable to power off PHY\n");
@@ -1995,7 +2000,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
.flags = IMX_PCIE_FLAG_IMX_PHY |
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
IMX_PCIE_FLAG_BROKEN_SUSPEND |
- IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
+ IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
+ IMX_PCIE_FLAG_KEEP_MSI_CAP,
.dbi_length = 0x200,
.gpr = "fsl,imx6q-iomuxc-gpr",
.ltssm_off = IOMUXC_GPR12,
@@ -2011,7 +2017,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
.flags = IMX_PCIE_FLAG_IMX_PHY |
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
IMX_PCIE_FLAG_SKIP_L23_READY |
- IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
+ IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
+ IMX_PCIE_FLAG_KEEP_MSI_CAP,
.gpr = "fsl,imx6q-iomuxc-gpr",
.ltssm_off = IOMUXC_GPR12,
.ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2,
@@ -2026,7 +2033,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
.flags = IMX_PCIE_FLAG_IMX_PHY |
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
IMX_PCIE_FLAG_SKIP_L23_READY |
- IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
+ IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
+ IMX_PCIE_FLAG_KEEP_MSI_CAP,
.dbi_length = 0x200,
.gpr = "fsl,imx6q-iomuxc-gpr",
.ltssm_off = IOMUXC_GPR12,
diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
index 0bec8657149c..275df38256de 100644
--- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c
@@ -221,7 +221,7 @@ disable_repeater:
disable_vreg:
regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs);
- return 0;
+ return ret;
}
static int m31eusb2_phy_exit(struct phy *uphy)
diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
index 2b0f152f5470..7843356a4dd4 100644
--- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -452,9 +452,6 @@ static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsign
struct device_node *np = priv->dev->of_node;
u32 val;
- if (!priv->enable_ssc)
- return;
-
/* Set SSC downward spread spectrum for PCIe and USB3 */
if (priv->type == PHY_TYPE_PCIE || priv->type == PHY_TYPE_USB3) {
val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD);
@@ -471,6 +468,9 @@ static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsign
RK3568_PHYREG32);
}
+ if (!priv->enable_ssc)
+ return;
+
/* Enable SSC */
val = readl(priv->mmio + RK3568_PHYREG8);
val |= RK3568_PHYREG8_SSC_EN;
diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c
index fe6b4925d166..240626b55475 100644
--- a/drivers/phy/xilinx/phy-zynqmp.c
+++ b/drivers/phy/xilinx/phy-zynqmp.c
@@ -53,7 +53,7 @@
#define L0_TM_DIG_6 0x106c
#define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f
#define L0_TX_DIG_61 0x00f4
-#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f
+#define L0_TM_DISABLE_SCRAMBLE_ENCODER (BIT(3) | GENMASK(1, 0))
/* PLL Test Mode register parameters */
#define L0_TM_PLL_DIG_37 0x2094
@@ -502,11 +502,30 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy)
}
}
-/* Bypass (de)scrambler and 8b/10b decoder and encoder. */
-static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy)
+/**
+ * xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior
+ * @gtr_phy: pointer to lane context
+ * @bypass: true to enable scrambler/encoder bypass (SATA/SGMII),
+ * false to disable scrambler/encoder bypass (USB3)
+ *
+ * Uses RMW to preserve reserved and unrelated register fields.
+ */
+static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy,
+ bool bypass)
{
- xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER);
- xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER);
+ if (bypass) {
+ xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+ L0_TM_DIS_DESCRAMBLE_DECODER,
+ L0_TM_DIS_DESCRAMBLE_DECODER);
+ xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER);
+ } else {
+ xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6,
+ L0_TM_DIS_DESCRAMBLE_DECODER, 0);
+ xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61,
+ L0_TM_DISABLE_SCRAMBLE_ENCODER, 0);
+ }
}
/* DP-specific initialization. */
@@ -527,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy)
{
struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
- xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy, true);
writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET);
}
@@ -543,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy)
xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val);
xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val);
- xpsgtr_bypass_scrambler_8b10b(gtr_phy);
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy, true);
}
/* Configure TX de-emphasis and margining for DP. */
@@ -658,12 +677,13 @@ static int xpsgtr_phy_init(struct phy *phy)
{
struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy);
struct xpsgtr_dev *gtr_dev = gtr_phy->dev;
- int ret = 0;
+ int ret;
mutex_lock(&gtr_dev->gtr_mutex);
/* Configure and enable the clock when peripheral phy_init call */
- if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]))
+ ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]);
+ if (ret)
goto out;
/* Skip initialization if not required. */
@@ -673,7 +693,7 @@ static int xpsgtr_phy_init(struct phy *phy)
if (gtr_dev->tx_term_fix) {
ret = xpsgtr_phy_tx_term_fix(gtr_phy);
if (ret < 0)
- goto out;
+ goto out_disable_clk;
gtr_dev->tx_term_fix = false;
}
@@ -687,7 +707,7 @@ static int xpsgtr_phy_init(struct phy *phy)
*/
ret = xpsgtr_configure_pll(gtr_phy);
if (ret)
- goto out;
+ goto out_disable_clk;
xpsgtr_lane_set_protocol(gtr_phy);
@@ -703,8 +723,16 @@ static int xpsgtr_phy_init(struct phy *phy)
case ICM_PROTOCOL_SGMII:
xpsgtr_phy_init_sgmii(gtr_phy);
break;
+
+ case ICM_PROTOCOL_USB:
+ xpsgtr_bypass_scrambler_8b10b(gtr_phy, false);
+ break;
}
+ goto out;
+
+out_disable_clk:
+ clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]);
out:
mutex_unlock(&gtr_dev->gtr_mutex);
return ret;
@@ -1039,6 +1067,12 @@ static int xpsgtr_probe(struct platform_device *pdev)
return PTR_ERR(provider);
}
+ gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
+ sizeof(save_reg_address),
+ GFP_KERNEL);
+ if (!gtr_dev->saved_regs)
+ return -ENOMEM;
+
pm_runtime_set_active(gtr_dev->dev);
pm_runtime_enable(gtr_dev->dev);
@@ -1048,12 +1082,6 @@ static int xpsgtr_probe(struct platform_device *pdev)
return ret;
}
- gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev,
- sizeof(save_reg_address),
- GFP_KERNEL);
- if (!gtr_dev->saved_regs)
- return -ENOMEM;
-
return 0;
}
diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c
index c1c12a447178..180bc137a863 100644
--- a/drivers/power/supply/bq25890_charger.c
+++ b/drivers/power/supply/bq25890_charger.c
@@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = {
145, 140, 130, 120, 115, 110, 100, 90,
80, 70, 60, 50, 40, 30, 20, 10,
0, -10, -20, -30, -40, -60, -70, -80,
- -90, -10, -120, -140, -150, -170, -190, -210,
+ -90, -100, -120, -140, -150, -170, -190, -210,
};
#define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl)
diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c
index ced07f71e0a8..ef735f86394e 100644
--- a/drivers/power/supply/macsmc-power.c
+++ b/drivers/power/supply/macsmc-power.c
@@ -86,6 +86,11 @@ struct macsmc_power {
bool has_ch0i; /* Force discharge (Older firmware) */
bool has_ch0c; /* Inhibit charge (Older firmware) */
bool has_chte; /* Inhibit charge (Modern firmware) */
+ /*
+ * Battery critical key is 1 byte and charge key is little endian
+ * (Modern firmware)
+ */
+ bool fw_ge_27;
u8 num_cells;
int nominal_voltage_mv;
@@ -273,6 +278,20 @@ static int macsmc_battery_get_date(const char *s, int *out)
return 0;
}
+static int macsmc_battery_read_bcf0(struct macsmc_power *power, u32 *val)
+{
+ u8 tval = 0;
+ int ret;
+
+ if (power->fw_ge_27) {
+ ret = apple_smc_read_u8(power->smc, SMC_KEY(BCF0), &tval);
+ *val = tval;
+ return ret;
+ }
+
+ return apple_smc_read_u32(power->smc, SMC_KEY(BCF0), val);
+}
+
static int macsmc_battery_get_capacity_level(struct macsmc_power *power)
{
bool flag;
@@ -280,7 +299,7 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power)
int ret;
/* Check for emergency shutdown condition */
- if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val)
+ if (macsmc_battery_read_bcf0(power, &val) >= 0 && val)
return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
/* Check AC status for whether we could boot in this state */
@@ -303,6 +322,12 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power)
return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
}
+static s16 macsmc_swap_b0rm(struct macsmc_power *power, s16 b0rm)
+{
+ /* B0RM was Big Endian, likely pass through from TI gas gauge */
+ return power->fw_ge_27 ? b0rm : (s16)swab16(b0rm);
+}
+
static int macsmc_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -397,8 +422,7 @@ static int macsmc_battery_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16);
- /* B0RM is Big Endian, likely pass through from TI gas gauge */
- val->intval = (s16)swab16(vu16) * 1000;
+ val->intval = macsmc_swap_b0rm(power, vu16) * 1000;
break;
case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16);
@@ -410,8 +434,7 @@ static int macsmc_battery_get_property(struct power_supply *psy,
break;
case POWER_SUPPLY_PROP_ENERGY_NOW:
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16);
- /* B0RM is Big Endian, likely pass through from TI gas gauge */
- val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv;
+ val->intval = macsmc_swap_b0rm(power, vu16) * power->nominal_voltage_mv;
break;
case POWER_SUPPLY_PROP_TEMP:
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16);
@@ -577,7 +600,7 @@ static void macsmc_power_critical_work(struct work_struct *wrk)
* Check if SMC flagged the battery as empty.
* We trigger a graceful shutdown to let the OS save data.
*/
- if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) {
+ if (macsmc_battery_read_bcf0(power, &bcf0) == 0 && bcf0 != 0) {
power->orderly_shutdown_triggered = true;
dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n");
orderly_poweroff(true);
@@ -616,6 +639,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
struct power_supply_config psy_cfg = {};
+ struct apple_smc_key_info info;
struct macsmc_power *power;
bool has_battery = false;
bool has_ac_adapter = false;
@@ -714,6 +738,20 @@ static int macsmc_power_probe(struct platform_device *pdev)
if (apple_smc_key_exists(smc, SMC_KEY(CH0I)))
power->has_ch0i = true;
+ ret = apple_smc_get_key_info(power->smc, SMC_KEY(BCF0), &info);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to determine BCF0 key size\n");
+ return ret;
+ }
+ if (info.size == 1)
+ power->fw_ge_27 = true;
+ else if (info.size == 4)
+ power->fw_ge_27 = false;
+ else {
+ dev_err(&pdev->dev, "Unexpected BCF0 key size %d\n", info.size);
+ return -EIO;
+ }
+
/* Reset "Optimised Battery Charging" flags to default state */
if (power->has_chte)
apple_smc_write_u32(smc, SMC_KEY(CHTE), 0);
@@ -766,7 +804,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells;
/* Enable critical shutdown notifications by reading status once */
- apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32);
+ macsmc_battery_read_bcf0(power, &val32);
psy_cfg.drv_data = power;
power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg);
diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c
index e94d53b36aa4..03ac569e185c 100644
--- a/drivers/power/supply/max17040_battery.c
+++ b/drivers/power/supply/max17040_battery.c
@@ -405,7 +405,11 @@ static int max17040_get_property(struct power_supply *psy,
val->intval = chip->low_soc_alert;
break;
case POWER_SUPPLY_PROP_STATUS:
- power_supply_get_property_from_supplier(psy, psp, val);
+ ret = power_supply_get_property_from_supplier(psy, psp, val);
+ if (ret == -ENODEV)
+ val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+ else if (ret)
+ return ret;
break;
case POWER_SUPPLY_PROP_TEMP:
if (!chip->channel_temp)
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 3181c06d91ce..d8d912a3b3fe 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -21,6 +21,7 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/vmalloc.h>
+#include <linux/delay.h>
#include <asm/machine.h>
#include <asm/ccwdev.h>
@@ -2511,6 +2512,13 @@ static inline int _dasd_term_running_cqr(struct dasd_device *device)
if (list_empty(&device->ccw_queue))
return 0;
cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
+ /*
+ * Path verification requests must not be terminated. They are critical
+ * for bringing paths back online. Terminating them would cause rc=-EIO
+ * because CLEARED requests skip the retry path.
+ */
+ if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
+ return -EAGAIN;
rc = device->discipline->term_IO(cqr);
if (!rc)
/*
@@ -2535,7 +2543,11 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
return -EIO;
}
spin_lock_irq(get_ccwdev_lock(device->cdev));
- rc = _dasd_term_running_cqr(device);
+ while ((rc = _dasd_term_running_cqr(device)) == -EAGAIN) {
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ msleep(1);
+ spin_lock_irq(get_ccwdev_lock(device->cdev));
+ }
if (rc) {
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 74fe73b5738a..d356a9f8f016 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -20,6 +20,7 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/io.h>
+#include <linux/overflow.h>
#include <asm/css_chars.h>
#include <asm/machine.h>
@@ -3475,11 +3476,11 @@ static int dasd_eckd_check_device_format(struct dasd_device *base,
{
struct dasd_eckd_private *private = base->private;
struct eckd_count *fmt_buffer;
- struct irb irb;
+ size_t fmt_buffer_size;
+ unsigned int trkcount;
int rpt_max, rpt_exp;
- int fmt_buffer_size;
+ struct irb irb;
int trk_per_cyl;
- int trkcount;
int tpm = 0;
int rc;
@@ -3490,7 +3491,9 @@ static int dasd_eckd_check_device_format(struct dasd_device *base,
rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize);
trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1;
- fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count);
+ if (check_mul_overflow(trkcount, rpt_max, &fmt_buffer_size) ||
+ check_mul_overflow(fmt_buffer_size, sizeof(struct eckd_count), &fmt_buffer_size))
+ return -EINVAL;
fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA);
if (!fmt_buffer)
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index c85ee42732a3..e5b8b413f5ab 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -324,7 +324,7 @@ out_err:
static int dasd_release_space(struct dasd_device *device,
struct format_data_t *rdata)
{
- if (!device->discipline->is_ese && !device->discipline->is_ese(device))
+ if (!device->discipline->is_ese || !device->discipline->is_ese(device))
return -ENOTSUPP;
if (!device->discipline->release_space)
return -ENOTSUPP;
diff --git a/drivers/s390/crypto/pkey_cca.c b/drivers/s390/crypto/pkey_cca.c
index 4bc47952324e..e8dc2f77c137 100644
--- a/drivers/s390/crypto/pkey_cca.c
+++ b/drivers/s390/crypto/pkey_cca.c
@@ -236,22 +236,16 @@ static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_AES) {
/* CCA AES data key */
- if (keylen < sizeof(struct secaeskeytoken))
- return -EINVAL;
- if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0))
+ if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0))
return -EINVAL;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
hdr->version == TOKVER_CCA_VLSC) {
/* CCA AES cipher key */
- if (keylen < hdr->len)
- return -EINVAL;
if (cca_check_secaescipherkey(pkey_dbf_info,
- 3, key, 0, 1))
+ 3, key, keylen, 0, 1))
return -EINVAL;
} else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
/* CCA ECC (private) key */
- if (keylen < sizeof(struct eccprivkeytoken))
- return -EINVAL;
if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1))
return -EINVAL;
} else {
@@ -484,7 +478,7 @@ static int cca_verifykey(const u8 *key, u32 keylen,
hdr->version == TOKVER_CCA_AES) {
struct secaeskeytoken *t = (struct secaeskeytoken *)key;
- rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0);
+ rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0);
if (rc)
goto out;
*keytype = PKEY_TYPE_CCA_DATA;
@@ -512,7 +506,8 @@ static int cca_verifykey(const u8 *key, u32 keylen,
hdr->version == TOKVER_CCA_VLSC) {
struct cipherkeytoken *t = (struct cipherkeytoken *)key;
- rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1);
+ rc = cca_check_secaescipherkey(pkey_dbf_info, 3,
+ key, keylen, 0, 1);
if (rc)
goto out;
*keytype = PKEY_TYPE_CCA_CIPHER;
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index f57189c2b839..ec6a4c2f9f04 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -879,6 +879,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
+ domain = array_index_nospec(domain, AP_DOMAINS);
if (!test_bit_inv(domain, perms->adm)) {
rc = -ENODEV;
goto out;
@@ -1077,8 +1078,9 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms,
print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1,
ap_msg.msg, ap_msg.len, false);
- if (perms != &ap_perms && domain < AUTOSEL_DOM) {
+ if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
+ domain = array_index_nospec(domain, AP_DOMAINS);
if (!test_bit_inv(domain, perms->adm)) {
rc = -ENODEV;
goto out;
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
index 84936a795b95..86d2ee78c9f4 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -62,12 +62,18 @@ static DEFINE_MUTEX(dev_status_mem_mutex);
* also checked. Returns 0 on success or errno value on failure.
*/
int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
- const u8 *token, int keybitsize)
+ const u8 *token, u32 keysize, int keybitsize)
{
struct secaeskeytoken *t = (struct secaeskeytoken *)token;
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
+ if (keysize < sizeof(*t)) {
+ if (dbg)
+ DBF("%s keysize %u < min token size %zu\n",
+ __func__, keysize, sizeof(*t));
+ return -EINVAL;
+ }
if (t->type != TOKTYPE_CCA_INTERNAL) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -101,14 +107,20 @@ EXPORT_SYMBOL(cca_check_secaeskeytoken);
* Returns 0 on success or errno value on failure.
*/
int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
- const u8 *token, int keybitsize,
- int checkcpacfexport)
+ const u8 *token, u32 keysize,
+ int keybitsize, int checkcpacfexport)
{
struct cipherkeytoken *t = (struct cipherkeytoken *)token;
bool keybitsizeok = true;
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
+ if (keysize < sizeof(*t)) {
+ if (dbg)
+ DBF("%s keysize %u < min token size %zu\n",
+ __func__, keysize, sizeof(*t));
+ return -EINVAL;
+ }
if (t->type != TOKTYPE_CCA_INTERNAL) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -121,6 +133,18 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
__func__, (int)t->version, TOKVER_CCA_VLSC);
return -EINVAL;
}
+ if (t->len > keysize) {
+ if (dbg)
+ DBF("%s token check failed, len %d > keysize %u\n",
+ __func__, (int)t->len, keysize);
+ return -EINVAL;
+ }
+ if (t->len < sizeof(*t)) {
+ if (dbg)
+ DBF("%s token check failed, len %d < min token size %zu\n",
+ __func__, (int)t->len, sizeof(*t));
+ return -EINVAL;
+ }
if (t->algtype != 0x02) {
if (dbg)
DBF("%s token check failed, algtype 0x%02x != 0x02\n",
@@ -195,6 +219,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
#define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
+ if (keysize < sizeof(*t)) {
+ if (dbg)
+ DBF("%s keysize %u < min token size %zu\n",
+ __func__, keysize, sizeof(*t));
+ return -EINVAL;
+ }
if (t->type != TOKTYPE_CCA_INTERNAL_PKA) {
if (dbg)
DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -207,6 +237,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
__func__, (int)t->len, keysize);
return -EINVAL;
}
+ if (t->len < sizeof(*t)) {
+ if (dbg)
+ DBF("%s token check failed, len %d < min token size %zu\n",
+ __func__, (int)t->len, sizeof(*t));
+ return -EINVAL;
+ }
if (t->secid != 0x20) {
if (dbg)
DBF("%s token check failed, secid 0x%02x != 0x20\n",
@@ -443,7 +479,8 @@ int cca_genseckey(u16 cardnr, u16 domain,
/* check secure key token */
rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
- prepparm->lv3.keyblock.tok, 8 * keysize);
+ prepparm->lv3.keyblock.tok,
+ seckeysize, 8 * keysize);
if (rc) {
rc = -EIO;
goto out;
@@ -582,7 +619,8 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
/* check secure key token */
rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
- prepparm->lv3.keyblock.tok, 8 * keysize);
+ prepparm->lv3.keyblock.tok,
+ seckeysize, 8 * keysize);
if (rc) {
rc = -EIO;
goto out;
@@ -842,6 +880,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
} kb;
} __packed * prepparm;
struct cipherkeytoken *t;
+ u32 keybuflen;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
@@ -936,23 +975,28 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
}
/* and some checks on the generated key */
+ t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key;
+ if (prepparm->kb.tlv1.len < 2 * sizeof(uint16_t) + sizeof(*t)) {
+ rc = -EIO;
+ goto out;
+ }
+ keybuflen = prepparm->kb.tlv1.len - 2 * sizeof(uint16_t);
rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR,
prepparm->kb.tlv1.gen_key,
- keybitsize, 1);
+ keybuflen, keybitsize, 1);
if (rc) {
rc = -EIO;
goto out;
}
/* copy the generated vlsc key token */
- t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key;
if (keybuf) {
- if (*keybufsize >= t->len)
- memcpy(keybuf, t, t->len);
+ if (*keybufsize >= keybuflen)
+ memcpy(keybuf, t, keybuflen);
else
rc = -EINVAL;
}
- *keybufsize = t->len;
+ *keybufsize = keybuflen;
out:
free_cprbmem(mem, PARMBSIZE, false, xflags);
@@ -971,7 +1015,8 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
int clr_key_bit_size,
u8 *key_token,
int *key_token_size,
- u32 xflags)
+ u32 xflags,
+ bool scrub)
{
int rc, n;
u8 *mem, *ptr;
@@ -1111,7 +1156,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
*key_token_size = t->len;
out:
- free_cprbmem(mem, PARMBSIZE, false, xflags);
+ free_cprbmem(mem, PARMBSIZE, scrub, xflags);
return rc;
}
@@ -1162,28 +1207,32 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
* 4/4 COMPLETE the secure cipher key import
*/
rc = _ip_cprb_helper(card, dom, "AES ", "FIRST ", "MIN3PART",
- exorbuf, keybitsize, token, &tokensize, xflags);
+ exorbuf, keybitsize, token, &tokensize,
+ xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL,
- clrkey, keybitsize, token, &tokensize, xflags);
+ clrkey, keybitsize, token, &tokensize,
+ xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL,
- exorbuf, keybitsize, token, &tokensize, xflags);
+ exorbuf, keybitsize, token, &tokensize,
+ xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
goto out;
}
rc = _ip_cprb_helper(card, dom, "AES ", "COMPLETE", NULL,
- NULL, keybitsize, token, &tokensize, xflags);
+ NULL, keybitsize, token, &tokensize,
+ xflags, true);
if (rc) {
ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n",
__func__, rc);
@@ -1200,6 +1249,8 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags,
*keybufsize = tokensize;
out:
+ memzero_explicit(exorbuf, sizeof(exorbuf));
+ memzero_explicit(mem, CPRB_MEMPOOL_ITEM_SIZE);
mempool_free(mem, cprb_mempool);
return rc;
}
@@ -1261,6 +1312,9 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
} __packed * prepparm;
int keytoklen = ((struct cipherkeytoken *)ckey)->len;
+ if (keytoklen > PARMBSIZE - sizeof(struct aureqparm))
+ return -EINVAL;
+
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
&preqcblk, &prepcblk, xflags);
@@ -1425,6 +1479,9 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
} __packed * prepparm;
int keylen = ((struct eccprivkeytoken *)key)->len;
+ if (keylen > PARMBSIZE - sizeof(struct aureqparm))
+ return -EINVAL;
+
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
&preqcblk, &prepcblk, xflags);
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h
index 07bbb1c20022..a4534f8b2f1d 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.h
+++ b/drivers/s390/crypto/zcrypt_ccamisc.h
@@ -136,7 +136,7 @@ struct eccprivkeytoken {
* also checked. Returns 0 on success or errno value on failure.
*/
int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
- const u8 *token, int keybitsize);
+ const u8 *token, u32 keysize, int keybitsize);
/*
* Simple check if the token is a valid CCA secure AES cipher key
@@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
* Returns 0 on success or errno value on failure.
*/
int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
- const u8 *token, int keybitsize,
- int checkcpacfexport);
+ const u8 *token, u32 keysize,
+ int keybitsize, int checkcpacfexport);
/*
* Simple check if the token is a valid CCA secure ECC private
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 8ff7db7921b5..fea573e00f9d 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
mempool_destroy(adapter->pool.erp_req);
+ mempool_destroy(adapter->pool.gid_pn_req);
mempool_destroy(adapter->pool.scsi_req);
mempool_destroy(adapter->pool.scsi_abort);
mempool_destroy(adapter->pool.qtcb_pool);
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 213d5b5dea94..8a2500993e19 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device)
return rc;
}
phys_init_v3_hw(hisi_hba);
-
- /*
- * If a directly-attached disk is removed during suspend, a deadlock
- * may occur, as the PHYE_RESUME_TIMEOUT processing will require the
- * hisi_hba->device to be active, which can only happen when resume
- * completes. So don't wait for the HA event workqueue to drain upon
- * resume.
- */
- sas_resume_ha_no_sync(sha);
+ sas_resume_ha(sha);
clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags);
dev_warn(dev, "end of resuming controller\n");
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 160f02f2f51d..5cbc51899de0 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -918,7 +918,7 @@ invalid_datalen:
}
senselen = get_unaligned_be16(data);
- if (datalen < senselen)
+ if (datalen < senselen + 2)
goto invalid_datalen;
memcpy(sc->sense_buffer, data + 2,
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index e90805ba868f..7223bb18b048 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
spin_unlock(&conn->session->back_lock);
break;
- case ISCSI_OP_SCSI_CMD_RSP:
- if (tcp_conn->in.datalen) {
- iscsi_tcp_data_recv_prep(tcp_conn);
- return 0;
- }
- rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
- break;
case ISCSI_OP_R2T:
if (ahslen) {
rc = ISCSI_ERR_AHSLEN;
@@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
}
rc = iscsi_tcp_r2t_rsp(conn, hdr);
break;
+ case ISCSI_OP_SCSI_CMD_RSP:
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
case ISCSI_OP_REJECT:
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index 0bec236f0fb5..c3f3d05b46de 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha)
}
}
-static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
+void sas_resume_ha(struct sas_ha_struct *ha)
{
const unsigned long tmo = msecs_to_jiffies(25000);
int i;
@@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n",
i, i > 1 ? "s" : "");
wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo);
+
+ /*
+ * All phys are back up or timed out. Turn on I/O and drain
+ * pending work.
+ */
+ scsi_unblock_requests(ha->shost);
+ sas_drain_work(ha);
+
+ /*
+ * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler
+ * calls sas_deform_port() -> sas_destruct_devices(), which removes
+ * SCSI devices and, for LLDDs using device_link() PM sync, waits
+ * for the host to be runtime-active. Sending it before the drain
+ * would deadlock: the drain waits for the handler, the handler
+ * waits for host resume, and host resume waits for the drain to
+ * finish.
+ */
for (i = 0; i < ha->num_phys; i++) {
struct asd_sas_phy *phy = ha->sas_phy[i];
@@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
}
}
- /* all phys are back up or timed out, turn on i/o so we can
- * flush out disks that did not return
- */
- scsi_unblock_requests(ha->shost);
- if (drain)
- sas_drain_work(ha);
clear_bit(SAS_HA_RESUMING, &ha->state);
sas_queue_deferred_work(ha);
@@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain)
*/
sas_resume_insert_broadcast_ha(ha);
}
-
-void sas_resume_ha(struct sas_ha_struct *ha)
-{
- _sas_resume_ha(ha, true);
-}
EXPORT_SYMBOL(sas_resume_ha);
-/* A no-sync variant, which does not call sas_drain_ha(). */
-void sas_resume_ha_no_sync(struct sas_ha_struct *ha)
-{
- _sas_resume_ha(ha, false);
-}
-EXPORT_SYMBOL(sas_resume_ha_no_sync);
-
void sas_suspend_ha(struct sas_ha_struct *ha)
{
int i;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index c83282733ec4..97ac3db5dccb 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -502,6 +502,21 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd)
}
EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler);
+/*
+ * Handle deferred QCs in case of a command timeout.
+ * See ata_scsi_eh_timed_out() for details.
+ */
+enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd)
+{
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+
+ if (dev_is_sata(dev))
+ return ata_scsi_retry_deferred_qc(dev->sata_dev.ap, cmd);
+
+ return SCSI_EH_NOT_HANDLED;
+}
+EXPORT_SYMBOL_GPL(sas_eh_timed_out);
+
/* Try to reset a device */
static int try_to_reset_cmd_device(struct scsi_cmnd *cmd)
{
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 31b19ed1528e..681868716ebd 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -9,6 +9,7 @@
#include "mpi3mr.h"
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/sched/mm.h>
static int
mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason);
@@ -1287,11 +1288,14 @@ out_failed:
static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc)
{
struct kobj_uevent_env *env;
+ unsigned int noio_flag;
int ret;
+ noio_flag = memalloc_noio_save();
+
env = kzalloc_obj(*env);
if (!env)
- return;
+ goto out_restore;
ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name);
if (ret)
@@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc)
out_free:
kfree(env);
-
+out_restore:
+ memalloc_noio_restore(noio_flag);
}
/**
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9d1c9c41d0f9..643051332132 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -5890,6 +5890,7 @@ static int resp_report_zones(struct scsi_cmnd *scp,
u32 alloc_len, rep_opts, rep_len;
bool partial;
u64 lba, zs_lba;
+ u64 arr_len;
u8 *arr = NULL, *desc;
u8 *cmd = scp->cmnd;
struct sdeb_zone_state *zsp = NULL;
@@ -5911,9 +5912,12 @@ static int resp_report_zones(struct scsi_cmnd *scp,
return check_condition_result;
}
- rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD);
+ rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >>
+ ilog2(RZONES_DESC_HD);
+ rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones);
+ arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1);
- arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN);
+ arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN);
if (!arr) {
mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC,
INSUFF_RES_ASCQ);
diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile
index b35d74592964..b5188dcde37a 100644
--- a/drivers/soc/aspeed/Makefile
+++ b/drivers/soc/aspeed/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o
obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o
obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o
+CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y
diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c
index 28e491fffc7d..b09dca34099f 100644
--- a/drivers/soc/aspeed/aspeed-lpc-snoop.c
+++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c
@@ -11,6 +11,7 @@
*/
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/dev_printk.h>
#include <linux/interrupt.h>
@@ -74,7 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg {
struct aspeed_lpc_snoop_channel {
const struct aspeed_lpc_snoop_channel_cfg *cfg;
bool enabled;
- struct kfifo fifo;
+ spinlock_t lock;
+ struct kfifo fifo __guarded_by(&lock);
wait_queue_head_t wq;
struct miscdevice miscdev;
};
@@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
+ u8 *buf __free(kfree) = NULL;
unsigned int copied;
int ret = 0;
@@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer,
if (ret == -ERESTARTSYS)
return -EINTR;
}
- ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
- if (ret)
- return ret;
+
+ count = min_t(size_t, count, SNOOP_FIFO_SIZE);
+
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock);
+ if (copied && copy_to_user(buffer, buf, copied))
+ return -EFAULT;
return copied;
}
@@ -151,11 +161,13 @@ static const struct file_operations snoop_fops = {
/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
{
- if (!kfifo_initialized(&chan->fifo))
- return;
- if (kfifo_is_full(&chan->fifo))
- kfifo_skip(&chan->fifo);
- kfifo_put(&chan->fifo, val);
+ scoped_guard(spinlock, &chan->lock) {
+ if (!kfifo_initialized(&chan->fifo))
+ return;
+ if (kfifo_is_full(&chan->fifo))
+ kfifo_skip(&chan->fifo);
+ kfifo_put(&chan->fifo, val);
+ }
wake_up_interruptible(&chan->wq);
}
@@ -239,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev,
if (!channel->miscdev.name)
return -ENOMEM;
- rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
- if (rc)
- return rc;
+ scoped_guard(spinlock_init, &channel->lock) {
+ rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
+ if (rc)
+ return rc;
+ }
rc = misc_register(&channel->miscdev);
if (rc)
diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 9b4e5b7013ae..af1a05e78492 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi)
/**
* cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
+ * @ctlr: Pointer to the spi_controller structure
* @xspi: Pointer to the cdns_spi structure
* @ntx: Number of bytes to pack into the TX FIFO
* @nrx: Number of bytes to drain from the RX FIFO
*/
-static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
+static void cdns_spi_process_fifo(struct spi_controller *ctlr,
+ struct cdns_spi *xspi, int ntx, int nrx)
{
ntx = clamp(ntx, 0, xspi->tx_bytes);
nrx = clamp(nrx, 0, xspi->rx_bytes);
@@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
}
if (ntx) {
+ /* When xspi in busy condition, bytes may send failed,
+ * then spi control didn't work thoroughly, add one byte
+ * delay. Only in host mode; in target mode this delay
+ * causes data corruption as the target fails to prepare
+ * data in time.
+ */
+ if (!spi_controller_is_target(ctlr) &&
+ (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL))
+ udelay(10);
+
cdns_spi_writer(xspi);
ntx--;
}
@@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id)
cdns_spi_write(xspi, CDNS_SPI_THLD, 1);
if (xspi->tx_bytes) {
- cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt);
+ cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt);
} else {
/* Fixed delay due to controller limitation with
* RX_NEMPTY incorrect status
* Xilinx AR:65885 contains more details
*/
udelay(10);
- cdns_spi_process_fifo(xspi, 0, trans_cnt);
+ cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt);
cdns_spi_write(xspi, CDNS_SPI_IDR,
CDNS_SPI_IXR_DEFAULT);
spi_finalize_current_transfer(ctlr);
@@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1);
}
- /* When xspi in busy condition, bytes may send failed,
- * then spi control didn't work thoroughly, add one byte delay
- */
- if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
- udelay(10);
-
xspi->n_bytes = cdns_spi_n_bytes(transfer);
xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
- cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
+ cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0);
cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
return transfer->len;
diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c
index 1e36ae084dd8..4425132432ec 100644
--- a/drivers/spi/spi-nxp-fspi.c
+++ b/drivers/spi/spi-nxp-fspi.c
@@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data {
unsigned int quirks;
unsigned int lut_num;
bool little_endian;
+ /*
+ * The max clock rate (Hz) that FlexSPI can output to the device
+ * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero.
+ * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode.
+ */
+ unsigned long max_sdr_rate;
+ /*
+ * The max clock rate (Hz) that FlexSPI can output to the device
+ * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero.
+ * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz.
+ */
+ unsigned long max_dtr_rate;
};
static struct nxp_fspi_devtype_data lx2160a_data = {
@@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = {
.quirks = FSPI_QUIRK_DISABLE_DTR,
.lut_num = 32,
.little_endian = true, /* little-endian */
+ /*
+ * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk.
+ */
+ .max_sdr_rate = 100000000,
};
static struct nxp_fspi_devtype_data imx8mm_data = {
@@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = {
.quirks = 0,
.lut_num = 32,
.little_endian = true, /* little-endian */
+ /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */
+ .max_sdr_rate = 66000000,
+ .max_dtr_rate = 166000000,
+};
+
+static struct nxp_fspi_devtype_data imx8mp_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .lut_num = 32,
+ .little_endian = true, /* little-endian */
+ /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */
+ .max_sdr_rate = 66000000,
+ .max_dtr_rate = 166000000,
};
static struct nxp_fspi_devtype_data imx8qxp_data = {
@@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = {
.quirks = 0,
.lut_num = 32,
.little_endian = true, /* little-endian */
+ /*
+ * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz.
+ * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits.
+ */
+ .max_sdr_rate = 60000000,
+ .max_dtr_rate = 200000000,
};
static struct nxp_fspi_devtype_data imx8dxl_data = {
@@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = {
.quirks = FSPI_QUIRK_USE_IP_ONLY,
.lut_num = 32,
.little_endian = true, /* little-endian */
+ /*
+ * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz,
+ * DDR RXCLKSRC=3 max 200MHz.
+ */
+ .max_sdr_rate = 60000000,
+ .max_dtr_rate = 200000000,
};
static struct nxp_fspi_devtype_data imx8ulp_data = {
@@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = {
.quirks = 0,
.lut_num = 16,
.little_endian = true, /* little-endian */
+ /*
+ * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode:
+ * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz.
+ * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR
+ * but is not the default use case.
+ */
+ .max_sdr_rate = 60000000,
+ .max_dtr_rate = 166000000,
+};
+
+static struct nxp_fspi_devtype_data imx95_data = {
+ .rxfifo = SZ_512, /* (64 * 64 bits) */
+ .txfifo = SZ_1K, /* (128 * 64 bits) */
+ .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */
+ .quirks = 0,
+ .lut_num = 32,
+ .little_endian = true, /* little-endian */
+ /*
+ * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz,
+ * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode).
+ */
+ .max_sdr_rate = 66000000,
+ .max_dtr_rate = 200000000,
};
struct nxp_fspi {
@@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f,
reg = fspi_readl(f, f->iobase + FSPI_MCR0);
if (op_is_dtr) {
reg |= FSPI_MCR0_RXCLKSRC(3);
- f->max_rate = 166000000;
+ /*
+ * Use the SoC-specific DTR max rate if provided, otherwise
+ * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9).
+ */
+ f->max_rate = f->devtype_data->max_dtr_rate ?
+ f->devtype_data->max_dtr_rate : 166000000;
} else { /*select mode 0 */
reg &= ~FSPI_MCR0_RXCLKSRC(3);
- f->max_rate = 66000000;
+ /*
+ * Use the SoC-specific SDR max rate if provided, otherwise
+ * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9).
+ */
+ f->max_rate = f->devtype_data->max_sdr_rate ?
+ f->devtype_data->max_sdr_rate : 66000000;
}
fspi_writel(f, reg, f->iobase + FSPI_MCR0);
}
@@ -783,14 +859,15 @@ static void nxp_fspi_dll_override(struct nxp_fspi *f)
* Value for rest of the CS FLSHxxCR0 register would be zero.
*
*/
-static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
- const struct spi_mem_op *op)
+static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
+ const struct spi_mem_op *op)
{
/* flexspi only support one DTR mode: 8D-8D-8D */
bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr;
unsigned long rate = op->max_freq;
int ret;
uint64_t size_kb;
+ u32 reg;
/*
* Return when following condition all meet,
@@ -804,7 +881,7 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
if ((f->selected == spi_get_chipselect(spi, 0)) &&
(!!(f->flags & FSPI_DTR_MODE) == op_is_dtr) &&
(f->pre_op_rate == op->max_freq))
- return;
+ return 0;
/* Reset FLSHxxCR0 registers */
fspi_writel(f, 0, f->iobase + FSPI_FLSHA1CR0);
@@ -820,6 +897,15 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0));
+ /*
+ * Per the FlexSPI reference manual (initialization sequence), MCR0 and
+ * the DLL control registers should be configured while the module is in
+ * stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the
+ * RX sample clock source and the DLL, then exit stop mode afterwards.
+ */
+ reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+ fspi_writel(f, reg | FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
+
nxp_fspi_select_rx_sample_clk_source(f, op_is_dtr);
rate = min(f->max_rate, op->max_freq);
@@ -836,12 +922,19 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
nxp_fspi_clk_disable_unprep(f);
ret = clk_set_rate(f->clk, rate);
- if (ret)
- return;
+ if (ret) {
+ /*
+ * clk_set_rate() failed with the clocks already disabled.
+ * Re-enable them so the enable count matches what the caller's
+ * pm_runtime_put() (runtime_suspend) will drop.
+ */
+ nxp_fspi_clk_prep_enable(f);
+ return ret;
+ }
ret = nxp_fspi_clk_prep_enable(f);
if (ret)
- return;
+ return ret;
/*
* If clock rate > 100MHz, then switch from DLL override mode to
@@ -852,9 +945,15 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi,
else
nxp_fspi_dll_override(f);
+ /* Exit stop mode now that MCR0 and the DLL have been reconfigured. */
+ reg = fspi_readl(f, f->iobase + FSPI_MCR0);
+ fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0);
+
f->pre_op_rate = op->max_freq;
f->selected = spi_get_chipselect(spi, 0);
+
+ return 0;
}
static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op)
@@ -1042,7 +1141,16 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true);
WARN_ON(err);
- nxp_fspi_select_mem(f, mem->spi, op);
+ err = nxp_fspi_select_mem(f, mem->spi, op);
+ if (err) {
+ /*
+ * On failure the FlexSPI clock may be left disabled, so avoid
+ * any further register access (which would trigger a synchronous
+ * external abort) and bail out.
+ */
+ pm_runtime_put_autosuspend(f->dev);
+ return err;
+ }
nxp_fspi_prepare_lut(f, op);
/*
@@ -1444,10 +1552,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = {
static const struct of_device_id nxp_fspi_dt_ids[] = {
{ .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },
{ .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, },
- { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },
+ { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, },
{ .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, },
{ .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },
{ .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, },
+ { .compatible = "nxp,imx95-fspi", .data = (void *)&imx95_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids);
diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c
index caf55a6f70b3..e9fdefa9d65d 100644
--- a/drivers/spi/spi-qcom-qspi.c
+++ b/drivers/spi/spi-qcom-qspi.c
@@ -775,7 +775,8 @@ static int qcom_qspi_probe(struct platform_device *pdev)
return dev_err_probe(dev, ret, "could not set DMA mask\n");
host->max_speed_hz = 300000000;
- host->max_dma_len = 65536; /* as per HPG */
+ /* as per HPG, it is 64KB, limit to 60KB to avoid boundary condition failures */
+ host->max_dma_len = 0xf000;
host->dma_alignment = QSPI_ALIGN_REQ;
host->num_chipselect = QSPI_NUM_CS;
host->bus_num = -1;
diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c
index 66f2d1b78ade..b6c58d9cfe14 100644
--- a/drivers/spi/spi-qpic-snand.c
+++ b/drivers/spi/spi-qpic-snand.c
@@ -1358,6 +1358,22 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc,
snandc->regs->addr0 = cpu_to_le32(op->addr.val);
snandc->regs->addr1 = cpu_to_le32(0);
+ /*
+ * The feature value has to reach NAND_FLASH_FEATURES before the
+ * command is executed, otherwise the controller programs the chip
+ * with whatever the register happened to hold from a previous
+ * operation.
+ */
+ if (opcode == SPINAND_SET_FEATURE) {
+ u32 ftr = 0;
+
+ memcpy(&ftr, op->data.buf.out,
+ min_t(size_t, op->data.nbytes, sizeof(ftr)));
+ snandc->regs->flash_feature = cpu_to_le32(ftr);
+ qcom_write_reg_dma(snandc, &snandc->regs->flash_feature,
+ NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL);
+ }
+
qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@@ -1395,10 +1411,8 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_
copy_ftr = true;
break;
case SPINAND_SET_FEATURE:
- snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out);
- qcom_write_reg_dma(snandc, &snandc->regs->flash_feature,
- NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL);
- break;
+ /* fully handled by qcom_spi_send_cmdaddr() */
+ return 0;
case SPINAND_PROGRAM_EXECUTE:
case SPINAND_WRITE_EN:
case SPINAND_RESET:
diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c
index 0faf7ffed676..fd5893a92e59 100644
--- a/drivers/spi/spi-spacemit-k1.c
+++ b/drivers/spi/spi-spacemit-k1.c
@@ -278,25 +278,25 @@ static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi,
struct spi_transfer *transfer)
{
struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
- struct dma_async_tx_descriptor *desc;
+ struct dma_async_tx_descriptor *txdesc, *rxdesc;
u32 val;
- /* Prepare the TX descriptor and submit it */
- desc = k1_spi_dma_prep(drv_data, transfer, true);
- if (!desc)
+ /* Prepare the TX descriptor */
+ txdesc = k1_spi_dma_prep(drv_data, transfer, true);
+ if (!txdesc)
goto fallback;
- dmaengine_submit(desc);
- /* Prepare the RX descriptor and submit it */
- desc = k1_spi_dma_prep(drv_data, transfer, false);
- if (!desc)
+ /* Prepare the RX descriptor */
+ rxdesc = k1_spi_dma_prep(drv_data, transfer, false);
+ if (!rxdesc)
goto fallback;
/* When RX is complete we also know TX has completed */
- desc->callback = k1_spi_dma_callback;
- desc->callback_param = drv_data;
+ rxdesc->callback = k1_spi_dma_callback;
+ rxdesc->callback_param = drv_data;
- dmaengine_submit(desc);
+ dmaengine_submit(txdesc);
+ dmaengine_submit(rxdesc);
val = readl(drv_data->base + SSP_TOP_CTRL);
val |= TOP_TRAIL; /* Trailing bytes handled by DMA */
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 1087d1d17c36..ea65d39fef88 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
break;
case PRO_PREEMPT:
case PRO_PREEMPT_AND_ABORT:
- if (!ops->pr_clear) {
+ if (!ops->pr_preempt) {
pr_err("block_device does not support pr_preempt.\n");
return TCM_UNSUPPORTED_SCSI_OPCODE;
}
@@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key,
sa == PRO_PREEMPT_AND_ABORT);
break;
case PRO_RELEASE:
- if (!ops->pr_clear) {
- pr_err("block_device does not support pr_pclear.\n");
+ if (!ops->pr_release) {
+ pr_err("block_device does not support pr_release.\n");
return TCM_UNSUPPORTED_SCSI_OPCODE;
}
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index fad03a15c969..dcfe94594916 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
u32 data_length, int task_attr, int data_dir, int flags)
{
struct se_portal_group *se_tpg;
+ int ret;
se_tpg = se_sess->se_tpg;
BUG_ON(!se_tpg);
@@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
* necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second
* kref_put() to happen during fabric packet acknowledgement.
*/
- return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
+ if (ret)
+ se_cmd->cmd_cnt = NULL;
+
+ return ret;
}
EXPORT_SYMBOL_GPL(target_init_cmd);
@@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
* allocation failure.
*/
ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
- if (ret < 0)
+ if (ret < 0) {
+ se_cmd->cmd_cnt = NULL;
return -ENOMEM;
+ }
if (tm_type == TMR_ABORT_TASK)
se_cmd->se_tmr_req->ref_task_tag = tag;
@@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
/* See target_submit_cmd for commentary */
ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF);
if (ret) {
+ se_cmd->cmd_cnt = NULL;
core_tmr_release_req(se_cmd->se_tmr_req);
return ret;
}
diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c
index ffad049872b9..62120dc2e9da 100644
--- a/drivers/ufs/core/ufs-rpmb.c
+++ b/drivers/ufs/core/ufs-rpmb.c
@@ -152,8 +152,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba)
return -EINVAL;
}
- INIT_LIST_HEAD(&hba->rpmbs);
-
struct rpmb_descr descr = {
.type = RPMB_TYPE_UFS,
.route_frames = ufs_rpmb_route_frames,
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index d3044a3089b5..13e6357a8bbb 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -7357,7 +7357,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
}
/**
- * ufshcd_threaded_intr - Threaded interrupt service routine
+ * ufshcd_intr - Main interrupt service routine
* @irq: irq number
* @__hba: pointer to adapter instance
*
@@ -7365,7 +7365,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
* IRQ_HANDLED - If interrupt is valid
* IRQ_NONE - If invalid interrupt
*/
-static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
+static irqreturn_t ufshcd_intr(int irq, void *__hba)
{
u32 last_intr_status, intr_status, enabled_intr_status = 0;
irqreturn_t retval = IRQ_NONE;
@@ -7404,38 +7404,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba)
return retval;
}
-/**
- * ufshcd_intr - Main interrupt service routine
- * @irq: irq number
- * @__hba: pointer to adapter instance
- *
- * Return:
- * IRQ_HANDLED - If interrupt is valid
- * IRQ_WAKE_THREAD - If handling is moved to threaded handled
- * IRQ_NONE - If invalid interrupt
- */
-static irqreturn_t ufshcd_intr(int irq, void *__hba)
-{
- struct ufs_hba *hba = __hba;
- u32 intr_status, enabled_intr_status;
-
- /*
- * Handle interrupt in thread if MCQ or ESI is disabled,
- * and no active UIC command.
- */
- if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) &&
- !hba->active_uic_cmd)
- return IRQ_WAKE_THREAD;
-
- intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
- enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
-
- ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS);
-
- /* Directly handle interrupts since MCQ ESI handlers does the hard job */
- return ufshcd_sl_intr(hba, enabled_intr_status);
-}
-
static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag)
{
int err = 0;
@@ -10269,6 +10237,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
req_link_state == UIC_LINK_ACTIVE_STATE) {
ufshcd_disable_auto_bkops(hba);
flush_work(&hba->eeh_work);
+ cancel_delayed_work_sync(&hba->ufs_rtc_update_work);
goto vops_suspend;
}
@@ -10478,10 +10447,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
if (ret)
goto set_old_link_state;
ufshcd_set_timestamp_attr(hba);
- schedule_delayed_work(&hba->ufs_rtc_update_work,
- msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
}
+ schedule_delayed_work(&hba->ufs_rtc_update_work,
+ msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS));
+
if (ufshcd_keep_autobkops_enabled_except_suspend(hba))
ufshcd_enable_auto_bkops(hba);
else
@@ -10988,6 +10958,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle)
hba->nop_out_timeout = NOP_OUT_TIMEOUT;
ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry));
INIT_LIST_HEAD(&hba->clk_list_head);
+ INIT_LIST_HEAD(&hba->rpmbs);
spin_lock_init(&hba->outstanding_lock);
*hba_handle = hba;
@@ -11236,8 +11207,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
/* IRQ registration */
- err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr,
- IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba);
+ err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
dev_err(hba->dev, "request irq failed\n");
goto out_disable;
diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
index 2cedf7e2dbc4..42f2f44b383c 100644
--- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h
+++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
@@ -11,6 +11,8 @@
#define MLX5V_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+extern int mlx5_vdpa_max_iotlb_entries;
+
struct mlx5_vdpa_direct_mr {
u64 start;
u64 end;
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index 6d02ccf9eb91..77a479aeaa85 100644
--- a/drivers/vdpa/mlx5/core/mr.c
+++ b/drivers/vdpa/mlx5/core/mr.c
@@ -233,7 +233,8 @@ static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr *
cmds[i].out = cmd_mem->out;
cmds[i].outlen = sizeof(cmd_mem->out);
cmds[i].in = cmd_mem->in;
- cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount);
+ cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount) -
+ offsetof(struct mlx5_create_mkey_mem, in);
fill_create_direct_mr(mvdev, dmr, cmd_mem);
@@ -776,6 +777,9 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
{
int err;
+ if (mlx5_vdpa_max_iotlb_entries < 2)
+ return -EINVAL;
+
if (iotlb)
err = create_user_mr(mvdev, mr, iotlb);
else
@@ -784,7 +788,7 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
if (err)
return err;
- mr->iotlb = vhost_iotlb_alloc(0, 0);
+ mr->iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mr->iotlb) {
err = -ENOMEM;
goto err_mr;
diff --git a/drivers/vdpa/mlx5/core/resources.c b/drivers/vdpa/mlx5/core/resources.c
index aeae31d0cefa..28a4d7a35bf4 100644
--- a/drivers/vdpa/mlx5/core/resources.c
+++ b/drivers/vdpa/mlx5/core/resources.c
@@ -3,8 +3,14 @@
#include <linux/iova.h>
#include <linux/mlx5/driver.h>
+#include <linux/moduleparam.h>
#include "mlx5_vdpa.h"
+int mlx5_vdpa_max_iotlb_entries = 2048;
+module_param_named(max_iotlb_entries, mlx5_vdpa_max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid)
{
struct mlx5_core_dev *mdev = dev->mdev;
@@ -229,7 +235,10 @@ int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey)
static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev)
{
- mvdev->cvq.iotlb = vhost_iotlb_alloc(0, 0);
+ if (mlx5_vdpa_max_iotlb_entries < 2)
+ return -EINVAL;
+
+ mvdev->cvq.iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0);
if (!mvdev->cvq.iotlb)
return -ENOMEM;
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c
index 8cb1cc2ea139..4d116644851d 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c
@@ -34,7 +34,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable");
static int max_iotlb_entries = 2048;
module_param(max_iotlb_entries, int, 0444);
MODULE_PARM_DESC(max_iotlb_entries,
- "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)");
+ "Maximum number of iotlb entries for each address space. (default: 2048)");
static bool use_va = true;
module_param(use_va, bool, 0444);
@@ -201,6 +201,8 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
if (!dev_attr->alloc_size)
return ERR_PTR(-EINVAL);
+ if (max_iotlb_entries < 2)
+ return ERR_PTR(-EINVAL);
if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) {
if (config->device_features &
@@ -261,8 +263,10 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr,
for (i = 0; i < vdpasim->dev_attr.nas; i++) {
vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0);
- vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, 0,
- VHOST_MAP_RW);
+ ret = vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX,
+ 0, VHOST_MAP_RW);
+ if (ret)
+ goto err_iommu;
vdpasim->iommu_pt[i] = true;
}
diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c
index 4dc76c0d0d13..b6c958224b7c 100644
--- a/drivers/vdpa/vdpa_user/iova_domain.c
+++ b/drivers/vdpa/vdpa_user/iova_domain.c
@@ -12,11 +12,17 @@
#include <linux/file.h>
#include <linux/anon_inodes.h>
#include <linux/highmem.h>
+#include <linux/moduleparam.h>
#include <linux/vmalloc.h>
#include <linux/vdpa.h>
#include "iova_domain.h"
+static int max_iotlb_entries = 2048;
+module_param(max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
static int vduse_iotlb_add_range(struct vduse_iova_domain *domain,
u64 start, u64 last,
u64 addr, unsigned int perm,
@@ -622,11 +628,14 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size)
if (iova_limit <= bounce_size)
return NULL;
+ if (max_iotlb_entries <= 0)
+ return NULL;
+
domain = kzalloc_obj(*domain);
if (!domain)
return NULL;
- domain->iotlb = vhost_iotlb_alloc(0, 0);
+ domain->iotlb = vhost_iotlb_alloc(max_iotlb_entries, 0);
if (!domain->iotlb)
goto err_iotlb;
diff --git a/drivers/vhost/iotlb.c b/drivers/vhost/iotlb.c
index e1414c774c34..a1d4376a5b87 100644
--- a/drivers/vhost/iotlb.c
+++ b/drivers/vhost/iotlb.c
@@ -20,6 +20,14 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
rb, __u64, __subtree_last,
START, LAST, static inline, vhost_iotlb_itree);
+static void vhost_iotlb_map_unlink(struct vhost_iotlb *iotlb,
+ struct vhost_iotlb_map *map)
+{
+ vhost_iotlb_itree_remove(map, &iotlb->root);
+ list_del(&map->link);
+ iotlb->nmaps--;
+}
+
/**
* vhost_iotlb_map_free - remove a map node and free it
* @iotlb: the IOTLB
@@ -28,10 +36,8 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map,
void vhost_iotlb_map_free(struct vhost_iotlb *iotlb,
struct vhost_iotlb_map *map)
{
- vhost_iotlb_itree_remove(map, &iotlb->root);
- list_del(&map->link);
+ vhost_iotlb_map_unlink(iotlb, map);
kfree(map);
- iotlb->nmaps--;
}
EXPORT_SYMBOL_GPL(vhost_iotlb_map_free);
@@ -57,14 +63,25 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
if (last < start)
return -EFAULT;
+ if (!iotlb->limit)
+ return -EINVAL;
+
/* If the range being mapped is [0, ULONG_MAX], split it into two entries
* otherwise its size would overflow u64.
*/
if (start == 0 && last == ULONG_MAX) {
u64 mid = last / 2;
- int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
- perm, opaque);
+ int err;
+
+ if (iotlb->limit < 2)
+ return -ENOSPC;
+ if (!(iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) &&
+ iotlb->nmaps > iotlb->limit - 2)
+ return -ENOSPC;
+
+ err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr,
+ perm, opaque);
if (err)
return err;
@@ -72,17 +89,19 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb,
start = mid + 1;
}
- if (iotlb->limit &&
- iotlb->nmaps == iotlb->limit &&
- iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
- map = list_first_entry(&iotlb->list, typeof(*map), link);
- vhost_iotlb_map_free(iotlb, map);
+ if (iotlb->nmaps >= iotlb->limit) {
+ if (iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) {
+ map = list_first_entry(&iotlb->list, typeof(*map), link);
+ vhost_iotlb_map_unlink(iotlb, map);
+ } else {
+ return -ENOSPC;
+ }
+ } else {
+ map = kmalloc_obj(*map, GFP_ATOMIC);
+ if (!map)
+ return -ENOMEM;
}
- map = kmalloc_obj(*map, GFP_ATOMIC);
- if (!map)
- return -ENOMEM;
-
map->start = start;
map->size = last - start + 1;
map->last = last;
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 9a1253b9d8c5..7a1f39a327da 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -972,6 +972,9 @@ vhost_scsi_mapal(struct vhost_scsi *vs, struct vhost_scsi_cmd *cmd,
if (prot_bytes) {
sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes,
VHOST_SCSI_PREALLOC_PROT_SGLS);
+ if (sgl_count < 0)
+ return sgl_count;
+
cmd->prot_table.sgl = cmd->prot_sgl;
ret = sg_alloc_table_chained(&cmd->prot_table, sgl_count,
cmd->prot_table.sgl,
@@ -1416,6 +1419,11 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
* actual data payload length.
*/
if (prot_bytes) {
+ if (prot_bytes >= exp_data_len) {
+ vq_err(vq, "Protection data exceeds payload length\n");
+ goto err;
+ }
+
exp_data_len -= prot_bytes;
prot_iter = data_iter;
iov_iter_truncate(&prot_iter, prot_bytes);
@@ -2219,6 +2227,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
{
struct vhost_virtqueue *vq;
bool is_log, was_log;
+ u64 old_features;
int i;
if (features & ~VHOST_SCSI_FEATURES)
@@ -2234,6 +2243,14 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
if (!vs->dev.nvqs)
goto out;
+ old_features = vs->vqs[0].vq.acked_features;
+ if (vs->vs_tpg &&
+ ((features ^ old_features) &
+ ~(1ULL << VHOST_F_LOG_ALL))) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EBUSY;
+ }
+
is_log = features & (1 << VHOST_F_LOG_ALL);
/*
* All VQs should have same feature.
@@ -2426,9 +2443,10 @@ vhost_scsi_ioctl(struct file *f,
default:
mutex_lock(&vs->dev.mutex);
r = vhost_dev_ioctl(&vs->dev, ioctl, argp);
- /* TODO: flush backend after dev ioctl. */
if (r == -ENOIOCTLCMD)
r = vhost_vring_ioctl(&vs->dev, ioctl, argp);
+ else
+ vhost_scsi_flush(vs);
mutex_unlock(&vs->dev.mutex);
return r;
}
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ac55275fa0d0..c3d913bd7cac 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -34,6 +34,11 @@ enum {
#define VHOST_VDPA_DEV_MAX (1U << MINORBITS)
+static int max_iotlb_entries = 2048;
+module_param(max_iotlb_entries, int, 0444);
+MODULE_PARM_DESC(max_iotlb_entries,
+ "Maximum number of iotlb entries. (default: 2048)");
+
#define VHOST_VDPA_IOTLB_BUCKETS 16
struct vhost_vdpa_as {
@@ -109,12 +114,14 @@ static struct vhost_vdpa_as *vhost_vdpa_alloc_as(struct vhost_vdpa *v, u32 asid)
if (asid >= v->vdpa->nas)
return NULL;
+ if (max_iotlb_entries <= 0)
+ return NULL;
as = kmalloc_obj(*as);
if (!as)
return NULL;
- vhost_iotlb_init(&as->iotlb, 0, 0);
+ vhost_iotlb_init(&as->iotlb, max_iotlb_entries, 0);
as->id = asid;
hlist_add_head(&as->hash_link, head);
@@ -1102,6 +1109,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
unsigned int gup_flags = FOLL_LONGTERM;
unsigned long npages, cur_base, map_pfn, last_pfn = 0;
unsigned long lock_limit, sz2pin, nchunks, i;
+ unsigned long page_offset;
u64 start = iova;
long pinned;
int ret = 0;
@@ -1114,7 +1122,13 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v,
if (perm & VHOST_ACCESS_WO)
gup_flags |= FOLL_WRITE;
- npages = PFN_UP(size + (iova & ~PAGE_MASK));
+ page_offset = iova & ~PAGE_MASK;
+ if (size > ULONG_MAX - page_offset) {
+ ret = -EINVAL;
+ goto free;
+ }
+
+ npages = PFN_UP(size + page_offset);
if (!npages) {
ret = -EINVAL;
goto free;
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 4c525b3e16ea..269efad90369 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -1137,6 +1137,9 @@ EXPORT_SYMBOL_GPL(vhost_dev_set_owner);
static struct vhost_iotlb *iotlb_alloc(void)
{
+ if (max_iotlb_entries <= 0)
+ return NULL;
+
return vhost_iotlb_alloc(max_iotlb_entries,
VHOST_IOTLB_FLAG_RETIRE);
}
@@ -1981,6 +1984,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m)
return -EOPNOTSUPP;
if (mem.nregions > max_mem_regions)
return -E2BIG;
+ if (max_iotlb_entries <= 0)
+ return -EINVAL;
newmem = kvzalloc_flex(*newmem, regions, mem.nregions);
if (!newmem)
return -ENOMEM;
@@ -2126,6 +2131,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d,
BUG();
}
+ /*
+ * The metadata cache holds the IOTLB mapping that backed the previous
+ * desc/avail/used addresses and vring size, both of which are being
+ * replaced here. iotlb_access_ok() takes a cache hit as proof that the
+ * region was validated, so the stale entries have to go.
+ */
+ __vhost_vq_meta_reset(vq);
+
mutex_unlock(&vq->mutex);
return r;
@@ -2275,6 +2288,9 @@ int vhost_init_device_iotlb(struct vhost_dev *d)
struct vhost_iotlb *niotlb, *oiotlb;
int i;
+ if (max_iotlb_entries <= 0)
+ return -EINVAL;
+
niotlb = iotlb_alloc();
if (!niotlb)
return -ENOMEM;
diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c
index a2ffd60889f8..1a3f186a6a11 100644
--- a/fs/afs/fsclient.c
+++ b/fs/afs/fsclient.c
@@ -477,6 +477,9 @@ void afs_fs_fetch_data(struct afs_operation *op)
if (!call)
return afs_op_nomem(op);
+ if (op->flags & AFS_OPERATION_ASYNC)
+ call->async = true;
+
/* marshall the parameters */
bp = call->request;
bp[0] = htonl(FSFETCHDATA);
@@ -484,7 +487,7 @@ void afs_fs_fetch_data(struct afs_operation *op)
bp[2] = htonl(vp->fid.vnode);
bp[3] = htonl(vp->fid.unique);
bp[4] = htonl(lower_32_bits(subreq->start + subreq->transferred));
- bp[5] = htonl(lower_32_bits(subreq->len + subreq->transferred));
+ bp[5] = htonl(lower_32_bits(subreq->len - subreq->transferred));
call->fid = vp->fid;
trace_afs_make_fs_call(call, &vp->fid);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 601f01e5c15f..290873bac89b 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1421,7 +1421,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c
{
struct afs_addr_list *alist = op->estate->addresses;
- op->call = call;
+ op->call = afs_get_call(call, afs_call_trace_get);
op->type = call->type;
call->op = op;
call->key = op->key;
@@ -1429,6 +1429,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c
call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer);
call->service_id = op->server->service_id;
afs_make_call(call, gfp);
+ afs_put_call(call);
}
static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size)
diff --git a/fs/attr.c b/fs/attr.c
index 4f437fabb7f0..71888ac903c2 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
* covered by the open-time check because sys_truncate() takes a
* path, not an open file.
*/
- if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode))
+ if (IS_VERITY(inode))
return -EPERM;
error = inode_newsize_ok(inode, attr->ia_size);
diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c
index 5de615ca7a75..c97f10b48b5b 100644
--- a/fs/binfmt_misc.c
+++ b/fs/binfmt_misc.c
@@ -162,8 +162,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc,
static void put_binfmt_handler(Node *e)
{
if (refcount_dec_and_test(&e->users)) {
- if (e->flags & MISC_FMT_OPEN_FILE)
+ if (e->flags & MISC_FMT_OPEN_FILE) {
+ exe_file_allow_write_access(e->interp_file);
filp_close(e->interp_file, NULL);
+ }
kfree(e);
}
}
@@ -247,8 +249,14 @@ static int load_misc_binary(struct linux_binprm *bprm)
if (fmt->flags & MISC_FMT_OPEN_FILE) {
interp_file = file_clone_open(fmt->interp_file);
- if (!IS_ERR(interp_file))
- deny_write_access(interp_file);
+ if (!IS_ERR(interp_file)) {
+ int err = exe_file_deny_write_access(interp_file);
+
+ if (err) {
+ fput(interp_file);
+ interp_file = ERR_PTR(err);
+ }
+ }
} else {
interp_file = open_exec(fmt->interpreter);
}
@@ -376,6 +384,10 @@ static Node *create_entry(const char __user *buffer, size_t count)
pr_debug("register: delim: %#x {%c}\n", del, del);
+ /* A flag-char delimiter runs the flag scan off the buffer. */
+ if (del == 'P' || del == 'O' || del == 'C' || del == 'F')
+ goto einval;
+
/* Pad the buffer with the delim to simplify parsing below. */
memset(buf + count, del, 8);
@@ -909,18 +921,9 @@ static const struct file_operations bm_status_operations = {
/* Superblock handling */
-static void bm_put_super(struct super_block *sb)
-{
- struct user_namespace *user_ns = sb->s_fs_info;
-
- sb->s_fs_info = NULL;
- put_user_ns(user_ns);
-}
-
static const struct super_operations s_ops = {
.statfs = simple_statfs,
.evict_inode = bm_evict_inode,
- .put_super = bm_put_super,
};
static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
@@ -937,6 +940,10 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
if (WARN_ON(user_ns != current_user_ns()))
return -EINVAL;
+ /* Never exec off this instance and never let anything stack on it. */
+ sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
+ sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH;
+
/*
* Lazily allocate a new binfmt_misc instance for this namespace, i.e.
* do it here during the first mount of binfmt_misc. We don't need to
@@ -974,13 +981,12 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
/*
* When the binfmt_misc superblock for this userns is shutdown
* ->enabled might have been set to false and we don't reinitialize
- * ->enabled again in put_super() as someone might already be mounting
- * binfmt_misc again. It also would be pointless since by the time
- * ->put_super() is called we know that the binary type list for this
- * bintfmt_misc mount is empty making load_misc_binary() return
- * -ENOEXEC independent of whether ->enabled is true. Instead, if
- * someone mounts binfmt_misc for the first time or again we simply
- * reset ->enabled to true.
+ * ->enabled again during shutdown as someone might already be mounting
+ * binfmt_misc again. It also would be pointless since by then we know
+ * that the binary type list for this binfmt_misc mount is empty making
+ * load_misc_binary() return -ENOEXEC independent of whether ->enabled
+ * is true. Instead, if someone mounts binfmt_misc for the first time or
+ * again we simply reset ->enabled to true.
*/
misc->enabled = true;
@@ -1006,6 +1012,14 @@ static const struct fs_context_operations bm_context_ops = {
.get_tree = bm_get_tree,
};
+static void bm_kill_sb(struct super_block *sb)
+{
+ struct user_namespace *user_ns = sb->s_fs_info;
+
+ kill_anon_super(sb);
+ put_user_ns(user_ns);
+}
+
static int bm_init_fs_context(struct fs_context *fc)
{
fc->ops = &bm_context_ops;
@@ -1022,7 +1036,7 @@ static struct file_system_type bm_fs_type = {
.name = "binfmt_misc",
.init_fs_context = bm_init_fs_context,
.fs_flags = FS_USERNS_MOUNT,
- .kill_sb = kill_anon_super,
+ .kill_sb = bm_kill_sb,
};
MODULE_ALIAS_FS("binfmt_misc");
diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig
index 9de04c37e11a..4b10d78ed99b 100644
--- a/fs/btrfs/Kconfig
+++ b/fs/btrfs/Kconfig
@@ -106,7 +106,8 @@ config BTRFS_EXPERIMENTAL
- extent tree v2 - complex rework of extent tracking
- - block size > page size support
+ - block size > page size support - needs transparent huge page and
+ non-HIGHMEM system
- huge folios for data - folios can be as large as 2MiB now
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 7fdc6c3fd066..1082fa92c145 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -600,6 +600,7 @@ int btrfs_prealloc_file_range_trans(struct inode *inode,
loff_t actual_len, u64 *alloc_hint);
int btrfs_run_delalloc_range(struct btrfs_inode *inode, struct folio *locked_folio,
u64 start, u64 end, struct writeback_control *wbc);
+void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio);
int btrfs_encoded_io_compression_from_extent(struct btrfs_fs_info *fs_info,
int compress_type);
int btrfs_encoded_read_regular_fill_pages(struct btrfs_inode *inode,
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 22c321719e4f..2f1666d9544e 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1760,6 +1760,8 @@ static int read_backup_root(struct btrfs_fs_info *fs_info, u8 priority)
/* helper to cleanup workers */
static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
{
+ if (fs_info->fixup_workers)
+ destroy_workqueue(fs_info->fixup_workers);
btrfs_destroy_workqueue(fs_info->delalloc_workers);
btrfs_destroy_workqueue(fs_info->workers);
if (fs_info->endio_workers)
@@ -1967,6 +1969,9 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
fs_info->caching_workers =
btrfs_alloc_workqueue(fs_info, "cache", flags, max_active, 0);
+ fs_info->fixup_workers =
+ alloc_ordered_workqueue("btrfs-fixup", ordered_flags);
+
fs_info->endio_workers =
alloc_workqueue("btrfs-endio", flags, max_active);
fs_info->endio_meta_workers =
@@ -1992,7 +1997,7 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
fs_info->endio_workers && fs_info->endio_meta_workers &&
fs_info->endio_write_workers &&
fs_info->endio_freespace_worker && fs_info->rmw_workers &&
- fs_info->caching_workers &&
+ fs_info->caching_workers && fs_info->fixup_workers &&
fs_info->delayed_workers && fs_info->qgroup_rescan_workers &&
fs_info->discard_ctl.discard_workers)) {
return -ENOMEM;
@@ -3468,7 +3473,15 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
fs_info->sectorsize = sectorsize;
fs_info->sectorsize_bits = ilog2(sectorsize);
fs_info->block_min_order = ilog2(round_up(sectorsize, PAGE_SIZE) >> PAGE_SHIFT);
- fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits);
+ /*
+ * For HIGHMEM, a large folio cannot be mapped in one go, breaking a lot
+ * of basic assumptions for btrfs IOs.
+ * Disable large folios for such 32-bit systems.
+ */
+ if (IS_ENABLED(CONFIG_HIGHMEM))
+ fs_info->block_max_order = fs_info->block_min_order;
+ else
+ fs_info->block_max_order = calc_block_max_order(fs_info->sectorsize_bits);
fs_info->csums_per_leaf = BTRFS_MAX_ITEM_SIZE(fs_info) / fs_info->csum_size;
fs_info->stripesize = stripesize;
fs_info->fs_devices->fs_info = fs_info;
@@ -4357,6 +4370,18 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
btrfs_cleanup_defrag_inodes(fs_info);
/*
+ * Before the unmount, we sync down all the writeback which can
+ * generate fixup work. We are about to run delalloc for autodefrag so
+ * piggy back on that by also flushing the fixup work which can also
+ * generate delalloc we would like to get run.
+ *
+ * After this, it is still possible that some thread doing writeback is
+ * in btrfs_queue_writepage_fixup() and might finish queueing some final
+ * work, racing the btrfs_fs_closing() check there.
+ */
+ flush_workqueue(fs_info->fixup_workers);
+
+ /*
* Handle the error fs first, as it will flush and wait for all ordered
* extents. This will generate delayed iputs, thus we want to handle
* it first.
@@ -4434,6 +4459,15 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
cancel_work_sync(&fs_info->em_shrinker_work);
/*
+ * Reclaim workers can run writeback which can queue fixup.
+ * After the above cancel_work_sync() calls, any such queueing attempts are
+ * guaranteed to see btrfs_fs_closing(), so at this point we can genuinely fully
+ * flush the fixup workqueue. This relies on the belief that *now* no thread can
+ * still be sitting in btrfs_queue_writepage_fixup().
+ */
+ flush_workqueue(fs_info->fixup_workers);
+
+ /*
* Run delayed iputs again because an async reclaim worker may have
* added new ones if it was flushing delalloc:
*
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index de5785117a47..f032f0858f40 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -1441,6 +1441,115 @@ static bool find_next_delalloc_bitmap(struct folio *folio,
}
/*
+ * Debug checks for fixup selection logic to help ensure the invariants
+ * we expect for fixup marking hold in practice.
+ *
+ * - A dirty block without a fixup bit is covered by delalloc or a running
+ * ordered extent (it was dirtied by a reserving write path).
+ * - A block with a fixup bit is never covered by delalloc: every delalloc
+ * setter holds the folio lock and cancels the fixup state of the blocks
+ * it covers (btrfs_folio_set_dirty()) before releasing it.
+ */
+static void debug_check_writepage_fixup(struct btrfs_inode *inode, u64 start,
+ u32 len, bool needs_fixup)
+{
+ struct btrfs_ordered_extent *ordered;
+ bool delalloc;
+
+ if (!IS_ENABLED(CONFIG_BTRFS_DEBUG))
+ return;
+
+ delalloc = btrfs_test_range_bit_exists(&inode->io_tree, start,
+ start + len - 1, EXTENT_DELALLOC);
+ if (needs_fixup) {
+ if (unlikely(delalloc))
+ DEBUG_WARN("writeback: delalloc and fixup conflict. ino %llu start %llu",
+ btrfs_ino(inode), start);
+ } else {
+ if (delalloc)
+ return;
+
+ ordered = btrfs_lookup_ordered_range(inode, start, len);
+ if (unlikely(!ordered))
+ DEBUG_WARN("dirty block, no delalloc, fixup, ordered. ino %llu start %llu",
+ btrfs_ino(inode), start);
+ else
+ btrfs_put_ordered_extent(ordered);
+ }
+}
+
+/*
+ * Handle folios dirtied without a delalloc reservation, e.g.
+ * O_DIRECT read into a MAP_SHARED mapping dirtying via set_page_dirty_lock().
+ *
+ * btrfs_data_dirty_folio() records the affected blocks in the fixup bitmap
+ * and the folio fixup flag and we check them here in writeback.
+ *
+ * Don't submit such blocks and queue work for the fixup worker to reserve
+ * space for them so that they can be submitted properly by writeback.
+ *
+ * Return 1 if the folio needed fixup, 0 if not, and a negative error code
+ * on error.
+ */
+static noinline_for_stack int writepage_fixup(struct btrfs_inode *inode,
+ struct folio *folio,
+ struct btrfs_bio_ctrl *bio_ctrl)
+{
+ struct btrfs_fs_info *fs_info = inode_to_fs_info(&inode->vfs_inode);
+ const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
+ const u32 sectorsize = fs_info->sectorsize;
+ const u64 page_start = folio_pos(folio);
+ bool found_fixup = false;
+ unsigned int bit;
+
+ /*
+ * A folio was dirtied without calling aops->dirty_folio() which we
+ * explicitly assert is not allowed.
+ */
+ if (unlikely(bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio))) {
+ DEBUG_WARN();
+ btrfs_err_rl(fs_info,
+ "root %lld ino %llu folio %llu is dirty with an empty dirty bitmap",
+ btrfs_root_id(inode->root), btrfs_ino(inode),
+ folio_pos(folio));
+ return -EUCLEAN;
+ }
+
+ /* Cheap check on the folio flag. Set iff the fixup bitmap is non-empty. */
+ if (likely(!folio_test_fixup_pending(folio)))
+ return 0;
+
+ for_each_set_bit(bit, bio_ctrl->submit_bitmap, blocks_per_folio) {
+ const u64 start = page_start + (bit << fs_info->sectorsize_bits);
+ const bool needs_fixup = btrfs_folio_test_fixup(fs_info, folio,
+ start, sectorsize);
+
+ debug_check_writepage_fixup(inode, start, sectorsize, needs_fixup);
+ if (needs_fixup) {
+ bitmap_clear(bio_ctrl->submit_bitmap, bit, 1);
+ found_fixup = true;
+ }
+ }
+ if (likely(found_fixup)) {
+ btrfs_queue_writepage_fixup(inode, folio);
+ folio_redirty_for_writepage(bio_ctrl->wbc, folio);
+ if (bitmap_empty(bio_ctrl->submit_bitmap, blocks_per_folio)) {
+ folio_unlock(folio);
+ return 1;
+ }
+ return 0;
+ }
+ /* We should always find fixup if the folio fixup flag was set. */
+ DEBUG_WARN();
+ btrfs_err_rl(fs_info,
+ "root %lld ino %llu folio %llu is fixup with an empty fixup bitmap",
+ btrfs_root_id(inode->root), btrfs_ino(inode),
+ folio_pos(folio));
+
+ return -EUCLEAN;
+}
+
+/*
* Do all of the delayed allocation setup.
*
* Return >0 if all the dirty blocks are submitted async (compression) or inlined.
@@ -1492,6 +1601,10 @@ static noinline_for_stack int writepage_delalloc(struct btrfs_inode *inode,
/* Save the dirty bitmap as our submission bitmap will be a subset of it. */
btrfs_copy_subpage_dirty_bitmap(fs_info, folio, bio_ctrl->submit_bitmap);
+ ret = writepage_fixup(inode, folio, bio_ctrl);
+ if (ret)
+ return ret;
+
for_each_set_bitrange(start_bit, end_bit, bio_ctrl->submit_bitmap,
blocks_per_folio) {
u64 start = page_start + (start_bit << fs_info->sectorsize_bits);
diff --git a/fs/btrfs/fs.c b/fs/btrfs/fs.c
index 14d83565cdee..dcf12979af33 100644
--- a/fs/btrfs/fs.c
+++ b/fs/btrfs/fs.c
@@ -166,9 +166,17 @@ bool __attribute_const__ btrfs_supported_blocksize(u32 blocksize)
*
* Considering HIGHMEM is such a pain to deal with and it's going
* to be deprecated eventually, just reject HIGHMEM && bs > ps cases.
+ *
+ * Finally, for bs > ps cases, we need to set the minimal folio order,
+ * which requires transparent hugepage.
*/
- if (IS_ENABLED(CONFIG_HIGHMEM) && blocksize > PAGE_SIZE)
- return false;
+ if (blocksize > PAGE_SIZE) {
+ if (IS_ENABLED(CONFIG_HIGHMEM))
+ return false;
+
+ if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+ return false;
+ }
return true;
#endif
return false;
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index 7ee9ec2b0efb..f7f343fbe732 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -713,6 +713,8 @@ struct btrfs_fs_info {
struct btrfs_workqueue *endio_write_workers;
struct btrfs_workqueue *endio_freespace_worker;
struct btrfs_workqueue *caching_workers;
+
+ struct workqueue_struct *fixup_workers;
struct btrfs_workqueue *delayed_workers;
struct task_struct *transaction_kthread;
@@ -1200,6 +1202,16 @@ static inline void btrfs_wake_unfinished_drop(struct btrfs_fs_info *fs_info)
clear_and_wake_up_bit(BTRFS_FS_UNFINISHED_DROPS, &fs_info->flags);
}
+/*
+ * We use the folio owner_2 flag to indicate the folio has blocks that were
+ * dirtied without a space reservation and need the writepage fixup before
+ * writeback. For bs < folio_size the fixup bitmap tracks the affected
+ * blocks.
+ */
+#define folio_test_fixup_pending(folio) folio_test_owner_2(folio)
+#define folio_set_fixup_pending(folio) folio_set_owner_2(folio)
+#define folio_clear_fixup_pending(folio) folio_clear_owner_2(folio)
+
#define BTRFS_FS_ERROR(fs_info) (READ_ONCE((fs_info)->fs_error))
#define BTRFS_FS_LOG_CLEANUP_ERROR(fs_info) \
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index b446c3014b24..2534cd9284d5 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2812,6 +2812,180 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
EXTENT_DELALLOC | extra_bits, cached_state);
}
+struct btrfs_writepage_fixup {
+ struct folio *folio;
+ struct btrfs_inode *inode;
+ struct work_struct work;
+};
+
+/*
+ * Do the real fixup work of reserving space for the blocks a folio's fixup
+ * state records. Queued by writepage_fixup() when writeback found the bits set.
+ *
+ * Since the fixup can be cancelled by a task dirtying with a reservation, we must
+ * re-check the state of fixup under the folio lock.
+ */
+static void btrfs_writepage_fixup_worker(struct work_struct *work)
+{
+ struct btrfs_writepage_fixup *fixup =
+ container_of(work, struct btrfs_writepage_fixup, work);
+ struct extent_state *cached_state = NULL;
+ struct extent_changeset *data_reserved = NULL;
+ unsigned long delalloc_bitmap[BITS_TO_LONGS(BTRFS_MAX_BLOCKS_PER_FOLIO)] = { 0 };
+ struct folio *folio = fixup->folio;
+ struct btrfs_inode *inode = fixup->inode;
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ const unsigned int blocks_per_folio = btrfs_blocks_per_folio(fs_info, folio);
+ const u32 sectorsize = fs_info->sectorsize;
+ const u64 page_start = folio_pos(folio);
+ const u64 page_end = folio_next_pos(folio) - 1;
+ unsigned int start_bit;
+ unsigned int end_bit;
+ unsigned int bit;
+ bool reserved;
+ int ret;
+
+ /*
+ * We would prefer to reserve under the folio lock when we know exactly
+ * which blocks need a reservation. Unfortunately, since the reservation
+ * can go into flushers which can go into writeback, which takes folio
+ * locks, that is not possible. Therefore, we have to reserve for the
+ * whole folio here, then release what we didn't end up needing once we
+ * figure it out.
+ *
+ * Also note the slightly strange error checking. If fixup is actually
+ * not set, we don't need to mark an error on the mapping. So hang on to
+ * ret until after we lock and find out if we actually care.
+ */
+ ret = btrfs_delalloc_reserve_space(inode, &data_reserved, page_start,
+ folio_size(folio));
+ reserved = (ret == 0);
+again:
+ folio_lock(folio);
+
+ if (!folio->mapping || !folio_test_fixup_pending(folio)) {
+ ret = 0;
+ goto out;
+ }
+ if (ret)
+ goto out;
+
+ btrfs_lock_extent(&inode->io_tree, page_start, page_end, &cached_state);
+
+ for (bit = 0; bit < blocks_per_folio; bit++) {
+ struct btrfs_ordered_extent *ordered;
+ const u64 start = page_start + (bit << fs_info->sectorsize_bits);
+
+ if (test_bit(bit, delalloc_bitmap))
+ continue;
+ if (!btrfs_folio_test_fixup(fs_info, folio, start, sectorsize))
+ continue;
+ /*
+ * Any task that sets EXTENT_DELALLOC clears the fixup bits
+ * under the folio lock, so it should be impossible to observe
+ * both under the lock. Setting delalloc twice would wrongly
+ * double account the space.
+ */
+ if (IS_ENABLED(CONFIG_BTRFS_DEBUG) &&
+ unlikely(btrfs_test_range_bit_exists(&inode->io_tree, start,
+ start + sectorsize - 1,
+ EXTENT_DELALLOC))) {
+ DEBUG_WARN("fixup worker: delalloc and fixup conflict. ino %llu start %llu",
+ btrfs_ino(inode), start);
+ btrfs_folio_clear_fixup(fs_info, folio, start, sectorsize);
+ continue;
+ }
+ ordered = btrfs_lookup_ordered_range(inode, start, sectorsize);
+ if (ordered) {
+ trace_btrfs_writepage_fixup_defer(inode, ordered);
+ btrfs_unlock_extent(&inode->io_tree, page_start,
+ page_end, &cached_state);
+ folio_unlock(folio);
+ btrfs_start_ordered_extent(ordered);
+ btrfs_put_ordered_extent(ordered);
+ goto again;
+ }
+ ret = btrfs_set_extent_delalloc(inode, start,
+ start + sectorsize - 1, 0,
+ &cached_state);
+ if (ret)
+ break;
+ trace_btrfs_writepage_fixup_reserve(inode, start, sectorsize);
+ btrfs_folio_clear_fixup(fs_info, folio, start, sectorsize);
+ set_bit(bit, delalloc_bitmap);
+ }
+
+ btrfs_unlock_extent(&inode->io_tree, page_start, page_end, &cached_state);
+out:
+ if (ret < 0) {
+ /* Failure here is analogous to failure in writeback. */
+ mapping_set_error(folio->mapping, ret);
+ btrfs_folio_clear_fixup_dirty(fs_info, folio, page_start,
+ folio_size(folio));
+ }
+ if (reserved) {
+ btrfs_delalloc_release_extents(inode, folio_size(folio));
+ for_each_clear_bitrange(start_bit, end_bit, delalloc_bitmap,
+ blocks_per_folio)
+ btrfs_delalloc_release_space(inode, data_reserved,
+ page_start + (start_bit << fs_info->sectorsize_bits),
+ (end_bit - start_bit) << fs_info->sectorsize_bits,
+ true);
+ }
+ folio_unlock(folio);
+ folio_put(folio);
+ kfree(fixup);
+ extent_changeset_free(data_reserved);
+ btrfs_add_delayed_iput(inode);
+}
+
+/*
+ * Queue space reservation fixup work for blocks dirtied without a space reservation.
+ *
+ * Should be used by writeback while holding the folio locked.
+ *
+ * If we fail to queue fixup, then the folio state is unchanged and a future
+ * writeback pass will still see it.
+ */
+void btrfs_queue_writepage_fixup(struct btrfs_inode *inode, struct folio *folio)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ struct btrfs_writepage_fixup *fixup;
+
+ /*
+ * Disallow queueing more fixup during unmount to break the cycle
+ * of writeback queuing fixup queuing writeback etc.
+ *
+ * If it actually hit, then something which was fixup wasn't written
+ * which we should warn about.
+ */
+ if (btrfs_fs_closing(fs_info)) {
+ btrfs_warn_rl(fs_info,
+ "dropping unqueued fixup blocks at unmount. root %lld ino %llu folio %llu",
+ btrfs_root_id(inode->root), btrfs_ino(inode),
+ folio_pos(folio));
+ btrfs_folio_clear_fixup_dirty(fs_info, folio,
+ folio_pos(folio), folio_size(folio));
+ return;
+ }
+
+ fixup = kzalloc_obj(*fixup, GFP_NOFS);
+ if (!fixup)
+ return;
+
+ /*
+ * This is called from within extent_write_cache_pages() which
+ * has successfully done an igrab(). But that will be released at the
+ * end of the writeback pass. We need to extend it for the worker as well.
+ */
+ ihold(&inode->vfs_inode);
+ folio_get(folio);
+ INIT_WORK(&fixup->work, btrfs_writepage_fixup_worker);
+ fixup->folio = folio;
+ fixup->inode = inode;
+ queue_work(fs_info->fixup_workers, &fixup->work);
+}
+
/*
* Clear the old accounting flags and set EXTENT_DELALLOC for the range.
*
@@ -3938,10 +4112,11 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path
btrfs_inode_split_flags(btrfs_inode_flags(leaf, inode_item),
&inode->flags, &inode->ro_flags);
+
+cache_index:
btrfs_update_inode_mapping_flags(inode);
btrfs_set_inode_mapping_order(inode);
-cache_index:
/*
* If we were modified in the current generation and evicted from memory
* and then re-read we need to do a full sync since we don't have any
@@ -7508,6 +7683,12 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset,
wait_subpage_spinlock(folio);
/*
+ * The invalidated blocks are going away; drop any fixup blocks among
+ * them, data included, as they have no space reservation.
+ */
+ btrfs_folio_clear_fixup_dirty(fs_info, folio, page_start + offset, length);
+
+ /*
* For subpage case, we have call sites like
* btrfs_punch_hole_lock_range() which passes range not aligned to
* sectorsize.
@@ -9954,6 +10135,7 @@ out_cb:
if (cb)
cleanup_compressed_bio(cb);
out:
+ extent_changeset_free(data_reserved);
if (ret >= 0)
iocb->ki_pos += encoded->len;
return ret;
@@ -10549,6 +10731,41 @@ static const struct file_operations btrfs_dir_file_operations = {
};
/*
+ * The folio is going dirty without a btrfs delalloc space reservation.
+ * This requires a fixup before writeback which we might sleep so cannot
+ * run in this context, so we merely set state on the folio indicating it
+ * needs fixup before writeback.
+ *
+ * Note that there is no range in the input, so the whole folio is marked
+ * dirty and fixup.
+ *
+ * We believe that all callers of dirty_folio either:
+ * - take the folio lock (e.g. pinned folio release notification).
+ * - take the pte lock but must be running on a dirty pte which means
+ * page_mkwrite() ran on it and reserved the space. zap_pte_range() cannot
+ * race with writeback cleaning the folio because writeback runs
+ * folio_mkclean() which also uses the pte lock and revokes outstanding
+ * writable mappings.
+ * Therefore, an additional folio private lock (a la bfs->lock for all cases,
+ * not just subpage) is not necessary.
+ */
+static bool btrfs_data_dirty_folio(struct address_space *mapping,
+ struct folio *folio)
+{
+ struct btrfs_inode *inode = BTRFS_I(mapping->host);
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ const u64 page_start = folio_pos(folio);
+ const u64 range_end = min_t(u64, folio_next_pos(folio),
+ round_up(i_size_read(&inode->vfs_inode),
+ fs_info->sectorsize));
+
+ if (range_end > page_start)
+ btrfs_folio_set_fixup_dirty(fs_info, folio, page_start,
+ range_end - page_start);
+ return filemap_dirty_folio(mapping, folio);
+}
+
+/*
* btrfs doesn't support the bmap operation because swapfiles
* use bmap to make a mapping of extents in the file. They assume
* these extents won't change over the life of the file and they
@@ -10568,7 +10785,7 @@ static const struct address_space_operations btrfs_aops = {
.launder_folio = btrfs_launder_folio,
.release_folio = btrfs_release_folio,
.migrate_folio = btrfs_migrate_folio,
- .dirty_folio = filemap_dirty_folio,
+ .dirty_folio = btrfs_data_dirty_folio,
.error_remove_folio = generic_error_remove_folio,
.swap_activate = btrfs_swap_activate,
.swap_deactivate = btrfs_swap_deactivate,
diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c
index 1531adb117d1..2f0996692da0 100644
--- a/fs/btrfs/lzo.c
+++ b/fs/btrfs/lzo.c
@@ -552,9 +552,10 @@ int lzo_decompress(struct list_head *ws, const u8 *data_in,
size_t max_segment_len = workspace_buf_length(fs_info);
int ret;
- if (unlikely(srclen < LZO_LEN || srclen > max_segment_len + LZO_LEN * 2)) {
+ if (unlikely(srclen <= LZO_LEN * 2 ||
+ srclen > max_segment_len + LZO_LEN * 2)) {
btrfs_err(fs_info, "invalid lzo header length, has %zu expect (%u, %zu)",
- srclen, LZO_LEN, max_segment_len + LZO_LEN * 2);
+ srclen, LZO_LEN * 2, max_segment_len + LZO_LEN * 2);
return -EUCLEAN;
}
diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c
index 2a9397be8116..27dd677ca687 100644
--- a/fs/btrfs/subpage.c
+++ b/fs/btrfs/subpage.c
@@ -345,18 +345,57 @@ void btrfs_subpage_clear_uptodate(const struct btrfs_fs_info *fs_info,
spin_unlock_irqrestore(&bfs->lock, flags);
}
+/*
+ * folio_mark_dirty() for a folio we are dirtying with a space reservation.
+ *
+ * Dirtiers without a reservation use btrfs_data_dirty_folio().
+ */
+static void btrfs_folio_mark_dirty(struct folio *folio)
+{
+ struct address_space *mapping = folio_mapping(folio);
+
+ if (!mapping || !mapping->host || !is_data_inode(BTRFS_I(mapping->host))) {
+ folio_mark_dirty(folio);
+ return;
+ }
+ if (folio_test_reclaim(folio))
+ folio_clear_reclaim(folio);
+ filemap_dirty_folio(mapping, folio);
+}
+
+/*
+ * The set helper of the dirty ops, so it only runs for folios without a
+ * fixup bitmap: for those the folio flag is the whole fixup state, and this
+ * reserving write covers the block, so retire it. Metadata never has the
+ * flag set and only pays the test.
+ */
+static void btrfs_folio_mark_dirty_reserved(struct folio *folio)
+{
+ if (folio_test_fixup_pending(folio))
+ folio_clear_fixup_pending(folio);
+ btrfs_folio_mark_dirty(folio);
+}
+
void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info,
struct folio *folio, u64 start, u32 len)
{
struct btrfs_folio_state *bfs = folio_get_private(folio);
- unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
+ unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio,
dirty, start, len);
+ unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio,
+ fixup, start, len);
+ const unsigned int nbits = len >> fs_info->sectorsize_bits;
unsigned long flags;
spin_lock_irqsave(&bfs->lock, flags);
- bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
+ bitmap_set(bfs->bitmaps, dirty_bit, nbits);
+ /* Proper dirtying obviates the need for fixup. */
+ bitmap_clear(bfs->bitmaps, fixup_bit, nbits);
+ if (folio_test_fixup_pending(folio) &&
+ subpage_test_bitmap_all_zero(fs_info, folio, fixup))
+ folio_clear_fixup_pending(folio);
spin_unlock_irqrestore(&bfs->lock, flags);
- folio_mark_dirty(folio);
+ btrfs_folio_mark_dirty(folio);
}
static void folio_clear_tags(struct folio *folio)
@@ -457,6 +496,172 @@ void btrfs_subpage_clear_writeback(const struct btrfs_fs_info *fs_info,
spin_unlock_irqrestore(&bfs->lock, flags);
}
+void btrfs_subpage_clear_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ struct btrfs_folio_state *bfs = folio_get_private(folio);
+ unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
+ fixup, start, len);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bfs->lock, flags);
+ bitmap_clear(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
+ if (subpage_test_bitmap_all_zero(fs_info, folio, fixup))
+ folio_clear_fixup_pending(folio);
+ spin_unlock_irqrestore(&bfs->lock, flags);
+}
+
+/*
+ * In one pass under bfs->lock, mark every block with a clear dirty bit in the
+ * range both dirty and needing fixup.
+ *
+ * Only called from the dirty_folio callback, which owns the folio-level
+ * dirty flag; calling folio_mark_dirty() here would recurse.
+ *
+ * The folio fixup flag and bits are both set under bfs->lock so that a
+ * writeback pass observing the new bits also observes the flag.
+ */
+static void btrfs_subpage_set_fixup_dirty(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ struct btrfs_folio_state *bfs = folio_get_private(folio);
+ unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio,
+ dirty, start, len);
+ unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio,
+ fixup, start, len);
+ const unsigned int nbits = len >> fs_info->sectorsize_bits;
+ unsigned long flags;
+ bool marked = false;
+
+ spin_lock_irqsave(&bfs->lock, flags);
+ for (unsigned int i = 0; i < nbits; i++) {
+ if (test_bit(dirty_bit + i, bfs->bitmaps))
+ continue;
+ set_bit(dirty_bit + i, bfs->bitmaps);
+ set_bit(fixup_bit + i, bfs->bitmaps);
+ marked = true;
+ }
+ if (marked)
+ folio_set_fixup_pending(folio);
+ spin_unlock_irqrestore(&bfs->lock, flags);
+}
+
+/*
+ * Mark the still-clean blocks of a folio dirty and needing fixup, for
+ * btrfs_data_dirty_folio().
+ *
+ * A subpage block size folio that is not uptodate is left alone: its clean
+ * blocks may hold content that was never read in, which must not be marked
+ * dirty.
+ */
+void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ if (!btrfs_is_subpage(fs_info, folio)) {
+ if (!folio_test_dirty(folio))
+ folio_set_fixup_pending(folio);
+ return;
+ }
+ if (!folio_test_uptodate(folio))
+ return;
+ btrfs_subpage_set_fixup_dirty(fs_info, folio, start, len);
+}
+
+/*
+ * Drop the fixup blocks inside the range: clear both their fixup and dirty
+ * bits.
+ *
+ * Fixup blocks carry no space reservation, so their fixup and dirty bits
+ * must be dropped together. Clearing only the fixup bit would leave a
+ * dirty block without a reservation which is not a valid state.
+ *
+ * Returns true if the folio has no dirty blocks left.
+ */
+static bool btrfs_subpage_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ struct btrfs_folio_state *bfs = folio_get_private(folio);
+ unsigned int dirty_bit = subpage_calc_start_bit(fs_info, folio,
+ dirty, start, len);
+ unsigned int fixup_bit = subpage_calc_start_bit(fs_info, folio,
+ fixup, start, len);
+ const unsigned int nbits = len >> fs_info->sectorsize_bits;
+ unsigned long flags;
+ bool last;
+
+ spin_lock_irqsave(&bfs->lock, flags);
+ for (unsigned int i = 0; i < nbits; i++) {
+ if (!test_bit(fixup_bit + i, bfs->bitmaps))
+ continue;
+ clear_bit(fixup_bit + i, bfs->bitmaps);
+ clear_bit(dirty_bit + i, bfs->bitmaps);
+ }
+ if (subpage_test_bitmap_all_zero(fs_info, folio, fixup))
+ folio_clear_fixup_pending(folio);
+ last = subpage_test_bitmap_all_zero(fs_info, folio, dirty);
+ spin_unlock_irqrestore(&bfs->lock, flags);
+ return last;
+}
+
+/*
+ * Drop the fixup blocks inside the range, for callers discarding their data:
+ * btrfs_invalidate_folio() and the writepage fixup worker's error path.
+ *
+ * Callers that have just reserved space for a block want
+ * btrfs_folio_clear_fixup() instead - there the block stays dirty and gets
+ * written.
+ *
+ * The range can be byte-granular (an unaligned truncate through
+ * btrfs_invalidate_folio()); only blocks fully inside it are dropped, as a
+ * partially covered block still holds live data outside the range. For
+ * single-block folios the folio flag is the fixup state, so it is dropped
+ * only when the range covers the whole folio.
+ */
+void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ u64 aligned_start;
+ u64 aligned_end;
+
+ /* The folio flag is set whenever any fixup bitmap bit is. */
+ if (!folio_test_fixup_pending(folio))
+ return;
+ if (!btrfs_is_subpage(fs_info, folio)) {
+ if (start <= folio_pos(folio) &&
+ start + len >= folio_next_pos(folio)) {
+ folio_clear_fixup_pending(folio);
+ folio_clear_dirty_for_io(folio);
+ }
+ return;
+ }
+ btrfs_subpage_clamp_range(folio, &start, &len);
+ aligned_start = round_up(start, fs_info->sectorsize);
+ aligned_end = round_down(start + len, fs_info->sectorsize);
+ if (aligned_end <= aligned_start)
+ return;
+ if (btrfs_subpage_clear_fixup_dirty(fs_info, folio, aligned_start,
+ aligned_end - aligned_start))
+ folio_clear_dirty_for_io(folio);
+}
+
+bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ if (!btrfs_is_subpage(fs_info, folio))
+ return folio_test_fixup_pending(folio);
+ return btrfs_subpage_test_fixup(fs_info, folio, start, len);
+}
+
+void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len)
+{
+ if (!btrfs_is_subpage(fs_info, folio)) {
+ folio_clear_fixup_pending(folio);
+ return;
+ }
+ btrfs_subpage_clear_fixup(fs_info, folio, start, len);
+}
+
/*
* Unlike set/clear which is dependent on each page status, for test all bits
* are tested in the same way.
@@ -480,6 +685,7 @@ bool btrfs_subpage_test_##name(const struct btrfs_fs_info *fs_info, \
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(uptodate);
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(dirty);
IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(writeback);
+IMPLEMENT_BTRFS_SUBPAGE_TEST_OP(fixup);
/*
* Note that, in selftests (extent-io-tests), we can have empty fs_info passed
@@ -571,8 +777,8 @@ bool btrfs_meta_folio_test_##name(struct folio *folio, const struct extent_buffe
}
IMPLEMENT_BTRFS_PAGE_OPS(uptodate, folio_mark_uptodate, folio_clear_uptodate,
folio_test_uptodate);
-IMPLEMENT_BTRFS_PAGE_OPS(dirty, folio_mark_dirty, folio_clear_dirty_for_io,
- folio_test_dirty);
+IMPLEMENT_BTRFS_PAGE_OPS(dirty, btrfs_folio_mark_dirty_reserved,
+ folio_clear_dirty_for_io, folio_test_dirty);
IMPLEMENT_BTRFS_PAGE_OPS(writeback, folio_start_writeback, folio_end_writeback,
folio_test_writeback);
diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h
index c6d7394e6418..9aceba93c818 100644
--- a/fs/btrfs/subpage.h
+++ b/fs/btrfs/subpage.h
@@ -14,15 +14,15 @@ struct folio;
/*
* Extra info for subpage bitmap.
*
- * For subpage we pack all uptodate/dirty/writeback bitmaps into
+ * For subpage we pack all uptodate/dirty/writeback/fixup bitmaps into
* one larger bitmap.
*
* This structure records how they are organized in the bitmap:
*
- * /- uptodate /- dirty /- writeback
- * | | |
- * v v v
- * |u|u|u|u|........|u|u|d|d|.......|d|d|w|w|.......|w|w|
+ * /- uptodate /- dirty /- writeback /- fixup
+ * | | | |
+ * v v v v
+ * |u|u|u|u|........|u|u|d|d|.......|d|d|w|w|.....|w|w|f|f|.....|f|f|
* |< sectors_per_page >|
*
* Unlike regular macro-like enums, here we do not go upper-case names, as
@@ -40,6 +40,14 @@ enum {
*/
btrfs_bitmap_nr_writeback,
+ /*
+ * Blocks dirtied by the dirty_folio callback instead of a reserving
+ * write path (e.g. set_page_dirty_lock() on a GUP pin). They have
+ * no space reservation and need the writepage fixup before they can
+ * be submitted.
+ */
+ btrfs_bitmap_nr_fixup,
+
btrfs_bitmap_nr_max
};
@@ -166,6 +174,29 @@ DECLARE_BTRFS_SUBPAGE_OPS(dirty);
DECLARE_BTRFS_SUBPAGE_OPS(writeback);
/*
+ * Fixup bit helpers.
+ *
+ * The fixup bit is data-only and has no plain set helper (setting happens
+ * together with dirtying in btrfs_subpage_set_fixup_dirty()), so it does not
+ * go through DECLARE_BTRFS_SUBPAGE_OPS(). For single-block folios the
+ * folio_*_fixup_pending() flag takes the place of the bitmap.
+ */
+void btrfs_subpage_clear_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len);
+bool btrfs_subpage_test_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len);
+bool btrfs_folio_test_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len);
+void btrfs_folio_set_fixup_dirty(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len);
+/* For a block that just got its space reserved; it stays dirty. */
+void btrfs_folio_clear_fixup(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len);
+/* For callers discarding the data; clears the dirty bits too. */
+void btrfs_folio_clear_fixup_dirty(const struct btrfs_fs_info *fs_info,
+ struct folio *folio, u64 start, u32 len);
+
+/*
* Helper for error cleanup, where a folio will have its dirty flag cleared,
* with writeback started and finished.
*/
diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c
index 9915e39362db..c80b24a941ad 100644
--- a/fs/crypto/policy.c
+++ b/fs/crypto/policy.c
@@ -534,7 +534,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg)
return -EFAULT;
policy.version = version;
- if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
+ if (!inode_owner_or_capable(file_mnt_idmap(filp), inode))
return -EACCES;
ret = mnt_want_write_file(filp);
diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c
index 30468d51b5ad..fb636dce43af 100644
--- a/fs/iomap/ioend.c
+++ b/fs/iomap/ioend.c
@@ -13,6 +13,7 @@
struct bio_set iomap_ioend_bioset;
EXPORT_SYMBOL_GPL(iomap_ioend_bioset);
+static struct bio_set iomap_ioend_split_bioset;
struct iomap_ioend *iomap_init_ioend(struct inode *inode,
struct bio *bio, loff_t file_offset, u16 ioend_flags)
@@ -488,7 +489,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend,
sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT,
i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT;
- split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset);
+ split = bio_split(bio, sector_offset, GFP_NOFS,
+ &iomap_ioend_split_bioset);
if (IS_ERR(split))
return ERR_CAST(split);
split->bi_private = bio->bi_private;
@@ -511,8 +513,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend);
static int __init iomap_ioend_init(void)
{
- return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
+ const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE);
+ int error;
+
+ error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries,
offsetof(struct iomap_ioend, io_bio),
BIOSET_NEED_BVECS);
+ if (error)
+ return error;
+ error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries,
+ offsetof(struct iomap_ioend, io_bio),
+ BIOSET_NEED_BVECS);
+ if (error)
+ goto out_exit_ioend_bioset;
+ return 0;
+
+out_exit_ioend_bioset:
+ bioset_exit(&iomap_ioend_bioset);
+ return error;
}
fs_initcall(iomap_ioend_init);
diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c
index 24a8a5418e31..7fdfa4f27e34 100644
--- a/fs/netfs/buffered_read.c
+++ b/fs/netfs/buffered_read.c
@@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq,
added = rolling_buffer_load_from_ra(&rreq->buffer, ractl,
&put_batch);
- if (added < 0)
+ if (added < 0) {
+ folio_batch_release(&put_batch);
return added;
+ }
rreq->submitted += added;
}
folio_batch_release(&put_batch);
@@ -359,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl)
netfs_rreq_expand(rreq, ractl);
rreq->submitted = rreq->start;
- if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0)
+ if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
goto cleanup_free;
netfs_read_to_pagecache(rreq, ractl);
@@ -378,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo
{
ssize_t added;
- if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0)
+ if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0)
return -ENOMEM;
- added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags);
+ added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp);
if (added < 0)
return added;
rreq->submitted = rreq->start + added;
diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h
index d889caa401dc..420ee7b26580 100644
--- a/fs/netfs/internal.h
+++ b/fs/netfs/internal.h
@@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests;
extern spinlock_t netfs_proc_lock;
extern mempool_t netfs_request_pool;
extern mempool_t netfs_subrequest_pool;
+extern mempool_t netfs_folioq_pool;
#ifdef CONFIG_PROC_FS
static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq)
diff --git a/fs/netfs/main.c b/fs/netfs/main.c
index 73da6c9f5777..927badf3989d 100644
--- a/fs/netfs/main.c
+++ b/fs/netfs/main.c
@@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab;
static struct kmem_cache *netfs_subrequest_slab;
mempool_t netfs_request_pool;
mempool_t netfs_subrequest_pool;
+mempool_t netfs_folioq_pool;
#ifdef CONFIG_PROC_FS
LIST_HEAD(netfs_io_requests);
@@ -108,6 +109,9 @@ static int __init netfs_init(void)
{
int ret = -ENOMEM;
+ if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct folio_queue)) < 0)
+ goto error_folioq_pool;
+
netfs_request_slab = kmem_cache_create("netfs_request",
sizeof(struct netfs_io_request), 0,
SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT,
@@ -160,6 +164,8 @@ error_subreq:
error_reqpool:
kmem_cache_destroy(netfs_request_slab);
error_req:
+ mempool_exit(&netfs_folioq_pool);
+error_folioq_pool:
return ret;
}
fs_initcall(netfs_init);
@@ -172,5 +178,6 @@ static void __exit netfs_exit(void)
kmem_cache_destroy(netfs_subrequest_slab);
mempool_exit(&netfs_request_pool);
kmem_cache_destroy(netfs_request_slab);
+ mempool_exit(&netfs_folioq_pool);
}
module_exit(netfs_exit);
diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c
index b8c4918d3dcd..01461a74642d 100644
--- a/fs/netfs/objects.c
+++ b/fs/netfs/objects.c
@@ -7,7 +7,6 @@
#include <linux/slab.h>
#include <linux/mempool.h>
-#include <linux/delay.h>
#include "internal.h"
static void netfs_free_request(struct work_struct *work);
@@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping,
struct netfs_io_request *rreq;
mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool;
struct kmem_cache *cache = mempool->pool_data;
+ gfp_t gfp = GFP_KERNEL;
int ret;
- for (;;) {
- rreq = mempool_alloc(mempool, GFP_KERNEL);
- if (rreq)
- break;
- msleep(10);
+ /* Writeback is part of memory reclaim and must not fail due to ENOMEM. */
+ if (origin == NETFS_WRITEBACK || origin == NETFS_WRITEBACK_SINGLE) {
+ gfp = GFP_NOFS; /* Allows use of mempools. */
+
+ rreq = mempool_alloc(mempool, gfp);
+ } else {
+ rreq = mempool->alloc(gfp, mempool->pool_data);
+ if (!rreq)
+ return ERR_PTR(-ENOMEM);
}
memset(rreq, 0, kmem_cache_size(cache));
INIT_WORK(&rreq->cleanup_work, netfs_free_request);
+ rreq->gfp = gfp;
rreq->start = start;
rreq->len = len;
rreq->origin = origin;
@@ -200,13 +205,12 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq
mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool;
struct kmem_cache *cache = mempool->pool_data;
- for (;;) {
- subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool,
- GFP_KERNEL);
- if (subreq)
- break;
- msleep(10);
- }
+ if (rreq->gfp == GFP_KERNEL)
+ subreq = mempool->alloc(rreq->gfp, mempool->pool_data);
+ else
+ subreq = mempool_alloc(mempool, rreq->gfp);
+ if (!subreq)
+ return NULL;
memset(subreq, 0, kmem_cache_size(cache));
INIT_WORK(&subreq->work, NULL);
diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c
index a1489aa29f78..c31190993b76 100644
--- a/fs/netfs/read_pgpriv2.c
+++ b/fs/netfs/read_pgpriv2.c
@@ -53,7 +53,8 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio
trace_netfs_folio(folio, netfs_folio_trace_store_copy);
/* Attach the folio to the rolling buffer. */
- if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) {
+ if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) {
+ folio_end_private_2(folio);
clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags);
return;
}
diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c
index a17fbf9853a4..8c0026836f9c 100644
--- a/fs/netfs/rolling_buffer.c
+++ b/fs/netfs/rolling_buffer.c
@@ -6,6 +6,7 @@
*/
#include <linux/bitops.h>
+#include <linux/mempool.h>
#include <linux/pagemap.h>
#include <linux/rolling_buffer.h>
#include <linux/slab.h>
@@ -27,7 +28,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp,
{
struct folio_queue *fq;
- fq = kmalloc_obj(*fq, gfp);
+ if (gfp == GFP_KERNEL)
+ fq = netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data);
+ else
+ fq = mempool_alloc(&netfs_folioq_pool, gfp);
if (fq) {
netfs_stat(&netfs_n_folioq);
folioq_init(fq, rreq_id);
@@ -50,7 +54,7 @@ void netfs_folioq_free(struct folio_queue *folioq,
{
trace_netfs_folioq(folioq, trace);
netfs_stat_d(&netfs_n_folioq);
- kfree(folioq);
+ mempool_free(folioq, &netfs_folioq_pool);
}
EXPORT_SYMBOL(netfs_folioq_free);
@@ -60,11 +64,11 @@ EXPORT_SYMBOL(netfs_folioq_free);
* consumer.
*/
int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
- unsigned int direction)
+ unsigned int direction, gfp_t gfp)
{
struct folio_queue *fq;
- fq = netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf_init);
+ fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init);
if (!fq)
return -ENOMEM;
@@ -77,14 +81,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
/*
* Add another folio_queue to a rolling buffer if there's no space left.
*/
-int rolling_buffer_make_space(struct rolling_buffer *roll)
+int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp)
{
struct folio_queue *fq, *head = roll->head;
if (!folioq_full(head))
return 0;
- fq = netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_make_space);
+ fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space);
if (!fq)
return -ENOMEM;
fq->prev = head;
@@ -122,7 +126,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
int nr, ix, to;
ssize_t size = 0;
- if (rolling_buffer_make_space(roll) < 0)
+ if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0)
return -ENOMEM;
fq = roll->head;
@@ -153,12 +157,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
* Append a folio to the rolling buffer.
*/
ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
- unsigned int flags)
+ unsigned int flags, gfp_t gfp)
{
ssize_t size = folio_size(folio);
int slot;
- if (rolling_buffer_make_space(roll) < 0)
+ if (rolling_buffer_make_space(roll, gfp) < 0)
return -ENOMEM;
slot = folioq_append(roll->head, folio);
diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c
index f2761c99795a..2d9cfcd43658 100644
--- a/fs/netfs/write_issue.c
+++ b/fs/netfs/write_issue.c
@@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping,
ictx = netfs_inode(wreq->inode);
if (is_cacheable)
fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx));
- if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0)
+ if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0)
goto nomem;
wreq->cleaned_to = wreq->start;
@@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq,
*/
if (iov_iter_is_folioq(wreq_iter) &&
wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq))
- rolling_buffer_make_space(&wreq->buffer);
+ rolling_buffer_make_space(&wreq->buffer, wreq->gfp);
subreq = netfs_alloc_subrequest(wreq);
subreq->source = stream->source;
@@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
_enter("");
- if (rolling_buffer_make_space(&wreq->buffer) < 0)
+ if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0)
return -ENOMEM;
/* netfs_perform_write() may shift i_size around the page or from out
@@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq,
}
/* Attach the folio to the rolling buffer. */
- rolling_buffer_append(&wreq->buffer, folio, 0);
+ rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp);
/* Move the submission point forward to allow for write-streaming data
* not starting at the front of the page. We don't do write-streaming
@@ -720,6 +720,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
size_t iter_off = 0;
size_t fsize = folio_size(folio), flen;
loff_t fpos = folio_pos(folio);
+ ssize_t ret;
bool to_eof = false;
bool no_debug = false;
@@ -748,7 +749,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq,
/* Attach the folio to the rolling buffer. */
folio_get(folio);
- rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK);
+ ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK, wreq->gfp);
+ if (ret < 0) {
+ folio_put(folio);
+ return ret;
+ }
/* Move the submission point forward to allow for write-streaming data
* not starting at the front of the page. We don't do write-streaming
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 1360409d8de9..5709c6fea85b 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -10364,6 +10364,7 @@ static void nfs41_free_stateid_release(void *calldata)
struct nfs_free_stateid_data *data = calldata;
struct nfs_client *clp = data->server->nfs_client;
+ nfs_sb_deactive(data->server->super);
nfs_put_client(clp);
kfree(calldata);
}
@@ -10402,17 +10403,22 @@ static int nfs41_free_stateid(struct nfs_server *server,
struct nfs_free_stateid_data *data;
struct rpc_task *task;
struct nfs_client *clp = server->nfs_client;
+ int ret = -EIO;
if (!refcount_inc_not_zero(&clp->cl_count))
- return -EIO;
+ return ret;
+ if (!nfs_sb_active(server->super))
+ goto out_put_clp;
nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID,
&task_setup.rpc_client, &msg);
dprintk("NFS call free_stateid %p\n", stateid);
data = kmalloc_obj(*data);
- if (!data)
- return -ENOMEM;
+ if (!data) {
+ ret = -ENOMEM;
+ goto out_put_server;
+ }
data->server = server;
nfs4_stateid_copy(&data->args.stateid, stateid);
@@ -10428,6 +10434,11 @@ static int nfs41_free_stateid(struct nfs_server *server,
rpc_put_task(task);
stateid->type = NFS4_FREED_STATEID_TYPE;
return 0;
+out_put_server:
+ nfs_sb_deactive(server->super);
+out_put_clp:
+ nfs_put_client(clp);
+ return ret;
}
static void
@@ -10585,7 +10596,8 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = {
static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
{
ssize_t error, error2, error3;
- size_t left = size;
+ ssize_t left = size;
+ ssize_t left2;
error = generic_listxattr(dentry, list, left);
if (error < 0)
@@ -10595,10 +10607,13 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size)
left -= error;
}
- error2 = security_inode_listsecurity(d_inode(dentry), &list, &left);
+ left2 = left;
+ error2 = security_inode_listsecurity(d_inode(dentry), &list, &left2);
if (error2 < 0)
return error2;
- error2 = size - error - left;
+ error2 = left - left2;
+ if (list)
+ left -= error2;
error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, left);
if (error3 < 0)
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 239b7bcbaedf..58f32aac5f61 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -4293,6 +4293,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz
ni->initialized_size = newsize;
ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize);
}
+
+ /*
+ * Drop any page-cache folios that now lie beyond the shrunk
+ * attribute. The clusters backing them have just been freed and the
+ * runlist truncated, so leaving stale dirty folios around makes a
+ * later writeback map a vcn past the new allocation, which fails with
+ * -ENOENT and loses the write.
+ */
+ truncate_inode_pages(VFS_I(ni)->i_mapping, newsize);
+
/* Update data size in the index. */
if (ni->type == AT_DATA && ni->name == AT_UNNAMED)
NInoSetFileNameDirty(ni);
diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c
index 5ff25e9aaa32..cd403b1d99ee 100644
--- a/fs/ntfs/namei.c
+++ b/fs/ntfs/namei.c
@@ -685,7 +685,8 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d
mutex_unlock(&dir_ni->mrec_lock);
mutex_unlock(&ni->mrec_lock);
- ni->flags = fn->file_attributes;
+ ni->flags = fn->file_attributes |
+ (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN);
/* Set the sequence number. */
vi->i_generation = ni->seq_no;
set_nlink(vi, 1);
diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c
index cbb6576cf725..8e0fd400e7f7 100644
--- a/fs/ntfs/runlist.c
+++ b/fs/ntfs/runlist.c
@@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst,
* On success, return a pointer to the newly allocated, or recycled, memory.
* On error, return -errno.
*/
-struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
- int old_size, int new_size)
+static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl,
+ int old_size, int new_size, gfp_t gfp)
{
struct runlist_element *new_rl;
+ size_t new_bytes;
+
+ if (old_size < 0 || new_size < 0)
+ return ERR_PTR(-EINVAL);
- old_size = old_size * sizeof(*rl);
- new_size = new_size * sizeof(*rl);
if (old_size == new_size)
return rl;
- new_rl = kvzalloc(new_size, GFP_NOFS);
+ if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes))
+ return ERR_PTR(-EINVAL);
+
+ new_rl = kvzalloc(new_bytes, gfp);
if (unlikely(!new_rl))
return ERR_PTR(-ENOMEM);
if (likely(rl != NULL)) {
- if (unlikely(old_size > new_size))
- old_size = new_size;
- memcpy(new_rl, rl, old_size);
+ size_t old_bytes;
+
+ if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) {
+ kvfree(new_rl);
+ return ERR_PTR(-EINVAL);
+ }
+ if (unlikely(old_bytes > new_bytes))
+ old_bytes = new_bytes;
+ memcpy(new_rl, rl, old_bytes);
kvfree(rl);
}
return new_rl;
}
+struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
+ int old_size, int new_size)
+{
+ return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS);
+}
+
/*
* ntfs_rl_realloc_nofail - Reallocate memory for runlists
* @rl: original runlist
@@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl,
static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl,
int old_size, int new_size)
{
- struct runlist_element *new_rl;
-
- old_size = old_size * sizeof(*rl);
- new_size = new_size * sizeof(*rl);
- if (old_size == new_size)
- return rl;
-
- new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL);
- if (likely(rl != NULL)) {
- if (unlikely(old_size > new_size))
- old_size = new_size;
- memcpy(new_rl, rl, old_size);
- kvfree(rl);
- }
- return new_rl;
+ return ntfs_rl_realloc_gfp(rl, old_size, new_size,
+ GFP_NOFS | __GFP_NOFAIL);
}
/*
diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c
index 9951817d0d7f..fe10719e627e 100644
--- a/fs/smb/client/cifs_swn.c
+++ b/fs/smb/client/cifs_swn.c
@@ -425,7 +425,7 @@ static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon)
/*
* Get a registration for the tcon's server and share name, allocating a new one if it does not
- * exists
+ * exist.
*/
static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
{
@@ -443,7 +443,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
goto unlock;
}
- reg = kmalloc_obj(struct cifs_swn_reg, GFP_ATOMIC);
+ reg = kmalloc_obj(struct cifs_swn_reg, GFP_KERNEL);
if (reg == NULL) {
ret = -ENOMEM;
goto fail_unlock;
@@ -451,7 +451,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon)
kref_init(&reg->ref_count);
- reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC);
+ reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_KERNEL);
if (reg->id < 0) {
cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__);
ret = reg->id;
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index 9424281a7674..12005f46307d 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -68,6 +68,9 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
{
char *payload;
+ if (prep->datalen > U16_MAX)
+ return -EINVAL;
+
/*
* If the payload is less than or equal to the size of a pointer, then
* an allocation here is wasteful. Just copy the data directly to the
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 40162d5554ea..1f77512252e7 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -1681,8 +1681,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
/* tcon and ses pointer are checked in smb_init */
- if (tcon->ses->server == NULL)
+ if (!tcon->ses->server) {
+ cifs_small_buf_release(pSMB);
return -ECONNABORTED;
+ }
pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid;
@@ -1796,8 +1798,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
/* tcon and ses pointer are checked in smb_init */
- if (tcon->ses->server == NULL)
+ if (!tcon->ses->server) {
+ cifs_buf_release(pSMB);
return -ECONNABORTED;
+ }
pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid;
@@ -2077,8 +2081,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16));
/* tcon and ses pointer are checked in smb_init */
- if (tcon->ses->server == NULL)
+ if (!tcon->ses->server) {
+ cifs_small_buf_release(pSMB);
return -ECONNABORTED;
+ }
pSMB->AndXCommand = 0xFF; /* none */
pSMB->Fid = netfid;
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index b279a44be729..ac89c1ba56b1 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -915,6 +915,14 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file,
cifs_set_oplock_level(cifsi, 0);
}
+ if (OPEN_FMODE(cifs_file->f_flags) & FMODE_WRITE) {
+ /* Stamp while open_file_lock is held; covers all close paths
+ * including background I/O. Pairs with smp_load_acquire() in
+ * is_size_safe_to_change().
+ */
+ smp_store_release(&cifsi->time_last_write, jiffies);
+ }
+
spin_unlock(&cifsi->open_file_lock);
spin_unlock(&tcon->open_file_lock);
@@ -1429,15 +1437,6 @@ void smb2_deferred_work_close(struct work_struct *work)
cifs_del_deferred_close(cfile);
cfile->deferred_close_scheduled = false;
spin_unlock(&cinode->deferred_lock);
- /*
- * Refresh time_last_write immediately before the actual server close
- * so the protection window is anchored to the real close time, not
- * the earlier userspace close time stored by cifs_close().
- */
- if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&cinode->time_last_write, jiffies);
- }
_cifsFileInfo_put(cfile, true, false);
}
@@ -1467,10 +1466,6 @@ int cifs_close(struct inode *inode, struct file *file)
if (file->private_data != NULL) {
cfile = file->private_data;
file->private_data = NULL;
- if (file->f_mode & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&cinode->time_last_write, jiffies);
- }
dclose = kmalloc_obj(struct cifs_deferred_close);
if ((cfile->status_file_deleted == false) &&
(smb2_can_defer_close(inode, dclose))) {
@@ -3276,13 +3271,13 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
* No writable handles open. Check whether we are within the attribute
* cache validity window of a recent local modification.
*
- * For the close() path: cifs_close() calls smp_store_release() on
- * time_last_write before _cifsFileInfo_put() removes the handle under
- * open_file_lock. That spin_unlock() is a store-release that pairs
- * with the spin_lock() (load-acquire) in is_inode_writable() above,
- * so if is_inode_writable() returned false the smp_load_acquire()
- * below is guaranteed to observe any time_last_write update from a
- * concurrent close().
+ * For the close() path: _cifsFileInfo_put() stamps time_last_write
+ * (via smp_store_release()) before releasing open_file_lock. That
+ * spin_unlock() is a store-release that pairs with the spin_lock()
+ * (load-acquire) in is_inode_writable() above, so if
+ * is_inode_writable() returned false the smp_load_acquire() below is
+ * guaranteed to observe any time_last_write update from a concurrent
+ * close(), covering all close paths including background I/O.
*
* For the setattr/truncate paths: those callers use smp_store_release()
* directly; the smp_load_acquire() below pairs with that store. There
@@ -3297,7 +3292,7 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
* jiffies is still close to INITIAL_JIFFIES on 32-bit systems.
*/
if (from_readdir) {
- /* Pairs with smp_store_release() at close and truncate sites. */
+ /* Pairs with smp_store_release() in _cifsFileInfo_put() and setattr. */
tlw = smp_load_acquire(&cifsInode->time_last_write);
if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax))
return false;
diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c
index 01424a5cdb99..9f66bbcd3801 100644
--- a/fs/smb/client/fscache.c
+++ b/fs/smb/client/fscache.c
@@ -38,7 +38,6 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
struct TCP_Server_Info *server = tcon->ses->server;
struct fscache_volume *vcookie;
const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr;
- size_t slen, i;
char *sharename;
char *key;
int ret = -ENOMEM;
@@ -73,10 +72,7 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)
return PTR_ERR(sharename);
}
- slen = strlen(sharename);
- for (i = 0; i < slen; i++)
- if (sharename[i] == '/')
- sharename[i] = ';';
+ strreplace(sharename, '/', ';');
key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename);
if (!key)
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index b2806371bfde..0afff761aab9 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -3059,6 +3059,7 @@ void cifs_setsize(struct inode *inode, loff_t offset)
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
truncate_pagecache(inode, offset);
netfs_wait_for_outstanding_io(inode);
+ fscache_resize_cookie(cifs_inode_cookie(inode), offset);
}
int cifs_file_set_size(const unsigned int xid, struct dentry *dentry,
@@ -3190,6 +3191,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
rc = 0;
if (attrs->ia_valid & ATTR_SIZE) {
+ if (attrs->ia_size != i_size_read(inode)) {
+ /* Stamp before RPC. On failure the stamp remains: restoring a
+ * stale snapshot could silently erase a concurrent
+ * _cifsFileInfo_put() close stamp. readdir is suppressed
+ * until the stamp expires; stat() bypasses this via the
+ * from_readdir=false path in is_size_safe_to_change() and
+ * always returns an authoritative QUERY_INFO result.
+ * Pairs with smp_load_acquire() in is_size_safe_to_change().
+ */
+ smp_store_release(&cifsInode->time_last_write, jiffies);
+ }
rc = cifs_file_set_size(xid, direntry, full_path,
open_file, attrs->ia_size);
if (rc != 0)
@@ -3279,8 +3291,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
if ((attrs->ia_valid & ATTR_SIZE) &&
attrs->ia_size != i_size_read(inode)) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&cifsInode->time_last_write, jiffies);
truncate_setsize(inode, attrs->ia_size);
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
@@ -3370,6 +3380,17 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
}
if (attrs->ia_valid & ATTR_SIZE) {
+ if (attrs->ia_size != i_size_read(inode)) {
+ /* Stamp before RPC. On failure the stamp remains: restoring a
+ * stale snapshot could silently erase a concurrent
+ * _cifsFileInfo_put() close stamp. readdir is suppressed
+ * until the stamp expires; stat() bypasses this via the
+ * from_readdir=false path in is_size_safe_to_change() and
+ * always returns an authoritative QUERY_INFO result.
+ * Pairs with smp_load_acquire() in is_size_safe_to_change().
+ */
+ smp_store_release(&cifsInode->time_last_write, jiffies);
+ }
rc = cifs_file_set_size(xid, direntry, full_path,
cfile, attrs->ia_size);
if (rc != 0)
@@ -3482,8 +3503,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if ((attrs->ia_valid & ATTR_SIZE) &&
attrs->ia_size != i_size_read(inode)) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&cifsInode->time_last_write, jiffies);
truncate_setsize(inode, attrs->ia_size);
netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 6edebc0807ea..46e1382e8e04 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -525,24 +525,11 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
}
spin_unlock(&cifs_inode->open_file_lock);
- if (failed_cfile) {
- if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
- jiffies);
- }
+ if (failed_cfile)
_cifsFileInfo_put(failed_cfile, false, false);
- }
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
- struct cifsFileInfo *cfile = tmp_list->cfile;
-
- if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
- jiffies);
- }
- _cifsFileInfo_put(cfile, false, false);
+ _cifsFileInfo_put(tmp_list->cfile, false, false);
list_del(&tmp_list->list);
kfree(tmp_list);
}
@@ -576,24 +563,11 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
}
spin_unlock(&tcon->open_file_lock);
- if (failed_cfile) {
- if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
- jiffies);
- }
+ if (failed_cfile)
_cifsFileInfo_put(failed_cfile, true, false);
- }
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
- struct cifsFileInfo *cfile = tmp_list->cfile;
-
- if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
- jiffies);
- }
- _cifsFileInfo_put(cfile, true, false);
+ _cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
kfree(tmp_list);
}
@@ -663,24 +637,11 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
}
spin_unlock(&tcon->open_file_lock);
- if (failed_cfile) {
- if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write,
- jiffies);
- }
+ if (failed_cfile)
_cifsFileInfo_put(failed_cfile, true, false);
- }
list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
- struct cifsFileInfo *cfile = tmp_list->cfile;
-
- if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) {
- /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */
- smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write,
- jiffies);
- }
- _cifsFileInfo_put(cfile, true, false);
+ _cifsFileInfo_put(tmp_list->cfile, true, false);
list_del(&tmp_list->list);
kfree(tmp_list);
}
diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c
index ab3d09613c91..395299f9121b 100644
--- a/fs/smb/client/smb1maperror.c
+++ b/fs/smb/client/smb1maperror.c
@@ -234,11 +234,7 @@ int __init smb1_init_maperror(void)
if (rc)
return rc;
- rc = mapping_table_ERRSRV_is_sorted();
- if (rc)
- return rc;
-
- return rc;
+ return mapping_table_ERRSRV_is_sorted();
}
#if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index cbd51a08e97e..192649fec25d 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -4745,10 +4745,10 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
size_t cur_size = 0;
rc = netfs_alloc_folioq_buffer(NULL, &buffer, &cur_size,
size, GFP_NOFS);
+ new->rq_buffer = buffer;
if (rc < 0)
goto err_free;
- new->rq_buffer = buffer;
iov_iter_folio_queue(&new->rq_iter, ITER_SOURCE,
buffer, 0, 0, size);
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index ec75633b7da0..2a194ee36fb4 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -200,6 +200,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn);
* This is a hack. We will move status to a proper place once we land
* a multi-sessions support.
*/
+static inline bool ksmbd_conn_new(struct ksmbd_conn *conn)
+{
+ return READ_ONCE(conn->status) == KSMBD_SESS_NEW;
+}
+
static inline bool ksmbd_conn_good(struct ksmbd_conn *conn)
{
return READ_ONCE(conn->status) == KSMBD_SESS_GOOD;
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index c1ba5e01aa7f..76f63f9adc72 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -1325,6 +1325,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn,
* smb2_handle_negotiate() - handler for smb2 negotiate command
* @work: smb work containing smb request buffer
*
+ * The caller holds conn->srv_mutex.
+ *
* Return: 0
*/
int smb2_handle_negotiate(struct ksmbd_work *work)
@@ -1338,13 +1340,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
ksmbd_debug(SMB, "Received negotiate request\n");
conn->need_neg = false;
- if (ksmbd_conn_good(conn)) {
- pr_err("conn->tcp_status is already in CifsGood State\n");
- work->send_no_response = 1;
- return rc;
- }
-
- ksmbd_conn_lock(conn);
smb2_buf_len = get_rfc1002_len(work->request_buf);
smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects);
if (smb2_neg_size > smb2_buf_len) {
@@ -1495,7 +1490,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
ksmbd_conn_set_need_setup(conn);
err_out:
- ksmbd_conn_unlock(conn);
if (rc)
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
@@ -1975,7 +1969,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
goto out_err;
}
- if (strncmp(conn->ClientGUID, sess->ClientGUID,
+ if (memcmp(conn->ClientGUID, sess->ClientGUID,
SMB2_CLIENT_GUID_SIZE)) {
rc = -ENOENT;
goto out_err;
@@ -8696,7 +8690,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn,
goto err_out;
}
- if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) {
+ if (memcmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) {
ret = -EINVAL;
goto err_out;
}
diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c
index 7de73223189a..080fbc9eb470 100644
--- a/fs/smb/server/smb_common.c
+++ b/fs/smb/server/smb_common.c
@@ -608,23 +608,46 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command)
struct ksmbd_conn *conn = work->conn;
int ret;
- conn->dialect =
- ksmbd_negotiate_smb_dialect(work->request_buf);
- ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
-
if (command == SMB2_NEGOTIATE_HE) {
+ /*
+ * An SMB2 NEGOTIATE is valid for a new connection, or after an
+ * SMB1 multi-protocol negotiate has selected SMB2. Do not allow
+ * a second SMB2 NEGOTIATE to replace connection-wide state
+ * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT
+ * is a transient session state and does not restart transport
+ * negotiation.
+ */
+ ksmbd_conn_lock(conn);
+ if (!ksmbd_conn_new(conn) &&
+ !ksmbd_conn_need_negotiate(conn)) {
+ work->send_no_response = 1;
+ ksmbd_conn_set_exiting(conn);
+ ksmbd_conn_unlock(conn);
+ return 0;
+ }
+
+ conn->dialect =
+ ksmbd_negotiate_smb_dialect(work->request_buf);
+ ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
ret = smb2_handle_negotiate(work);
+ ksmbd_conn_unlock(conn);
return ret;
}
if (command == SMB_COM_NEGOTIATE) {
+ ksmbd_conn_lock(conn);
+ conn->dialect =
+ ksmbd_negotiate_smb_dialect(work->request_buf);
+ ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
if (__smb2_negotiate(conn)) {
init_smb3_11_server(conn);
- init_smb2_neg_rsp(work);
+ ret = init_smb2_neg_rsp(work);
ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n");
- return 0;
+ } else {
+ ret = smb_handle_negotiate(work);
}
- return smb_handle_negotiate(work);
+ ksmbd_conn_unlock(conn);
+ return ret;
}
pr_err("Unknown SMB negotiation command: %u\n", command);
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index d95c405eab11..a141025581af 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -697,6 +697,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
fp = NULL;
else {
fp->f_state = FP_CLOSED;
+ idr_remove(ft->idr, id);
+ fp->volatile_id = KSMBD_NO_FID;
closed = true;
if (!atomic_dec_and_test(&fp->refcount))
fp = NULL;
diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c
index dcd0bd0b13b4..3efed37cb98a 100644
--- a/fs/xfs/libxfs/xfs_exchmaps.c
+++ b/fs/xfs/libxfs/xfs_exchmaps.c
@@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags(
{
struct xfs_mount *mp = req->ip1->i_mount;
+ /*
+ * The INO1_WRITTEN optimization can skip exchanging hole and
+ * unwritten mappings, which means we cannot guarantee that all
+ * shared extents actually moved to the other file. Clearing the
+ * reflink flag of an inode that still holds shared extents breaks
+ * the CoW write path, so refuse to exchange the flags in that case.
+ */
+ if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN)
+ return false;
+
if (hweight32(reflink_state) != 1)
return false;
if (req->startoff1 != 0 || req->startoff2 != 0)
diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.c b/fs/xfs/libxfs/xfs_rtrefcount_btree.c
index f27b80a199ba..22acc1411aac 100644
--- a/fs/xfs/libxfs/xfs_rtrefcount_btree.c
+++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.c
@@ -201,7 +201,7 @@ xfs_rtrefcountbt_verify(
if (fa)
return fa;
level = be16_to_cpu(block->bb_level);
- if (level > mp->m_rtrefc_maxlevels)
+ if (level >= mp->m_rtrefc_maxlevels)
return __this_address;
return xfs_btree_fsblock_verify(bp, mp->m_rtrefc_mxr[level != 0]);
@@ -651,7 +651,7 @@ xfs_iformat_rtrefcount(
numrecs = be16_to_cpu(dfp->bb_numrecs);
level = be16_to_cpu(dfp->bb_level);
- if (level > mp->m_rtrefc_maxlevels ||
+ if (level >= mp->m_rtrefc_maxlevels ||
xfs_rtrefcount_droot_space_calc(level, numrecs) > dsize) {
xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE);
return -EFSCORRUPTED;
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index 47322adb7690..75f2a021ee6d 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -1118,10 +1118,10 @@ xfs_sb_read_verify(
* because _verify_common checks the on-disk values.
*/
__xfs_sb_from_disk(&sb, dsb, false);
- error = xfs_validate_sb_common(mp, bp, &sb);
+ error = xfs_validate_sb_read(mp, &sb);
if (error)
goto out_error;
- error = xfs_validate_sb_read(mp, &sb);
+ error = xfs_validate_sb_common(mp, bp, &sb);
out_error:
if (error == -EFSCORRUPTED || error == -EFSBADCRC)
diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c
index 9ed053b5f061..1fa66aa68e16 100644
--- a/fs/xfs/scrub/agheader.c
+++ b/fs/xfs/scrub/agheader.c
@@ -18,6 +18,8 @@
#include "xfs_inode.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
+#include "scrub/bitmap.h"
+#include "scrub/agino_bitmap.h"
int
xchk_setup_agheader(
@@ -266,7 +268,7 @@ xchk_superblock(
xchk_block_set_corrupt(sc, bp);
if (sb->sb_gquotino != cpu_to_be64(0))
- xchk_block_set_preen(sc, bp);
+ xchk_block_set_corrupt(sc, bp);
} else {
if (sb->sb_uquotino != cpu_to_be64(mp->m_sb.sb_uquotino))
xchk_block_set_preen(sc, bp);
@@ -933,40 +935,84 @@ xchk_agi_xref(
}
/*
+ * Walk the incore unlinked list for a particular AGI bucket to construct
+ * the unlinked inode bitmap for later reconstruction of the unlinked list.
+ * Returns 1 if we should keep checking, 0 to stop checking, or a negative
+ * errno.
+ */
+static int
+xchk_iunlink_bucket(
+ struct xfs_scrub *sc,
+ unsigned int bucket,
+ xfs_agino_t agino)
+{
+ struct xagino_bitmap seen;
+ int ret;
+
+ xagino_bitmap_init(&seen);
+
+ while (agino != NULLAGINO) {
+ struct xfs_inode *ip;
+ unsigned int len = 1;
+
+ if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) {
+ xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+ goto bad;
+ }
+
+ if (xagino_bitmap_test(&seen, agino, &len)) {
+ xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+ goto bad;
+ }
+
+ ip = xfs_iunlink_lookup(sc->sa.pag, agino);
+ if (!ip) {
+ xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+ goto bad;
+ }
+
+ if (!xfs_inode_on_unlinked_list(ip)) {
+ xchk_block_set_corrupt(sc, sc->sa.agi_bp);
+ goto bad;
+ }
+
+ ret = xagino_bitmap_set(&seen, agino, 1);
+ if (ret)
+ goto out_bitmap;
+
+ agino = ip->i_next_unlinked;
+ }
+ ret = 1;
+
+out_bitmap:
+ xagino_bitmap_destroy(&seen);
+ return ret;
+bad:
+ ret = 0;
+ goto out_bitmap;
+}
+
+/*
* Check the unlinked buckets for links to bad inodes. We hold the AGI, so
* there cannot be any threads updating unlinked list pointers in this AG.
*/
-STATIC void
+STATIC int
xchk_iunlink(
struct xfs_scrub *sc,
struct xfs_agi *agi)
{
unsigned int i;
- struct xfs_inode *ip;
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
- xfs_agino_t agino = be32_to_cpu(agi->agi_unlinked[i]);
-
- while (agino != NULLAGINO) {
- if (agino % XFS_AGI_UNLINKED_BUCKETS != i) {
- xchk_block_set_corrupt(sc, sc->sa.agi_bp);
- return;
- }
-
- ip = xfs_iunlink_lookup(sc->sa.pag, agino);
- if (!ip) {
- xchk_block_set_corrupt(sc, sc->sa.agi_bp);
- return;
- }
-
- if (!xfs_inode_on_unlinked_list(ip)) {
- xchk_block_set_corrupt(sc, sc->sa.agi_bp);
- return;
- }
-
- agino = ip->i_next_unlinked;
- }
+ int ret;
+
+ ret = xchk_iunlink_bucket(sc, i,
+ be32_to_cpu(agi->agi_unlinked[i]));
+ if (ret < 1)
+ return ret;
}
+
+ return 0;
}
/* Scrub the AGI. */
@@ -1053,7 +1099,9 @@ xchk_agi(
if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount))
xchk_block_set_corrupt(sc, sc->sa.agi_bp);
- xchk_iunlink(sc, agi);
+ error = xchk_iunlink(sc, agi);
+ if (error)
+ goto out;
xchk_agi_xref(sc);
out:
diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c
index 2554494847ff..2104512f1ee1 100644
--- a/fs/xfs/scrub/agheader_repair.c
+++ b/fs/xfs/scrub/agheader_repair.c
@@ -980,6 +980,13 @@ err:
}
/*
+ * Magic value that means "not unlinked" because xfarrays don't support storing
+ * totally zeroed elements. There can't be a cluster that starts in daddr 0 so
+ * there can't be an inode #1 either.
+ */
+#define LINKED_AGINO (0x1)
+
+/*
* Record a forwards unlinked chain pointer from agino -> next_agino in our
* staging information.
*/
@@ -1034,31 +1041,40 @@ xrep_iunlink_next(
* the chain or if we should stop walking the chain due to corruption; or a
* per-AG inode number.
*/
-STATIC xfs_agino_t
+STATIC int
xrep_iunlink_reload_next(
struct xrep_agi *ragi,
xfs_agino_t prev_agino,
- xfs_agino_t agino)
+ xfs_agino_t agino,
+ xfs_agino_t *next_agino)
{
struct xfs_scrub *sc = ragi->sc;
struct xfs_inode *ip;
- xfs_agino_t ret = NULLAGINO;
int error;
+ *next_agino = NULLAGINO;
+
error = xchk_iget(ragi->sc, xfs_agino_to_ino(sc->sa.pag, agino), &ip);
if (error)
- return ret;
+ return 0;
trace_xrep_iunlink_reload_next(ip, prev_agino);
/* If this is a linked inode, stop processing the chain. */
if (VFS_I(ip)->i_nlink != 0) {
- xrep_iunlink_store_next(ragi, agino, NULLAGINO);
+ error = xrep_iunlink_store_next(ragi, agino, NULLAGINO);
+ if (error)
+ return error;
+
+ error = xrep_iunlink_store_prev(ragi, agino, LINKED_AGINO);
+ if (error)
+ return error;
+
goto rele;
}
ip->i_prev_unlinked = prev_agino;
- ret = ip->i_next_unlinked;
+ *next_agino = ip->i_next_unlinked;
/*
* Drop the inode reference that we just took. We hold the AGI, so
@@ -1067,7 +1083,7 @@ xrep_iunlink_reload_next(
*/
rele:
xchk_irele(sc, ip);
- return ret;
+ return 0;
}
/*
@@ -1080,18 +1096,22 @@ xrep_iunlink_walk_ondisk_bucket(
struct xrep_agi *ragi,
unsigned int bucket)
{
+ struct xagino_bitmap seen;
struct xfs_scrub *sc = ragi->sc;
- struct xfs_agi *agi = sc->sa.agi_bp->b_addr;
+ struct xfs_agi *agi = ragi->agi_bp->b_addr;
xfs_agino_t prev_agino = NULLAGINO;
xfs_agino_t next_agino;
int error = 0;
+ xagino_bitmap_init(&seen);
+
next_agino = be32_to_cpu(agi->agi_unlinked[bucket]);
while (next_agino != NULLAGINO) {
xfs_agino_t agino = next_agino;
+ unsigned int len = 1;
if (xchk_should_terminate(ragi->sc, &error))
- return error;
+ goto out_bitmap;
trace_xrep_iunlink_walk_ondisk_bucket(sc->sa.pag, bucket,
prev_agino, agino);
@@ -1099,15 +1119,27 @@ xrep_iunlink_walk_ondisk_bucket(
if (bucket != agino % XFS_AGI_UNLINKED_BUCKETS)
break;
+ if (xagino_bitmap_test(&seen, agino, &len))
+ break;
+
next_agino = xrep_iunlink_next(sc, agino);
- if (!next_agino)
- next_agino = xrep_iunlink_reload_next(ragi, prev_agino,
- agino);
+ if (!next_agino) {
+ error = xrep_iunlink_reload_next(ragi, prev_agino,
+ agino, &next_agino);
+ if (error)
+ break;
+ }
+
+ error = xagino_bitmap_set(&seen, agino, 1);
+ if (error)
+ goto out_bitmap;
prev_agino = agino;
}
- return 0;
+out_bitmap:
+ xagino_bitmap_destroy(&seen);
+ return error;
}
/* Decide if this is an unlinked inode in this AG. */
@@ -1296,7 +1328,7 @@ xrep_iunlink_mark_ondisk_rec(
* iunlink_bmp. We haven't checked the inobt yet, so we don't error out if
* the btree is corrupt.
*/
-STATIC void
+STATIC int
xrep_iunlink_mark_ondisk(
struct xrep_agi *ragi)
{
@@ -1308,6 +1340,14 @@ xrep_iunlink_mark_ondisk(
cur = xfs_inobt_init_cursor(sc->sa.pag, sc->tp, agi_bp);
error = xfs_btree_query_all(cur, xrep_iunlink_mark_ondisk_rec, ragi);
xfs_btree_del_cursor(cur, error);
+
+ /*
+ * Don't proceed if we couldn't set a bit in the bitmap. All other
+ * errors we ignore because we haven't actually checked the inobt yet.
+ */
+ if (error == -ENOMEM)
+ return -ENOMEM;
+ return 0;
}
/*
@@ -1320,15 +1360,32 @@ xrep_iunlink_resolve_bucket(
struct xrep_agi *ragi,
unsigned int bucket)
{
+ struct xagino_bitmap seen;
struct xfs_scrub *sc = ragi->sc;
struct xfs_inode *ip;
xfs_agino_t prev_agino = NULLAGINO;
xfs_agino_t next_agino = ragi->iunlink_heads[bucket];
int error = 0;
+ xagino_bitmap_init(&seen);
+
while (next_agino != NULLAGINO) {
+ unsigned int len = 1;
+
if (xchk_should_terminate(ragi->sc, &error))
- return error;
+ goto out_bitmap;
+
+ /* Inode already seen? We're stuck in a loop */
+ if (xagino_bitmap_test(&seen, next_agino, &len)) {
+ trace_xrep_iunlink_resolve_infinite_loop(sc->sa.pag,
+ bucket, prev_agino, next_agino);
+ next_agino = NULLAGINO;
+ break;
+ }
+
+ error = xagino_bitmap_set(&seen, next_agino, 1);
+ if (error)
+ goto out_bitmap;
/* Find the next inode in the chain. */
ip = xfs_iunlink_lookup(sc->sa.pag, next_agino);
@@ -1341,6 +1398,35 @@ xrep_iunlink_resolve_bucket(
break;
}
+ if (VFS_I(ip)->i_nlink != 0) {
+ /*
+ * Inode is linked somewhere! Blow out both unlinked
+ * list pointers, advance the list, and pretend we
+ * didn't see this inode. Clear it from iunlink_bmp
+ * because it's linked.
+ */
+ trace_xrep_iunlink_resolve_allocated(sc->sa.pag,
+ bucket, prev_agino, next_agino);
+
+ error = xrep_iunlink_store_next(ragi, next_agino,
+ NULLAGINO);
+ if (error)
+ goto out_bitmap;
+
+ error = xrep_iunlink_store_prev(ragi, next_agino,
+ LINKED_AGINO);
+ if (error)
+ goto out_bitmap;
+
+ error = xagino_bitmap_clear(&ragi->iunlink_bmp,
+ next_agino, 1);
+ if (error)
+ goto out_bitmap;
+
+ next_agino = ip->i_next_unlinked;
+ continue;
+ }
+
if (next_agino % XFS_AGI_UNLINKED_BUCKETS != bucket) {
/*
* Inode is in the wrong bucket. Advance the list,
@@ -1376,20 +1462,20 @@ xrep_iunlink_resolve_bucket(
*/
error = xagino_bitmap_clear(&ragi->iunlink_bmp, next_agino, 1);
if (error)
- return error;
+ goto out_bitmap;
/* Remember the previous inode's next pointer. */
if (prev_agino != NULLAGINO) {
error = xrep_iunlink_store_next(ragi, prev_agino,
next_agino);
if (error)
- return error;
+ goto out_bitmap;
}
/* Remember this inode's previous pointer. */
error = xrep_iunlink_store_prev(ragi, next_agino, prev_agino);
if (error)
- return error;
+ goto out_bitmap;
/* Advance the list and remember this inode. */
prev_agino = next_agino;
@@ -1400,10 +1486,12 @@ xrep_iunlink_resolve_bucket(
if (prev_agino != NULLAGINO) {
error = xrep_iunlink_store_next(ragi, prev_agino, next_agino);
if (error)
- return error;
+ goto out_bitmap;
}
- return 0;
+out_bitmap:
+ xagino_bitmap_destroy(&seen);
+ return error;
}
/* Reinsert this unlinked inode into the head of the staged bucket list. */
@@ -1428,6 +1516,10 @@ xrep_iunlink_add_to_bucket(
if (error)
return error;
+ error = xrep_iunlink_store_prev(ragi, agino, NULLAGINO);
+ if (error)
+ return error;
+
/* Remember the head inode's previous pointer. */
if (current_head != NULLAGINO) {
error = xrep_iunlink_store_prev(ragi, current_head, agino);
@@ -1495,7 +1587,9 @@ xrep_iunlink_rebuild_buckets(
* If there are ondisk inodes that are unlinked and are not been loaded
* into cache, record them in iunlink_bmp.
*/
- xrep_iunlink_mark_ondisk(ragi);
+ error = xrep_iunlink_mark_ondisk(ragi);
+ if (error)
+ return error;
/*
* Walk each iunlink bucket to (re)construct as much of the incore list
@@ -1517,6 +1611,24 @@ xrep_iunlink_rebuild_buckets(
xrep_iunlink_add_lost_inodes, ragi);
}
+static inline void
+set_inode_prev_unlinked(
+ struct xfs_inode *ip,
+ xfs_agino_t prev_agino)
+{
+ /*
+ * Magic value that means "not unlinked" because xfarrays don't support
+ * storing totally zeroed elements.
+ */
+ if (prev_agino == LINKED_AGINO)
+ prev_agino = 0;
+
+ if (ip->i_prev_unlinked != prev_agino) {
+ trace_xrep_iunlink_relink_prev(ip, prev_agino);
+ ip->i_prev_unlinked = prev_agino;
+ }
+}
+
/* Update i_next_iunlinked for the inode @agino. */
STATIC int
xrep_iunlink_relink_next(
@@ -1550,8 +1662,7 @@ xrep_iunlink_relink_next(
if (error)
goto out_rele;
- trace_xrep_iunlink_relink_prev(ip, prev_agino);
- ip->i_prev_unlinked = prev_agino;
+ set_inode_prev_unlinked(ip, prev_agino);
}
/* Update the forward pointer. */
@@ -1606,7 +1717,7 @@ xrep_iunlink_relink_prev(
want_rele = true;
/* Set the forward pointer since this just came off disk. */
- error = xfarray_load(ragi->iunlink_prev, agino, &next_agino);
+ error = xfarray_load(ragi->iunlink_next, agino, &next_agino);
if (error)
goto out_rele;
@@ -1618,11 +1729,7 @@ xrep_iunlink_relink_prev(
ip->i_next_unlinked = next_agino;
}
- /* Update the backward pointer. */
- if (ip->i_prev_unlinked != prev_agino) {
- trace_xrep_iunlink_relink_prev(ip, prev_agino);
- ip->i_prev_unlinked = prev_agino;
- }
+ set_inode_prev_unlinked(ip, prev_agino);
out_rele:
/*
@@ -1652,6 +1759,8 @@ xrep_iunlink_commit(
if (error)
return error;
}
+ if (error < 0)
+ return error;
/* Fix all the back links */
idx = XFARRAY_CURSOR_INIT;
@@ -1660,6 +1769,8 @@ xrep_iunlink_commit(
if (error)
return error;
}
+ if (error < 0)
+ return error;
/* Copy the staged iunlink buckets to the new AGI. */
for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c
index 48edaa2cb1e0..c666be69f164 100644
--- a/fs/xfs/scrub/alloc.c
+++ b/fs/xfs/scrub/alloc.c
@@ -136,7 +136,7 @@ xchk_allocbt_rec(
const union xfs_btree_rec *rec)
{
struct xfs_alloc_rec_incore irec;
- struct xchk_alloc *ca = bs->private;
+ struct xchk_alloc *ca = bs->private;
xfs_alloc_btrec_to_irec(rec, &irec);
if (xfs_alloc_check_irec(to_perag(bs->cur->bc_group), &irec) != NULL) {
@@ -144,7 +144,8 @@ xchk_allocbt_rec(
return 0;
}
- xchk_allocbt_mergeable(bs, ca, &irec);
+ if (bs->sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT)
+ xchk_allocbt_mergeable(bs, ca, &irec);
xchk_allocbt_xref(bs->sc, &irec);
return 0;
diff --git a/fs/xfs/scrub/attr_repair.c b/fs/xfs/scrub/attr_repair.c
index be627ab655ad..6e6af142f1fb 100644
--- a/fs/xfs/scrub/attr_repair.c
+++ b/fs/xfs/scrub/attr_repair.c
@@ -1427,7 +1427,8 @@ xrep_xattr_rebuild_tree(
* If we didn't find any attributes to salvage, repair the file by
* zapping its attr fork.
*/
- if (rx->attrs_found == 0) {
+ if (rx->attrs_found == 0 &&
+ (!xfs_has_parent(sc->mp) || xfarray_length(rx->pptr_recs) == 0)) {
xfs_trans_ijoin(sc->tp, sc->ip, 0);
error = xrep_xattr_reset_fork(sc);
if (error)
diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c
index 70028da1aacc..401c278725d2 100644
--- a/fs/xfs/scrub/bmap.c
+++ b/fs/xfs/scrub/bmap.c
@@ -1170,6 +1170,11 @@ xchk_bmap_attr(
}
error = xchk_bmap(sc, XFS_ATTR_FORK);
+ /* A repaired, empty attr fork no longer has mappings to check. */
+ if (error == -ENOENT && (sc->flags & XREP_ALREADY_FIXED)) {
+ xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_BMBTA_ZAPPED);
+ return 0;
+ }
if (error)
return error;
diff --git a/fs/xfs/scrub/dirtree_repair.c b/fs/xfs/scrub/dirtree_repair.c
index 1c0d7ea4a5be..bbf6acf6fd40 100644
--- a/fs/xfs/scrub/dirtree_repair.c
+++ b/fs/xfs/scrub/dirtree_repair.c
@@ -349,6 +349,8 @@ xrep_dirtree_unlink_iolock(
ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
+ if (sc->ip == dp)
+ return 0;
if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL))
return 0;
@@ -400,8 +402,18 @@ xrep_dirtree_unlink(
* directory code can handle a reservationless update.
*/
resblks = xfs_remove_space_res(mp, step->name_len);
- error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,
- &resblks, &sc->tp, &dontcare);
+ if (sc->ip == dp) {
+again:
+ error = xfs_trans_alloc_inode(dp, &M_RES(mp)->tr_remove,
+ resblks, 0, false, &sc->tp);
+ if ((error == -ENOSPC || error == -EDQUOT) && resblks > 0) {
+ resblks = 0;
+ goto again;
+ }
+ } else {
+ error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,
+ &resblks, &sc->tp, &dontcare);
+ }
if (error)
goto out_iolock;
@@ -489,9 +501,11 @@ out_trans_cancel:
xchk_trans_cancel(sc);
out_ilock:
xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
- xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ if (dp != sc->ip)
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
out_iolock:
- xfs_iunlock(dp, XFS_IOLOCK_EXCL);
+ if (dp != sc->ip)
+ xfs_iunlock(dp, XFS_IOLOCK_EXCL);
return error;
}
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index 3ec41c198351..8bc508336aa5 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -1797,7 +1797,7 @@ xrep_inode_flags(
/* Clear junk flags */
if (sc->ip->i_diflags & ~XFS_DIFLAG_ANY)
- sc->ip->i_diflags &= ~XFS_DIFLAG_ANY;
+ sc->ip->i_diflags &= XFS_DIFLAG_ANY;
/* NEWRTBM only applies to realtime bitmaps */
if (I_INO(sc->ip) == sc->mp->m_sb.sb_rbmino)
@@ -1828,7 +1828,7 @@ xrep_inode_flags(
/* Clear junk flags. */
if (sc->ip->i_diflags2 & ~XFS_DIFLAG2_ANY)
- sc->ip->i_diflags2 &= ~XFS_DIFLAG2_ANY;
+ sc->ip->i_diflags2 &= XFS_DIFLAG2_ANY;
/* No reflink flag unless we support it and it's a file. */
if (!xfs_has_reflink(sc->mp) || !S_ISREG(mode))
@@ -1960,7 +1960,7 @@ xrep_inode_cowextsize(
/* Fix misaligned CoW extent size hints on a directory. */
if ((sc->ip->i_diflags & XFS_DIFLAG_RTINHERIT) &&
(sc->ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) &&
- sc->ip->i_extsize % sc->mp->m_sb.sb_rextsize > 0) {
+ xfs_extlen_to_rtxmod(sc->mp, sc->ip->i_cowextsize) > 0) {
sc->ip->i_cowextsize = 0;
sc->ip->i_diflags2 &= ~XFS_DIFLAG2_COWEXTSIZE;
}
diff --git a/fs/xfs/scrub/nlinks.c b/fs/xfs/scrub/nlinks.c
index 355ab6de23ea..bcedb8c3e4e6 100644
--- a/fs/xfs/scrub/nlinks.c
+++ b/fs/xfs/scrub/nlinks.c
@@ -383,6 +383,12 @@ xchk_nlinks_ilock_dir(
uint lock_mode = XFS_ILOCK_SHARED;
/*
+ * Take the IOLOCK so that other threads cannot start a directory
+ * update while we're scanning.
+ */
+ xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+ /*
* We're going to scan the directory entries, so we must be ready to
* pull the data fork mappings into memory if they aren't already.
*/
@@ -397,13 +403,8 @@ xchk_nlinks_ilock_dir(
xfs_need_iread_extents(&ip->i_af))
lock_mode = XFS_ILOCK_EXCL;
- /*
- * Take the IOLOCK so that other threads cannot start a directory
- * update while we're scanning.
- */
- lock_mode |= XFS_IOLOCK_SHARED;
xfs_ilock(ip, lock_mode);
- return lock_mode;
+ return lock_mode | XFS_IOLOCK_SHARED;
}
/* Walk a directory to bump the observed link counts of the children. */
diff --git a/fs/xfs/scrub/nlinks_repair.c b/fs/xfs/scrub/nlinks_repair.c
index fbc2ff809fc0..09e097e16689 100644
--- a/fs/xfs/scrub/nlinks_repair.c
+++ b/fs/xfs/scrub/nlinks_repair.c
@@ -232,9 +232,14 @@ xrep_nlinks_repair_inode(
* unlinked list, put it on the unlinked list.
*/
if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) {
+ if (actual_nlink)
+ clear_nlink(VFS_I(ip));
error = xfs_iunlink(sc->tp, ip);
- if (error)
+ if (error) {
+ if (actual_nlink)
+ set_nlink(VFS_I(ip), actual_nlink);
goto out_trans;
+ }
dirty = true;
}
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index a8c4807e1d94..99b60773b715 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -485,7 +485,7 @@ xchk_parent_scan_attr(
valuelen, &parent_ino, NULL);
if (error) {
xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
- return error;
+ return -ECANCELED;
}
/* No self-referential parent pointers. */
diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c
index dc64902d6c25..442a17bf9720 100644
--- a/fs/xfs/scrub/rtbitmap_repair.c
+++ b/fs/xfs/scrub/rtbitmap_repair.c
@@ -36,6 +36,24 @@
/* rt bitmap content repairs */
+/*
+ * Reserve enough blocks to write out a completely new bitmap file, plus twice
+ * as many blocks as we would need if we can only allocate one block per data
+ * fork mapping. This should cover the preallocation of the temporary file and
+ * exchanging the extent mappings.
+ *
+ * We cannot use xfs_exchmaps_estimate because we have not yet constructed the
+ * replacement bitmap and therefore do not know how many extents it will use.
+ * By the time we do, we will have a dirty transaction (which we cannot drop
+ * because we cannot drop the rtbitmap ILOCK) and cannot ask for more
+ * reservation.
+ */
+static inline unsigned long long
+xrep_rtbitmap_calc_blocks(struct xfs_mount *mp, unsigned long long blocks)
+{
+ return blocks + (xfs_bmbt_calc_size(mp, blocks) * 2);
+}
+
/* Set up to repair the realtime bitmap for this group. */
int
xrep_setup_rtbitmap(
@@ -56,20 +74,7 @@ xrep_setup_rtbitmap(
if (error)
return error;
- /*
- * Reserve enough blocks to write out a completely new bitmap file,
- * plus twice as many blocks as we would need if we can only allocate
- * one block per data fork mapping. This should cover the
- * preallocation of the temporary file and exchanging the extent
- * mappings.
- *
- * We cannot use xfs_exchmaps_estimate because we have not yet
- * constructed the replacement bitmap and therefore do not know how
- * many extents it will use. By the time we do, we will have a dirty
- * transaction (which we cannot drop because we cannot drop the
- * rtbitmap ILOCK) and cannot ask for more reservation.
- */
- blocks += xfs_bmbt_calc_size(mp, blocks) * 2;
+ blocks = xrep_rtbitmap_calc_blocks(mp, mp->m_sb.sb_rbmblocks);
if (blocks > UINT_MAX)
return -EOPNOTSUPP;
@@ -512,7 +517,7 @@ xrep_rtbitmap(
struct xchk_rtbitmap *rtb = sc->buf;
struct xfs_mount *mp = sc->mp;
struct xfs_group *xg = rtg_group(sc->sr.rtg);
- unsigned long long blocks = 0;
+ unsigned long long blocks;
unsigned int busy_gen;
int error;
@@ -532,15 +537,20 @@ xrep_rtbitmap(
* figure out if we need to adjust the block reservation in the
* transaction.
*/
- blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks);
+ blocks = xrep_rtbitmap_calc_blocks(mp, rtb->rbmblocks);
if (blocks > UINT_MAX)
return -EOPNOTSUPP;
if (blocks > rtb->resblks) {
- error = xfs_trans_reserve_more(sc->tp, blocks, 0);
+ uint64_t delta = blocks - rtb->resblks;
+
+ if (delta > UINT_MAX)
+ return -EOPNOTSUPP;
+
+ error = xfs_trans_reserve_more(sc->tp, delta, 0);
if (error)
return error;
- rtb->resblks += blocks;
+ rtb->resblks += delta;
}
/* Fix inode core and forks. */
diff --git a/fs/xfs/scrub/rtsummary.c b/fs/xfs/scrub/rtsummary.c
index 78f72a046887..546b335ade13 100644
--- a/fs/xfs/scrub/rtsummary.c
+++ b/fs/xfs/scrub/rtsummary.c
@@ -358,7 +358,7 @@ xchk_rtsummary(
* EFSCORRUPTED means the rtbitmap is corrupt, which is an xref
* error since we're checking the summary file.
*/
- xchk_ip_set_corrupt(sc, rbmip);
+ xchk_ip_xref_set_corrupt(sc, rbmip);
return 0;
}
if (error)
diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c
index e0c630f888cf..98820003b929 100644
--- a/fs/xfs/scrub/tempfile.c
+++ b/fs/xfs/scrub/tempfile.c
@@ -174,6 +174,7 @@ out_release_inode:
xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
xfs_finish_inode_setup(sc->tempip);
xchk_irele(sc, sc->tempip);
+ sc->tempip = NULL;
}
out_release_dquots:
xfs_qm_dqrele(udqp);
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index d5d39d82749e..14aa0ec1f09e 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -3538,10 +3538,12 @@ DEFINE_EVENT(xrep_iunlink_resolve_class, name, \
TP_PROTO(const struct xfs_perag *pag, unsigned int bucket, \
xfs_agino_t prev_agino, xfs_agino_t next_agino), \
TP_ARGS(pag, bucket, prev_agino, next_agino))
+DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_infinite_loop);
DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_uncached);
DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_wronglist);
DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_nolist);
DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_ok);
+DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_allocated);
TRACE_EVENT(xrep_iunlink_relink_next,
TP_PROTO(struct xfs_inode *ip, xfs_agino_t next_agino),
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index e1465e950acc..48d7dfd3e15f 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -114,7 +114,7 @@ xfs_buf_free(
vfree(bp->b_addr);
else if (bp->b_flags & _XBF_KMEM)
kfree(bp->b_addr);
- else
+ else if (bp->b_addr)
folio_put(virt_to_folio(bp->b_addr));
call_rcu(&bp->b_rcu, xfs_buf_free_callback);
diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c
index 02b95b89d1b5..240deb3f7827 100644
--- a/fs/xfs/xfs_buf_item_recover.c
+++ b/fs/xfs/xfs_buf_item_recover.c
@@ -461,7 +461,7 @@ xlog_recover_validate_buf_type(
* given buffer. The bitmap in the buf log format structure indicates
* where to place the logged data.
*/
-STATIC void
+STATIC int
xlog_recover_do_reg_buffer(
struct xfs_mount *mp,
struct xlog_recover_item *item,
@@ -489,8 +489,24 @@ xlog_recover_do_reg_buffer(
ASSERT(nbits > 0);
ASSERT(item->ri_buf[i].iov_base != NULL);
ASSERT(item->ri_buf[i].iov_len % XFS_BLF_CHUNK == 0);
- ASSERT(BBTOB(bp->b_length) >=
- ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT));
+ /*
+ * The bitmap is only trustworthy to the extent that it
+ * describes a region that actually fits inside the buffer we
+ * read in based on the (attacker-controlled) blf_len. Do not
+ * rely on an ASSERT() for this -- it compiles away entirely on
+ * non-DEBUG kernels, which is exactly where this matters, so
+ * validate it for real and abort recovery of this buffer rather
+ * than copying past the end of it.
+ */
+ if (XFS_IS_CORRUPT(mp, BBTOB(bp->b_length) <
+ ((uint)bit << XFS_BLF_SHIFT) +
+ (nbits << XFS_BLF_SHIFT))) {
+ xfs_alert(mp,
+ "Bad buffer log item dirty bitmap (bit %d, nbits %d) for %d-byte buffer at daddr 0x%llx.",
+ bit, nbits, BBTOB(bp->b_length),
+ xfs_buf_daddr(bp));
+ return -EFSCORRUPTED;
+ }
/*
* The dirty regions logged in the buffer, even though
@@ -544,6 +560,7 @@ xlog_recover_do_reg_buffer(
ASSERT(i == item->ri_total);
xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn);
+ return 0;
}
/*
@@ -552,10 +569,10 @@ xlog_recover_do_reg_buffer(
* (ie. USR or GRP), then just toss this buffer away; don't recover it.
* Else, treat it as a regular buffer and do recovery.
*
- * Return false if the buffer was tossed and true if we recovered the buffer to
- * indicate to the caller if the buffer needs writing.
+ * Return 0 if the buffer was not recovered (tossed), 1 if it was recovered and
+ * needs writing, or a negative errno if recovery of the buffer failed.
*/
-STATIC bool
+STATIC int
xlog_recover_do_dquot_buffer(
struct xfs_mount *mp,
struct xlog *log,
@@ -564,6 +581,7 @@ xlog_recover_do_dquot_buffer(
struct xfs_buf_log_format *buf_f)
{
uint type;
+ int error;
trace_xfs_log_recover_buf_dquot_buf(log, buf_f);
@@ -571,7 +589,7 @@ xlog_recover_do_dquot_buffer(
* Filesystems are required to send in quota flags at mount time.
*/
if (!mp->m_qflags)
- return false;
+ return 0;
type = 0;
if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF)
@@ -584,10 +602,12 @@ xlog_recover_do_dquot_buffer(
* This type of quotas was turned off, so ignore this buffer
*/
if (log->l_quotaoffs_flag & type)
- return false;
+ return 0;
- xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
- return true;
+ error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN);
+ if (error)
+ return error;
+ return 1;
}
/*
@@ -724,7 +744,9 @@ xlog_recover_do_primary_sb_buffer(
xfs_rgnumber_t orig_rgcount = mp->m_sb.sb_rgcount;
int error;
- xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
+ error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
+ if (error)
+ return error;
if (orig_agcount == 0) {
xfs_alert(mp, "Trying to grow file system without AGs");
@@ -1081,11 +1103,11 @@ xlog_recover_buf_commit_pass2(
goto out_release;
} else if (buf_f->blf_flags &
(XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) {
- bool dirty;
-
- dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
- if (!dirty)
+ error = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f);
+ if (error <= 0)
goto out_release;
+ /* write dirty buffer */
+ error = 0;
} else if ((xfs_blft_from_flags(buf_f) & XFS_BLFT_SB_BUF) &&
xfs_buf_daddr(bp) == 0) {
error = xlog_recover_do_primary_sb_buffer(mp, item, bp, buf_f,
@@ -1105,7 +1127,10 @@ xlog_recover_buf_commit_pass2(
xfs_buf_relse(rtsb_bp);
}
} else {
- xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn);
+ error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f,
+ current_lsn);
+ if (error)
+ goto out_release;
}
/*
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index c311f61d9554..b4f6c594808c 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -778,7 +778,7 @@ xfs_dq_get_next_id(
lock_flags = xfs_ilock_data_map_shared(quotip);
error = xfs_iread_extents(NULL, quotip, XFS_DATA_FORK);
if (error)
- return error;
+ goto out_unlock;
if (xfs_iext_lookup_extent(quotip, &quotip->i_df, start, &cur, &got)) {
/* contiguous chunk, bump startoff for the id calculation */
@@ -789,6 +789,7 @@ xfs_dq_get_next_id(
error = -ENOENT;
}
+out_unlock:
xfs_iunlock(quotip, lock_flags);
return error;
diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c
index fe419b28de22..63bc9ab7d947 100644
--- a/fs/xfs/xfs_dquot_item_recover.c
+++ b/fs/xfs/xfs_dquot_item_recover.c
@@ -173,7 +173,7 @@ xlog_recover_dquot_commit_pass2(
out_release:
xfs_buf_relse(bp);
- return 0;
+ return error;
}
const struct xlog_recover_item_ops xlog_dquot_item_ops = {
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 66a02d1b9ad7..216a38a354e7 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -349,6 +349,13 @@ typedef struct xfs_mount {
/* Index of uuid record in the uuid xarray. */
unsigned int m_uuid_table_index;
+
+ /*
+ * Old io_pages/ra_pages valued in the main bdev BDI, and our initial
+ * calculated values.
+ */
+ unsigned long m_old_io_pages, m_initial_io_pages;
+ unsigned long m_old_ra_pages, m_initial_ra_pages;
} xfs_mount_t;
#define M_IGEO(mp) (&(mp)->m_ino_geo)
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index 7a3f97686989..84efe5a8fb11 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -737,7 +737,7 @@ xfs_rtginode_ensure(
xfs_trans_cancel(tp);
if (error != -ENOENT)
- return 0;
+ return error;
return xfs_rtginode_create(rtg, type, true);
}
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 8531d526fc44..63c4bcbe6c2b 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -549,6 +549,52 @@ xfs_open_devices(
}
/*
+ * When using a RT device some or all data I/O is using the RT device, but
+ * the BDI is inherited from the main data device. When the underlying block
+ * device for the RT device has larger I/O sizes, the BDI settings might be
+ * incorrect, which is especially bad if the main device is a SSD and the
+ * RT device is a HDD, as the io_opt fixup in blk_apply_bdi_limits is missing
+ * for this case.
+ *
+ * Update the BDI values to the max of the data and RT device to cover our
+ * bases.
+ */
+static void
+xfs_update_bdi_rahead(
+ struct xfs_mount *mp)
+{
+ struct backing_dev_info *rt_bdi =
+ mp->m_rtdev_targp->bt_bdev->bd_disk->bdi;
+ struct backing_dev_info *sb_bdi = mp->m_super->s_bdi;
+
+ mp->m_old_io_pages = sb_bdi->io_pages;
+ mp->m_old_ra_pages = sb_bdi->ra_pages;
+
+ sb_bdi->io_pages = mp->m_initial_io_pages =
+ max(sb_bdi->io_pages, rt_bdi->io_pages);
+ sb_bdi->ra_pages = mp->m_initial_ra_pages =
+ max(sb_bdi->ra_pages, rt_bdi->ra_pages);
+}
+
+static void
+xfs_restore_bdi_rahead(
+ struct xfs_mount *mp)
+{
+ struct backing_dev_info *sb_bdi = mp->m_super->s_bdi;
+
+ if (sb_bdi->io_pages == mp->m_initial_io_pages)
+ sb_bdi->io_pages = mp->m_old_io_pages;
+ else
+ xfs_info(mp, "io_pages changed from %lu to %lu, not restoring.",
+ mp->m_initial_io_pages, sb_bdi->io_pages);
+ if (sb_bdi->ra_pages == mp->m_initial_ra_pages)
+ sb_bdi->ra_pages = mp->m_old_ra_pages;
+ else
+ xfs_info(mp, "ra_pages changed from %lu to %lu, not restoring.",
+ mp->m_initial_ra_pages, sb_bdi->ra_pages);
+}
+
+/*
* Setup xfs_mount buffer target pointers based on superblock
*/
STATIC int
@@ -585,6 +631,7 @@ xfs_setup_devices(
mp->m_sb.sb_sectsize, mp->m_sb.sb_rblocks);
if (error)
return error;
+ xfs_update_bdi_rahead(mp);
}
return 0;
@@ -2283,8 +2330,12 @@ static void
xfs_kill_sb(
struct super_block *sb)
{
+ struct xfs_mount *mp = XFS_M(sb);
+
+ if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp)
+ xfs_restore_bdi_rahead(mp);
kill_block_super(sb);
- xfs_mount_free(XFS_M(sb));
+ xfs_mount_free(mp);
}
static struct file_system_type xfs_fs_type = {
diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c
index f76a09130852..7ab8f2218c6a 100644
--- a/fs/xfs/xfs_zone_gc.c
+++ b/fs/xfs/xfs_zone_gc.c
@@ -103,6 +103,7 @@ struct xfs_gc_bio {
/* Open Zone being written to */
struct xfs_open_zone *oz;
+ /* Realtime group currently being reclaimed */
struct xfs_rtgroup *victim_rtg;
/* Bio used for reads and writes, including the bvec used by it */
@@ -130,6 +131,9 @@ struct xfs_zone_gc_data {
/* bioset used to allocate the gc_bios */
struct bio_set bio_set;
+ /* bioset used when writes need to be split to hardware limits */
+ struct bio_set split_bio_set;
+
/*
* Scratchpad to buffer GC data, organized as a ring buffer over
* discontiguous folios. scratch_head is where the buffer is filled,
@@ -221,6 +225,9 @@ xfs_zone_gc_data_alloc(
if (bioset_init(&data->bio_set, 16, offsetof(struct xfs_gc_bio, bio),
BIOSET_NEED_BVECS))
goto out_free_recs;
+ if (bioset_init(&data->split_bio_set, 16,
+ offsetof(struct xfs_gc_bio, bio), 0))
+ goto out_exit_bio_set;
for (i = 0; i < XFS_GC_NR_BUFS; i++) {
data->scratch_folios[i] =
folio_alloc(GFP_KERNEL, get_order(XFS_GC_BUF_SIZE));
@@ -238,6 +245,8 @@ xfs_zone_gc_data_alloc(
out_free_scratch:
while (--i >= 0)
folio_put(data->scratch_folios[i]);
+ bioset_exit(&data->split_bio_set);
+out_exit_bio_set:
bioset_exit(&data->bio_set);
out_free_recs:
kfree(data->iter.recs);
@@ -254,6 +263,7 @@ xfs_zone_gc_data_free(
for (i = 0; i < XFS_GC_NR_BUFS; i++)
folio_put(data->scratch_folios[i]);
+ bioset_exit(&data->split_bio_set);
bioset_exit(&data->bio_set);
kfree(data->iter.recs);
kfree(data);
@@ -810,7 +820,8 @@ xfs_zone_gc_split_write(
data->mp->m_sb.sb_blocksize) >> SECTOR_SHIFT;
split_len = split_sectors << SECTOR_SHIFT;
- split = bio_split(&chunk->bio, split_sectors, GFP_NOFS, &data->bio_set);
+ split = bio_split(&chunk->bio, split_sectors, GFP_NOFS,
+ &data->split_bio_set);
split_chunk = container_of(split, struct xfs_gc_bio, bio);
split_chunk->data = data;
ihold(VFS_I(chunk->ip));
diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h
index 8693890a7275..8c191b9ac18f 100644
--- a/include/acpi/cppc_acpi.h
+++ b/include/acpi/cppc_acpi.h
@@ -170,7 +170,7 @@ extern u64 cppc_get_dmi_max_khz(void);
extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf);
extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq);
extern bool acpi_cpc_valid(void);
-extern bool cppc_allow_fast_switch(void);
+bool cppc_allow_fast_switch(const struct cpumask *cpus);
extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data);
extern int cppc_get_transition_latency(int cpu);
extern bool cpc_ffh_supported(void);
@@ -234,7 +234,8 @@ static inline bool acpi_cpc_valid(void)
{
return false;
}
-static inline bool cppc_allow_fast_switch(void)
+
+static inline bool cppc_allow_fast_switch(const struct cpumask *cpus)
{
return false;
}
diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
index b4cb2fa26e9b..035ba20870f7 100644
--- a/include/hyperv/hvhdk_mini.h
+++ b/include/hyperv/hvhdk_mini.h
@@ -184,8 +184,9 @@ enum hv_dynamic_processor_feature_property {
struct hv_input_get_system_property {
u32 property_id; /* enum hv_system_property */
+ u32 reserved;
union {
- u32 as_uint32;
+ u64 as_uint64;
#if IS_ENABLED(CONFIG_X86)
/* enum hv_dynamic_processor_feature_property */
u32 hv_processor_feature;
diff --git a/include/linux/ata.h b/include/linux/ata.h
index 8fd48bcb2a46..7daad4cad985 100644
--- a/include/linux/ata.h
+++ b/include/linux/ata.h
@@ -762,8 +762,7 @@ static inline bool ata_id_sense_reporting_enabled(const u16 *id)
return id[ATA_ID_COMMAND_SET_4] & BIT(6);
}
-/**
- *
+/*
* Word: 206 - SCT Command Transport
* 15:12 - Vendor Specific
* 11:6 - Reserved
@@ -810,8 +809,9 @@ static inline bool ata_id_sct_supported(const u16 *id)
*
* The practical impact of this is that ata_id_major_version cannot
* reliably report on drives below ATA3.
+ *
+ * Returns: major version of ATA drive level or %0 if unknown
*/
-
static inline unsigned int ata_id_major_version(const u16 *id)
{
unsigned int mver;
diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h
index 68fc0e643b1b..d9d07a9ab313 100644
--- a/include/linux/dma/qcom_bam_dma.h
+++ b/include/linux/dma/qcom_bam_dma.h
@@ -13,9 +13,12 @@
* supported by BAM DMA Engine.
*
* @cmd_and_addr - upper 8 bits command and lower 24 bits register address.
- * @data - for write command: content to be written into peripheral register.
- * for read command: dest addr to write peripheral register value.
- * @mask - register mask.
+ * @data - For write command: content to be written into peripheral register.
+ * For read command: lower 32 bits of destination address.
+ * @mask - For write command: register write mask.
+ * For read command on BAM v1.6.0+: upper 4 bits of destination address.
+ * For read command on BAM < v1.6.0: ignored by hardware.
+ * Setting to 0 ensures 32-bit addressing compatibility.
* @reserved - for future usage.
*
*/
@@ -42,6 +45,10 @@ enum bam_command_type {
* @addr: target address
* @cmd: BAM command
* @data: actual data for write and dest addr for read in le32
+ *
+ * For BAM v1.6.0+, the mask field behavior depends on command type:
+ * - Write commands: mask = write mask (typically 0xffffffff)
+ * - Read commands: mask = upper 4 bits of destination address (0 for 32-bit)
*/
static inline void
bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr,
@@ -50,7 +57,11 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr,
bam_ce->cmd_and_addr =
cpu_to_le32((addr & 0xffffff) | ((cmd & 0xff) << 24));
bam_ce->data = data;
- bam_ce->mask = cpu_to_le32(0xffffffff);
+ if (cmd == BAM_READ_COMMAND)
+ bam_ce->mask = cpu_to_le32(0x0); /* 32-bit addressing */
+ else
+ bam_ce->mask = cpu_to_le32(0xffffffff); /* Write mask */
+ bam_ce->reserved = 0;
}
/*
@@ -60,7 +71,7 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr,
* @bam_ce: BAM command element
* @addr: target address
* @cmd: BAM command
- * @data: actual data for write and dest addr for read
+ * @data: actual data for write and destination address for read
*/
static inline void
bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index d20aa6f6863a..ac43b8b93f14 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1704,6 +1704,7 @@ void iopf_free_group(struct iopf_group *group);
int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt);
void iopf_group_response(struct iopf_group *group,
enum iommu_page_response_code status);
+void iopf_group_dequeue(struct iopf_group *group);
#else
static inline int
iopf_queue_add_device(struct iopf_queue *queue, struct device *dev)
@@ -1749,5 +1750,9 @@ static inline void iopf_group_response(struct iopf_group *group,
enum iommu_page_response_code status)
{
}
+
+static inline void iopf_group_dequeue(struct iopf_group *group)
+{
+}
#endif /* CONFIG_IOMMU_IOPF */
#endif /* __LINUX_IOMMU_H */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 96e626d6a7ca..1827502b9cf2 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -425,7 +425,7 @@ enum {
/* This should match the actual table size of
* ata_eh_cmd_timeout_table in libata-eh.c.
*/
- ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8,
+ ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 9,
/* User visible DMA mask for DMA control. DO NOT renumber. */
ATA_DMA_MASK_ATA = (1 << 0), /* DMA on ATA Disk */
@@ -1153,6 +1153,9 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd,
#endif
extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h,
struct scsi_cmnd *cmd);
+enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap,
+ struct scsi_cmnd *scmd);
+enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd);
#if IS_REACHABLE(CONFIG_ATA)
bool ata_scsi_dma_need_drain(struct request *rq);
#else
@@ -1464,6 +1467,7 @@ extern const struct attribute_group *ata_common_sdev_groups[];
.ioctl = ata_scsi_ioctl, \
ATA_SCSI_COMPAT_IOCTL \
.queuecommand = ata_scsi_queuecmd, \
+ .eh_timed_out = ata_scsi_eh_timed_out, \
.dma_need_drain = ata_scsi_dma_need_drain, \
.this_id = ATA_SHT_THIS_ID, \
.emulated = ATA_SHT_EMULATED, \
diff --git a/include/linux/netfs.h b/include/linux/netfs.h
index 1bc120d61c5b..d0b62d53eea9 100644
--- a/include/linux/netfs.h
+++ b/include/linux/netfs.h
@@ -255,6 +255,7 @@ struct netfs_io_request {
unsigned long long cleaned_to; /* Position we've cleaned folios to */
unsigned long long abandon_to; /* Position to abandon folios to */
const struct folio *no_unlock_folio; /* Don't unlock this folio after read */
+ gfp_t gfp; /* GFP flags to use */
unsigned int direct_bv_count; /* Number of elements in direct_bv[] */
unsigned int debug_id;
unsigned int rsize; /* Maximum read size (0 for none) */
diff --git a/include/linux/psi.h b/include/linux/psi.h
index e0745873e3f2..7966e3ac03b9 100644
--- a/include/linux/psi.h
+++ b/include/linux/psi.h
@@ -25,7 +25,9 @@ void psi_memstall_leave(unsigned long *flags);
int psi_show(struct seq_file *s, struct psi_group *group, enum psi_res res);
struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
enum psi_res res, struct file *file,
- struct kernfs_open_file *of);
+ struct kernfs_open_file *of,
+ bool *need_rtpoll_worker);
+int psi_trigger_create_rtpoll_worker(struct psi_group *group);
void psi_trigger_destroy(struct psi_trigger *t);
__poll_t psi_trigger_poll(void **trigger_ptr, struct file *file,
diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h
index ac15b1ffdd83..9e5dad29669c 100644
--- a/include/linux/rolling_buffer.h
+++ b/include/linux/rolling_buffer.h
@@ -43,13 +43,13 @@ struct rolling_buffer_snapshot {
#define ROLLBUF_MARK_2 BIT(1)
int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id,
- unsigned int direction);
-int rolling_buffer_make_space(struct rolling_buffer *roll);
+ unsigned int direction, gfp_t gfp);
+int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp);
ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll,
struct readahead_control *ractl,
struct folio_batch *put_batch);
ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio,
- unsigned int flags);
+ unsigned int flags, gfp_t gfp);
struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll);
void rolling_buffer_clear(struct rolling_buffer *roll);
diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h
index 20b2343aa344..87e353f7e011 100644
--- a/include/linux/sched/ext.h
+++ b/include/linux/sched/ext.h
@@ -244,11 +244,11 @@ struct sched_ext_entity {
* to %SCHED_EXT with -%EACCES.
*
* Can be set from ops.init_task() while the BPF scheduler is being
- * loaded (!scx_init_task_args->fork). If set and the task's policy is
- * already %SCHED_EXT, the task's policy is rejected and forcefully
- * reverted to %SCHED_NORMAL. The number of such events are reported
- * through /sys/kernel/debug/sched_ext::nr_rejected. Setting this flag
- * during fork is not allowed.
+ * loaded. If set and the task's policy is already %SCHED_EXT, the
+ * task's policy is rejected and forcefully reverted to %SCHED_NORMAL.
+ * The number of such events are reported through
+ * /sys/kernel/sched_ext/nr_rejected. Setting this flag from any other
+ * ops.init_task() invocation, such as during fork, fails the scheduler.
*/
bool disallow; /* reject switching into SCX */
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
index 163f23c92b41..619892098f52 100644
--- a/include/scsi/libsas.h
+++ b/include/scsi/libsas.h
@@ -680,7 +680,6 @@ extern int sas_register_ha(struct sas_ha_struct *);
extern int sas_unregister_ha(struct sas_ha_struct *);
extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha);
extern void sas_resume_ha(struct sas_ha_struct *sas_ha);
-extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha);
extern void sas_suspend_ha(struct sas_ha_struct *sas_ha);
int sas_phy_reset(struct sas_phy *phy, int hard_reset);
@@ -705,6 +704,7 @@ void sas_task_abort(struct sas_task *);
int sas_eh_abort_handler(struct scsi_cmnd *cmd);
int sas_eh_device_reset_handler(struct scsi_cmnd *cmd);
int sas_eh_target_reset_handler(struct scsi_cmnd *cmd);
+enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd);
extern void sas_target_destroy(struct scsi_target *);
extern int sas_sdev_init(struct scsi_device *);
@@ -743,6 +743,7 @@ void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
.this_id = -1, \
.eh_device_reset_handler = sas_eh_device_reset_handler, \
.eh_target_reset_handler = sas_eh_target_reset_handler, \
+ .eh_timed_out = sas_eh_timed_out, \
.target_destroy = sas_target_destroy, \
.ioctl = sas_ioctl, \
diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h
index fbaf4b384c8a..dc33927b82bd 100644
--- a/include/sound/sdca_fdl.h
+++ b/include/sound/sdca_fdl.h
@@ -67,6 +67,8 @@ struct fdl_state {
#if IS_ENABLED(CONFIG_SND_SOC_SDCA_FDL)
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt);
+void sdca_fdl_free_state(struct sdca_interrupt *interrupt);
+
int sdca_fdl_process(struct sdca_interrupt *interrupt);
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
struct sdca_interrupt_info *info);
diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h
index a515cc3df097..3b30146e21db 100644
--- a/include/sound/sdca_interrupts.h
+++ b/include/sound/sdca_interrupts.h
@@ -30,9 +30,12 @@ struct sdca_function_data;
* @function: Pointer to the Function that the interrupt is associated with.
* @entity: Pointer to the Entity that the interrupt is associated with.
* @control: Pointer to the Control that the interrupt is associated with.
+ * @handler: Handler function to be called for the IRQ.
* @priv: Pointer to private data for use by the handler.
+ * @free_priv: Pointer to a function that can be used to free the priv data.
* @irq: IRQ number allocated to this interrupt, also used internally to track
* the IRQ being assigned.
+ * @early_request: Flag to indicate this IRQ was requested at bus probe time.
*/
struct sdca_interrupt {
const char *name;
@@ -44,10 +47,13 @@ struct sdca_interrupt {
struct sdca_function_data *function;
struct sdca_entity *entity;
struct sdca_control *control;
+ irq_handler_t handler;
void *priv;
+ void (*free_priv)(struct sdca_interrupt *interrupt);
int irq;
+ bool early_request;
};
/**
@@ -86,8 +92,12 @@ int sdca_irq_populate(struct sdca_function_data *function,
void sdca_irq_cleanup(struct device *dev,
struct sdca_function_data *function,
struct sdca_interrupt_info *info);
-struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev,
- struct regmap *regmap, int irq);
+void sdca_irq_cleanup_late(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info);
+
+struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *dev,
+ struct regmap *regmap, int irq);
void sdca_irq_enable_early(struct sdca_function_data *function,
struct sdca_interrupt_info *info);
diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h
index 181541f0f4d8..871ba2d8146a 100644
--- a/include/sound/sdca_jack.h
+++ b/include/sound/sdca_jack.h
@@ -28,6 +28,9 @@ struct jack_state {
};
int sdca_jack_alloc_state(struct sdca_interrupt *interrupt);
+int sdca_jack_init_state(struct sdca_interrupt *interrupt);
+void sdca_jack_free_state(struct sdca_interrupt *interrupt);
+
int sdca_jack_process(struct sdca_interrupt *interrupt);
int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack);
int sdca_jack_report(struct sdca_interrupt *interrupt);
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index aa423865dbe7..4b7d7954953d 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -78,6 +78,7 @@ struct snd_soc_component_driver {
unsigned int num_dapm_routes;
int (*probe)(struct snd_soc_component *component);
+ int (*fixup_controls)(struct snd_soc_component *component);
void (*remove)(struct snd_soc_component *component);
int (*suspend)(struct snd_soc_component *component);
int (*resume)(struct snd_soc_component *component);
@@ -380,6 +381,7 @@ void snd_soc_component_suspend(struct snd_soc_component *component);
void snd_soc_component_resume(struct snd_soc_component *component);
int snd_soc_component_is_suspended(struct snd_soc_component *component);
int snd_soc_component_probe(struct snd_soc_component *component);
+int snd_soc_component_fixup_controls(struct snd_soc_component *component);
void snd_soc_component_remove(struct snd_soc_component *component);
int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component,
struct device_node *ep);
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
index 4c5c47c5edb7..6c1438f6a4d3 100644
--- a/include/trace/events/btrfs.h
+++ b/include/trace/events/btrfs.h
@@ -689,6 +689,41 @@ DEFINE_EVENT(btrfs__ordered_extent, btrfs_ordered_extent_lookup_first,
TP_ARGS(inode, ordered)
);
+/*
+ * The writepage fixup worker deferred a block because this still-running
+ * ordered extent covers it.
+ */
+DEFINE_EVENT(btrfs__ordered_extent, btrfs_writepage_fixup_defer,
+
+ TP_PROTO(const struct btrfs_inode *inode,
+ const struct btrfs_ordered_extent *ordered),
+
+ TP_ARGS(inode, ordered)
+);
+
+/* The writepage fixup worker reserved space for a block and set delalloc. */
+TRACE_EVENT(btrfs_writepage_fixup_reserve,
+
+ TP_PROTO(const struct btrfs_inode *inode, u64 start, u32 len),
+
+ TP_ARGS(inode, start, len),
+
+ TP_STRUCT__entry_btrfs(
+ __field( u64, ino )
+ __field( u64, start )
+ __field( u32, len )
+ ),
+
+ TP_fast_assign_btrfs(inode->root->fs_info,
+ __entry->ino = btrfs_ino(inode);
+ __entry->start = start;
+ __entry->len = len;
+ ),
+
+ TP_printk_btrfs("ino=%llu start=%llu len=%u",
+ __entry->ino, __entry->start, __entry->len)
+);
+
DEFINE_EVENT(btrfs__ordered_extent, btrfs_ordered_extent_split,
TP_PROTO(const struct btrfs_inode *inode,
diff --git a/io_uring/cancel.c b/io_uring/cancel.c
index 8c6fa6f367e4..7d7820eab878 100644
--- a/io_uring/cancel.c
+++ b/io_uring/cancel.c
@@ -660,6 +660,6 @@ end_wait:
*/
atomic_dec(&tctx->in_cancel);
/* for exec all current's requests should be gone, kill tctx */
- __io_uring_free(current);
+ io_uring_free_tctx(current);
}
}
diff --git a/io_uring/epoll.c b/io_uring/epoll.c
index eecd748cad01..b9db8bde27ec 100644
--- a/io_uring/epoll.c
+++ b/io_uring/epoll.c
@@ -62,9 +62,6 @@ int io_epoll_ctl(struct io_kiocb *req, unsigned int issue_flags)
CLASS(fd, tf)(ie->fd);
if (fd_empty(tf))
return -EBADF;
- /* disallow adding an epoll context to another epoll context */
- if (ie->op == EPOLL_CTL_ADD && is_file_epoll(fd_file(tf)))
- return -EINVAL;
key.file = fd_file(tf);
key.fd = ie->fd;
diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c
index de0129bceaba..1cf5be62bb65 100644
--- a/io_uring/kbuf.c
+++ b/io_uring/kbuf.c
@@ -266,6 +266,9 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
if (unlikely(!nr_avail))
return -ENOBUFS;
+ /* MAX_RW_COUNT is the universal Linux per-call IO maximum */
+ arg->max_len = min_t(size_t, arg->max_len, MAX_RW_COUNT);
+
buf = io_ring_head_to_buf(br, head, bl->mask);
if (arg->max_len) {
u32 len = READ_ONCE(buf->len);
@@ -295,7 +298,7 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg,
/* set it to max, if not set, so we can use it unconditionally */
if (!arg->max_len)
- arg->max_len = INT_MAX;
+ arg->max_len = MAX_RW_COUNT;
req->buf_index = READ_ONCE(buf->bid);
do {
diff --git a/io_uring/net.c b/io_uring/net.c
index 00a7df803b99..a74d15f7b7d2 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -445,6 +445,7 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
req->flags |= REQ_F_NOWAIT;
if (req->flags & REQ_F_BUFFER_SELECT)
sr->buf_group = req->buf_index;
+ sr->mshot_total_len = sr->mshot_len = 0;
if (sr->flags & IORING_RECVSEND_BUNDLE) {
if (req->opcode == IORING_OP_SENDMSG)
return -EINVAL;
diff --git a/io_uring/tctx.c b/io_uring/tctx.c
index cc3bf2b3bdbc..466b7300e208 100644
--- a/io_uring/tctx.c
+++ b/io_uring/tctx.c
@@ -43,7 +43,7 @@ static struct io_wq *io_init_wq_offload(struct io_ring_ctx *ctx,
return io_wq_create(concurrency, &data);
}
-void __io_uring_free(struct task_struct *tsk)
+void io_uring_free_tctx(struct task_struct *tsk)
{
struct io_uring_task *tctx = tsk->io_uring;
struct io_tctx_node *node;
@@ -67,6 +67,11 @@ void __io_uring_free(struct task_struct *tsk)
kfree(tctx);
tsk->io_uring = NULL;
}
+}
+
+void __io_uring_free(struct task_struct *tsk)
+{
+ io_uring_free_tctx(tsk);
if (tsk->io_uring_restrict) {
io_put_bpf_filters(tsk->io_uring_restrict);
kfree(tsk->io_uring_restrict);
diff --git a/io_uring/tctx.h b/io_uring/tctx.h
index 2310d2a0c46d..76ad1ad4594e 100644
--- a/io_uring/tctx.h
+++ b/io_uring/tctx.h
@@ -12,6 +12,7 @@ void io_uring_del_tctx_node(unsigned long index);
int __io_uring_add_tctx_node(struct io_ring_ctx *ctx);
int __io_uring_add_tctx_node_from_submit(struct io_ring_ctx *ctx);
void io_uring_clean_tctx(struct io_uring_task *tctx);
+void io_uring_free_tctx(struct task_struct *tsk);
void io_uring_unreg_ringfd(void);
int io_ringfd_register(struct io_ring_ctx *ctx, void __user *__arg,
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c
index 76b9b0d54af9..f1464ea8ca64 100644
--- a/io_uring/zcrx.c
+++ b/io_uring/zcrx.c
@@ -808,7 +808,8 @@ err_xa_erase:
scoped_guard(mutex, &ctx->mmap_lock)
xa_erase(&ctx->zcrx_ctxs, id);
err:
- zcrx_unregister(ifq, ctx);
+ /* the import path never set the ->master_ctx ref, don't drop it */
+ zcrx_unregister(ifq, NULL);
return ret;
}
diff --git a/kernel/audit.c b/kernel/audit.c
index 562476937fa7..9412af9144bc 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -2120,7 +2120,8 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf,
void audit_log_n_string(struct audit_buffer *ab, const char *string,
size_t slen)
{
- int avail, new_len;
+ int avail;
+ size_t new_len;
unsigned char *ptr;
struct sk_buff *skb;
@@ -2130,7 +2131,13 @@ void audit_log_n_string(struct audit_buffer *ab, const char *string,
BUG_ON(!ab->skb);
skb = ab->skb;
avail = skb_tailroom(skb);
- new_len = slen + 3; /* enclosing quotes + null terminator */
+
+ /* enclosing quotes + null terminator */
+ if (check_add_overflow(slen, 3, &new_len)) {
+ audit_log_format(ab, "?");
+ return;
+ }
+
if (new_len > avail) {
avail = audit_expand(ab, new_len);
if (!avail)
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 4401119b5275..7f791afe5791 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -1045,6 +1045,10 @@ int audit_del_rule(struct audit_entry *entry)
goto out;
}
+ list_del_rcu(&e->list);
+ list_del(&e->rule.list);
+ synchronize_rcu();
+
if (e->rule.watch)
audit_remove_watch_rule(&e->rule);
@@ -1062,8 +1066,6 @@ int audit_del_rule(struct audit_entry *entry)
audit_signals--;
#endif
- list_del_rcu(&e->list);
- list_del(&e->rule.list);
call_rcu(&e->rcu, audit_free_rule_rcu);
out:
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index 38f8d9df8fbc..b5b461d4418b 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -3996,6 +3996,7 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
struct psi_trigger *new;
struct cgroup *cgrp;
struct psi_group *psi;
+ bool need_rtpoll_worker;
ssize_t ret = 0;
cgrp = cgroup_kn_lock_live(of->kn, false);
@@ -4015,12 +4016,32 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf,
}
psi = cgroup_psi(cgrp);
- new = psi_trigger_create(psi, buf, res, of->file, of);
+ new = psi_trigger_create(psi, buf, res, of->file, of,
+ &need_rtpoll_worker);
if (IS_ERR(new)) {
ret = PTR_ERR(new);
goto out_unlock;
}
+ /*
+ * The worker fork must run with neither cgroup_mutex nor the file's
+ * kernfs active reference held. The latter is broken since
+ * cgroup_kn_lock_live(). @of->priv may be released while unlocked, so
+ * recheck before publishing @new.
+ */
+ if (need_rtpoll_worker) {
+ cgroup_unlock();
+ ret = psi_trigger_create_rtpoll_worker(psi);
+ cgroup_lock();
+
+ if (!ret && !of->priv)
+ ret = -ENODEV;
+ if (ret) {
+ psi_trigger_destroy(new);
+ goto out_unlock;
+ }
+ }
+
smp_store_release(&ctx->psi.trigger, new);
out_unlock:
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4084e926e284..6300b216012c 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -830,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get)
if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) {
/* We won the race, we are the ones to unlock SRCU */
__srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx);
- return get ? get_uprobe(uprobe) : uprobe;
+ return get && uprobe ? get_uprobe(uprobe) : uprobe;
}
/*
diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index 179b26e9c934..2650d1e52803 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -982,8 +982,11 @@ retry:
return -1;
/*
- * Special case for regular (non PI) futexes. The unlock path in
- * user space has two race scenarios:
+ * Special case for regular (non PI) futexes. Ordinarily, we do
+ * not perform any processing here unless the current thread was
+ * the owner of the futex (by the TID check below).
+ *
+ * However, the unlock path has three race scenarios:
*
* 1. The unlock path releases the user space futex value and
* before it can execute the futex() syscall to wake up
@@ -992,42 +995,70 @@ retry:
* 2. A woken up waiter is killed before it can acquire the
* futex in user space.
*
- * In the second case, the wake up notification could be generated
- * by the unlock path in user space after setting the futex value
- * to zero or by the kernel after setting the OWNER_DIED bit below.
+ * 3. A woken up waiter is killed in user space after another
+ * thread has acquired the futex, but before it can set
+ * FUTEX_WAITERS.
+ *
+ * Note that, if userspace uses the FUTEX_ROBUST_UNLOCK flag, we
+ * will not see case 1 here.
+ *
+ * In the second and third case, the wake up notification could
+ * be generated from any of:
+ *
+ * i. An ordinary futex wakeup after unlock (with or
+ * without FUTEX_ROBUST_UNLOCK)
+ * ii. A robust wakeup from another thread's death
+ * iii. A previous round through this special case
+ *
+ * As a result, the futex world will be in one of four states:
+ *
+ * A. The futex word is 0 (unlocked)
+ * B. The futex word is owned by another thread
+ * (FUTEX_WAITERS is not set)
+ * C. The futex word is owned by another thread
+ * (FUTEX_WAITERS set)
+ * D. The futex's owner died and OWNER_DIED is set
+ * (the owner part of the word is 0)
*
- * In both cases the TID validation below prevents a wakeup of
- * potential waiters which can cause these waiters to block
- * forever.
+ * The key issue is that the kernel usually (at least from
+ * sources ii. and iii. or when so requested by userspace from
+ * source i.) only ever wakes *one* waiter at a time. If this
+ * waiter dies before acquiring the futex (or setting the
+ * FUTEX_WAITERS bit), the kernel *must* still wake the next
+ * waiter down the line to uphold the futex invariants and
+ * avoid lost wakeups. Note we do not need to handle state C,
+ * as it does not matter to us whether *we* successfully set
+ * the bit or a third thread did so in the meantime.
*
- * In both cases the following conditions are met:
+ * Therefore, in these cases we must issue an additional
+ * futex_wake(). Note however that we *must not* set OWNER_DIED
+ * here. Our thread is *not* the owner of the futex.
*
- * 1) task->futex.robust_list->list_op_pending != NULL
- * @pending_op == true
- * 2) The owner part of user space futex value == 0
+ * Thus to summarize, the conditions for needing the additional
+ * futex_wake() are:
+ *
+ * 1) @pending_op == true (the thread has not finished the
+ * mutex operation)
+ * 2) The futex word is in one of the states A, B or D
* 3) Regular futex: @pi == false
*
- * If these conditions are met, it is safe to attempt waking up a
- * potential waiter without touching the user space futex value and
- * trying to set the OWNER_DIED bit. If the futex value is zero,
- * the rest of the user space mutex state is consistent, so a woken
- * waiter will just take over the uncontended futex. Setting the
- * OWNER_DIED bit would create inconsistent state and malfunction
- * of the user space owner died handling. Otherwise, the OWNER_DIED
- * bit is already set, and the woken waiter is expected to deal with
- * this.
+ * Note in particular that in all of the states A-D the owner
+ * portion of the futex word differs from our thread's TID
+ * (unless the actual owner has the same TID in another PID
+ * namespace, but we cannot currently distinguish that
+ * scenario), so this can be a special-case wakeup in the bail
+ * path of the ordinary TID check.
*/
owner = uval & FUTEX_TID_MASK;
- if (pending_op && !pi && !owner) {
- futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, NULL, 1,
- FUTEX_BITSET_MATCH_ANY);
+ if (owner != task_pid_vnr(curr)) {
+ if (pending_op && !pi && (!owner || !(uval & FUTEX_WAITERS))) {
+ futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, NULL, 1,
+ FUTEX_BITSET_MATCH_ANY);
+ }
return 0;
}
- if (owner != task_pid_vnr(curr))
- return 0;
-
/*
* Ok, this dying thread is truly holding a futex
* of interest. Set the OWNER_DIED bit atomically
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 4834a809985a..175c08a6e41e 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -38,6 +38,16 @@
#include "../kexec_internal.h"
#include "kexec_handover_internal.h"
+/*
+ * This is the minimal alignment required by deferred struct page init.
+ * deferred_init_memmap_chunk frees memory to the buddy allocator, which looks
+ * at the neighboring pages (up to MAX_PAGE_ORDER) to merge them.
+ * If KHO scratch is not aligned to that value, buddy can access uninitialized
+ * struct pages, which can cause a crash.
+ */
+#define SCRATCH_ALIGNMENT_BYTES (PAGE_SIZE * MAX_ORDER_NR_PAGES)
+static_assert(SCRATCH_ALIGNMENT_BYTES >= CMA_MIN_ALIGNMENT_BYTES);
+
/* The magic token for preserved pages */
#define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */
@@ -640,8 +650,8 @@ static void __init scratch_size_update(void)
* Scratch areas are released as MIGRATE_CMA. Round them up to the right
* size.
*/
- scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES);
- scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES);
+ scratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES);
+ scratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES);
}
static phys_addr_t __init scratch_size_node(int nid)
@@ -656,7 +666,7 @@ static phys_addr_t __init scratch_size_node(int nid)
size = scratch_size_pernode;
}
- return round_up(size, CMA_MIN_ALIGNMENT_BYTES);
+ return round_up(size, SCRATCH_ALIGNMENT_BYTES);
}
/**
@@ -692,7 +702,7 @@ static void __init kho_reserve_scratch(void)
* next kernel
*/
size = scratch_size_lowmem;
- addr = memblock_phys_alloc_range(size, CMA_MIN_ALIGNMENT_BYTES, 0,
+ addr = memblock_phys_alloc_range(size, SCRATCH_ALIGNMENT_BYTES, 0,
ARCH_LOW_ADDRESS_LIMIT);
if (!addr) {
pr_err("Failed to reserve lowmem scratch buffer\n");
@@ -705,7 +715,7 @@ static void __init kho_reserve_scratch(void)
/* reserve large contiguous area for allocations without nid */
size = scratch_size_global;
- addr = memblock_phys_alloc(size, CMA_MIN_ALIGNMENT_BYTES);
+ addr = memblock_phys_alloc(size, SCRATCH_ALIGNMENT_BYTES);
if (!addr) {
pr_err("Failed to reserve global scratch buffer\n");
goto err_free_scratch_areas;
@@ -721,7 +731,7 @@ static void __init kho_reserve_scratch(void)
*/
for_each_node_state(nid, N_MEMORY) {
size = scratch_size_node(nid);
- addr = memblock_alloc_range_nid(size, CMA_MIN_ALIGNMENT_BYTES,
+ addr = memblock_alloc_range_nid(size, SCRATCH_ALIGNMENT_BYTES,
0, MEMBLOCK_ALLOC_ACCESSIBLE,
nid, true);
if (!addr) {
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index a4e689eefdfb..dff4ee04694c 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -870,8 +870,19 @@ static int sugov_start(struct cpufreq_policy *policy)
memset(sg_cpu, 0, sizeof(*sg_cpu));
sg_cpu->cpu = cpu;
sg_cpu->sg_policy = sg_policy;
+ }
+
+ /*
+ * Publish the hooks only after all per-CPU data is initialized, so a
+ * shared policy's sugov_update_shared() never reads an uninitialized
+ * sibling sugov_cpu.
+ */
+ for_each_cpu(cpu, policy->cpus) {
+ struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
+
cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu);
}
+
return 0;
}
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
index 0f858b98c9aa..200300043fa5 100644
--- a/kernel/sched/deadline.c
+++ b/kernel/sched/deadline.c
@@ -1017,7 +1017,8 @@ static void update_dl_entity(struct sched_dl_entity *dl_se)
if (dl_time_before(dl_se->deadline, rq_clock(rq)) ||
dl_entity_overflow(dl_se, rq_clock(rq))) {
- if (unlikely((!dl_is_implicit(dl_se) || dl_se->dl_defer) &&
+ if (unlikely((!dl_is_implicit(dl_se) ||
+ (dl_se->dl_defer && dl_se->dl_defer_running)) &&
!dl_time_before(dl_se->deadline, rq_clock(rq)) &&
!is_dl_boosted(dl_se))) {
update_dl_revised_wakeup(dl_se, rq);
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index e3fa7b2fac9d..18183062f751 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -3606,6 +3606,9 @@ static int __scx_init_task(struct scx_sched *sch, struct task_struct *p, bool fo
} else if (unlikely(fork)) {
scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] during fork",
p->comm, p->pid);
+ } else if (unlikely(scx_enable_state() != SCX_ENABLING)) {
+ scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] outside the enable path",
+ p->comm, p->pid);
} else {
struct rq *rq;
struct rq_flags rf;
@@ -4577,20 +4580,25 @@ static struct cgroup *root_cgroup(void)
return &cgrp_dfl_root.cgrp;
}
+/*
+ * cgroup_lock() must nest outside the rwsem write side: a writer waiting
+ * for cgroup_mutex deadlocks with cgroup teardown, which holds it while
+ * draining a set_* file write blocked on the rwsem behind the writer.
+ */
static void scx_cgroup_lock(void)
{
+ cgroup_lock();
#ifdef CONFIG_EXT_GROUP_SCHED
percpu_down_write(&scx_cgroup_ops_rwsem);
#endif
- cgroup_lock();
}
static void scx_cgroup_unlock(void)
{
- cgroup_unlock();
#ifdef CONFIG_EXT_GROUP_SCHED
percpu_up_write(&scx_cgroup_ops_rwsem);
#endif
+ cgroup_unlock();
}
#else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */
static inline struct cgroup *root_cgroup(void) { return NULL; }
@@ -5929,6 +5937,15 @@ static void scx_sub_disable(struct scx_sched *sch)
percpu_down_write(&scx_fork_rwsem);
scx_cgroup_lock();
+ /*
+ * An enable that failed before scx_link_sched() never owned a cgroup or
+ * task and won't be waited on by an ancestor's drain_descendants().
+ * Nothing to reparent and walking the tasks can misbehave as the task
+ * ownership invariant (either owned by self or parent) does not hold.
+ */
+ if (list_empty(&sch->sibling))
+ goto dump;
+
set_cgroup_sched(sch_cgroup(sch), parent);
scx_task_iter_start(&sti, sch->cgrp);
@@ -5941,8 +5958,8 @@ static void scx_sub_disable(struct scx_sched *sch)
continue;
/*
- * By the time control reaches here, all descendant schedulers
- * should already have been disabled.
+ * By the time control reaches here, all linked descendant
+ * schedulers should have been disabled.
*/
WARN_ON_ONCE(!scx_task_on_sched(sch, p));
@@ -5993,15 +6010,22 @@ static void scx_sub_disable(struct scx_sched *sch)
/*
* $p is initialized for $parent and still attached to
* @sch. Disable and exit for @sch, switch over to
- * $parent, override the state to READY to account for
- * $p having already been initialized, and then enable.
+ * $parent and override the state to READY to account
+ * for $p having already been initialized.
*/
scx_disable_and_exit_task(sch, p);
scx_set_task_state(p, SCX_TASK_INIT_BEGIN);
scx_set_task_state(p, SCX_TASK_INIT);
scx_set_task_sched(p, parent);
scx_set_task_state(p, SCX_TASK_READY);
- scx_enable_task(parent, p);
+
+ /*
+ * A task on a non-ext class, possible under an
+ * %SCX_OPS_SWITCH_PARTIAL root, stays READY and is
+ * enabled by switching_to_scx() if it switches over.
+ */
+ if (p->sched_class == &ext_sched_class)
+ scx_enable_task(parent, p);
}
task_rq_unlock(rq, p, &rf);
@@ -6009,6 +6033,7 @@ static void scx_sub_disable(struct scx_sched *sch)
}
scx_task_iter_stop(&sti);
+dump:
scx_disable_dump(sch);
scx_cgroup_unlock();
@@ -7708,10 +7733,14 @@ static void scx_sub_enable_workfn(struct kthread_work *work)
/*
* $p is now only initialized for @sch and READY, which
- * is what we want. Assign it to @sch and enable.
+ * is what we want. Assign it to @sch and, if it's on
+ * the ext class, enable. A non-ext task, possible under
+ * an %SCX_OPS_SWITCH_PARTIAL root, stays READY and is
+ * enabled by switching_to_scx() if it switches over.
*/
scx_set_task_sched(p, sch);
- scx_enable_task(sch, p);
+ if (p->sched_class == &ext_sched_class)
+ scx_enable_task(sch, p);
p->scx.flags &= ~SCX_TASK_SUB_INIT;
}
diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c
index 8e8c6201b7df..6f93cc32b650 100644
--- a/kernel/sched/ext/idle.c
+++ b/kernel/sched/ext/idle.c
@@ -554,8 +554,10 @@ s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags,
cpu_rq(cpu)->scx.local_dsq.nr == 0 &&
(!(flags & SCX_PICK_IDLE_IN_NODE) || (waker_node == node)) &&
!cpumask_empty(idle_cpumask(waker_node)->cpu)) {
- if (cpumask_test_cpu(cpu, allowed))
+ if (cpumask_test_cpu(cpu, allowed)) {
+ scx_idle_test_and_clear_cpu(cpu);
goto out_unlock;
+ }
}
}
diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
index d9c9d9480a45..e2e825dcd088 100644
--- a/kernel/sched/psi.c
+++ b/kernel/sched/psi.c
@@ -1134,6 +1134,12 @@ void psi_cgroup_free(struct cgroup *cgroup)
return;
cancel_delayed_work_sync(&cgroup->psi->avgs_work);
+ /*
+ * A psi_schedule_rtpoll_work() call racing the last trigger's
+ * destruction may have re-armed the timer after psi_trigger_destroy()
+ * deleted it. Spurious firing while the group is alive is harmless.
+ */
+ timer_shutdown_sync(&cgroup->psi->rtpoll_timer);
free_percpu(cgroup->psi->pcpu);
/* All triggers must be removed by now */
WARN_ONCE(cgroup->psi->rtpoll_states, "psi: trigger leak\n");
@@ -1292,9 +1298,44 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res)
return 0;
}
+/*
+ * Create @group's rtpoll worker after psi_trigger_create() reported the need
+ * for one. kthread creation depends on the whole fork path and we don't want
+ * all of that nested inside cgroup_mutex, so the caller must drop it and any
+ * other lock that forks can wait behind. If two callers race, the loser stops
+ * its never-woken kthread.
+ */
+int psi_trigger_create_rtpoll_worker(struct psi_group *group)
+{
+ struct task_struct *task;
+
+ task = kthread_create(psi_rtpoll_worker, group, "psimon");
+ if (IS_ERR(task))
+ return PTR_ERR(task);
+
+ scoped_guard(mutex, &group->rtpoll_trigger_lock) {
+ if (!rcu_access_pointer(group->rtpoll_task)) {
+ atomic_set(&group->rtpoll_wakeup, 0);
+ wake_up_process(task);
+ rcu_assign_pointer(group->rtpoll_task, task);
+
+ /*
+ * Poll once to catch up on scheduling attempts dropped
+ * while there was no rtpoll worker.
+ */
+ psi_schedule_rtpoll_work(group, 1, true);
+ return 0;
+ }
+ }
+
+ kthread_stop(task);
+ return 0;
+}
+
struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
enum psi_res res, struct file *file,
- struct kernfs_open_file *of)
+ struct kernfs_open_file *of,
+ bool *need_rtpoll_worker)
{
struct psi_trigger *t;
enum psi_states state;
@@ -1302,6 +1343,8 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
bool privileged;
u32 window_us;
+ *need_rtpoll_worker = false;
+
if (static_branch_likely(&psi_disabled))
return ERR_PTR(-EOPNOTSUPP);
@@ -1362,26 +1405,14 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf,
if (privileged) {
mutex_lock(&group->rtpoll_trigger_lock);
- if (!rcu_access_pointer(group->rtpoll_task)) {
- struct task_struct *task;
-
- task = kthread_create(psi_rtpoll_worker, group, "psimon");
- if (IS_ERR(task)) {
- kfree(t);
- mutex_unlock(&group->rtpoll_trigger_lock);
- return ERR_CAST(task);
- }
- atomic_set(&group->rtpoll_wakeup, 0);
- wake_up_process(task);
- rcu_assign_pointer(group->rtpoll_task, task);
- }
-
list_add(&t->node, &group->rtpoll_triggers);
group->rtpoll_min_period = min(group->rtpoll_min_period,
div_u64(t->win.size, UPDATES_PER_WINDOW));
group->rtpoll_nr_triggers[t->state]++;
group->rtpoll_states |= (1 << t->state);
+ *need_rtpoll_worker = !rcu_access_pointer(group->rtpoll_task);
+
mutex_unlock(&group->rtpoll_trigger_lock);
} else {
mutex_lock(&group->avgs_lock);
@@ -1541,6 +1572,8 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
size_t buf_size;
struct seq_file *seq;
struct psi_trigger *new;
+ bool need_rtpoll_worker;
+ int ret;
if (static_branch_likely(&psi_disabled))
return -EOPNOTSUPP;
@@ -1565,12 +1598,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
return -EBUSY;
}
- new = psi_trigger_create(&psi_system, buf, res, file, NULL);
+ new = psi_trigger_create(&psi_system, buf, res, file, NULL,
+ &need_rtpoll_worker);
if (IS_ERR(new)) {
mutex_unlock(&seq->lock);
return PTR_ERR(new);
}
+ if (need_rtpoll_worker) {
+ ret = psi_trigger_create_rtpoll_worker(&psi_system);
+ if (ret) {
+ psi_trigger_destroy(new);
+ mutex_unlock(&seq->lock);
+ return ret;
+ }
+ }
+
smp_store_release(&seq->private, new);
mutex_unlock(&seq->lock);
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 804ccae694d2..8e2485bb3aa8 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2599,6 +2599,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu)
return_ptr(cpu_buffer);
fail_free_reader:
+ kfree(cpu_buffer->subbuf_ids);
free_buffer_page(cpu_buffer->reader_page);
return NULL;
@@ -5783,6 +5784,7 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer)
cpu_buffer->head_page = new_head;
cpu_buffer->reader_page = new_reader;
+ cpu_buffer->reader_page->read = 0;
cpu_buffer->pages = &new_head->list;
cpu_buffer->read_stamp = new_reader->page->time_stamp;
cpu_buffer->lost_events = cpu_buffer->meta_page->reader.lost_events;
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 956692856fa8..c01b10b99f67 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -3933,8 +3933,8 @@ static void trace_module_add_events(struct module *mod)
end = mod->trace_events + mod->num_trace_events;
for_each_event(call, start, end) {
- __register_event(*call, mod);
- __add_event_to_tracers(*call);
+ if (!__register_event(*call, mod))
+ __add_event_to_tracers(*call);
}
update_cache_events(mod);
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 6385cd662d8d..2b46ca536045 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1027,6 +1027,9 @@ static int regex_match_full(char *str, struct regex *r, int len)
if (!len)
return strcmp(str, r->pattern) == 0;
+ if (len < r->len)
+ return 0;
+
return strncmp(str, r->pattern, len) == 0;
}
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index b88b8d9923ad..df8692c2dea8 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr)
{
overrun_detected = false;
prev_overruns = 0;
+ atomic_set(&dropped_count, 0);
tracing_reset_online_cpus(&tr->array_buffer);
}
@@ -293,11 +294,15 @@ device_initcall(init_mmio_trace);
static void __trace_mmiotrace_rw(struct trace_array *tr,
struct mmiotrace_rw *rw)
{
- struct trace_buffer *buffer = tr->array_buffer.buffer;
+ struct trace_buffer *buffer;
struct ring_buffer_event *event;
struct trace_mmiotrace_rw *entry;
unsigned int trace_ctx;
+ if (!tr)
+ return;
+
+ buffer = tr->array_buffer.buffer;
trace_ctx = tracing_gen_ctx_flags(0);
event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
sizeof(*entry), trace_ctx);
@@ -320,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw)
static void __trace_mmiotrace_map(struct trace_array *tr,
struct mmiotrace_map *map)
{
- struct trace_buffer *buffer = tr->array_buffer.buffer;
+ struct trace_buffer *buffer;
struct ring_buffer_event *event;
struct trace_mmiotrace_map *entry;
unsigned int trace_ctx;
+ if (!tr)
+ return;
+
+ buffer = tr->array_buffer.buffer;
trace_ctx = tracing_gen_ctx_flags(0);
event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
sizeof(*entry), trace_ctx);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 032702a4637b..58cabe6af33d 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -4114,6 +4114,18 @@ fail:
remap_page(folio, 1 << old_order, ttu_flags);
/*
+ * Drop the mapping while the inode is still pinned. @folio stays
+ * locked and present in the page cache until the loop below, so
+ * eviction cannot free the inode yet; @lock_at is not enough, it may
+ * be a tail beyond EOF that the split already dropped from the page
+ * cache. Nothing past this point may touch the inode or the mapping.
+ */
+ if (mapping) {
+ i_mmap_unlock_read(mapping);
+ mapping = NULL;
+ }
+
+ /*
* Unlock all after-split folios except the one containing
* @lock_at page. If @folio is not split, it will be kept locked.
*/
diff --git a/mm/memcontrol-v1.h b/mm/memcontrol-v1.h
index f92f81108d5e..4fa6e2bc8413 100644
--- a/mm/memcontrol-v1.h
+++ b/mm/memcontrol-v1.h
@@ -107,7 +107,11 @@ static inline void memcg1_remove_from_trees(struct mem_cgroup *memcg) {}
static inline void memcg1_soft_limit_reset(struct mem_cgroup *memcg) {}
static inline void memcg1_css_offline(struct mem_cgroup *memcg) {}
-static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) { return true; }
+static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked)
+{
+ *locked = false;
+ return true;
+}
static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {}
static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {}
diff --git a/mm/memory.c b/mm/memory.c
index ff338c2abe92..6b8280cfc1db 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1981,7 +1981,7 @@ retry:
if (can_reclaim_pt) {
if (direct_reclaim || zap_pte_table_if_empty(mm, pmd, start, &pmdval)) {
- pte_free_tlb(tlb, pmd_pgtable(pmdval), addr);
+ pte_free_tlb(tlb, pmd_pgtable(pmdval), start);
mm_dec_nr_ptes(mm);
}
}
diff --git a/mm/memremap.c b/mm/memremap.c
index 81766d822400..accba23aef28 100644
--- a/mm/memremap.c
+++ b/mm/memremap.c
@@ -425,6 +425,7 @@ void free_zone_device_folio(struct folio *folio)
mem_cgroup_uncharge(folio);
if (folio_test_anon(folio)) {
+ mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, -1);
for (i = 0; i < nr; i++)
__ClearPageAnonExclusive(folio_page(folio, i));
}
diff --git a/mm/migrate.c b/mm/migrate.c
index d9b23909d716..dd15a84b2a52 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping,
/* No turning back from here */
newfolio->index = folio->index;
newfolio->mapping = folio->mapping;
- if (folio_test_anon(folio) && folio_test_large(folio))
+ if (folio_test_anon(folio) && folio_test_large(folio) &&
+ !folio_test_hugetlb(folio))
mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1);
if (folio_test_swapbacked(folio))
__folio_set_swapbacked(newfolio);
diff --git a/mm/page_reporting.c b/mm/page_reporting.c
index 942e84b6908a..3e30731b940e 100644
--- a/mm/page_reporting.c
+++ b/mm/page_reporting.c
@@ -80,7 +80,8 @@ __page_reporting_request(struct page_reporting_dev_info *prdev)
* now we are limiting this to running no more than once every
* couple of seconds.
*/
- schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
+ queue_delayed_work(system_freezable_wq, &prdev->work,
+ PAGE_REPORTING_DELAY);
}
/* notify prdev of free page reporting request */
@@ -340,7 +341,8 @@ err_out:
*/
state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE);
if (state == PAGE_REPORTING_REQUESTED)
- schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY);
+ queue_delayed_work(system_freezable_wq, &prdev->work,
+ PAGE_REPORTING_DELAY);
}
static DEFINE_MUTEX(page_reporting_mutex);
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 1a142c58700d..56708d1d2dfd 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4926,6 +4926,9 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc)
int i;
enum zone_watermarks mark;
+ if (unlikely(sc->proactive && signal_pending(current)))
+ return true;
+
if (sc->nr_reclaimed >= max(sc->nr_to_reclaim, compact_gap(sc->order)))
return true;
@@ -7906,8 +7909,15 @@ int user_proactive_reclaim(char *buf,
unsigned long batch_size = (nr_to_reclaim - nr_reclaimed) / 4;
unsigned long reclaimed;
+ /*
+ * Return -ERESTARTSYS to allow the freezer to interrupt the
+ * task. The syscall will be transparently restarted upon
+ * resume. For real signals, it either restarts the syscall
+ * (if SA_RESTART is set) or is converted to -EINTR by the
+ * signal layer.
+ */
if (signal_pending(current))
- return -EINTR;
+ return -ERESTARTSYS;
/*
* This is the final attempt, drain percpu lru caches in the
diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec
index c732415662ef..46e80970f723 100644
--- a/scripts/package/kernel.spec
+++ b/scripts/package/kernel.spec
@@ -67,7 +67,18 @@ This package provides debug information for the kernel image and modules from th
%undefine _unique_debug_srcs
%undefine _debugsource_packages
%undefine _debuginfo_subpackages
+
+# Preserve .BTF and .BTF.base sections in kernel modules during debuginfo
+# stripping. find-debuginfo.sh uses eu-strip which removes non-allocated ELF
+# sections like .BTF by default. .BTF.base is required for BTF distillation
+# support; without it, module BTF validation fails.
+%global with_keep_section %(%{__find_debuginfo} --help 2>&1 | grep -c keep-section)
+%if %{with_keep_section}
+%global _find_debuginfo_opts -r --keep-section .BTF --keep-section .BTF.base
+%else
%global _find_debuginfo_opts -r
+%endif
+
%global _missing_build_ids_terminate_build 1
%global _no_recompute_build_ids 1
%{debug_package}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 18d0d9154317..ced2e131b061 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -274,8 +274,13 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
} else {
set_bit(IMA_DIGSIG, &iint->atomic_flags);
}
- if (xattr_len - sizeof(xattr_value->type) - hash_start >=
- iint->ima_hash->length)
+ /*
+ * Use addition, not subtraction: sizeof() forces unsigned
+ * math and a short xattr_len would wrap around, bypassing
+ * this bounds check.
+ */
+ if (xattr_len >= (int)sizeof(xattr_value->type) + hash_start +
+ (int)iint->ima_hash->length)
/*
* xattr length may be longer. md5 hash in previous
* version occupied 20 bytes in xattr, instead of 16
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 5cea53fc36df..ff52becc3031 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -687,6 +687,43 @@ static int ima_file_check(struct file *file, int mask)
MAY_APPEND), FILE_CHECK, 0, false);
}
+/*
+ * ima_reset_action_flags - invalidate action flags after a content change
+ * @inode: inode of the file whose content is about to be truncated
+ *
+ * Clear IMA_DONE_MASK so the file is re-collected, re-measured,
+ * re-audited, and re-appraised on next access.
+ */
+static void ima_reset_action_flags(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ return;
+
+ iint = ima_iint_find(inode);
+ if (!iint)
+ return;
+
+ mutex_lock(&iint->mutex);
+ iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
+ mutex_unlock(&iint->mutex);
+ return;
+}
+
+static int ima_path_truncate(const struct path *path)
+{
+ ima_reset_action_flags(path->dentry->d_inode);
+ return 0;
+}
+
+static int ima_file_truncate(struct file *file)
+{
+ ima_reset_action_flags(file_inode(file));
+ return 0;
+}
+
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
size_t buf_size)
{
@@ -1300,11 +1337,13 @@ static struct security_hook_list ima_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_release, ima_file_free),
LSM_HOOK_INIT(mmap_file, ima_file_mmap),
LSM_HOOK_INIT(file_mprotect, ima_file_mprotect),
+ LSM_HOOK_INIT(file_truncate, ima_file_truncate),
LSM_HOOK_INIT(kernel_load_data, ima_load_data),
LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data),
LSM_HOOK_INIT(kernel_read_file, ima_read_file),
LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file),
LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod),
+ LSM_HOOK_INIT(path_truncate, ima_path_truncate),
#ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS
LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update),
#endif
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8d6945edae7a..18dd28b2bb13 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2974,6 +2974,10 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
sbsec = selinux_superblock(dir->i_sb);
+ if (!selinux_initialized() ||
+ !(sbsec->flags & SBLABEL_MNT))
+ return -EOPNOTSUPP;
+
newsid = crsec->create_sid;
newsclass = inode_mode_to_security_class(inode->i_mode);
rc = selinux_determine_inode_label(crsec, dir, qstr, newsclass, &newsid);
@@ -2988,10 +2992,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
isec->initialized = LABEL_INITIALIZED;
}
- if (!selinux_initialized() ||
- !(sbsec->flags & SBLABEL_MNT))
- return -EOPNOTSUPP;
-
xattr = lsm_get_xattr_slot(xattrs, xattr_count);
if (xattr) {
rc = security_sid_to_context_force(newsid,
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
index 3cd36e2015fa..40c62600650e 100644
--- a/security/selinux/ss/mls.c
+++ b/security/selinux/ss/mls.c
@@ -160,9 +160,6 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
{
const char *name;
const struct level_datum *levdatum;
- struct ebitmap_node *node;
- u32 bit;
- int rc;
if (!l->sens || l->sens > p->p_levels.nprim)
return false;
@@ -176,21 +173,14 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l)
return false;
/*
- * Validate that all bits set in l->cat are also be set in
- * levdatum->level->cat and no bit in l->cat is larger than
- * p->p_cats.nprim.
+ * l is valid iff every bit in l->cat is set in levdatum->level.cat
+ * and no bit in l->cat is larger than p->p_cats.nprim.
+ * policydb_index() has already verified that every bit set in
+ * levdatum->level.cat names a defined category, so containment is
+ * sufficient here.
*/
- rc = ebitmap_contains(&levdatum->level.cat, &l->cat,
- p->p_cats.nprim);
- if (!rc)
- return false;
-
- ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) {
- if (!sym_name(p, SYM_CATS, bit))
- return false;
- }
-
- return true;
+ return ebitmap_contains(&levdatum->level.cat, &l->cat,
+ p->p_cats.nprim);
}
bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r)
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index ead504a639e3..8a32666c0ba2 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -665,6 +665,23 @@ static int cat_index(void *key, void *datum, void *datap)
return 0;
}
+static int sens_cat_index_check(void *key, void *datum, void *datap)
+{
+ struct policydb *p = datap;
+ struct level_datum *levdatum = datum;
+ struct ebitmap_node *node;
+ u32 bit;
+
+ ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) {
+ if (bit >= p->p_cats.nprim || !sym_name(p, SYM_CATS, bit)) {
+ pr_err("SELinux: sensitivity %s allows undefined category %u\n",
+ (const char *)key, bit + 1);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
/* clang-format off */
static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = {
common_index,
@@ -719,6 +736,7 @@ static inline void symtab_hash_eval(struct symtab *s)
static int policydb_index(struct policydb *p)
{
int i, rc;
+ u32 v;
if (p->mls_enabled)
pr_debug(
@@ -769,6 +787,30 @@ static int policydb_index(struct policydb *p)
if (rc)
goto out;
}
+
+ /*
+ * A sparse class value is absorbed by policydb_class_isvalid() and
+ * its siblings, but no such predicate exists for booleans: every
+ * user of bool_val_to_struct[] walks it by index and dereferences
+ * each entry -- cond_evaluate_expr(), the two getters and
+ * security_set_bools() -- so an unclaimed one has no consumer that
+ * can tolerate it.
+ */
+ for (v = 0; v < p->p_bools.nprim; v++) {
+ if (!p->bool_val_to_struct[v]) {
+ pr_err("SELinux: boolean %u is declared but not defined\n",
+ v + 1);
+ rc = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (p->mls_enabled) {
+ rc = hashtab_map(&p->p_levels.table, sens_cat_index_check, p);
+ if (rc)
+ goto out;
+ }
+
rc = 0;
out:
return rc;
@@ -1154,7 +1196,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len)
return 0;
}
-static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp)
+/*
+ * Bitmap of the permission values a symtab has claimed. Values are 1-based
+ * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set
+ * fits in a u32 and the callers reject an nprim past that width.
+ */
+static u32 perm_claimed_mask(u32 nprim)
+{
+ return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0;
+}
+
+static int perm_read(struct policydb *p, struct symtab *s,
+ struct policy_file *fp, u32 *claimed)
{
char *key = NULL;
struct perm_datum *perdatum;
@@ -1175,6 +1228,13 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f
rc = -EINVAL;
if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX)
goto bad;
+ /* indexes an nprim-sized array in security_get_permissions() */
+ if (perdatum->value > s->nprim)
+ goto bad;
+ /* two permissions cannot share one slot of that array */
+ if (*claimed & (1U << (perdatum->value - 1)))
+ goto bad;
+ *claimed |= 1U << (perdatum->value - 1);
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
@@ -1195,7 +1255,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
char *key = NULL;
struct common_datum *comdatum;
__le32 buf[4];
- u32 i, len, nel;
+ u32 i, len, nel, claimed = 0;
int rc;
comdatum = kzalloc_obj(*comdatum);
@@ -1222,17 +1282,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file
if (rc)
goto bad;
comdatum->permissions.nprim = le32_to_cpu(buf[2]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (comdatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
rc = str_read(&key, GFP_KERNEL, fp, len);
if (rc)
goto bad;
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &comdatum->permissions, fp);
+ rc = perm_read(p, &comdatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ rc = -EINVAL;
+ if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) {
+ pr_err("SELinux: common %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&comdatum->permissions.table, "common_permissions", key);
rc = symtab_insert(s, key, comdatum);
@@ -1366,7 +1437,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
char *key = NULL;
struct class_datum *cladatum;
__le32 buf[6];
- u32 i, len, len2, ncons, nel, val;
+ u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0;
int rc;
cladatum = kzalloc_obj(*cladatum);
@@ -1399,6 +1470,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
if (rc)
goto bad;
cladatum->permissions.nprim = le32_to_cpu(buf[3]);
+ /* no permission value can reach a slot past SEL_VEC_MAX */
+ rc = -EINVAL;
+ if (cladatum->permissions.nprim > SEL_VEC_MAX)
+ goto bad;
ncons = le32_to_cpu(buf[5]);
@@ -1419,13 +1494,36 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file *
cladatum->comkey);
goto bad;
}
+
+ /*
+ * security_get_permissions() maps the common's permissions
+ * into an array sized by this class's nprim, so a class must
+ * declare at least as many as the common it inherits.
+ */
+ if (cladatum->permissions.nprim <
+ cladatum->comdatum->permissions.nprim) {
+ pr_err("SELinux: class %s has fewer permissions than common %s\n",
+ key, cladatum->comkey);
+ goto bad;
+ }
}
for (i = 0; i < nel; i++) {
- rc = perm_read(p, &cladatum->permissions, fp);
+ rc = perm_read(p, &cladatum->permissions, fp, &claimed);
if (rc)
goto bad;
}
+ /* the class's own permissions must claim the slots the common leaves */
+ if (cladatum->comdatum)
+ inherited = cladatum->comdatum->permissions.nprim;
+ rc = -EINVAL;
+ if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) &
+ ~perm_claimed_mask(inherited))) {
+ pr_err("SELinux: class %s does not define every permission it declares\n",
+ key);
+ goto bad;
+ }
+
hash_eval(&cladatum->permissions.table, "class_permissions", key);
rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 2d828548f3db..7afce975436e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2221,7 +2221,9 @@ void selinux_policy_cancel(struct selinux_load_state *load_state)
oldpolicy = rcu_dereference_protected(state->policy,
lockdep_is_held(&state->policy_mutex));
- sidtab_cancel_convert(oldpolicy->sidtab);
+ /* a first load has no outgoing policy and converted nothing */
+ if (oldpolicy)
+ sidtab_cancel_convert(oldpolicy->sidtab);
selinux_policy_free(load_state->policy);
kfree(load_state->convert_data);
}
@@ -3302,6 +3304,7 @@ int security_get_classes(struct selinux_policy *policy,
char ***classes, u32 *nclasses)
{
struct policydb *policydb;
+ u32 i;
int rc;
policydb = &policy->policydb;
@@ -3314,16 +3317,29 @@ int security_get_classes(struct selinux_policy *policy,
rc = hashtab_map(&policydb->p_classes.table, get_classes_callback,
*classes);
- if (rc) {
- u32 i;
+ if (rc)
+ goto err;
- for (i = 0; i < *nclasses; i++)
- kfree((*classes)[i]);
- kfree(*classes);
+ /*
+ * The class symtab may be sparse, which policydb_class_isvalid() exists
+ * to absorb; the callback fills this array by value, so an unclaimed
+ * one leaves a NULL that sel_make_classes() hands to sel_make_dir().
+ */
+ for (i = 0; i < *nclasses; i++) {
+ if (!(*classes)[i]) {
+ rc = -EINVAL;
+ goto err;
+ }
}
out:
return rc;
+
+err:
+ for (i = 0; i < *nclasses; i++)
+ kfree((*classes)[i]);
+ kfree(*classes);
+ return rc;
}
static int get_permissions_callback(void *k, void *d, void *args)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 7dc0060617f1..c9d0c2bb55b2 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2367,6 +2367,7 @@ static void relink_to_local(struct snd_pcm_substream *substream)
static int snd_pcm_unlink(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_substream *s;
struct snd_pcm_group *group;
bool nonatomic = substream->pcm->nonatomic;
bool do_free = false;
@@ -2379,6 +2380,12 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
group = substream->group;
snd_pcm_group_lock_irq(group, nonatomic);
+ /* release drain waiters before changing membership, else snd_pcm_drain()
+ * leaves its on-stack wait entry queued on a member's sleep list
+ */
+ snd_pcm_group_for_each_entry(s, substream)
+ wake_up(&s->runtime->sleep);
+
relink_to_local(substream);
refcount_dec(&group->refs);
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 419288eec4bb..7b671e270ef4 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -362,11 +362,10 @@ static int initialize_timer(struct snd_seq_timer *tmr)
tmr->ticks = 1;
if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
unsigned long r = snd_timer_resolution(tmr->timeri);
- if (r) {
- tmr->ticks = (unsigned int)(1000000000uL / (r * freq));
- if (! tmr->ticks)
- tmr->ticks = 1;
- }
+ unsigned long den;
+
+ if (r && !check_mul_overflow(r, freq, &den))
+ tmr->ticks = max(1U, (unsigned int)(1000000000uL / den));
}
tmr->initialized = 1;
return 0;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 07ef127b29e1..9407954034cf 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -458,6 +458,8 @@ static void remove_slave_links(struct snd_timer_instance *timeri,
list_del_init(&slave->ack_list);
list_del_init(&slave->active_list);
}
+ /* the close is done; a reopen must not see the mark */
+ timeri->flags &= ~SNDRV_TIMER_IFLG_DEAD;
}
/*
diff --git a/sound/core/ump.c b/sound/core/ump.c
index 70520c7ca293..632c13baf21e 100644
--- a/sound/core/ump.c
+++ b/sound/core/ump.c
@@ -1365,6 +1365,7 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
&rmidi);
if (err < 0) {
kfree(ump->out_cvts);
+ ump->out_cvts = NULL;
return err;
}
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index 379e1458f4ac..48a2e7f2ebb1 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -3863,6 +3863,16 @@ static void alc288_fixup_surface_swap_dacs(struct hda_codec *codec,
spec->gen.preferred_dacs = preferred_pairs;
}
+static void alc274_fixup_hp_89e9_amp(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_INIT) {
+ /* need to toggle GPIO to enable the amp */
+ snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x03, 0);
+ msleep(100);
+ snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x00, 0);
+ }
+}
enum {
ALC269_FIXUP_GPIO2,
ALC269_FIXUP_SONY_VAIO,
@@ -3939,6 +3949,7 @@ enum {
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13,
ALC269VC_FIXUP_INFINIX_Y4_MAX,
+ ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM,
ALC269VC_FIXUP_LUNNEN_GROUND_14,
ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO,
ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
@@ -4213,6 +4224,8 @@ enum {
ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX,
ALC287_FIXUP_ACER_MICMUTE_LED,
ALC236_FIXUP_DELL_HP_POP_NOISE,
+ ALC274_FIXUP_HP_89E9_GPIO,
+ ALC274_FIXUP_HP_VERBS,
};
/* A special fixup for Lenovo C940 and Yoga Duet 7;
@@ -4409,6 +4422,14 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST
},
+ [ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x14, 0x411111f0 }, /* disable, not connected */
+ { 0x1b, 0x90170110 }, /* use as internal speaker */
+ { }
+ },
+ },
[ALC269VC_FIXUP_LUNNEN_GROUND_14] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -6855,6 +6876,28 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_invalidate_dacs,
},
+ [ALC274_FIXUP_HP_89E9_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc274_fixup_hp_89e9_amp,
+ },
+ [ALC274_FIXUP_HP_VERBS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xc580 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x5757 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 },
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x0060 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC274_FIXUP_HP_89E9_GPIO,
+ },
};
static const struct hda_quirk alc269_fixup_tbl[] = {
@@ -6906,6 +6949,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
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),
SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
@@ -7108,6 +7152,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
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),
SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1),
SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1),
@@ -7177,6 +7222,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x88d1, "HP Pavilion 15-eh1xxx (mainboard 88D1)", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x88ed, "HP Victus 16-e0xxx (MB 88ED)",
+ ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x88ee, "HP Victus 16-e0xxx (MB 88EE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
@@ -7210,6 +7257,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX),
SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
@@ -7231,6 +7279,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
+ SND_PCI_QUIRK(0x103c, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS),
SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4),
SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED),
@@ -7927,6 +7976,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
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),
SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C),
+ /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5;
+ * use codec SSID to distinguish them
+ */
+ HDA_CODEC_QUIRK(0x17aa, 0x395b, "Lenovo Legion 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7),
SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7),
@@ -8000,7 +8053,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC),
- SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -8020,6 +8074,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
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),
+ SND_PCI_QUIRK(0x2782, 0x0250, "Infinix INBOOK X3 Slim", ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM),
SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC),
SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC),
SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX),
diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c
index 4b03c64e72ab..28c55c5a2d2e 100644
--- a/sound/hda/controllers/intel.c
+++ b/sound/hda/controllers/intel.c
@@ -2306,6 +2306,8 @@ static const struct snd_pci_quirk power_save_denylist[] = {
SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0),
/* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */
SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0),
+ /* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */
+ SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0),
{}
};
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index 96df00db51d5..3b40ec071379 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -402,11 +402,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
int err;
- const u32 channels = substream->runtime->channels;
- const u32 bytes_per_frame = channels * 3;
- const u32 period_size = substream->runtime->period_size;
const u32 periods = substream->runtime->periods;
- const u32 period_bytes = period_size * bytes_per_frame;
+ const u32 period_bytes = snd_pcm_lib_period_bytes(substream);
dma_addr_t buf = substream->dma_buffer.addr;
int i;
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
index 6f0843cfb3be..9909f1067871 100644
--- a/sound/pci/lx6464es/lx_core.c
+++ b/sound/pci/lx6464es/lx_core.c
@@ -969,10 +969,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
const unsigned int is_capture = lx_stream->is_capture;
int err;
- const u32 channels = substream->runtime->channels;
- const u32 bytes_per_frame = channels * 3;
- const u32 period_size = substream->runtime->period_size;
- const u32 period_bytes = period_size * bytes_per_frame;
+ const u32 period_bytes = snd_pcm_lib_period_bytes(substream);
const u32 pos = lx_stream->frame_pos;
const u32 next_pos = ((pos+1) == substream->runtime->periods) ?
0 : pos + 1;
diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c
index 051946562725..88e4230d66c3 100644
--- a/sound/soc/amd/acp-config.c
+++ b/sound/soc/amd/acp-config.c
@@ -45,6 +45,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = {
},
},
{
+ /* Lenovo Legion 7 15ASH11 (Strix Halo, ACP 7.0) */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83V9"),
+ },
+ },
+ {
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"),
DMI_MATCH(DMI_PRODUCT_NAME, "TUF Gaming A14 FA401EA"),
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index af1dce9c9b8a..de5c895e9fbd 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -48,6 +48,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire AG14-22P"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Lecoo"),
DMI_MATCH(DMI_PRODUCT_NAME, "Bellator N176"),
}
@@ -525,6 +532,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Crosshair A16 HX D7WFKG"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WIG"),
}
},
@@ -829,6 +843,14 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "PM1403CDA"),
}
},
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6500RE_M6500RE"),
+ }
+ },
+
{}
};
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index da416329b038..57bd5a1c34c8 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2423,8 +2423,9 @@ static int max98090_probe(struct snd_soc_component *component)
dev_dbg(component->dev, "max98090_probe\n");
max98090->mclk = devm_clk_get(component->dev, "mclk");
- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (IS_ERR(max98090->mclk))
+ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
max98090->component = component;
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index ced9bd4d94da..130b6694a634 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1987,8 +1987,9 @@ static int max98095_probe(struct snd_soc_component *component)
int ret = 0;
max98095->mclk = devm_clk_get(component->dev, "mclk");
- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ if (IS_ERR(max98095->mclk))
+ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
/* reset the codec, the DSP core, and disable all interrupts */
max98095_reset(component);
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index e68aa0350a5b..7482a9a8d198 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -113,6 +113,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
case 0x2000090 ... 0x2000094:
case 0x20000b1:
case 0x20000b4:
+ case 0x3010000:
case 0x3110000:
case 0x5300000 ... 0x5300300:
case 0x5400002:
@@ -196,15 +197,18 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
case 0x2000007:
case 0x200000d:
case 0x2000019:
+ case 0x200001a:
case 0x2000020:
case 0x2000030:
case 0x2000046:
case 0x2000067:
case 0x2000084:
case 0x2000086:
+ case 0x3010000:
case 0x3110000:
case 0x5800003:
case 0x5810000:
+ case 0x6100008:
case 0x44011000 ... 0x440115ff:
case 0x44012000:
case 0x44012021:
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
index 1b6729f363fc..6b509eb65d77 100644
--- a/sound/soc/codecs/rt722-sdca.c
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -1851,6 +1851,17 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
}
}
+static void rt722_sdca_reset(struct rt722_sdca_priv *rt722)
+{
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_LDO1_CTL, RT722_HIDDEN_REG_SW_RESET,
+ RT722_HIDDEN_REG_SW_RESET);
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+ if (rt722->hw_vid == RT722_VA)
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_LDO1_CTL, 0xb091);
+}
+
int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
@@ -1888,6 +1899,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
rt722->hw_vid = (val & 0x0f00) >> 8;
dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid);
+ if (!rt722->first_hw_init)
+ rt722_sdca_reset(rt722);
+
rt722_sdca_dmic_preset(rt722);
rt722_sdca_amp_preset(rt722);
rt722_sdca_jack_preset(rt722);
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index e1d62f30418a..0b8644ad74f1 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -32,15 +32,16 @@
static const unsigned int float_vol_db_lookup[] = {
0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151,
0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b,
-0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a,
+0x000084a3, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a,
0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f,
0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7,
0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d,
0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a,
0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27,
0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68,
-0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362,
-0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000
+0x06666666, 0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987,
+0x143d1362, 0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617,
+0x40000000
};
struct tas2562_data {
@@ -471,30 +472,45 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component);
- int ret;
+ int ret, index;
u32 reg_val;
- reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2];
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG4,
- (reg_val & 0xff));
- if (ret)
- return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG3,
- ((reg_val >> 8) & 0xff));
+ if (tas2562->volume_lvl == ucontrol->value.integer.value[0])
+ return 0;
+
+ index = ucontrol->value.integer.value[0] / 2;
+ if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup))
+ return -EINVAL;
+
+ reg_val = float_vol_db_lookup[index];
+
+ /*
+ * The device applies the 32-bit coefficient to the playback path on
+ * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the
+ * bytes must be written MSB first and DVC_CFG4 last. Writing CFG4
+ * first latches a mix of the previous coefficient's upper bytes and
+ * the new LSB instead of the requested value.
+ */
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG1,
+ ((reg_val >> 24) & 0xff));
if (ret)
return ret;
ret = snd_soc_component_write(component, TAS2562_DVC_CFG2,
((reg_val >> 16) & 0xff));
if (ret)
return ret;
- ret = snd_soc_component_write(component, TAS2562_DVC_CFG1,
- ((reg_val >> 24) & 0xff));
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG3,
+ ((reg_val >> 8) & 0xff));
+ if (ret)
+ return ret;
+ ret = snd_soc_component_write(component, TAS2562_DVC_CFG4,
+ (reg_val & 0xff));
if (ret)
return ret;
tas2562->volume_lvl = ucontrol->value.integer.value[0];
- return 0;
+ return 1;
}
/* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */
@@ -732,6 +748,8 @@ static int tas2562_probe(struct i2c_client *client)
data->client = client;
data->dev = &client->dev;
data->model_id = (uintptr_t)i2c_get_match_data(client);
+ /* Register default is 0x40400000, this is closest */
+ data->volume_lvl = (ARRAY_SIZE(float_vol_db_lookup) - 1) * 2;
tas2562_parse_dt(data);
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 9e6f0ad5f05d..209067e98e1f 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -1310,8 +1310,8 @@ static void cali_reg_update(struct bulk_reg_val *p,
t->sin_gn[2]);
break;
case TAS2781_PRM_SINEGAIN2_REG:
- reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
- t->sin_gn[2]);
+ reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1],
+ t->sin_gn2[2]);
break;
default:
reg = 0;
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 5fda9b647c70..f23c21032287 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -1419,7 +1419,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
ret = fsl_asrc_m2m_init(asrc);
if (ret) {
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
- return ret;
+ goto err_pm_get_sync;
}
return 0;
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index 114a6c0b6b73..8535ef844ce0 100644
--- a/sound/soc/fsl/fsl_easrc.c
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -2269,7 +2269,7 @@ static int fsl_easrc_probe(struct platform_device *pdev)
ret = fsl_asrc_m2m_init(easrc);
if (ret) {
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
- return ret;
+ goto err_pm_disable;
}
return 0;
diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c
index 217272260803..a7f4097d426c 100644
--- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c
@@ -8,6 +8,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
+#include <sound/soc-acpi-intel-ssp-common.h>
#include "soc-acpi-intel-sdw-mockup-match.h"
static const struct snd_soc_acpi_codecs nvl_essx_83x6 = {
@@ -15,6 +16,11 @@ static const struct snd_soc_acpi_codecs nvl_essx_83x6 = {
.codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
};
+static const struct snd_soc_acpi_codecs nvl_rt5682_rt5682s_hp = {
+ .num_codecs = 2,
+ .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = {
{
.comp_ids = &nvl_essx_83x6,
@@ -24,6 +30,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = {
SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
},
+ {
+ .comp_ids = &nvl_rt5682_rt5682s_hp,
+ .drv_name = "sof_rt5682",
+ .sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB,
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines);
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
index 8d7b007a068f..d7444f442c71 100644
--- a/sound/soc/sdca/sdca_class.c
+++ b/sound/soc/sdca/sdca_class.c
@@ -111,8 +111,8 @@ static void class_boot_work(struct work_struct *work)
regcache_cache_only(drv->dev_regmap, false);
- drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap,
- drv->sdw->irq);
+ drv->irq_info = devm_sdca_irq_allocate(drv->dev, drv->dev_regmap,
+ drv->sdw->irq);
if (IS_ERR(drv->irq_info))
goto err;
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 1496a15f7d2a..2fb2b043c979 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -191,7 +191,7 @@ static const struct snd_soc_dai_ops class_function_sdw_ops = {
.hw_free = class_function_sdw_remove_peripheral,
};
-static int class_function_component_probe(struct snd_soc_component *component)
+static int class_function_component_fixup_controls(struct snd_soc_component *component)
{
struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
struct sdca_class_drv *core = drv->core;
@@ -217,7 +217,7 @@ static int class_function_set_jack(struct snd_soc_component *component,
}
static const struct snd_soc_component_driver class_function_component_drv = {
- .probe = class_function_component_probe,
+ .fixup_controls = class_function_component_fixup_controls,
.remove = class_function_component_remove,
.endianness = 1,
};
@@ -408,7 +408,7 @@ static void class_function_remove(struct auxiliary_device *auxdev)
{
struct class_function_drv *drv = auxiliary_get_drvdata(auxdev);
- sdca_irq_cleanup(drv->dev, drv->function, drv->core->irq_info);
+ sdca_irq_cleanup_late(drv->dev, drv->function, drv->core->irq_info);
}
static int class_function_runtime_suspend(struct device *dev)
diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c
index 405e80b979de..4bcd8d1fdff8 100644
--- a/sound/soc/sdca/sdca_device.c
+++ b/sound/soc/sdca/sdca_device.c
@@ -43,7 +43,7 @@ void sdca_lookup_swft(struct sdw_slave *slave)
dev_info(&slave->dev, "SWFT not available\n");
else
devm_add_action_or_reset(&slave->dev, devm_acpi_table_put,
- &slave->sdca_data.swft);
+ slave->sdca_data.swft);
}
EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index 994821a6df61..dbe572336f8c 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -258,7 +258,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
if (!swf) {
dev_err(dev, "failed to locate SWF\n");
- return -ENOENT;
+ ret = -ENOENT;
+ goto error;
}
dev_info(dev, "loading SWF: %x-%x-%x\n",
@@ -270,6 +271,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset,
SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data,
swf->file_length - offsetof(struct acpi_sw_file, data));
+
+error:
release_firmware(firmware);
return ret;
}
@@ -481,10 +484,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA");
*/
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
{
- struct device *dev = interrupt->dev;
struct fdl_state *fdl_state;
- fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL);
+ fdl_state = kzalloc_obj(*fdl_state);
if (!fdl_state)
return -ENOMEM;
@@ -499,3 +501,13 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
return 0;
}
EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA");
+
+/**
+ * sdca_fdl_free_state - free state for an FDL interrupt
+ * @interrupt: SDCA interrupt structure.
+ */
+void sdca_fdl_free_state(struct sdca_interrupt *interrupt)
+{
+ kfree(interrupt->priv);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_fdl_free_state, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 77940bd6b33c..7a7a9f1a4938 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -855,6 +855,8 @@ static int find_sdca_control_range(struct device *dev,
return 0;
else if (num_range < 0)
return num_range;
+ else if (num_range < 2 * sizeof(*limits))
+ return -EINVAL;
range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL);
if (!range_list)
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
index 4539a52a8e32..42fbd3af8a75 100644
--- a/sound/soc/sdca/sdca_interrupts.c
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -421,7 +421,8 @@ static struct sdca_interrupt *get_interrupt_data(struct device *dev, int irq,
*
* This is intended to be used as part of the Function boot process. It
* can be called before the soundcard is registered (ie. doesn't depend
- * on component) and will register the FDL interrupts.
+ * on component) and will populate all the required IRQ data, as well as
+ * registering the FDL interrupts to start booting the device.
*
* Return: Zero on success, and a negative error code on failure.
*/
@@ -448,21 +449,36 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
else if (!interrupt)
continue;
+ ret = sdca_irq_data_populate(dev, regmap, NULL, function,
+ entity, control, interrupt);
+ if (ret)
+ return ret;
+
switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
- case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
- ret = sdca_irq_data_populate(dev, regmap, NULL,
- function, entity,
- control, interrupt);
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ interrupt->handler = function_status_handler;
+ break;
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ interrupt->handler = detected_mode_handler;
+ interrupt->free_priv = sdca_jack_free_state;
+
+ ret = sdca_jack_alloc_state(interrupt);
if (ret)
return ret;
+ break;
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ interrupt->handler = fdl_owner_handler;
+ interrupt->free_priv = sdca_fdl_free_state;
ret = sdca_fdl_alloc_state(interrupt);
if (ret)
return ret;
+ interrupt->early_request = true;
+
ret = sdca_irq_request_locked(dev, info, irq,
interrupt->name,
- fdl_owner_handler,
+ interrupt->handler,
interrupt);
if (ret) {
dev_err(dev, "failed to request irq %s: %d\n",
@@ -470,7 +486,11 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap,
return ret;
}
break;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ interrupt->handler = hid_handler;
+ break;
default:
+ interrupt->handler = base_handler;
break;
}
}
@@ -495,66 +515,38 @@ int sdca_irq_populate(struct sdca_function_data *function,
struct sdca_interrupt_info *info)
{
struct device *dev = component->dev;
- int i, j;
+ int i, ret;
guard(mutex)(&info->irq_lock);
- for (i = 0; i < function->num_entities; i++) {
- struct sdca_entity *entity = &function->entities[i];
+ for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
+ struct sdca_interrupt *interrupt = &info->irqs[i];
+ struct sdca_control *control = interrupt->control;
+ struct sdca_entity *entity = interrupt->entity;
+ int irq;
- for (j = 0; j < entity->num_controls; j++) {
- struct sdca_control *control = &entity->controls[j];
- int irq = control->interrupt_position;
- struct sdca_interrupt *interrupt;
- irq_handler_t handler;
- int ret;
+ if (interrupt->function != function || interrupt->irq)
+ continue;
- interrupt = get_interrupt_data(dev, irq, info);
- if (IS_ERR(interrupt))
- return PTR_ERR(interrupt);
- else if (!interrupt)
- continue;
+ interrupt->component = component;
- ret = sdca_irq_data_populate(dev, NULL, component,
- function, entity, control,
- interrupt);
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ ret = sdca_jack_init_state(interrupt);
if (ret)
return ret;
+ break;
+ default:
+ break;
+ }
- handler = base_handler;
-
- switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
- case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
- handler = function_status_handler;
- break;
- case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
- ret = sdca_jack_alloc_state(interrupt);
- if (ret)
- return ret;
-
- handler = detected_mode_handler;
- break;
- case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
- ret = sdca_fdl_alloc_state(interrupt);
- if (ret)
- return ret;
-
- handler = fdl_owner_handler;
- break;
- case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
- handler = hid_handler;
- break;
- default:
- break;
- }
-
- ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
- handler, interrupt);
- if (ret) {
- dev_err(dev, "failed to request irq %s: %d\n",
- interrupt->name, ret);
- return ret;
- }
+ irq = interrupt->control->interrupt_position;
+ ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
+ interrupt->handler, interrupt);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n",
+ interrupt->name, ret);
+ return ret;
}
}
@@ -562,17 +554,10 @@ int sdca_irq_populate(struct sdca_function_data *function,
}
EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA");
-/**
- * sdca_irq_cleanup - Free all the individual IRQs for an SDCA Function
- * @dev: Device pointer against which the sdca_interrupt_info was allocated.
- * @function: Pointer to the SDCA Function.
- * @info: Pointer to the SDCA interrupt info for this device.
- *
- * Typically this would be called from the driver for a single SDCA Function.
- */
-void sdca_irq_cleanup(struct device *dev,
- struct sdca_function_data *function,
- struct sdca_interrupt_info *info)
+static void sdca_irq_cleanup_flags(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info,
+ bool late_cleanup)
{
int i;
@@ -581,18 +566,59 @@ void sdca_irq_cleanup(struct device *dev,
for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) {
struct sdca_interrupt *interrupt = &info->irqs[i];
- if (interrupt->function != function || !interrupt->irq)
+ if (interrupt->function != function ||
+ (interrupt->early_request && !late_cleanup))
+ continue;
+
+ if (interrupt->irq)
+ sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);
+
+ if (!late_cleanup)
continue;
- sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt);
+ if (interrupt->free_priv)
+ interrupt->free_priv(interrupt);
kfree(interrupt->name);
}
}
+
+/**
+ * sdca_irq_cleanup - Free the regular IRQs for an SDCA Function
+ * @dev: Device pointer against which the sdca_interrupt_info was allocated.
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function
+ * from component remove.
+ */
+void sdca_irq_cleanup(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ sdca_irq_cleanup_flags(dev, function, info, false);
+}
EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA");
/**
- * sdca_irq_allocate - allocate an SDCA interrupt structure for a device
+ * sdca_irq_cleanup_late - Free the early IRQs for an SDCA Function
+ * @dev: Device pointer against which the sdca_interrupt_info was allocated.
+ * @function: Pointer to the SDCA Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function
+ * from bus remove.
+ */
+void sdca_irq_cleanup_late(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_interrupt_info *info)
+{
+ sdca_irq_cleanup_flags(dev, function, info, true);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup_late, "SND_SOC_SDCA");
+
+/**
+ * devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device
* @sdev: Device pointer against which things should be allocated.
* @regmap: regmap to be used for accessing the SDCA IRQ registers.
* @irq: The interrupt number.
@@ -604,8 +630,8 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA");
* Return: A pointer to the allocated sdca_interrupt_info struct, or an
* error code.
*/
-struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
- struct regmap *regmap, int irq)
+struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *sdev,
+ struct regmap *regmap, int irq)
{
struct sdca_interrupt_info *info;
int ret, i;
@@ -634,7 +660,7 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev,
return info;
}
-EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
+EXPORT_SYMBOL_NS_GPL(devm_sdca_irq_allocate, "SND_SOC_SDCA");
static void irq_enable_flags(struct sdca_function_data *function,
struct sdca_interrupt_info *info, bool early)
diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c
index ae9636622a84..73f8067906a9 100644
--- a/sound/soc/sdca/sdca_jack.c
+++ b/sound/soc/sdca/sdca_jack.c
@@ -41,26 +41,12 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
struct jack_state *state = interrupt->priv;
struct snd_kcontrol *kctl = state->kctl;
struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ struct soc_enum *soc_enum;
unsigned int reg, val;
int ret;
guard(rwsem_write)(rwsem);
- if (!kctl) {
- const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
- interrupt->entity->label,
- SDCA_CTL_SELECTED_MODE_NAME);
-
- if (!name)
- return -ENOMEM;
-
- kctl = snd_soc_component_get_kcontrol(component, name);
- if (!kctl)
- dev_dbg(dev, "control not found: %s\n", name);
- else
- state->kctl = kctl;
- }
-
reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
interrupt->control->sel, 0);
@@ -96,30 +82,21 @@ int sdca_jack_process(struct sdca_interrupt *interrupt)
dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
- if (kctl) {
- struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value;
-
- ucontrol = kzalloc_obj(*ucontrol);
- if (!ucontrol)
- return -ENOMEM;
+ ucontrol = kzalloc_obj(*ucontrol);
+ if (!ucontrol)
+ return -ENOMEM;
- ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
+ soc_enum = (struct soc_enum *)kctl->private_value;
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
- ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
- if (ret < 0) {
- dev_err(dev, "failed to update selected mode: %d\n", ret);
- return ret;
- }
-
- snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
- } else {
- ret = regmap_write(interrupt->function_regmap, reg, val);
- if (ret) {
- dev_err(dev, "failed to write selected mode: %d\n", ret);
- return ret;
- }
+ ret = snd_soc_dapm_put_enum_double(kctl, ucontrol);
+ if (ret < 0) {
+ dev_err(dev, "failed to update selected mode: %d\n", ret);
+ return ret;
}
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+
return sdca_jack_report(interrupt);
}
EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
@@ -132,10 +109,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA");
*/
int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
{
- struct device *dev = interrupt->dev;
struct jack_state *jack_state;
- jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL);
+ jack_state = kzalloc_obj(*jack_state);
if (!jack_state)
return -ENOMEM;
@@ -145,6 +121,42 @@ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt)
}
EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA");
+/**
+ * sdca_jack_free_state - free state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ */
+void sdca_jack_free_state(struct sdca_interrupt *interrupt)
+{
+ kfree(interrupt->priv);
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA");
+
+/**
+ * sdca_jack_init_state - Initialise transient state for a jack interrupt
+ * @interrupt: SDCA interrupt structure.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int sdca_jack_init_state(struct sdca_interrupt *interrupt)
+{
+ struct jack_state *jack_state = interrupt->priv;
+ const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+ interrupt->entity->label,
+ SDCA_CTL_SELECTED_MODE_NAME);
+
+ if (!name)
+ return -ENOMEM;
+
+ jack_state->kctl = snd_soc_component_get_kcontrol(interrupt->component, name);
+ if (!jack_state->kctl) {
+ dev_err(interrupt->dev, "control not found: %s\n", name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA");
+
static int type_get_mask(enum sdca_terminal_type type)
{
switch (type) {
@@ -192,7 +204,7 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja
struct sdca_control_range *range;
struct jack_state *jack_state;
- if (!interrupt->irq)
+ if (!interrupt->dev)
continue;
switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c
index a86bb28c6d0a..82a8bf75bbca 100644
--- a/sound/soc/sdca/sdca_ump.c
+++ b/sound/soc/sdca/sdca_ump.c
@@ -141,7 +141,7 @@ int sdca_ump_read_message(struct device *dev,
return ret;
}
- if (msg_len > buf_len - msg_offset) {
+ if (msg_offset + msg_len > buf_len) {
dev_err(dev, "%s: message too big for UMP buffer: %d\n",
entity->label, msg_len);
return -EINVAL;
@@ -207,7 +207,7 @@ int sdca_ump_write_message(struct device *dev,
buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0);
ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0);
- if (msg_len > buf_len - msg_offset) {
+ if (msg_offset + msg_len > buf_len) {
dev_err(dev, "%s: message too big for UMP buffer: %d\n",
entity->label, msg_len);
return -EINVAL;
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index 21492d15833f..2ce24513fac5 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -310,6 +310,16 @@ int snd_soc_component_probe(struct snd_soc_component *component)
return soc_component_ret(component, ret);
}
+int snd_soc_component_fixup_controls(struct snd_soc_component *component)
+{
+ int ret = 0;
+
+ if (component->driver->fixup_controls)
+ ret = component->driver->fixup_controls(component);
+
+ return soc_component_ret(component, ret);
+}
+
void snd_soc_component_remove(struct snd_soc_component *component)
{
if (component->driver->remove)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 7817beea5b3b..44f9bb4473f5 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2162,6 +2162,11 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
goto probe_end;
snd_soc_dapm_new_widgets(card);
+ for_each_card_components(card, component) {
+ ret = snd_soc_component_fixup_controls(component);
+ if (ret < 0)
+ goto probe_end;
+ }
snd_soc_card_fixup_controls(card);
ret = snd_card_register(card->snd_card);
diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c
index b66761156b99..bd93e261bdd1 100644
--- a/sound/soc/sophgo/cv1800b-sound-adc.c
+++ b/sound/soc/sophgo/cv1800b-sound-adc.c
@@ -251,16 +251,22 @@ static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol,
u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24);
u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24);
- u32 val;
+ u32 val, old_val;
val = readl(priv->regs + CV1800B_RXADC_ANA0);
+ old_val = val;
+
val = u32_replace_bits(val, cv1800b_gains[v_left],
REG_COMB_LEFT_VOLUME);
val = u32_replace_bits(val, cv1800b_gains[v_right],
REG_COMB_RIGHT_VOLUME);
+
+ if (val == old_val)
+ return 0;
+
writel(val, priv->regs + CV1800B_RXADC_ANA0);
- return 0;
+ return 1;
}
static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0);
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c
index 2c5648966412..6cf410a92fa2 100644
--- a/sound/usb/6fire/chip.c
+++ b/sound/usb/6fire/chip.c
@@ -150,6 +150,10 @@ static int usb6fire_chip_probe(struct usb_interface *intf,
return 0;
destroy_chip:
+ chip->shutdown = true;
+ if (card)
+ snd_card_disconnect(card);
+ usb6fire_chip_abort(chip);
snd_card_free(card);
return ret;
}
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 24cd7692bd01..dca06ba1c67e 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -1168,10 +1168,12 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep)
<< (16 - ep->datainterval);
}
- if (ep->fill_max)
+ if (ep->fill_max) {
ep->curpacksize = ep->maxpacksize;
- else
+ maxsize = ep->curpacksize;
+ } else {
ep->curpacksize = maxsize;
+ }
if (snd_usb_get_speed(chip->dev) != USB_SPEED_FULL) {
packs_per_ms = 8 >> ep->datainterval;
@@ -1817,11 +1819,13 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
out_packet->packets = in_ctx->packets;
for (i = 0; i < in_ctx->packets; i++) {
- if (urb->iso_frame_desc[i].status == 0)
- out_packet->packet_size[i] =
+ if (urb->iso_frame_desc[i].status == 0) {
+ unsigned int frames =
urb->iso_frame_desc[i].actual_length / sender->stride;
- else
+ out_packet->packet_size[i] = min(frames, ep->maxframesize);
+ } else {
out_packet->packet_size[i] = 0;
+ }
}
spin_unlock_irqrestore(&ep->lock, flags);
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index d87e3f357cf7..f8996416c3be 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -797,6 +797,8 @@ static void snd_usbmidi_akai_output(struct snd_usb_midi_out_endpoint *ep,
msg = urb->transfer_buffer + urb->transfer_buffer_length;
buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1;
+ if (buf_end <= 0)
+ return;
/* only try adding more data when there's space for at least 1 SysEx */
while (urb->transfer_buffer_length < buf_end) {
diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c
index 3ec633291772..83980fb83ac8 100644
--- a/sound/usb/midi2.c
+++ b/sound/usb/midi2.c
@@ -329,7 +329,7 @@ ump_to_endpoint(struct snd_ump_endpoint *ump, int dir)
{
struct snd_usb_midi2_ump *rmidi = ump->private_data;
- return rmidi->eps[dir];
+ return rmidi ? rmidi->eps[dir] : NULL;
}
/* ump open callback */
@@ -685,6 +685,8 @@ static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi)
rmidi = list_first_entry(&umidi->rawmidi_list,
struct snd_usb_midi2_ump, list);
list_del(&rmidi->list);
+ if (rmidi->ump)
+ rmidi->ump->private_data = NULL;
kfree(rmidi);
}
}
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index faac7df1fbcf..ce27fc871f51 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -344,6 +344,16 @@ static const struct usbmix_name_map bose_soundlink_map[] = {
{ 0 } /* terminator */
};
+/*
+ * Razer Barracuda X 2.4: Firmware reports cval->min = -16800 in 1/256 dB units
+ * (-65.62 dB), which stock ALSA misinterprets as a -168 dB floor
+ */
+static const struct usbmix_dB_map razer_barracuda_x_2_4_dB = {-6562, 0};
+static const struct usbmix_name_map razer_barracuda_x_2_4_map[] = {
+ { 2, NULL, .dB = &razer_barracuda_x_2_4_dB },
+ { 0 } /* terminator */
+};
+
/* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */
static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0};
static const struct usbmix_name_map sennheiser_pc8_map[] = {
@@ -629,6 +639,16 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = corsair_virtuoso_map,
},
{
+ /* Corsair Virtuoso (wired mode, later revision) */
+ .id = USB_ID(0x1b1c, 0x0a43),
+ .map = corsair_virtuoso_map,
+ },
+ {
+ /* Corsair Virtuoso (wireless mode, later revision) */
+ .id = USB_ID(0x1b1c, 0x0a44),
+ .map = corsair_virtuoso_map,
+ },
+ {
/* Corsair HS80 RGB Wireless (wired mode) */
.id = USB_ID(0x1b1c, 0x0a6a),
.map = corsair_virtuoso_map,
@@ -690,6 +710,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = lenovo_p620_rear_map,
},
{
+ /* Razer Barracuda X 2.4 */
+ .id = USB_ID(0x1532, 0x0552),
+ .map = razer_barracuda_x_2_4_map,
+ },
+ {
/* Sennheiser Communications Headset [PC 8] */
.id = USB_ID(0x1395, 0x0025),
.map = sennheiser_pc8_map,
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 10792d26fa94..74d26fd86efb 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3496,7 +3496,7 @@ static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 statu
struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
struct snd_usb_audio *chip = list->mixer->chip;
struct usb_device *dev = chip->dev;
- __le32 buf[4];
+ __le32 buf[4] = {};
int err;
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 0de7c9d94259..d2a111ee50ab 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -2200,6 +2200,9 @@ struct usb_audio_quirk_flags_table {
static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
/* Device and string descriptor matches */
+ DEVICE_STRING_FLG(0x05ac, 0x110b, /* VID conflicts with Apple */
+ "JKY Technology", NULL, /* Q2A */
+ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
/* Device matches */
DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */
@@ -2208,6 +2211,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x0020, 0x0b21, /* GHW-123P */
QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
+ DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */
+ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY),
DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16),
DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */
@@ -2342,6 +2347,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0d8c, 0x0014, /* C-Media */
QUIRK_FLAG_CTL_MSG_DELAY_1M | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
+ DEVICE_FLG(0x0d8c, 0x0102, /* C-Media CM6206 */
+ QUIRK_FLAG_GET_SAMPLE_RATE),
DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */
QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE),
DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */
diff --git a/tools/testing/selftests/clone3/clone3_set_tid.c b/tools/testing/selftests/clone3/clone3_set_tid.c
index 5c944aee6b41..485efa7c9eed 100644
--- a/tools/testing/selftests/clone3/clone3_set_tid.c
+++ b/tools/testing/selftests/clone3/clone3_set_tid.c
@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
{
FILE *f;
char buf;
- char *line;
+ char *line = NULL;
int status;
int ret = -1;
size_t len = 0;
diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
index c300eb020262..e2322693d0c3 100644
--- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
+++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc
@@ -1,16 +1,16 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Generic dynamic event - add/remove eprobe events
-# requires: dynamic_events events/syscalls/sys_enter_openat "<attached-group>.<attached-event> [<args>]":README
+# requires: dynamic_events events/syscalls/sys_enter_chdir "<attached-group>.<attached-event> [<args>]":README
echo 0 > events/enable
clear_dynamic_events
SYSTEM="syscalls"
-EVENT="sys_enter_openat"
+EVENT="sys_enter_chdir"
FIELD="filename"
-EPROBE="eprobe_open"
+EPROBE="eprobe_chdir"
OPTIONS="file=+0(\$filename):ustring"
echo "e:$EPROBE $SYSTEM/$EVENT $OPTIONS" >> dynamic_events
@@ -18,20 +18,14 @@ grep -q "$EPROBE" dynamic_events
test -d events/eprobes/$EPROBE
echo 1 > events/eprobes/$EPROBE/enable
-ls
+cd /sys/kernel/tracing
echo 0 > events/eprobes/$EPROBE/enable
-content=`grep '^ *ls-' trace | grep 'file='`
-nocontent=`grep '^ *ls-' trace | grep 'file=' | grep -v -e '"/' -e '"."' -e '(fault)' ` || true
-
+content=`grep -e 'file="/sys/kernel/tracing"\|(fault)' trace`
if [ -z "$content" ]; then
exit_fail
fi
-if [ ! -z "$nocontent" ]; then
- exit_fail
-fi
-
echo "-:$EPROBE" >> dynamic_events
! grep -q "$EPROBE" dynamic_events
diff --git a/tools/testing/selftests/mm/mlock-random-test.c b/tools/testing/selftests/mm/mlock-random-test.c
index 9d349c151360..16294bc7dae6 100644
--- a/tools/testing/selftests/mm/mlock-random-test.c
+++ b/tools/testing/selftests/mm/mlock-random-test.c
@@ -84,7 +84,7 @@ int get_proc_locked_vm_size(void)
int get_proc_page_size(unsigned long addr)
{
FILE *smaps;
- char *line;
+ char *line = NULL;
unsigned long mmupage_size = 0;
size_t size;
diff --git a/tools/testing/selftests/sched_ext/numa.bpf.c b/tools/testing/selftests/sched_ext/numa.bpf.c
index 78cc49a7f9a6..6b4515c28aa0 100644
--- a/tools/testing/selftests/sched_ext/numa.bpf.c
+++ b/tools/testing/selftests/sched_ext/numa.bpf.c
@@ -34,7 +34,8 @@ static bool is_cpu_idle(s32 cpu, int node)
s32 BPF_STRUCT_OPS(numa_select_cpu,
struct task_struct *p, s32 prev_cpu, u64 wake_flags)
{
- int node = __COMPAT_scx_bpf_cpu_node(scx_bpf_task_cpu(p));
+ s32 task_cpu = scx_bpf_task_cpu(p);
+ int node = __COMPAT_scx_bpf_cpu_node(task_cpu);
s32 cpu;
/*
@@ -48,6 +49,16 @@ s32 BPF_STRUCT_OPS(numa_select_cpu,
cpu = __COMPAT_scx_bpf_pick_any_cpu_node(p->cpus_ptr, node,
__COMPAT_SCX_PICK_IDLE_IN_NODE);
+ /*
+ * @task_cpu may be outside of p->cpus_ptr if @p's affinity
+ * changed while it was sleeping. This means it's possible for
+ * p->cpus_ptr to not include any CPUs from @node.
+ * If we failed to find a cpu in @node, check if @task_cpu
+ * is outside of p->cpus_ptr and just return @prev_cpu if it is.
+ */
+ if (cpu < 0 && !bpf_cpumask_test_cpu(task_cpu, p->cpus_ptr))
+ return prev_cpu;
+
if (is_cpu_idle(cpu, node))
scx_bpf_error("CPU %d should be marked as busy", cpu);
diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c
index 18e1071a2e24..6206a0a565ad 100644
--- a/tools/tracing/rtla/src/timerlat_top.c
+++ b/tools/tracing/rtla/src/timerlat_top.c
@@ -536,7 +536,7 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool)
if (!params->quiet)
timerlat_print_stats(tool);
- if (wait_retval != 0) {
+ if (wait_retval > 0) {
/* Stopping requested by tracer */
retval = common_threshold_handler(tool);
if (retval)