summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap7
-rw-r--r--CREDITS4
-rw-r--r--Documentation/ABI/testing/ima_policy3
-rw-r--r--Documentation/admin-guide/cgroup-v1/memory.rst49
-rw-r--r--Documentation/admin-guide/cgroup-v2.rst18
-rw-r--r--Documentation/fault-injection/nvme-fault-injection.rst65
-rw-r--r--Documentation/netlink/specs/conntrack.yaml21
-rw-r--r--Documentation/scheduler/sched-ext.rst17
-rw-r--r--MAINTAINERS37
-rw-r--r--arch/arm64/include/asm/cpu.h1
-rw-r--r--arch/arm64/include/asm/cpufeature.h7
-rw-r--r--arch/arm64/kernel/cpu_errata.c2
-rw-r--r--arch/arm64/kernel/cpufeature.c33
-rw-r--r--arch/arm64/kernel/cpuinfo.c2
-rw-r--r--arch/arm64/kernel/machine_kexec.c3
-rw-r--r--arch/arm64/mm/fault.c12
-rw-r--r--arch/s390/boot/alternative.c5
-rw-r--r--arch/s390/boot/ipl_parm.c28
-rw-r--r--arch/s390/boot/physmem_info.c2
-rw-r--r--arch/s390/include/asm/cpacf.h6
-rw-r--r--arch/s390/include/asm/processor.h1
-rw-r--r--arch/s390/include/asm/smp.h4
-rw-r--r--arch/s390/kernel/diag/diag324.c3
-rw-r--r--arch/s390/kernel/ipl.c12
-rw-r--r--arch/s390/kernel/perf_pai.c27
-rw-r--r--arch/s390/kernel/smp.c16
-rw-r--r--arch/s390/kernel/topology.c2
-rw-r--r--arch/s390/kernel/vtime.c6
-rw-r--r--arch/s390/mm/pgalloc.c89
-rw-r--r--arch/s390/pci/pci_sysfs.c3
-rw-r--r--block/bio.c9
-rw-r--r--block/genhd.c7
-rw-r--r--drivers/accel/amdxdna/aie2_message.c2
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.c4
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.h2
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.c10
-rw-r--r--drivers/accel/ethosu/ethosu_drv.c2
-rw-r--r--drivers/accel/ethosu/ethosu_gem.c2
-rw-r--r--drivers/accel/ethosu/ethosu_job.c10
-rw-r--r--drivers/accel/qaic/qaic_control.c46
-rw-r--r--drivers/acpi/scan.c4
-rw-r--r--drivers/ata/ahci.c49
-rw-r--r--drivers/ata/libata-scsi.c24
-rw-r--r--drivers/block/loop.c8
-rw-r--r--drivers/block/ublk_drv.c6
-rw-r--r--drivers/block/zloop.c8
-rw-r--r--drivers/bluetooth/btintel.c44
-rw-r--r--drivers/bluetooth/btintel_pcie.c3
-rw-r--r--drivers/bluetooth/hci_mrvl.c3
-rw-r--r--drivers/cpuidle/cpuidle-psci.c42
-rw-r--r--drivers/cpuidle/dt_idle_genpd.c3
-rw-r--r--drivers/dma-buf/dma-buf.c20
-rw-r--r--drivers/dma-buf/dma-heap.c80
-rw-r--r--drivers/edac/al_mc_edac.c12
-rw-r--r--drivers/edac/altera_edac.c127
-rw-r--r--drivers/edac/altera_edac.h1
-rw-r--r--drivers/edac/amd64_edac.c2
-rw-r--r--drivers/edac/aspeed_edac.c4
-rw-r--r--drivers/edac/debugfs.c65
-rw-r--r--drivers/edac/edac_device.c10
-rw-r--r--drivers/edac/edac_device_sysfs.c23
-rw-r--r--drivers/edac/edac_mc_sysfs.c2
-rw-r--r--drivers/edac/edac_module.h3
-rw-r--r--drivers/edac/highbank_mc_edac.c4
-rw-r--r--drivers/edac/ie31200_edac.c18
-rw-r--r--drivers/edac/igen6_edac.c471
-rw-r--r--drivers/edac/thunderx_edac.c4
-rw-r--r--drivers/edac/xgene_edac.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c8
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c13
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c12
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c6
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c8
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c31
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c34
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c5
-rw-r--r--drivers/gpu/drm/drm_atomic_state_helper.c7
-rw-r--r--drivers/gpu/drm/drm_atomic_uapi.c5
-rw-r--r--drivers/gpu/drm/drm_pagemap.c270
-rw-r--r--drivers/gpu/drm/drm_prime.c2
-rw-r--r--drivers/gpu/drm/gud/gud_connector.c12
-rw-r--r--drivers/gpu/drm/gud/gud_drv.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c10
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.c15
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c11
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy.c6
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c15
-rw-r--r--drivers/gpu/drm/i915/i915_pci.c3
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_chan.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dmem.c18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sgdma.c4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.c6
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c13
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c191
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h17
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c86
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c125
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c64
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c3
-rw-r--r--drivers/gpu/drm/sysfb/ofdrm.c8
-rw-r--r--drivers/gpu/drm/tegra/dc.c6
-rw-r--r--drivers/gpu/drm/tegra/hub.c2
-rw-r--r--drivers/gpu/drm/tiny/cirrus-qemu.c3
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c9
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.h21
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c21
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c6
-rw-r--r--drivers/gpu/drm/xe/xe_vram.c3
-rw-r--r--drivers/hid/Kconfig2
-rw-r--r--drivers/hid/bpf/hid_bpf_struct_ops.c23
-rw-r--r--drivers/hid/hid-hyperv.c4
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-multitouch.c19
-rw-r--r--drivers/hid/hid-rmi.c46
-rw-r--r--drivers/hid/i2c-hid/i2c-hid-core.c2
-rw-r--r--drivers/hid/wacom_wac.c13
-rw-r--r--drivers/md/dm-cache-target.c59
-rw-r--r--drivers/md/dm-crypt.c3
-rw-r--r--drivers/md/dm-ebs-target.c3
-rw-r--r--drivers/md/dm-integrity.c13
-rw-r--r--drivers/misc/fastrpc.c16
-rw-r--r--drivers/net/bonding/bond_alb.c15
-rw-r--r--drivers/net/bonding/bond_main.c18
-rw-r--r--drivers/net/bonding/bond_options.c4
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.h2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c3
-rw-r--r--drivers/net/ethernet/cadence/macb.h3
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c21
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.c33
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c7
-rw-r--r--drivers/net/ethernet/oa_tc6.c258
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c61
-rw-r--r--drivers/net/gtp.c5
-rw-r--r--drivers/net/ipvlan/ipvlan_main.c4
-rw-r--r--drivers/net/ntb_netdev.c47
-rw-r--r--drivers/net/ppp/ppp_async.c82
-rw-r--r--drivers/net/ppp/ppp_synctty.c83
-rw-r--r--drivers/net/usb/qmi_wwan.c1
-rw-r--r--drivers/net/vxlan/vxlan_mdb.c8
-rw-r--r--drivers/nvme/host/core.c99
-rw-r--r--drivers/nvme/host/fabrics.c2
-rw-r--r--drivers/nvme/host/fault_inject.c14
-rw-r--r--drivers/nvme/host/fc.c26
-rw-r--r--drivers/nvme/host/nvme.h2
-rw-r--r--drivers/nvme/host/rdma.c18
-rw-r--r--drivers/nvme/host/sysfs.c2
-rw-r--r--drivers/nvme/host/tcp.c12
-rw-r--r--drivers/nvme/host/zns.c4
-rw-r--r--drivers/nvme/target/auth.c6
-rw-r--r--drivers/nvme/target/configfs.c4
-rw-r--r--drivers/nvme/target/core.c9
-rw-r--r--drivers/nvme/target/nvmet.h2
-rw-r--r--drivers/nvme/target/pr.c2
-rw-r--r--drivers/nvme/target/rdma.c15
-rw-r--r--drivers/nvme/target/tcp.c10
-rw-r--r--drivers/pmdomain/mediatek/Kconfig5
-rw-r--r--drivers/pmdomain/qcom/rpmhpd.c4
-rw-r--r--drivers/ras/amd/atl/core.c1
-rw-r--r--drivers/ras/amd/atl/map.c6
-rw-r--r--drivers/s390/crypto/zcrypt_cca_key.h1
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.c15
-rw-r--r--drivers/s390/net/ctcm_mpc.c3
-rw-r--r--fs/ceph/mds_client.c4
-rw-r--r--fs/ceph/mds_client.h1
-rw-r--r--fs/ceph/super.c5
-rw-r--r--fs/configfs/mount.c4
-rw-r--r--fs/ntfs/attrib.c18
-rw-r--r--fs/ntfs/bdev-io.c2
-rw-r--r--fs/ntfs/bitmap.c8
-rw-r--r--fs/ntfs/compress.c2
-rw-r--r--fs/ntfs/ea.c49
-rw-r--r--fs/ntfs/file.c50
-rw-r--r--fs/ntfs/inode.c10
-rw-r--r--fs/ntfs/lcnalloc.c9
-rw-r--r--fs/ntfs/mft.c16
-rw-r--r--fs/ntfs/ntfs.h10
-rw-r--r--fs/ntfs/reparse.c7
-rw-r--r--fs/ntfs/super.c15
-rw-r--r--fs/ntfs/wof.c127
-rw-r--r--fs/quota/dquot.c2
-rw-r--r--fs/smb/client/cifssmb.c24
-rw-r--r--fs/smb/client/connect.c21
-rw-r--r--fs/smb/client/file.c36
-rw-r--r--fs/smb/client/smb2ops.c195
-rw-r--r--fs/smb/client/transport.c11
-rw-r--r--fs/smb/server/connection.c8
-rw-r--r--fs/smb/server/ksmbd_work.h2
-rw-r--r--fs/smb/server/mgmt/share_config.c40
-rw-r--r--fs/smb/server/mgmt/tree_connect.c8
-rw-r--r--fs/smb/server/mgmt/user_session.c13
-rw-r--r--fs/smb/server/mgmt/user_session.h1
-rw-r--r--fs/smb/server/oplock.c73
-rw-r--r--fs/smb/server/smb2pdu.c100
-rw-r--r--fs/smb/server/smbacl.c67
-rw-r--r--fs/smb/server/transport_ipc.c21
-rw-r--r--fs/smb/server/transport_tcp.c36
-rw-r--r--fs/smb/server/vfs.c12
-rw-r--r--fs/smb/server/vfs_cache.c17
-rw-r--r--fs/smb/server/vfs_cache.h1
-rw-r--r--fs/xfs/Makefile1
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.h6
-rw-r--r--fs/xfs/libxfs/xfs_sb.c37
-rw-r--r--fs/xfs/xfs_aops.c188
-rw-r--r--fs/xfs/xfs_aops.h1
-rw-r--r--fs/xfs/xfs_file.c2
-rw-r--r--fs/xfs/xfs_fsmap.c3
-rw-r--r--fs/xfs/xfs_icache.c16
-rw-r--r--fs/xfs/xfs_ioctl.c2
-rw-r--r--fs/xfs/xfs_ioend.c184
-rw-r--r--fs/xfs/xfs_ioend.h16
-rw-r--r--fs/xfs/xfs_iomap.c7
-rw-r--r--fs/xfs/xfs_iomap.h14
-rw-r--r--fs/xfs/xfs_iops.c2
-rw-r--r--fs/xfs/xfs_platform.h11
-rw-r--r--fs/xfs/xfs_super.c2
-rw-r--r--fs/xfs/xfs_trans_buf.c3
-rw-r--r--fs/xfs/xfs_zone_alloc.c66
-rw-r--r--fs/xfs/xfs_zone_gc.c5
-rw-r--r--include/acpi/acpi_bus.h11
-rw-r--r--include/drm/drm_pagemap.h8
-rw-r--r--include/linux/capability.h5
-rw-r--r--include/linux/cgroup-defs.h5
-rw-r--r--include/linux/device-id/ap.h2
-rw-r--r--include/linux/dma-buf.h1
-rw-r--r--include/linux/dma-fence-array.h1
-rw-r--r--include/linux/dma-fence-chain.h9
-rw-r--r--include/linux/edac.h3
-rw-r--r--include/linux/igmp.h7
-rw-r--r--include/linux/kprobes.h1
-rw-r--r--include/linux/nvme-tcp.h18
-rw-r--r--include/linux/sched/user.h3
-rw-r--r--include/linux/skbuff.h5
-rw-r--r--include/net/af_vsock.h3
-rw-r--r--include/net/if_inet6.h2
-rw-r--r--include/net/ip.h3
-rw-r--r--include/net/tcp.h3
-rw-r--r--include/trace/events/icmp.h13
-rw-r--r--include/uapi/drm/amdgpu_drm.h2
-rw-r--r--include/uapi/linux/magic.h1
-rw-r--r--kernel/capability.c19
-rw-r--r--kernel/cgroup/cgroup.c6
-rw-r--r--kernel/cgroup/cpuset.c39
-rw-r--r--kernel/kprobes.c14
-rw-r--r--kernel/sched/ext/ext.c47
-rw-r--r--kernel/sched/ext/internal.h25
-rw-r--r--kernel/trace/trace_btf.c31
-rw-r--r--kernel/trace/trace_btf.h3
-rw-r--r--kernel/trace/trace_probe.c63
-rw-r--r--kernel/trace/trace_probe.h2
-rw-r--r--kernel/trace/trace_uprobe.c4
-rw-r--r--kernel/workqueue.c30
-rw-r--r--lib/once.c2
-rw-r--r--mm/huge_memory.c2
-rw-r--r--mm/hugetlb.c20
-rw-r--r--mm/hugetlb_cgroup.c7
-rw-r--r--mm/hugetlb_cma.c21
-rw-r--r--mm/khugepaged.c6
-rw-r--r--mm/madvise.c8
-rw-r--r--mm/memcontrol-v1.c43
-rw-r--r--mm/mempolicy.c2
-rw-r--r--mm/migrate_device.c18
-rw-r--r--mm/mremap.c22
-rw-r--r--mm/secretmem.c116
-rw-r--r--mm/userfaultfd.c4
-rw-r--r--net/bluetooth/hci_core.c4
-rw-r--r--net/bluetooth/l2cap_core.c33
-rw-r--r--net/bluetooth/msft.c2
-rw-r--r--net/bridge/br_multicast.c13
-rw-r--r--net/ceph/messenger.c1
-rw-r--r--net/core/dev.c25
-rw-r--r--net/core/gro_cells.c2
-rw-r--r--net/core/page_pool.c3
-rw-r--r--net/core/sock.c3
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/igmp.c210
-rw-r--r--net/ipv4/tcp.c32
-rw-r--r--net/ipv4/tcp_cong.c4
-rw-r--r--net/ipv4/tcp_dctcp.c4
-rw-r--r--net/ipv4/tcp_minisocks.c2
-rw-r--r--net/ipv4/tcp_offload.c22
-rw-r--r--net/ipv4/tcp_output.c6
-rw-r--r--net/ipv4/tcp_timer.c6
-rw-r--r--net/ipv4/udp.c15
-rw-r--r--net/ipv6/exthdrs.c4
-rw-r--r--net/ipv6/ip6_gre.c6
-rw-r--r--net/ipv6/mcast.c148
-rw-r--r--net/ipv6/route.c2
-rw-r--r--net/ipv6/tcpv6_offload.c15
-rw-r--r--net/ipv6/udp.c13
-rw-r--r--net/iucv/af_iucv.c42
-rw-r--r--net/mac802154/ieee802154_i.h7
-rw-r--r--net/mac802154/main.c1
-rw-r--r--net/mac802154/scan.c51
-rw-r--r--net/mptcp/protocol.c3
-rw-r--r--net/mptcp/protocol.h2
-rw-r--r--net/packet/af_packet.c5
-rw-r--r--net/qrtr/af_qrtr.c66
-rw-r--r--net/qrtr/ns.c35
-rw-r--r--net/rds/connection.c89
-rw-r--r--net/rds/ib_recv.c9
-rw-r--r--net/rds/send.c14
-rw-r--r--net/rds/tcp.c101
-rw-r--r--net/rds/tcp_listen.c6
-rw-r--r--net/sched/act_api.c37
-rw-r--r--net/sched/cls_flower.c5
-rw-r--r--net/sched/cls_u32.c32
-rw-r--r--net/sctp/inqueue.c6
-rw-r--r--net/sctp/sm_make_chunk.c14
-rw-r--r--net/sctp/sm_sideeffect.c11
-rw-r--r--net/tipc/link.c6
-rw-r--r--net/tipc/name_table.c30
-rw-r--r--net/tipc/node.c2
-rw-r--r--net/vmw_vsock/af_vsock.c32
-rw-r--r--net/vmw_vsock/virtio_transport_common.c3
-rw-r--r--net/vmw_vsock/vmci_transport.c34
-rw-r--r--security/Kconfig.hardening2
-rw-r--r--security/integrity/ima/Kconfig6
-rw-r--r--security/integrity/ima/ima_appraise.c2
-rw-r--r--security/integrity/ima/ima_policy.c7
-rw-r--r--security/selinux/hooks.c36
-rw-r--r--sound/core/pcm_native.c37
-rw-r--r--sound/core/rawmidi.c2
-rw-r--r--sound/core/ump.c4
-rw-r--r--sound/drivers/dummy.c2
-rw-r--r--sound/hda/codecs/cirrus/cs420x.c2
-rw-r--r--sound/hda/codecs/conexant.c20
-rw-r--r--sound/hda/codecs/realtek/alc269.c39
-rw-r--r--sound/hda/core/device.c5
-rw-r--r--sound/parisc/harmony.c6
-rw-r--r--sound/usb/caiaq/audio.c10
-rw-r--r--sound/usb/fcp.c8
-rw-r--r--sound/usb/midi.c2
-rw-r--r--sound/usb/mixer_maps.c18
-rw-r--r--sound/usb/mixer_quirks.c8
-rw-r--r--sound/usb/mixer_s1810c.c8
-rw-r--r--sound/usb/mixer_scarlett.c4
-rw-r--r--sound/usb/mixer_scarlett2.c36
-rw-r--r--sound/usb/mixer_us16x08.c7
-rw-r--r--tools/sched_ext/include/scx/common.bpf.h140
-rw-r--r--tools/sched_ext/include/scx/compat.bpf.h68
-rw-r--r--tools/sched_ext/include/scx/compat.h97
-rw-r--r--tools/sched_ext/include/scx/enum_defs.autogen.h5
-rw-r--r--tools/sched_ext/include/scx/enums_abi.autogen.h223
-rw-r--r--tools/sched_ext/scx_central.bpf.c6
-rw-r--r--tools/sched_ext/scx_flatcg.bpf.c2
-rw-r--r--tools/sched_ext/scx_qmap.bpf.c9
-rw-r--r--tools/testing/selftests/alsa/mixer-test.c4
-rw-r--r--tools/testing/selftests/cgroup/test_core.c2
-rw-r--r--tools/testing/selftests/cgroup/test_cpu.c2
-rw-r--r--tools/testing/selftests/cgroup/test_cpuset.c2
-rwxr-xr-xtools/testing/selftests/cgroup/test_cpuset_prs.sh64
-rw-r--r--tools/testing/selftests/cgroup/test_freezer.c2
-rw-r--r--tools/testing/selftests/cgroup/test_kill.c57
-rw-r--r--tools/testing/selftests/cgroup/test_kmem.c4
-rw-r--r--tools/testing/selftests/cgroup/test_memcontrol.c2
-rw-r--r--tools/testing/selftests/cgroup/test_pids.c2
-rw-r--r--tools/testing/selftests/cgroup/test_zswap.c2
-rw-r--r--tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c113
-rw-r--r--tools/testing/selftests/hid/hid_bpf.c51
-rw-r--r--tools/testing/selftests/hid/progs/hid.c26
-rw-r--r--tools/testing/selftests/hid/progs/hid_bpf_helpers.h3
-rw-r--r--tools/testing/selftests/mm/memfd_secret.c30
-rw-r--r--tools/testing/selftests/net/Makefile1
-rwxr-xr-xtools/testing/selftests/net/exception_cache.sh521
-rwxr-xr-xtools/testing/selftests/net/test_vxlan_mdb.sh6
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/filters/u32.json23
-rw-r--r--tools/workqueue/wq_dump.py10
395 files changed, 6534 insertions, 2631 deletions
diff --git a/.mailmap b/.mailmap
index 6803f3bd2865..37aad399e4e0 100644
--- a/.mailmap
+++ b/.mailmap
@@ -19,7 +19,8 @@ Abhinav Kumar <quic_abhinavk@quicinc.com> <abhinavk@codeaurora.org>
Ahmad Masri <quic_amasri@quicinc.com> <amasri@codeaurora.org>
Adam Oldham <oldhamca@gmail.com>
Adam Radford <aradford@gmail.com>
-Aditya Garg <gargaditya08@proton.me> <gargaditya08@live.com>
+Aditya Garg <aditya.garg@linux.dev> <gargaditya08@live.com>
+Aditya Garg <aditya.garg@linux.dev> <gargaditya08@proton.me>
Adriana Reus <adi.reus@gmail.com> <adriana.reus@intel.com>
Adrian Bunk <bunk@stusta.de>
Ajay Kaher <ajay.kaher@broadcom.com> <akaher@vmware.com>
@@ -699,6 +700,7 @@ Oliver Hartkopp <socketcan@hartkopp.net> <oliver.hartkopp@volkswagen.de>
Oliver Hartkopp <socketcan@hartkopp.net> <oliver@hartkopp.net>
Oliver Upton <oupton@kernel.org> <oupton@google.com>
Oliver Upton <oupton@kernel.org> <oliver.upton@linux.dev>
+Ondrej Mosnáček <omosnacek@gmail.com> <omosnace@redhat.com>
Ondřej Jirman <megi@xff.cz> <megous@megous.com>
Oza Pawandeep <quic_poza@quicinc.com> <poza@codeaurora.org>
Pali Rohár <pali@kernel.org> <pali.rohar@gmail.com>
@@ -898,7 +900,8 @@ Thomas Graf <tgraf@suug.ch>
Thomas Gleixner <tglx@kernel.org> <tglx@linutronix.de>
Thomas Körper <socketcan@esd.eu> <thomas.koerper@esd.eu>
Thomas Pedersen <twp@codeaurora.org>
-Thorsten Blum <thorsten.blum@linux.dev> <thorsten.blum@toblux.com>
+Thorsten Blum <blum@kernel.org> <thorsten.blum@toblux.com>
+Thorsten Blum <blum@kernel.org> <thorsten.blum@linux.dev>
Tiezhu Yang <yangtiezhu@loongson.cn> <kernelpatch@126.com>
Tingwei Zhang <quic_tingwei@quicinc.com> <tingwei@codeaurora.org>
Tirupathi Reddy <quic_tirupath@quicinc.com> <tirupath@codeaurora.org>
diff --git a/CREDITS b/CREDITS
index a1455b471051..bba5071c29a5 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1512,6 +1512,10 @@ N: Andy Gross
E: agross@kernel.org
D: Qualcomm SoC subsystem and drivers
+N: Mark Gross
+E: markgross@kernel.org
+D: x86/mellanox platform maintenance and various x86 specific drivers
+
N: Grant Grundler
E: grantgrundler@gmail.com
W: http://obmouse.sourceforge.net/
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index 19258471b7b2..b8a763e4c9fb 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -108,6 +108,9 @@ Description:
# NSFS_MAGIC
dont_measure fsmagic=0x6e736673
dont_appraise fsmagic=0x6e736673
+ # CONFIGFS_MAGIC
+ dont_measure fsmagic=0x62656570
+ dont_appraise fsmagic=0x62656570
measure func=BPRM_CHECK
measure func=FILE_MMAP mask=MAY_EXEC
diff --git a/Documentation/admin-guide/cgroup-v1/memory.rst b/Documentation/admin-guide/cgroup-v1/memory.rst
index 7db63c002922..7d2a44af52c9 100644
--- a/Documentation/admin-guide/cgroup-v1/memory.rst
+++ b/Documentation/admin-guide/cgroup-v1/memory.rst
@@ -47,7 +47,6 @@ Features:
- pages are linked to per-memcg LRU exclusively, and there is no global LRU.
- optionally, memory+swap usage can be accounted and limited.
- hierarchical accounting
- - soft limit
- moving (recharging) account at moving a task is selectable.
- usage threshold notifier
- memory pressure notifier
@@ -76,10 +75,9 @@ Brief summary of control files.
memory.memsw.failcnt show the number of memory+Swap hits limits
memory.max_usage_in_bytes show max memory usage recorded
memory.memsw.max_usage_in_bytes show max memory+Swap usage recorded
- memory.soft_limit_in_bytes set/show soft limit of memory usage
- This knob is not available on CONFIG_PREEMPT_RT systems.
- This knob is deprecated and shouldn't be
- used.
+ memory.soft_limit_in_bytes This knob is deprecated and has no effect.
+ Writes are ignored and reads always
+ return the maximum value.
memory.stat show various statistics
memory.use_hierarchy set/show hierarchical account enabled
This knob is deprecated and shouldn't be
@@ -340,9 +338,6 @@ memory.kmem.usage_in_bytes, or in a separate counter when it makes sense.
The main "kmem" counter is fed into the main counter, so kmem charges will
also be visible from the user counter.
-Currently no soft limit is implemented for kernel memory. It is future work
-to trigger slab reclaim when those limits are reached.
-
2.7.1 Current Kernel Memory resources accounted
-----------------------------------------------
@@ -710,42 +705,10 @@ For compatibility reasons writing 1 to memory.use_hierarchy will always pass::
THIS IS DEPRECATED!
-Soft limits allow for greater sharing of memory. The idea behind soft limits
-is to allow control groups to use as much of the memory as needed, provided
-
-a. There is no memory contention
-b. They do not exceed their hard limit
-
-When the system detects memory contention or low memory, control groups
-are pushed back to their soft limits. If the soft limit of each control
-group is very high, they are pushed back as much as possible to make
-sure that one control group does not starve the others of memory.
-
-Please note that soft limits is a best-effort feature; it comes with
-no guarantees, but it does its best to make sure that when memory is
-heavily contended for, memory is allocated based on the soft limit
-hints/setup. Currently soft limit based reclaim is set up such that
-it gets invoked from balance_pgdat (kswapd).
-
-7.1 Interface
--------------
-
-Soft limits can be setup by using the following commands (in this example we
-assume a soft limit of 256 MiB)::
-
- # echo 256M > memory.soft_limit_in_bytes
-
-If we want to change this to 1G, we can at any time use::
+Writing to memory.soft_limit_in_bytes has no effect and reading it will
+always return the maximum value.
- # echo 1G > memory.soft_limit_in_bytes
-
-.. note::
- Soft limits take effect over a long period of time, since they involve
- reclaiming memory for balancing between memory cgroups
-
-.. note::
- It is recommended to set the soft limit always below the hard limit,
- otherwise the hard limit will take precedence.
+Use memory.low and memory.min in cgroup v2 instead.
.. _cgroup-v1-memory-move-charges:
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 7c2a8ed80071..86a2a0099178 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -1130,9 +1130,9 @@ policy and the underlying scheduler. From the point of view of the cpu controlle
processes can be categorized as follows:
* Processes under the fair-class scheduler
-* Processes under a BPF scheduler with the ``cgroup_set_weight`` callback
+* Processes under a BPF scheduler with the corresponding ``cgroup_set_*`` callback
* Everything else: ``SCHED_{FIFO,RR,DEADLINE}`` and processes under a BPF scheduler
- without the ``cgroup_set_weight`` callback
+ without the corresponding ``cgroup_set_*`` callback
For details on when a process is under the fair-class scheduler or a BPF scheduler,
check out :ref:`Documentation/scheduler/sched-ext.rst <sched-ext>`.
@@ -1223,7 +1223,9 @@ will be referred to. All time durations are in microseconds.
$PERIOD duration. "max" for $MAX indicates no limit. If only
one number is written, $MAX is updated.
- This file affects only processes under the fair-class scheduler.
+ This file affects only processes under the fair-class scheduler and a BPF
+ scheduler with the ``cgroup_set_bandwidth`` callback depending on what
+ the callback actually does.
cpu.max.burst
A read-write single value file which exists on non-root
@@ -1231,7 +1233,9 @@ will be referred to. All time durations are in microseconds.
The burst in the range [0, $MAX].
- This file affects only processes under the fair-class scheduler.
+ This file affects only processes under the fair-class scheduler and a BPF
+ scheduler with the ``cgroup_set_bandwidth`` callback depending on what
+ the callback actually does.
cpu.pressure
A read-write nested-keyed file.
@@ -1283,7 +1287,9 @@ will be referred to. All time durations are in microseconds.
own relative priorities, but the cgroup itself will be treated as
very low priority relative to its peers.
- This file affects only processes under the fair-class scheduler.
+ This file affects only processes under the fair-class scheduler and a BPF
+ scheduler with the ``cgroup_set_idle`` callback depending on what the
+ callback actually does.
Memory
------
@@ -3064,7 +3070,7 @@ resources (res_a and res_b) are registered then:
change in this file generates a file modified event. All fields in
this file are hierarchical.
- max
+ <res>.max
The number of times the cgroup's resource usage was
about to go over the max boundary.
diff --git a/Documentation/fault-injection/nvme-fault-injection.rst b/Documentation/fault-injection/nvme-fault-injection.rst
index 1d4427890d75..09730acf0163 100644
--- a/Documentation/fault-injection/nvme-fault-injection.rst
+++ b/Documentation/fault-injection/nvme-fault-injection.rst
@@ -176,3 +176,68 @@ Message from dmesg::
secondary_startup_64+0xa4/0xb0
nvme nvme0: Could not set queue count (16385)
nvme nvme0: IO queues not created
+
+Example 4: Inject an error into the first write command
+-------------------------------------------------------
+
+::
+
+ echo 0x01 > /sys/kernel/debug/nvme0n1/fault_inject/opcode
+ echo 1 > /sys/kernel/debug/nvme0n1/fault_inject/times
+ echo 100 > /sys/kernel/debug/nvme0n1/fault_inject/probability
+ dd if=/dev/zero of=/dev/nvme0n1 oflag=direct bs=512 count=1
+
+Expected Result::
+
+ The first write command sent to nvme0n1 fails
+
+Message from dmesg::
+
+ FAULT_INJECTION: forcing a failure.
+ name fault_inject, interval 1, probability 100, space 0, times 1
+ CPU: 4 UID: 0 PID: 0 Comm: swapper/4 Not tainted 7.1.0+ #5 PREEMPT(full)
+ Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-20240910_120124-localhost 04/01/2014
+ Call Trace:
+ <IRQ>
+ dump_stack_lvl+0x6e/0xa0
+ dump_stack+0x10/0x16
+ should_fail_ex+0x461/0x510
+ should_fail+0xb/0x20
+ nvme_should_fail+0x11b/0x240 [nvme_core]
+ nvme_poll_cq+0x6ad/0xb30 [nvme]
+ nvme_irq+0x84/0xe0 [nvme]
+ ? __pfx_nvme_irq+0x10/0x10 [nvme]
+ ? rcu_core+0xa40/0xa90
+ ? __pfx_sched_balance_softirq+0x10/0x10
+ ? debug_smp_processor_id+0x17/0x20
+ ? rcu_is_watching+0x13/0xa0
+ __handle_irq_event_percpu+0x396/0x610
+ handle_irq_event_percpu+0xf/0x90
+ handle_irq_event+0xab/0x110
+ handle_edge_irq+0x1a3/0x210
+ __common_interrupt+0xff/0x170
+ common_interrupt+0x90/0xc0
+ </IRQ>
+ <TASK>
+ asm_common_interrupt+0x27/0x40
+ RIP: 0010:pv_native_safe_halt+0x13/0x20
+ Code: 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 8b 05 0a 2a 58 01 85 c0 7e 07 0f 00 2d ff cc 0d 00 fb f4 <c3> cc 0
+ RSP: 0018:ffff888100a67e40 EFLAGS: 00000242
+ RAX: 0000000000000001 RBX: ffff888100a49c40 RCX: ffffed102b6c645b
+ RDX: ffffed102b6c645b RSI: ffffffff82a0d3c0 RDI: ffffffff81428b9b
+ RBP: ffff888100a67e48 R08: ffffed102b6c645b R09: 0000000000000004
+ R10: ffffed102b6c645a R11: 0000000000000001 R12: 0000000000000000
+ R13: 0000000000000000 R14: ffffed1020149388 R15: dffffc0000000000
+ ? do_idle+0x19b/0x2c0
+ ? default_idle+0x9/0x20
+ arch_cpu_idle+0x9/0x10
+ default_idle_call+0x6b/0xa0
+ do_idle+0x19b/0x2c0
+ ? __pfx_do_idle+0x10/0x10
+ ? complete_with_flags+0x63/0x70
+ cpu_startup_entry+0x55/0x60
+ start_secondary+0x1df/0x1e0
+ common_startup_64+0x13e/0x158
+ </TASK>
+ nvme0n1: Write(0x1) @ LBA 0, 1 blocks, Invalid Command Opcode (sct 0x0 / sc 0x1) DNR
+ operation not supported error, dev nvme0n1, sector 0 op 0x1:(WRITE) flags 0x8800 phys_seg 1 prio class 2
diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml
index db7cddcda50a..b1eb102ab843 100644
--- a/Documentation/netlink/specs/conntrack.yaml
+++ b/Documentation/netlink/specs/conntrack.yaml
@@ -361,6 +361,17 @@ attribute-sets:
type: u32
byte-order: big-endian
-
+ name: filter-attrs
+ attributes:
+ -
+ name: orig-flags
+ type: u32
+ doc: bitmask of tuple fields to filter on, original direction
+ -
+ name: reply-flags
+ type: u32
+ doc: bitmask of tuple fields to filter on, reply direction
+ -
name: conntrack-attrs
attributes:
-
@@ -466,7 +477,7 @@ attribute-sets:
-
name: filter
type: nest
- nested-attributes: tuple-attrs
+ nested-attributes: filter-attrs
-
name: status-mask
type: u32
@@ -591,10 +602,14 @@ operations:
request:
value: 0x101
attributes:
- - mark
- - filter
+ - tuple-orig
+ - tuple-reply
- status
+ - mark
- zone
+ - mark-mask
+ - filter
+ - status-mask
reply:
value: 0x100
attributes:
diff --git a/Documentation/scheduler/sched-ext.rst b/Documentation/scheduler/sched-ext.rst
index 0e97fd019994..794ae80b3ba3 100644
--- a/Documentation/scheduler/sched-ext.rst
+++ b/Documentation/scheduler/sched-ext.rst
@@ -230,7 +230,7 @@ optional. The following modified excerpt is from
void BPF_STRUCT_OPS(simple_exit, struct scx_exit_info *ei)
{
- exit_type = ei->type;
+ exit_type = ei->kind;
}
SEC(".struct_ops")
@@ -242,6 +242,21 @@ optional. The following modified excerpt is from
.name = "simple",
};
+Scheduler-Dependent Knobs
+-------------------------
+
+The fair-class scheduler enforces CPU controller settings such as
+``cpu.max``, ``cpu.weight`` and ``cpu.idle``. For sched_ext tasks, the
+scheduler core communicates these settings to the BPF scheduler
+through ``ops.cgroup_init()`` and reports subsequent changes through
+the corresponding ``ops.cgroup_set_*()`` callbacks. Similarly, per-task
+nice changes are converted to weights and reported through
+``ops.set_weight()``.
+
+Each BPF scheduler is responsible for implementing the scheduling
+semantics of these settings and may choose to ignore them. Consult the
+loaded scheduler's documentation before relying on these controls.
+
Dispatch Queues
---------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..c9f866debdf0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8039,7 +8039,7 @@ F: drivers/gpu/drm/sun4i/sun8i*
DRM DRIVER FOR APPLE TOUCH BARS
M: Aun-Ali Zaidi <admin@kodeit.net>
-M: Aditya Garg <gargaditya08@proton.me>
+M: Aditya Garg <aditya.garg@linux.dev>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
@@ -9387,9 +9387,8 @@ S: Maintained
F: drivers/edac/octeon_edac*
EDAC-CAVIUM THUNDERX
-M: Robert Richter <rric@kernel.org>
L: linux-edac@vger.kernel.org
-S: Odd Fixes
+S: Orphan
F: drivers/edac/thunderx_edac*
EDAC-CORE
@@ -9416,9 +9415,8 @@ S: Supported
F: drivers/edac/dmc520_edac.c
EDAC-E752X
-M: Mark Gross <markgross@kernel.org>
L: linux-edac@vger.kernel.org
-S: Maintained
+S: Orphan
F: drivers/edac/e752x_edac.c
EDAC-E7XXX
@@ -9493,9 +9491,8 @@ S: Maintained
F: drivers/edac/igen6_edac.c
EDAC-MPC85XX
-M: Johannes Thumshirn <morbidrsa@gmail.com>
L: linux-edac@vger.kernel.org
-S: Maintained
+S: Orphan
F: drivers/edac/mpc85xx_edac.[ch]
EDAC-NPCM
@@ -12224,7 +12221,7 @@ F: drivers/tty/hvc/
HUNG TASK DETECTOR
M: Andrew Morton <akpm@linux-foundation.org>
-R: Lance Yang <lance.yang@linux.dev>
+M: Lance Yang <lance.yang@linux.dev>
R: Masami Hiramatsu <mhiramat@kernel.org>
R: Petr Mladek <pmladek@suse.com>
L: linux-kernel@vger.kernel.org
@@ -13283,7 +13280,6 @@ F: drivers/crypto/intel/keembay/ocs-aes.h
INTEL KEEM BAY OCS ECC CRYPTO DRIVER
M: Prabhjot Khurana <prabhjot.khurana@intel.com>
-M: Mark Gross <mgross@linux.intel.com>
S: Maintained
F: Documentation/devicetree/bindings/crypto/intel,keembay-ocs-ecc.yaml
F: drivers/crypto/intel/keembay/Kconfig
@@ -14240,8 +14236,6 @@ F: tools/testing/selftests/
KERNEL SMB3 SERVER (KSMBD)
M: Namjae Jeon <linkinjeon@kernel.org>
M: Namjae Jeon <linkinjeon@samba.org>
-M: Steve French <smfrench@gmail.com>
-M: Steve French <sfrench@samba.org>
R: Sergey Senozhatsky <senozhatsky@chromium.org>
R: Tom Talpey <tom@talpey.com>
R: ChenXiaoSong <chenxiaosong@chenxiaosong.com>
@@ -17416,7 +17410,6 @@ F: mm/swapfile.c
MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE)
M: Andrew Morton <akpm@linux-foundation.org>
M: David Hildenbrand <david@kernel.org>
-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>
@@ -17426,6 +17419,7 @@ R: Dev Jain <dev.jain@arm.com>
R: Barry Song <baohua@kernel.org>
R: Lance Yang <lance.yang@linux.dev>
R: Usama Arif <usama.arif@linux.dev>
+R: Kiryl Shutsemau <kas@kernel.org>
L: linux-mm@kvack.org
S: Maintained
W: http://www.linux-mm.org
@@ -17698,7 +17692,7 @@ F: Documentation/devicetree/bindings/serial/atmel,at91-usart.yaml
F: drivers/spi/spi-at91-usart.c
MICROCHIP ATSHA204A DRIVER
-M: Thorsten Blum <thorsten.blum@linux.dev>
+M: Thorsten Blum <blum@kernel.org>
L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/crypto/atmel-sha204a.c
@@ -17722,7 +17716,7 @@ F: Documentation/devicetree/bindings/media/microchip,csi2dc.yaml
F: drivers/media/platform/microchip/microchip-csi2dc.c
MICROCHIP ECC DRIVER
-M: Thorsten Blum <thorsten.blum@linux.dev>
+M: Thorsten Blum <blum@kernel.org>
L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/crypto/atmel-ecc.c
@@ -19569,7 +19563,7 @@ F: include/linux/platform_data/x86/nvidia-wmi-ec-backlight.h
NVM EXPRESS DRIVER
M: Keith Busch <kbusch@kernel.org>
-M: Jens Axboe <axboe@fb.com>
+M: Jens Axboe <axboe@kernel.dk>
M: Christoph Hellwig <hch@lst.de>
M: Sagi Grimberg <sagi@grimberg.me>
L: linux-nvme@lists.infradead.org
@@ -24778,7 +24772,7 @@ K: \bsecurity_[a-z_0-9]\+\b
SELINUX SECURITY MODULE
M: Paul Moore <paul@paul-moore.com>
M: Stephen Smalley <stephen.smalley.work@gmail.com>
-R: Ondrej Mosnacek <omosnace@redhat.com>
+R: Ondrej Mosnáček <omosnacek@gmail.com>
L: selinux@vger.kernel.org
S: Supported
W: https://github.com/SELinuxProject
@@ -25236,10 +25230,9 @@ F: Documentation/admin-guide/LSM/Smack.rst
F: security/smack/
SMBDIRECT (RDMA Stream Transport with Read/Write-Offload, MS-SMBD)
-M: Steve French <smfrench@gmail.com>
-M: Steve French <sfrench@samba.org>
M: Namjae Jeon <linkinjeon@kernel.org>
M: Namjae Jeon <linkinjeon@samba.org>
+M: Paulo Alcantara <pc@manguebit.org>
R: Stefan Metzmacher <metze@samba.org>
R: Tom Talpey <tom@talpey.com>
L: linux-cifs@vger.kernel.org
@@ -25410,7 +25403,7 @@ F: drivers/md/md*
F: drivers/md/raid*
F: include/linux/raid/
F: include/uapi/linux/raid/
-F: lib/raid/raid6/
+F: lib/raid/
SOLIDRUN CLEARFOG SUPPORT
M: Russell King <linux@armlinux.org.uk>
@@ -26954,8 +26947,7 @@ S: Maintained
F: drivers/net/ethernet/tehuti/tn40*
TELECOM CLOCK DRIVER FOR MCPL0010
-M: Mark Gross <markgross@kernel.org>
-S: Supported
+S: Orphan
F: drivers/char/tlclk.c
TEMPO SEMICONDUCTOR DRIVERS
@@ -29937,12 +29929,14 @@ F: include/uapi/linux/xilinx-v4l2-controls.h
XILINX VERSAL EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
+R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-ddrmc-edac.yaml
F: drivers/edac/versal_edac.c
XILINX VERSALNET EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
+R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,versal-net-ddrmc5.yaml
F: drivers/edac/versalnet_edac.c
@@ -29988,6 +29982,7 @@ F: include/dt-bindings/dma/xlnx-zynqmp-dpdma.h
XILINX ZYNQMP OCM EDAC DRIVER
M: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
M: Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>
+R: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/memory-controllers/xlnx,zynqmp-ocmc-1.0.yaml
F: drivers/edac/zynqmp_edac.c
diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
index 71493b760b83..3c008821219c 100644
--- a/arch/arm64/include/asm/cpu.h
+++ b/arch/arm64/include/asm/cpu.h
@@ -78,5 +78,6 @@ void __init cpuinfo_store_boot_cpu(void);
void __init init_cpu_features(struct cpuinfo_arm64 *info);
void update_cpu_features(int cpu, struct cpuinfo_arm64 *info,
struct cpuinfo_arm64 *boot);
+bool gmid_el1_accessible(const struct cpuinfo_arm64 *info);
#endif /* __ASM_CPU_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 7404a6e83a93..4f04ad82ea34 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -627,13 +627,6 @@ static inline bool id_aa64pfr1_mpamfrac(u64 pfr1)
return val > 0;
}
-static inline bool id_aa64pfr1_mte(u64 pfr1)
-{
- u32 val = cpuid_feature_extract_unsigned_field(pfr1, ID_AA64PFR1_EL1_MTE_SHIFT);
-
- return val >= ID_AA64PFR1_EL1_MTE_MTE2;
-}
-
void __init setup_boot_cpu_features(void);
void __init setup_system_features(void);
void __init setup_user_features(void);
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 5db8f0619e4b..b33dccfafaf8 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -82,7 +82,7 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry, int scope)
for (i = 0; i < target_impl_cpu_num; i++) {
if (__is_affected_midr_range(entry, target_impl_cpus[i].midr,
- target_impl_cpus[i].midr))
+ target_impl_cpus[i].revidr))
return true;
}
return false;
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 17b83a2518a8..32102c3912fa 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -1178,6 +1178,33 @@ static bool detect_ftr_has_mpam(void)
return id_aa64pfr0_mpam(pfr0) || id_aa64pfr1_mpamfrac(pfr1);
}
+bool gmid_el1_accessible(const struct cpuinfo_arm64 *info)
+{
+ const struct arm64_ftr_bits *ftrp;
+ s64 mte, ovr;
+ u64 ftr_mask;
+
+ /* No ID register reflects CONFIG_ARM64_MTE. */
+ if (!IS_ENABLED(CONFIG_ARM64_MTE))
+ return false;
+
+ for (ftrp = ftr_id_aa64pfr1; ftrp->width; ftrp++) {
+ if (ftrp->shift == ID_AA64PFR1_EL1_MTE_SHIFT)
+ break;
+ }
+
+ ftr_mask = arm64_ftr_mask(ftrp);
+ mte = arm64_ftr_value(ftrp, info->reg_id_aa64pfr1);
+
+ /* The boot CPU runs before init_cpu_ftr_reg() strips unsafe overrides. */
+ if ((id_aa64pfr1_override.mask & ftr_mask) == ftr_mask) {
+ ovr = arm64_ftr_value(ftrp, id_aa64pfr1_override.val);
+ mte = arm64_ftr_safe_value(ftrp, ovr, mte);
+ }
+
+ return mte >= ID_AA64PFR1_EL1_MTE_MTE2;
+}
+
void __init init_cpu_features(struct cpuinfo_arm64 *info)
{
/* Before we start using the tables, make sure it is sorted */
@@ -1230,7 +1257,7 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
init_cpu_ftr_reg(SYS_MPAMIDR_EL1, info->reg_mpamidr);
}
- if (id_aa64pfr1_mte(info->reg_id_aa64pfr1))
+ if (gmid_el1_accessible(info))
init_cpu_ftr_reg(SYS_GMID_EL1, info->reg_gmid);
}
@@ -1492,11 +1519,9 @@ void update_cpu_features(int cpu,
* they read/write depends on the GMID_EL1.BS field. Check that the
* value is the same on all CPUs.
*/
- if (IS_ENABLED(CONFIG_ARM64_MTE) &&
- id_aa64pfr1_mte(info->reg_id_aa64pfr1)) {
+ if (gmid_el1_accessible(info))
taint |= check_update_ftr_reg(SYS_GMID_EL1, cpu,
info->reg_gmid, boot->reg_gmid);
- }
/*
* If we don't have AArch32 at all then skip the checks entirely
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index d50e2a9b066b..45c63f3d75c5 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -502,7 +502,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
info->reg_id_aa64smfr0 = read_cpuid(ID_AA64SMFR0_EL1);
info->reg_id_aa64fpfr0 = read_cpuid(ID_AA64FPFR0_EL1);
- if (id_aa64pfr1_mte(info->reg_id_aa64pfr1))
+ if (gmid_el1_accessible(info))
info->reg_gmid = read_cpuid(GMID_EL1);
if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0))
diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c
index c5693a32e49b..8f9bc2327dc8 100644
--- a/arch/arm64/kernel/machine_kexec.c
+++ b/arch/arm64/kernel/machine_kexec.c
@@ -129,7 +129,8 @@ int machine_kexec_post_load(struct kimage *kimage)
}
/* Create a copy of the linear map */
- rc = trans_pgd_create_copy(&info, &trans_pgd, PAGE_OFFSET, PAGE_END);
+ rc = trans_pgd_create_copy(&info, &trans_pgd,
+ _PAGE_OFFSET(vabits_actual), PAGE_END);
if (rc)
return rc;
kimage->arch.ttbr1 = __pa(trans_pgd);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 0b52557652be..75c3e463df2e 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -16,6 +16,7 @@
#include <linux/mm.h>
#include <linux/hardirq.h>
#include <linux/init.h>
+#include <linux/irqflags.h>
#include <linux/kasan.h>
#include <linux/kprobes.h>
#include <linux/uaccess.h>
@@ -154,6 +155,9 @@ static void show_pte(unsigned long addr)
pr_alert("%s pgtable: %luk pages, %llu-bit VAs, pgdp=%016lx\n",
mm == &init_mm ? "swapper" : "user", PAGE_SIZE / SZ_1K,
vabits_actual, mm_to_pgd_phys(mm));
+
+ guard(irqsave)();
+
pgdp = pgd_offset(mm, addr);
pgd = READ_ONCE(*pgdp);
pr_alert("[%016lx] pgd=%016llx", addr, pgd_val(pgd));
@@ -167,25 +171,25 @@ static void show_pte(unsigned long addr)
if (pgd_none(pgd) || pgd_bad(pgd))
break;
- p4dp = p4d_offset(pgdp, addr);
+ p4dp = p4d_offset_lockless(pgdp, pgd, addr);
p4d = READ_ONCE(*p4dp);
pr_cont(", p4d=%016llx", p4d_val(p4d));
if (p4d_none(p4d) || p4d_bad(p4d))
break;
- pudp = pud_offset(p4dp, addr);
+ pudp = pud_offset_lockless(p4dp, p4d, addr);
pud = READ_ONCE(*pudp);
pr_cont(", pud=%016llx", pud_val(pud));
if (pud_none(pud) || pud_bad(pud))
break;
- pmdp = pmd_offset(pudp, addr);
+ pmdp = pmd_offset_lockless(pudp, pud, addr);
pmd = READ_ONCE(*pmdp);
pr_cont(", pmd=%016llx", pmd_val(pmd));
if (pmd_none(pmd) || pmd_bad(pmd))
break;
- ptep = pte_offset_map(pmdp, addr);
+ ptep = pte_offset_map(&pmd, addr);
if (!ptep)
break;
diff --git a/arch/s390/boot/alternative.c b/arch/s390/boot/alternative.c
index 19ea7934b918..77e8bad560c5 100644
--- a/arch/s390/boot/alternative.c
+++ b/arch/s390/boot/alternative.c
@@ -45,11 +45,12 @@ static void alt_debug_modify(int type, unsigned int nr, bool clear)
static char *alt_debug_parse(int type, char *str)
{
- unsigned long val, endval;
+ unsigned long val, endval, limit;
char *endp;
bool clear;
int i;
+ limit = type == ALT_TYPE_FACILITY ? MAX_FACILITY_BIT : MAX_MFEATURE_BIT;
if (*str == ':') {
str++;
} else {
@@ -73,7 +74,7 @@ static char *alt_debug_parse(int type, char *str)
if (str == endp)
break;
str = endp;
- while (val <= endval) {
+ while (val <= endval && val < limit) {
alt_debug_modify(type, val, clear);
val++;
}
diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c
index 6bc950b92be7..c1b43e5e688a 100644
--- a/arch/s390/boot/ipl_parm.c
+++ b/arch/s390/boot/ipl_parm.c
@@ -23,6 +23,7 @@ struct parmarea parmarea __section(".parmarea") = {
};
char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
+static char command_line_buf[COMMAND_LINE_SIZE];
unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;
struct ipl_parameter_block __bootdata_preserved(ipl_block);
@@ -135,31 +136,29 @@ out:
static void append_ipl_block_parm(void)
{
- char *parm, *delim;
- size_t len, rc = 0;
+ size_t len, extra = 0;
+ char *delim;
len = strlen(early_command_line);
-
- delim = early_command_line + len; /* '\0' character position */
- parm = early_command_line + len + 1; /* append right after '\0' */
+ delim = early_command_line + len; /* '\0' character position */
switch (ipl_block.pb0_hdr.pbt) {
case IPL_PBT_CCW:
- rc = ipl_block_get_ascii_vmparm(
- parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
+ extra = ipl_block_get_ascii_vmparm(command_line_buf, sizeof(command_line_buf), &ipl_block);
break;
case IPL_PBT_FCP:
case IPL_PBT_NVME:
case IPL_PBT_ECKD:
- rc = ipl_block_get_ascii_scpdata(
- parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
+ extra = ipl_block_get_ascii_scpdata(command_line_buf, sizeof(command_line_buf), &ipl_block);
break;
}
- if (rc) {
- if (*parm == '=')
- memmove(early_command_line, parm + 1, rc);
- else
+ if (extra) {
+ if (command_line_buf[0] == '=') {
+ memmove(early_command_line, command_line_buf + 1, extra);
+ } else if (len < COMMAND_LINE_SIZE - 2) {
*delim = ' '; /* replace '\0' with space */
+ sized_strscpy(delim + 1, command_line_buf, COMMAND_LINE_SIZE - len - 1);
+ }
}
}
@@ -231,7 +230,7 @@ static void modify_fac_list(char *str)
if (str == endp)
break;
str = endp;
- while (val <= endval) {
+ while (val <= endval && val < MAX_FACILITY_BIT) {
modify_facility(val, clear);
val++;
}
@@ -245,7 +244,6 @@ static void modify_fac_list(char *str)
check_cleared_facilities();
}
-static char command_line_buf[COMMAND_LINE_SIZE];
void parse_boot_command_line(void)
{
char *param, *val;
diff --git a/arch/s390/boot/physmem_info.c b/arch/s390/boot/physmem_info.c
index 1f2ca5435838..0ebb2174713f 100644
--- a/arch/s390/boot/physmem_info.c
+++ b/arch/s390/boot/physmem_info.c
@@ -141,7 +141,7 @@ static int tprot(unsigned long addr)
static unsigned long search_mem_end(void)
{
- unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */
+ unsigned long range = 1UL << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */
unsigned long offset = 0;
unsigned long pivot;
diff --git a/arch/s390/include/asm/cpacf.h b/arch/s390/include/asm/cpacf.h
index a83683169d98..6174552d856d 100644
--- a/arch/s390/include/asm/cpacf.h
+++ b/arch/s390/include/asm/cpacf.h
@@ -301,6 +301,7 @@ static __always_inline void __cpacf_query(unsigned int opcode,
cpacf_mask_t *mask)
{
__cpacf_query_insn(opcode, mask, CPACF_FC_QUERY);
+ kmsan_unpoison_memory(mask, sizeof(*mask));
}
static __always_inline int __cpacf_check_opcode(unsigned int opcode)
@@ -370,6 +371,7 @@ static __always_inline int cpacf_query_func(unsigned int opcode,
static __always_inline void __cpacf_qai(unsigned int opcode, cpacf_qai_t *qai)
{
__cpacf_query_insn(opcode, qai, CPACF_FC_QUERY_AUTH_INFO);
+ kmsan_unpoison_memory(qai, sizeof(*qai));
}
/**
@@ -422,6 +424,7 @@ static inline int cpacf_km(unsigned long func, void *param,
[opc] "i" (CPACF_KM)
: "cc", "memory", "0", "1");
+ kmsan_unpoison_memory(dest, src_len - s.odd);
return src_len - s.odd;
}
@@ -454,6 +457,7 @@ static inline int cpacf_kmc(unsigned long func, void *param,
[opc] "i" (CPACF_KMC)
: "cc", "memory", "0", "1");
+ kmsan_unpoison_memory(dest, src_len - s.odd);
return src_len - s.odd;
}
@@ -587,6 +591,7 @@ static inline int cpacf_kmctr(unsigned long func, void *param, u8 *dest,
[opc] "i" (CPACF_KMCTR)
: "cc", "memory", "0", "1");
+ kmsan_unpoison_memory(dest, src_len - s.odd);
return src_len - s.odd;
}
@@ -619,6 +624,7 @@ static inline void cpacf_prno(unsigned long func, void *param,
: [fc] "d" (func), [pba] "d" ((unsigned long)param),
[seed] "d" (s.pair), [opc] "i" (CPACF_PRNO)
: "cc", "memory", "0", "1");
+ kmsan_unpoison_memory(dest, dest_len);
}
/**
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index be8369115f6d..9434c76c25b8 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -46,7 +46,6 @@ struct pcpu {
unsigned long ec_mask; /* bit mask for ec_xxx functions */
unsigned long ec_clk; /* sigp timestamp for ec_xxx */
unsigned long flags; /* per CPU flags */
- unsigned long capacity; /* cpu capacity for scheduler */
signed char state; /* physical cpu state */
signed char polarization; /* physical polarization */
u16 address; /* physical cpu address */
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h
index fb2bdbf35da5..a6c621e0491c 100644
--- a/arch/s390/include/asm/smp.h
+++ b/arch/s390/include/asm/smp.h
@@ -30,7 +30,7 @@ static __always_inline unsigned int raw_smp_processor_id(void)
return cpu;
}
-#define arch_scale_cpu_capacity smp_cpu_get_capacity
+#define arch_scale_cpu_capacity topology_get_cpu_scale
extern struct mutex smp_cpu_state_mutex;
extern unsigned int smp_cpu_mt_shift;
@@ -53,9 +53,7 @@ extern void smp_save_dump_secondary_cpus(void);
extern void smp_yield_cpu(int cpu);
extern void smp_cpu_set_polarization(int cpu, int val);
extern int smp_cpu_get_polarization(int cpu);
-extern void smp_cpu_set_capacity(int cpu, unsigned long val);
extern void smp_set_core_capacity(int cpu, unsigned long val);
-extern unsigned long smp_cpu_get_capacity(int cpu);
extern int smp_cpu_get_cpu_address(int cpu);
extern void smp_fill_possible_mask(void);
extern void smp_detect_cpus(void);
diff --git a/arch/s390/kernel/diag/diag324.c b/arch/s390/kernel/diag/diag324.c
index fe325c2a2d0d..3eec0cc8fb9e 100644
--- a/arch/s390/kernel/diag/diag324.c
+++ b/arch/s390/kernel/diag/diag324.c
@@ -182,8 +182,7 @@ long diag324_pibbuf(unsigned long arg)
goto out;
rc = copy_to_user((void __user *)address, data->pib, data->pib->len);
rc |= put_user(data->sequence, &udata->sequence);
- if (rc)
- rc = -EFAULT;
+ rc = rc ? -EFAULT : data->rc;
out:
mutex_unlock(&pibmutex);
return rc;
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index d74ef30155aa..b1e798f8e1dd 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -1157,6 +1157,8 @@ static struct attribute_group reipl_nss_attr_group = {
void set_os_info_reipl_block(void)
{
+ if (!reipl_block_actual)
+ return;
os_info_entry_add_data(OS_INFO_REIPL_BLOCK, reipl_block_actual,
reipl_block_actual->hdr.len);
}
@@ -1927,7 +1929,8 @@ static struct shutdown_action __refdata dump_action = {
static void dump_reipl_run(struct shutdown_trigger *trigger)
{
struct lowcore *abs_lc;
- unsigned int csum;
+ unsigned long ipib = 0;
+ unsigned int csum = 0;
/*
* Set REIPL_CLEAR flag in os_info flags entry indicating
@@ -1943,9 +1946,12 @@ static void dump_reipl_run(struct shutdown_trigger *trigger)
reipl_type == IPL_TYPE_UNKNOWN)
os_info_flags |= OS_INFO_FLAG_REIPL_CLEAR;
os_info_entry_add_data(OS_INFO_FLAGS_ENTRY, &os_info_flags, sizeof(os_info_flags));
- csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0);
+ if (reipl_block_actual) {
+ ipib = __pa(reipl_block_actual);
+ csum = (__force unsigned int)cksm(reipl_block_actual, reipl_block_actual->hdr.len, 0);
+ }
abs_lc = get_abs_lowcore();
- abs_lc->ipib = __pa(reipl_block_actual);
+ abs_lc->ipib = ipib;
abs_lc->ipib_checksum = csum;
put_abs_lowcore(abs_lc);
dump_run(trigger);
diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
index cdb8006220ca..5c18c8b82ab7 100644
--- a/arch/s390/kernel/perf_pai.c
+++ b/arch/s390/kernel/perf_pai.c
@@ -464,6 +464,7 @@ static void pai_start(struct perf_event *event, int flags,
cpump->event = event;
}
}
+ event->hw.state &= ~PERF_HES_STOPPED;
}
static void paicrypt_start(struct perf_event *event, int flags)
@@ -510,6 +511,13 @@ static void pai_stop(struct perf_event *event, int flags)
struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
struct pai_map *cpump = mp->mapptr;
+ /* Cope with multiple invocations:
+ * 1. perf_event_throttle() --> PMU->stop()
+ * 2. task schedules out --> PMU->stop()
+ * Check for event already stopped.
+ */
+ if (event->hw.state & PERF_HES_STOPPED)
+ return;
if (!event->attr.sample_period) { /* Counting */
pai_pmu[idx].pmu->read(event);
} else { /* Sampling */
@@ -672,9 +680,9 @@ static void pai_have_samples(int idx)
{
struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
struct pai_map *cpump = mp->mapptr;
- struct perf_event *event;
+ struct perf_event *event, *e2;
- list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
+ list_for_each_entry_safe(event, e2, &cpump->syswide_list, hw.tp_list)
pai_have_sample(event, cpump);
}
@@ -691,6 +699,17 @@ static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx,
pai_have_samples(PAI_PMU_CRYPTO);
}
+/* Prevent ioctl(fd, PERF_EVENT_IOC_PERIOD, ...) call.
+ * It sets perf_event::event_limit to a positive value and causes
+ * perf_event_overflow() to invoke pai_stop() call back function when
+ * perf_event::event_limit hits zero. This is not supported because the
+ * sample events CRYPTO_ALL and NNPA_ALL are always taken at schedule out
+ * of a task.
+ */
+static int pai_check_period(struct perf_event *event, u64 value)
+{
+ return -EINVAL;
+}
/* ============================= paiext ====================================*/
static void paiext_event_destroy(struct perf_event *event)
@@ -804,6 +823,7 @@ static struct pmu paicrypt = {
.stop = paicrypt_stop,
.read = paicrypt_read,
.sched_task = paicrypt_sched_task,
+ .check_period = pai_check_period,
.attr_groups = paicrypt_attr_groups
};
@@ -1015,6 +1035,7 @@ static struct pmu paiext = {
.stop = paiext_stop,
.read = paiext_read,
.sched_task = paiext_sched_task,
+ .check_period = pai_check_period,
.attr_groups = paiext_attr_groups,
};
@@ -1221,7 +1242,7 @@ static int __init paipmu_setup(void)
static int __init pai_init(void)
{
/* Setup s390dbf facility */
- paidbg = debug_register("pai", 32, 256, 128);
+ paidbg = debug_register("pai", 1, 1, 128);
if (!paidbg) {
pr_err("Registration of s390dbf pai failed\n");
return -ENOMEM;
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 167c72803ccf..32499cad86f0 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -659,23 +659,13 @@ int smp_cpu_get_polarization(int cpu)
return per_cpu(pcpu_devices, cpu).polarization;
}
-void smp_cpu_set_capacity(int cpu, unsigned long val)
-{
- per_cpu(pcpu_devices, cpu).capacity = val;
-}
-
-unsigned long smp_cpu_get_capacity(int cpu)
-{
- return per_cpu(pcpu_devices, cpu).capacity;
-}
-
void smp_set_core_capacity(int cpu, unsigned long val)
{
int i;
cpu = smp_get_base_cpu(cpu);
for (i = cpu; (i <= cpu + smp_cpu_mtid) && (i < nr_cpu_ids); i++)
- smp_cpu_set_capacity(i, val);
+ topology_set_cpu_scale(i, val);
}
int smp_cpu_get_cpu_address(int cpu)
@@ -727,7 +717,7 @@ static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail,
else
pcpu->state = CPU_STATE_STANDBY;
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
- smp_cpu_set_capacity(cpu, CPU_CAPACITY_HIGH);
+ topology_set_cpu_scale(cpu, CPU_CAPACITY_HIGH);
set_cpu_present(cpu, true);
if (!early && arch_register_cpu(cpu))
set_cpu_present(cpu, false);
@@ -967,7 +957,7 @@ void __init smp_prepare_boot_cpu(void)
ipl_pcpu->state = CPU_STATE_CONFIGURED;
lc->pcpu = (unsigned long)ipl_pcpu;
smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);
- smp_cpu_set_capacity(0, CPU_CAPACITY_HIGH);
+ topology_set_cpu_scale(0, CPU_CAPACITY_HIGH);
}
void __init smp_setup_processor_id(void)
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 1377c6f3f670..42fc0294f543 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -147,7 +147,7 @@ static void add_cpus_to_mask(struct topology_core *tl_core,
cpumask_set_cpu(cpu, &book->mask);
cpumask_set_cpu(cpu, &socket->mask);
smp_cpu_set_polarization(cpu, tl_core->pp);
- smp_cpu_set_capacity(cpu, CPU_CAPACITY_HIGH);
+ topology_set_cpu_scale(cpu, CPU_CAPACITY_HIGH);
}
}
}
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index d804e1140c2e..efcbf406f03e 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -32,7 +32,7 @@ static atomic64_t virt_timer_elapsed;
DEFINE_PER_CPU(u64, mt_cycles[8]);
static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 };
static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 };
-static DEFINE_PER_CPU(u64, mt_scaling_jiffies);
+static DEFINE_PER_CPU(unsigned long, mt_scaling_jiffies);
static inline void set_vtimer(u64 expires)
{
@@ -81,7 +81,7 @@ static void update_mt_scaling(void)
memcpy(cycles_old, cycles_new,
sizeof(u64) * (smp_cpu_mtid + 1));
}
- __this_cpu_write(mt_scaling_jiffies, jiffies_64);
+ __this_cpu_write(mt_scaling_jiffies, jiffies);
}
static inline u64 update_tsk_timer(unsigned long *tsk_vtime, u64 new)
@@ -144,7 +144,7 @@ static int do_account_vtime(struct task_struct *tsk)
lc->system_timer += timer;
/* Update MT utilization calculation */
- if (smp_cpu_mtid && time_after64(jiffies_64, __this_cpu_read(mt_scaling_jiffies)))
+ if (smp_cpu_mtid && time_after(jiffies, __this_cpu_read(mt_scaling_jiffies)))
update_mt_scaling();
/* Calculate cputime delta */
diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c
index 9610770fcf6d..4b160eedc5a0 100644
--- a/arch/s390/mm/pgalloc.c
+++ b/arch/s390/mm/pgalloc.c
@@ -55,63 +55,46 @@ static void __crst_table_upgrade(void *arg)
int crst_table_upgrade(struct mm_struct *mm, unsigned long end)
{
- unsigned long *pgd = NULL, *p4d = NULL, *__pgd;
- unsigned long asce_limit = mm->context.asce_limit;
+ unsigned long *table, *pgd;
+ int rc, notify;
mmap_assert_write_locked(mm);
-
/* upgrade should only happen from 3 to 4, 3 to 5, or 4 to 5 levels */
- VM_BUG_ON(asce_limit < _REGION2_SIZE);
-
- if (end <= asce_limit)
- return 0;
-
- if (asce_limit == _REGION2_SIZE) {
- p4d = crst_table_alloc(mm);
- if (unlikely(!p4d))
- goto err_p4d;
- crst_table_init(p4d, _REGION2_ENTRY_EMPTY);
- pagetable_p4d_ctor(virt_to_ptdesc(p4d));
- }
- if (end > _REGION1_SIZE) {
- pgd = crst_table_alloc(mm);
- if (unlikely(!pgd))
- goto err_pgd;
- crst_table_init(pgd, _REGION1_ENTRY_EMPTY);
- pagetable_pgd_ctor(virt_to_ptdesc(pgd));
- }
-
- spin_lock_bh(&mm->page_table_lock);
-
- if (p4d) {
- __pgd = (unsigned long *) mm->pgd;
- p4d_populate(mm, (p4d_t *) p4d, (pud_t *) __pgd);
- mm->pgd = (pgd_t *) p4d;
- mm->context.asce_limit = _REGION1_SIZE;
- mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH |
- _ASCE_USER_BITS | _ASCE_TYPE_REGION2;
- mm_inc_nr_puds(mm);
- }
- if (pgd) {
- __pgd = (unsigned long *) mm->pgd;
- pgd_populate(mm, (pgd_t *) pgd, (p4d_t *) __pgd);
- mm->pgd = (pgd_t *) pgd;
- mm->context.asce_limit = TASK_SIZE_MAX;
- mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH |
- _ASCE_USER_BITS | _ASCE_TYPE_REGION1;
+ VM_BUG_ON(mm->context.asce_limit < _REGION2_SIZE);
+ rc = 0;
+ notify = 0;
+ while (mm->context.asce_limit < end) {
+ table = crst_table_alloc(mm);
+ if (!table) {
+ rc = -ENOMEM;
+ break;
+ }
+ spin_lock_bh(&mm->page_table_lock);
+ pgd = (unsigned long *)mm->pgd;
+ if (mm->context.asce_limit == _REGION2_SIZE) {
+ crst_table_init(table, _REGION2_ENTRY_EMPTY);
+ p4d_populate(mm, (p4d_t *)table, (pud_t *)pgd);
+ pagetable_p4d_ctor(virt_to_ptdesc(table));
+ mm->pgd = (pgd_t *)table;
+ mm->context.asce_limit = _REGION1_SIZE;
+ mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH |
+ _ASCE_USER_BITS | _ASCE_TYPE_REGION2;
+ mm_inc_nr_puds(mm);
+ } else {
+ crst_table_init(table, _REGION1_ENTRY_EMPTY);
+ pgd_populate(mm, (pgd_t *)table, (p4d_t *)pgd);
+ pagetable_pgd_ctor(virt_to_ptdesc(table));
+ mm->pgd = (pgd_t *)table;
+ mm->context.asce_limit = TASK_SIZE_MAX;
+ mm->context.asce = __pa(mm->pgd) | _ASCE_TABLE_LENGTH |
+ _ASCE_USER_BITS | _ASCE_TYPE_REGION1;
+ }
+ notify = 1;
+ spin_unlock_bh(&mm->page_table_lock);
}
-
- spin_unlock_bh(&mm->page_table_lock);
-
- on_each_cpu(__crst_table_upgrade, mm, 0);
-
- return 0;
-
-err_pgd:
- pagetable_dtor(virt_to_ptdesc(p4d));
- crst_table_free(mm, p4d);
-err_p4d:
- return -ENOMEM;
+ if (notify)
+ on_each_cpu(__crst_table_upgrade, mm, 0);
+ return rc;
}
unsigned long *page_table_alloc_noprof(struct mm_struct *mm)
diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c
index d98d97df792a..bbb76113a4d0 100644
--- a/arch/s390/pci/pci_sysfs.c
+++ b/arch/s390/pci/pci_sysfs.c
@@ -153,6 +153,9 @@ static ssize_t report_error_write(struct file *filp, struct kobject *kobj,
if (off || (count < sizeof(*report)))
return -EINVAL;
+ if (count < (report->length + sizeof(*report)))
+ return -EINVAL;
+
ret = sclp_pci_report(report, zdev->fh, zdev->fid);
return ret ? ret : count;
diff --git a/block/bio.c b/block/bio.c
index 898b2f5ef8c8..f95b63c0604a 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -859,6 +859,7 @@ static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp)
bio->bi_ioprio = bio_src->bi_ioprio;
bio->bi_write_hint = bio_src->bi_write_hint;
bio->bi_write_stream = bio_src->bi_write_stream;
+ bio->bi_bvec_gap_bit = bio_src->bi_bvec_gap_bit;
bio->bi_iter = bio_src->bi_iter;
bio->bi_io_vec = bio_src->bi_io_vec;
@@ -1972,6 +1973,14 @@ struct bio *bio_split(struct bio *bio, int sectors,
bio_advance(bio, split->bi_iter.bi_size);
+ /*
+ * The gap bit is set when splitting to limits and only applies to the
+ * front bio that was split off. The remaining bio will calcualte its
+ * gap value when it is subsequently split to limits, so it is safe to
+ * re-initialize the value back to 0.
+ */
+ bio->bi_bvec_gap_bit = 0;
+
if (bio_flagged(bio, BIO_TRACE_COMPLETION))
bio_set_flag(split, BIO_TRACE_COMPLETION);
diff --git a/block/genhd.c b/block/genhd.c
index f1990c7cdfb9..10ca8b4d6eea 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -448,6 +448,13 @@ static int __add_disk(struct device *parent, struct gendisk *disk,
}
/*
+ * We do not support partitions with zoned block devices, so do not try
+ * to scan the partitions table.
+ */
+ if (blk_queue_is_zoned(disk->queue))
+ disk->flags |= GENHD_FL_NO_PART;
+
+ /*
* If the driver provides an explicit major number it also must provide
* the number of minors numbers supported, and those will be used to
* setup the gendisk.
diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c
index dfe0fbdf066d..b4c49259a1a2 100644
--- a/drivers/accel/amdxdna/aie2_message.c
+++ b/drivers/accel/amdxdna/aie2_message.c
@@ -994,7 +994,7 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
}
ccnt = payload->command_count;
- if (payload_len < struct_size(payload, data, ccnt)) {
+ if (!ccnt || payload_len < struct_size(payload, data, ccnt)) {
XDNA_DBG(xdna, "Invalid command count %d", ccnt);
return -EINVAL;
}
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c
index 31a414c3f0d9..888e857ec558 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.c
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -183,8 +183,10 @@ int amdxdna_cmd_set_error(struct amdxdna_gem_obj *abo,
if (!abo)
return -EINVAL;
cmd = amdxdna_gem_vmap(abo);
- if (!cmd)
+ if (!cmd) {
+ amdxdna_gem_put_obj(abo);
return -ENOMEM;
+ }
}
memset(cmd->data, 0xff, abo->mem.size - sizeof(*cmd));
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h
index b6bef3af7dab..6e78bab8a02c 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.h
+++ b/drivers/accel/amdxdna/amdxdna_ctx.h
@@ -55,7 +55,7 @@ struct amdxdna_cmd_chain {
u32 submit_index;
u32 error_index;
u32 reserved[3];
- u64 data[] __counted_by(command_count);
+ u64 data[];
};
/*
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index b01f5d95fac8..4b0d58d0329b 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.c
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -1246,6 +1246,9 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)
{
u64 end;
+ if (is_import_bo(abo))
+ return -EOPNOTSUPP;
+
if (offset >= abo->mem.size)
return -EINVAL;
@@ -1253,9 +1256,10 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)
return -EINVAL;
size = min(abo->mem.size, end) - offset;
- if (is_import_bo(abo))
- drm_clflush_sg(abo->base.sgt);
- else if (amdxdna_gem_vmap(abo))
+ if (!size)
+ return 0;
+
+ if (amdxdna_gem_vmap(abo))
drm_clflush_virt_range(amdxdna_gem_vmap(abo) + offset, size);
else if (abo->base.pages)
drm_clflush_pages(abo->base.pages, abo->mem.size >> PAGE_SHIFT);
diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c
index d121fb0d7732..1cf284e7f300 100644
--- a/drivers/accel/ethosu/ethosu_drv.c
+++ b/drivers/accel/ethosu/ethosu_drv.c
@@ -356,6 +356,8 @@ static int ethosu_probe(struct platform_device *pdev)
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ethosudev->regs))
+ return PTR_ERR(ethosudev->regs);
ethosudev->pmu_regs = ethosudev->regs;
ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &ethosudev->clks);
diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c
index d50fed64d4d9..fa37a190e9ff 100644
--- a/drivers/accel/ethosu/ethosu_gem.c
+++ b/drivers/accel/ethosu/ethosu_gem.c
@@ -204,7 +204,7 @@ static u64 feat_matrix_length(struct ethosu_device *edev,
struct feat_matrix *fm,
u32 x, u32 y, u32 c, bool ofm)
{
- u32 element_size, storage = fm->precision >> 14;
+ u32 element_size, storage = ethosu_is_u65(edev) ? 0 : fm->precision >> 14;
int tile = 0;
u64 addr;
diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c
index 6a038c0384cc..4ced44a65f23 100644
--- a/drivers/accel/ethosu/ethosu_job.c
+++ b/drivers/accel/ethosu/ethosu_job.c
@@ -154,6 +154,13 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job)
drm_gem_object_put(job->cmd_bo);
+ if (job->done_fence) {
+ if (dma_fence_was_initialized(job->done_fence))
+ dma_fence_put(job->done_fence);
+ else
+ dma_fence_free(job->done_fence);
+ }
+
kfree(job);
}
@@ -164,7 +171,6 @@ static void ethosu_job_cleanup(struct kref *ref)
pm_runtime_put_autosuspend(job->dev->base.dev);
- dma_fence_put(job->done_fence);
dma_fence_put(job->inference_done_fence);
ethosu_job_err_cleanup(job);
@@ -415,7 +421,7 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
ejob->done_fence = kzalloc_obj(*ejob->done_fence);
if (!ejob->done_fence) {
ret = -ENOMEM;
- goto out_cleanup_job;
+ goto out_put_job;
}
ret = drm_sched_job_init(&ejob->base,
diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
index 50bf3340e49c..2ccc55486aac 100644
--- a/drivers/accel/qaic/qaic_control.c
+++ b/drivers/accel/qaic/qaic_control.c
@@ -963,11 +963,13 @@ static int decode_status(struct qaic_device *qdev, void *trans, struct manage_ms
static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
struct wire_msg *msg, struct ioctl_resources *resources,
- struct qaic_user *usr)
+ struct qaic_user *usr, bool orphaned_deactivate)
{
+ u32 msg_hdr_count = le32_to_cpu(msg->hdr.count);
u32 msg_hdr_len = le32_to_cpu(msg->hdr.len);
struct wire_trans_hdr *trans_hdr;
u32 msg_len = 0;
+ int trans_type;
int ret;
int i;
@@ -975,10 +977,12 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
msg_hdr_len > QAIC_MANAGE_MAX_MSG_LENGTH)
return -EINVAL;
- user_msg->len = 0;
- user_msg->count = le32_to_cpu(msg->hdr.count);
+ if (user_msg) {
+ user_msg->len = 0;
+ user_msg->count = msg_hdr_count;
+ }
- for (i = 0; i < user_msg->count; ++i) {
+ for (i = 0; i < msg_hdr_count; ++i) {
u32 hdr_len;
if (msg_len > msg_hdr_len - sizeof(*trans_hdr))
@@ -990,7 +994,20 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
size_add(msg_len, hdr_len) > msg_hdr_len)
return -EINVAL;
- switch (le32_to_cpu(trans_hdr->type)) {
+ trans_type = le32_to_cpu(trans_hdr->type);
+ /*
+ * orphaned_deactivate is the case where a deactivate response
+ * is received from the device after the user owning the DBC,
+ * and the message requesting deactivation, has gone away.
+ * In this case, only process QAIC_TRANS_DEACTIVATE_FROM_DEV
+ * transaction and skip the others.
+ */
+ if (orphaned_deactivate && trans_type != QAIC_TRANS_DEACTIVATE_FROM_DEV) {
+ msg_len += hdr_len;
+ continue;
+ }
+
+ switch (trans_type) {
case QAIC_TRANS_PASSTHROUGH_FROM_DEV:
ret = decode_passthrough(qdev, trans_hdr, user_msg, &msg_len);
break;
@@ -1281,7 +1298,7 @@ dma_xfer_continue:
goto dma_cont_failed;
}
- ret = decode_message(qdev, user_msg, rsp, &resources, usr);
+ ret = decode_message(qdev, user_msg, rsp, &resources, usr, false);
dma_cont_failed:
free_dbc_buf(qdev, &resources);
@@ -1446,22 +1463,7 @@ static void resp_worker(struct work_struct *work)
* response to the QAIC_TRANS_TERMINATE_TO_DEV transaction,
* otherwise, the user can issue an soc_reset to the device.
*/
- u32 msg_count = le32_to_cpu(msg->hdr.count);
- u32 msg_len = le32_to_cpu(msg->hdr.len);
- u32 len = 0;
- int j;
-
- for (j = 0; j < msg_count && len < msg_len; ++j) {
- struct wire_trans_hdr *trans_hdr;
-
- trans_hdr = (struct wire_trans_hdr *)(msg->data + len);
- if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) {
- if (decode_deactivate(qdev, trans_hdr, &len, NULL))
- len += le32_to_cpu(trans_hdr->len);
- } else {
- len += le32_to_cpu(trans_hdr->len);
- }
- }
+ decode_message(qdev, NULL, msg, NULL, NULL, true);
/* request must have timed out, drop packet */
kfree(msg);
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index f48715ed827c..163a3cccf197 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -28,9 +28,7 @@
#include "internal.h"
#include "sleep.h"
-#define ACPI_BUS_CLASS "system_bus"
#define ACPI_BUS_HID "LNXSYBUS"
-#define ACPI_BUS_DEVICE_NAME "System Bus"
#define INVALID_ACPI_HANDLE ((acpi_handle)ZERO_PAGE(0))
@@ -1450,8 +1448,6 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
acpi_object_is_system_bus(handle)) {
/* \_SB, \_TZ, LNXSYBUS */
acpi_add_id(pnp, ACPI_BUS_HID);
- strscpy(pnp->device_name, ACPI_BUS_DEVICE_NAME);
- strscpy(pnp->device_class, ACPI_BUS_CLASS);
}
break;
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 58f512f8952a..9b8c0935001c 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -2614,6 +2614,51 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
}
#endif
+/*
+ * The Marvell 88SE6111/6121/6145 ("Thor") family stops reporting interrupts
+ * for a port when HOST_IRQ_STAT is cleared while PxIS still holds bits: PxIS
+ * keeps its content, HOST_IRQ_STAT reads back as 0, the port is never looked
+ * at again and the command in flight only ends in a timeout. On a 88SE6121
+ * this makes every SATA-2 or SATA-3 disk fail to IDENTIFY, while SATA-1 disks
+ * happen to win the race often enough to work.
+ *
+ * Clearing the host status before servicing the ports avoids it. Marvell's
+ * own driver for these chips does the same and says so ("clear global before
+ * channel"), and ahci_xgene handles its broken edge latch the same way. The
+ * price is at most one spurious interrupt per valid one, which is why this is
+ * not the generic behaviour - see AHCI 1.1 section 10.6.2.
+ *
+ * Link: https://bugzilla.kernel.org/show_bug.cgi?id=216094
+ */
+static irqreturn_t ahci_mv_irq_handler(int irq, void *dev_instance)
+{
+ struct ata_host *host = dev_instance;
+ struct ahci_host_priv *hpriv = host->private_data;
+ void __iomem *mmio = hpriv->mmio;
+ unsigned int rc;
+ u32 irq_stat, irq_masked;
+
+ irq_stat = readl(mmio + HOST_IRQ_STAT);
+ if (!irq_stat)
+ return IRQ_NONE;
+
+ irq_masked = irq_stat & hpriv->port_map;
+
+ spin_lock(&host->lock);
+
+ /*
+ * Use the unmasked value to clear the interrupt, as a spurious pending
+ * event on a dummy port might cause a screaming IRQ.
+ */
+ writel(irq_stat, mmio + HOST_IRQ_STAT);
+
+ rc = ahci_handle_port_intr(host, irq_masked);
+
+ spin_unlock(&host->lock);
+
+ return IRQ_RETVAL(rc);
+}
+
static void ahci_remap_check(struct pci_dev *pdev, int bar,
struct ahci_host_priv *hpriv)
{
@@ -2917,6 +2962,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return -ENOMEM;
hpriv->flags |= (unsigned long)pi.private_data;
+ /* the Marvell "Thor" family needs HOST_IRQ_STAT cleared first */
+ if (board_id == board_ahci_mv)
+ hpriv->irq_handler = ahci_mv_irq_handler;
+
/* MCP65 revision A1 and A2 can't do MSI */
if (board_id == board_ahci_mcp65 &&
(pdev->revision == 0xa1 || pdev->revision == 0xa2))
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index a7d667cfcfec..b3666519b648 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -4823,28 +4823,6 @@ ata_scsi_get_phys_element_status_xlat(struct ata_queued_cmd *qc)
return 0;
}
-static void ata_scsi_depop_ua_cap_changed_complete(struct ata_queued_cmd *qc)
-{
- struct scsi_cmnd *scmd = qc->scsicmd;
- u8 *cdb = scmd->cmnd;
- bool is_ata_passthru = cdb[0] == ATA_16 || cdb[0] == ATA_12;
- bool is_success = qc->err_mask == 0;
-
- /*
- * For successful non-passthrough commands, raise a UNIT ATTENTION with
- * the additional sense code set to CAPACITY DATA HAS CHANGED to be
- * raised. Note that this should be done only if the capacity has
- * actually changed, which may not be the case if the element that was
- * specified for depopulation was already depopulated, or we did not
- * restore any removed element. But a capacity change unit attention is
- * harmless, so always raise the unit attention.
- */
- if (is_success && !is_ata_passthru)
- ata_scsi_set_sense(qc->dev, scmd, UNIT_ATTENTION,
- UA_CHANGED_ASC, CAPACITY_CHANGED_ASCQ);
- ata_scsi_qc_complete(qc);
-}
-
static unsigned int
ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc)
{
@@ -4884,7 +4862,6 @@ ata_scsi_remove_element_and_truncate_xlat(struct ata_queued_cmd *qc)
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48;
qc->flags |= ATA_QCFLAG_RESULT_TF;
- qc->complete_fn = ata_scsi_depop_ua_cap_changed_complete;
return 0;
}
@@ -4937,7 +4914,6 @@ ata_scsi_restore_elements_and_rebuild_xlat(struct ata_queued_cmd *qc)
tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48;
qc->flags |= ATA_QCFLAG_RESULT_TF;
- qc->complete_fn = ata_scsi_depop_ua_cap_changed_complete;
return 0;
}
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 6f12976035b0..758c20678bf6 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -458,12 +458,14 @@ static void loop_update_dio_alignment(struct loop_device *lo)
* Use the dio alignment of the file system if provided. The incomoing
* request's bio_vec is forwarded to the backing file unchanged, so its
* required memory alignment becomes the device's dma_alignment when
- * used for direct-io.
+ * used for direct-io. The file system reports zeroed alignments if the
+ * file can't be used for direct-io at all, so fall back to the block
+ * device limits in that case.
*/
if (!vfs_getattr(&file->f_path, &st, STATX_DIOALIGN, 0) &&
- (st.result_mask & STATX_DIOALIGN)) {
+ (st.result_mask & STATX_DIOALIGN) && st.dio_mem_align) {
lo->lo_min_dio_size = st.dio_offset_align;
- lo->lo_dio_mem_align = st.dio_mem_align - 1;
+ lo->lo_dio_mem_align = min(st.dio_mem_align - 1, PAGE_SIZE - 1);
return;
}
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 6c5bec7da97c..e5ba07d8d281 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -2653,6 +2653,12 @@ static int ublk_ch_mmap(struct file *filp, struct vm_area_struct *vma)
if (vma->vm_flags & VM_WRITE)
return -EPERM;
+ /*
+ * The per-queue command buffer is kernel-written ABI; prevent
+ * the daemon from upgrading to writable via mprotect().
+ */
+ vm_flags_clear(vma, VM_MAYWRITE);
+
end = UBLKSRV_CMD_BUF_OFFSET + ub->dev_info.nr_hw_queues * max_sz;
if (phys_off < UBLKSRV_CMD_BUF_OFFSET || phys_off >= end)
return -EINVAL;
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index 4323ac108cae..f0ca221524db 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -1042,12 +1042,14 @@ static int zloop_get_block_size(struct zloop_device *zlo,
* Use the dio alignment of the file system if provided. The incoming
* request's bio_vec is forwarded to the backing file unchanged, so its
* required memory alignment becomes the device's dma_alignment when
- * used for direct-io.
+ * used for direct-io. The file system reports zeroed alignments if the
+ * file can't be used for direct-io at all, so fall back to the block
+ * device limits in that case.
*/
if (!vfs_getattr(&zone->file->f_path, &st, STATX_DIOALIGN, 0) &&
- (st.result_mask & STATX_DIOALIGN)) {
+ (st.result_mask & STATX_DIOALIGN) && st.dio_mem_align) {
zlo->block_size = st.dio_offset_align;
- zlo->dio_mem_align = st.dio_mem_align - 1;
+ zlo->dio_mem_align = min(st.dio_mem_align - 1, PAGE_SIZE - 1);
} else if (sb_bdev) {
zlo->block_size = bdev_physical_block_size(sb_bdev);
zlo->dio_mem_align = bdev_dma_alignment(sb_bdev);
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index bcb2514b7bc0..909a265fd906 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -571,12 +571,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
}
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
+static u8 btintel_version_tlv_min_len(u8 type)
+{
+ switch (type) {
+ case INTEL_TLV_CNVI_TOP:
+ case INTEL_TLV_CNVR_TOP:
+ case INTEL_TLV_CNVI_BT:
+ case INTEL_TLV_CNVR_BT:
+ case INTEL_TLV_BUILD_NUM:
+ case INTEL_TLV_GIT_SHA1:
+ return sizeof(u32);
+ case INTEL_TLV_DEV_REV_ID:
+ case INTEL_TLV_TIME_STAMP:
+ return sizeof(u16);
+ case INTEL_TLV_IMAGE_TYPE:
+ case INTEL_TLV_BUILD_TYPE:
+ case INTEL_TLV_SECURE_BOOT:
+ case INTEL_TLV_OTP_LOCK:
+ case INTEL_TLV_API_LOCK:
+ case INTEL_TLV_DEBUG_LOCK:
+ case INTEL_TLV_LIMITED_CCE:
+ case INTEL_TLV_SBE_TYPE:
+ return sizeof(u8);
+ case INTEL_TLV_MIN_FW:
+ return 3;
+ case INTEL_TLV_OTP_BDADDR:
+ return sizeof(bdaddr_t);
+ default:
+ return 0;
+ }
+}
+
int btintel_parse_version_tlv(struct hci_dev *hdev,
struct intel_version_tlv *version,
struct sk_buff *skb)
{
/* Consume Command Complete Status field */
- skb_pull(skb, 1);
+ if (!skb_pull(skb, 1))
+ return -EINVAL;
/* Event parameters contain multiple TLVs. Read each of them
* and only keep the required data. Also, it use existing legacy
@@ -596,6 +628,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
if (skb->len < tlv->len + sizeof(*tlv))
return -EINVAL;
+ if (tlv->len < btintel_version_tlv_min_len(tlv->type))
+ return -EINVAL;
+
switch (tlv->type) {
case INTEL_TLV_CNVI_TOP:
version->cnvi_top = get_unaligned_le32(tlv->val);
@@ -667,7 +702,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
break;
case INTEL_TLV_FW_ID:
snprintf(version->fw_id, sizeof(version->fw_id),
- "%s", tlv->val);
+ "%.*s", tlv->len, tlv->val);
break;
default:
/* Ignore rest of information */
@@ -686,6 +721,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
{
struct sk_buff *skb;
const u8 param[1] = { 0xFF };
+ int err;
if (!version)
return -EINVAL;
@@ -704,10 +740,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
return -EIO;
}
- btintel_parse_version_tlv(hdev, version, skb);
+ err = btintel_parse_version_tlv(hdev, version, skb);
kfree_skb(skb);
- return 0;
+ return err;
}
/* ------- REGMAP IBT SUPPORT ------- */
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 005c77a4f5eb..eec95e5f3dbb 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1696,6 +1696,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (unlikely(!(intr_fh | intr_hw))) {
/* Ignore interrupt, inta == 0 */
+ bt_warn_ratelimited("Bluetooth: btintel_pcie: Received spurious interrupt\n");
+ btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST,
+ BIT(entry->entry));
return IRQ_NONE;
}
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
index 516b8f74c434..5798a8db016e 100644
--- a/drivers/bluetooth/hci_mrvl.c
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(2000));
- if (err == 1) {
+ if (err == -EINTR) {
bt_dev_err(hdev, "Firmware load interrupted");
- err = -EINTR;
break;
} else if (err) {
bt_dev_err(hdev, "Firmware request timeout");
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index dcf20ea5ef5e..b250d0dde760 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -16,7 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/device/faux.h>
+#include <linux/platform_device.h>
#include <linux/psci.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -428,14 +428,14 @@ deinit:
* to register cpuidle driver then rollback to cancel all CPUs
* registration.
*/
-static int psci_cpuidle_probe(struct faux_device *fdev)
+static int psci_cpuidle_probe(struct platform_device *pdev)
{
int cpu, ret;
struct cpuidle_driver *drv;
struct cpuidle_device *dev;
for_each_present_cpu(cpu) {
- ret = psci_idle_init_cpu(&fdev->dev, cpu);
+ ret = psci_idle_init_cpu(&pdev->dev, cpu);
if (ret)
goto out_fail;
}
@@ -455,36 +455,26 @@ out_fail:
return ret;
}
-static struct faux_device_ops psci_cpuidle_ops = {
+static struct platform_driver psci_cpuidle_driver = {
.probe = psci_cpuidle_probe,
+ .driver = {
+ .name = "psci-cpuidle",
+ },
};
-static bool __init dt_idle_state_present(void)
-{
- struct device_node *cpu_node __free(device_node) =
- of_cpu_device_node_get(cpumask_first(cpu_possible_mask));
- if (!cpu_node)
- return false;
-
- struct device_node *state_node __free(device_node) =
- of_get_cpu_state_node(cpu_node, 0);
- if (!state_node)
- return false;
-
- return !!of_match_node(psci_idle_state_match, state_node);
-}
-
static int __init psci_idle_init(void)
{
- struct faux_device *fdev;
+ struct platform_device *pdev;
+ int ret;
- if (!dt_idle_state_present())
- return 0;
+ ret = platform_driver_register(&psci_cpuidle_driver);
+ if (ret)
+ return ret;
- fdev = faux_device_create("psci-cpuidle", NULL, &psci_cpuidle_ops);
- if (!fdev) {
- pr_err("Failed to create psci-cpuidle device\n");
- return -ENODEV;
+ pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ platform_driver_unregister(&psci_cpuidle_driver);
+ return PTR_ERR(pdev);
}
return 0;
diff --git a/drivers/cpuidle/dt_idle_genpd.c b/drivers/cpuidle/dt_idle_genpd.c
index d292975cc468..ed41a90eeeb7 100644
--- a/drivers/cpuidle/dt_idle_genpd.c
+++ b/drivers/cpuidle/dt_idle_genpd.c
@@ -99,7 +99,7 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
if (!pd)
goto out;
- pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
+ pd->name = kstrdup(kbasename(of_node_full_name(np)), GFP_KERNEL);
if (!pd->name)
goto free_pd;
@@ -112,7 +112,6 @@ struct generic_pm_domain *dt_idle_pd_alloc(struct device_node *np,
goto free_name;
pd->free_states = pd_free_states;
- pd->name = kbasename(pd->name);
pd->states = states;
pd->state_count = state_count;
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index d504c636dc29..4c9add51f9ef 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -804,6 +804,26 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags)
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");
/**
+ * dma_buf_fd_install - install a reserved fd for a dma-buf
+ * @dmabuf: [in] pointer to dma_buf
+ * @fd: [in] fd reserved with get_unused_fd_flags()
+ *
+ * Publishes a previously reserved fd into the caller's fd table.
+ * Must only be called after all fallible work (e.g. copy_to_user)
+ * has succeeded, as it cannot be undone safely once called.
+ *
+ * The caller is responsible for having emitted the trace event
+ * (via dma_buf_fd() or get_unused_fd_flags() + this function)
+ * before calling this.
+ */
+void dma_buf_fd_install(struct dma_buf *dmabuf, int fd)
+{
+ DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd);
+ fd_install(fd, dmabuf->file);
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_fd_install, "DMA_BUF");
+
+/**
* dma_buf_get - returns the struct dma_buf related to an fd
* @fd: [in] fd associated with the struct dma_buf to be returned
*
diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
index 3937dd41bb0f..8a6c2b6e0cc6 100644
--- a/drivers/dma-buf/dma-heap.c
+++ b/drivers/dma-buf/dma-heap.c
@@ -56,33 +56,6 @@ MODULE_PARM_DESC(mem_accounting,
"Enable cgroup-based memory accounting for dma-buf heap allocations (default=false).");
EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP");
-static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
- u32 fd_flags,
- u64 heap_flags)
-{
- struct dma_buf *dmabuf;
- int fd;
-
- /*
- * Allocations from all heaps have to begin
- * and end on page boundaries.
- */
- len = PAGE_ALIGN(len);
- if (!len)
- return -EINVAL;
-
- dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags);
- if (IS_ERR(dmabuf))
- return PTR_ERR(dmabuf);
-
- fd = dma_buf_fd(dmabuf, fd_flags);
- if (fd < 0) {
- dma_buf_put(dmabuf);
- /* just return, as put will call release and that will free */
- }
- return fd;
-}
-
static int dma_heap_open(struct inode *inode, struct file *file)
{
struct dma_heap *heap;
@@ -100,30 +73,42 @@ static int dma_heap_open(struct inode *inode, struct file *file)
return 0;
}
-static long dma_heap_ioctl_allocate(struct file *file, void *data)
+static struct dma_buf *dma_heap_ioctl_allocate(struct file *file, void *data)
{
struct dma_heap_allocation_data *heap_allocation = data;
struct dma_heap *heap = file->private_data;
+ struct dma_buf *dmabuf;
int fd;
+ size_t len;
if (heap_allocation->fd)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
+
+ len = PAGE_ALIGN(heap_allocation->len);
+ if (!len)
+ return ERR_PTR(-EINVAL);
+
+ dmabuf = heap->ops->allocate(heap, len, heap_allocation->fd_flags,
+ heap_allocation->heap_flags);
- fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
- heap_allocation->fd_flags,
- heap_allocation->heap_flags);
- if (fd < 0)
- return fd;
+ if (IS_ERR(dmabuf))
+ return dmabuf;
+
+ fd = get_unused_fd_flags(heap_allocation->fd_flags);
+ if (fd < 0) {
+ dma_buf_put(dmabuf);
+ return ERR_PTR(fd);
+ }
heap_allocation->fd = fd;
- return 0;
+ return dmabuf;
}
static unsigned int dma_heap_ioctl_cmds[] = {
@@ -139,6 +124,8 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
unsigned int in_size, out_size, drv_size, ksize;
int nr = _IOC_NR(ucmd);
int ret = 0;
+ int fd;
+ struct dma_buf *dmabuf;
if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
return -EINVAL;
@@ -175,15 +162,28 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
switch (kcmd) {
case DMA_HEAP_IOCTL_ALLOC:
- ret = dma_heap_ioctl_allocate(file, kdata);
+ dmabuf = dma_heap_ioctl_allocate(file, kdata);
+
+ if (IS_ERR(dmabuf)) {
+ ret = PTR_ERR(dmabuf);
+ break;
+ }
+
+ fd = ((struct dma_heap_allocation_data *)kdata)->fd;
+ if (copy_to_user((void __user *)arg, kdata, out_size) != 0) {
+ put_unused_fd(fd);
+ dma_buf_put(dmabuf);
+ ret = -EFAULT;
+ } else {
+ dma_buf_fd_install(dmabuf, fd);
+ }
+
break;
default:
ret = -ENOTTY;
goto err;
}
- if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
- ret = -EFAULT;
err:
if (kdata != stack_kdata)
kfree(kdata);
diff --git a/drivers/edac/al_mc_edac.c b/drivers/edac/al_mc_edac.c
index 178b9e581a72..bf6921d8890b 100644
--- a/drivers/edac/al_mc_edac.c
+++ b/drivers/edac/al_mc_edac.c
@@ -302,12 +302,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
IRQF_SHARED,
pdev->name,
pdev);
- if (ret != 0) {
- dev_err(&pdev->dev,
- "failed to request UE IRQ %d (%d)\n",
- al_mc->irq_ue, ret);
+ if (ret != 0)
return ret;
- }
}
if (al_mc->irq_ce > 0) {
@@ -317,12 +313,8 @@ static int al_mc_edac_probe(struct platform_device *pdev)
IRQF_SHARED,
pdev->name,
pdev);
- if (ret != 0) {
- dev_err(&pdev->dev,
- "failed to request CE IRQ %d (%d)\n",
- al_mc->irq_ce, ret);
+ if (ret != 0)
return ret;
- }
}
return 0;
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 4edd2088c2db..1d1e2b5ca14c 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -1507,6 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
int edac_idx, rc;
struct device_node *np;
const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
+ bool is_s10 = device->edac->is_s10;
rc = altr_check_ecc_deps(device);
if (rc)
@@ -1548,15 +1549,14 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
/*
* Update the PortB IRQs - A10 has 4, S10 has 2, Index accordingly
- *
- * FIXME: Instead of ifdefs with different architectures the driver
- * should properly use compatibles.
*/
-#ifdef CONFIG_64BIT
- altdev->sb_irq = irq_of_parse_and_map(np, 1);
-#else
- altdev->sb_irq = irq_of_parse_and_map(np, 2);
-#endif
+
+ /* Using compatibles to determine the IRQ Index */
+ if (is_s10)
+ altdev->sb_irq = irq_of_parse_and_map(np, 1);
+ else
+ altdev->sb_irq = irq_of_parse_and_map(np, 2);
+
if (!altdev->sb_irq) {
edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB SBIRQ alloc\n");
rc = -ENODEV;
@@ -1570,29 +1570,28 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
goto err_release_group_1;
}
-#ifdef CONFIG_64BIT
- /* Use IRQ to determine SError origin instead of assigning IRQ */
- rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
- if (rc) {
- edac_printk(KERN_ERR, EDAC_DEVICE,
- "Error PortB DBIRQ alloc\n");
- goto err_release_group_1;
- }
-#else
- altdev->db_irq = irq_of_parse_and_map(np, 3);
- if (!altdev->db_irq) {
- edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
- rc = -ENODEV;
- goto err_release_group_1;
- }
- rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
- prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
- ecc_name, altdev);
- if (rc) {
- edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
- goto err_release_group_1;
+ if (is_s10) {
+ /* Use IRQ to determine SError origin instead of assigning IRQ */
+ rc = of_property_read_u32_index(np, "interrupts", 1, &altdev->db_irq);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
+ goto err_release_group_1;
+ }
+ } else {
+ altdev->db_irq = irq_of_parse_and_map(np, 3);
+ if (!altdev->db_irq) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "Error PortB DBIRQ alloc\n");
+ rc = -ENODEV;
+ goto err_release_group_1;
+ }
+ rc = devm_request_irq(&altdev->ddev, altdev->db_irq,
+ prv->ecc_irq_handler, IRQF_TRIGGER_HIGH,
+ ecc_name, altdev);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "PortB DBERR IRQ error\n");
+ goto err_release_group_1;
+ }
}
-#endif
rc = edac_device_add_device(dci);
if (rc) {
@@ -1974,29 +1973,29 @@ static int altr_edac_a10_device_add(struct altr_arria10_edac *edac,
goto err_release_group1;
}
-#ifdef CONFIG_64BIT
- /* Use IRQ to determine SError origin instead of assigning IRQ */
- rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
- if (rc) {
- edac_printk(KERN_ERR, EDAC_DEVICE,
- "Unable to parse DB IRQ index\n");
- goto err_release_group1;
- }
-#else
- altdev->db_irq = irq_of_parse_and_map(np, 1);
- if (!altdev->db_irq) {
- edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
- rc = -ENODEV;
- goto err_release_group1;
- }
- rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
- IRQF_TRIGGER_HIGH,
- ecc_name, altdev);
- if (rc) {
- edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
- goto err_release_group1;
+ if (edac->is_s10) {
+ /* Use IRQ to determine SError origin instead of assigning IRQ */
+ rc = of_property_read_u32_index(np, "interrupts", 0, &altdev->db_irq);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_DEVICE,
+ "Unable to parse DB IRQ index\n");
+ goto err_release_group1;
+ }
+ } else {
+ altdev->db_irq = irq_of_parse_and_map(np, 1);
+ if (!altdev->db_irq) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "Error allocating DBIRQ\n");
+ rc = -ENODEV;
+ goto err_release_group1;
+ }
+ rc = devm_request_irq(edac->dev, altdev->db_irq, prv->ecc_irq_handler,
+ IRQF_TRIGGER_HIGH,
+ ecc_name, altdev);
+ if (rc) {
+ edac_printk(KERN_ERR, EDAC_DEVICE, "No DBERR IRQ resource\n");
+ goto err_release_group1;
+ }
}
-#endif
rc = edac_device_add_device(dci);
if (rc) {
@@ -2059,7 +2058,6 @@ static const struct irq_domain_ops a10_eccmgr_ic_ops = {
/************** Stratix 10 EDAC Double Bit Error Handler ************/
#define to_a10edac(p, m) container_of(p, struct altr_arria10_edac, m)
-#ifdef CONFIG_64BIT
/* panic routine issues reboot on non-zero panic_timeout */
extern int panic_timeout;
@@ -2106,7 +2104,6 @@ static int s10_edac_dberr_handler(struct notifier_block *this,
return NOTIFY_DONE;
}
-#endif
/****************** Arria 10 EDAC Probe Function *********************/
static int altr_edac_a10_probe(struct platform_device *pdev)
@@ -2122,6 +2119,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, edac);
INIT_LIST_HEAD(&edac->a10_ecc_devices);
+ edac->is_s10 = !!device_get_match_data(&pdev->dev);
+
edac->ecc_mgr_map =
altr_sysmgr_regmap_lookup_by_phandle(pdev->dev.of_node,
"altr,sysmgr-syscon");
@@ -2153,8 +2152,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
irq_set_chained_handler_and_data(edac->sb_irq,
altr_edac_a10_irq_handler,
edac);
-
-#ifdef CONFIG_64BIT
+ if (edac->is_s10)
{
int dberror, err_addr;
@@ -2177,15 +2175,14 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
regmap_write(edac->ecc_mgr_map,
S10_SYSMGR_UE_ADDR_OFST, 0);
}
- }
-#else
- edac->db_irq = platform_get_irq(pdev, 1);
- if (edac->db_irq < 0)
- return edac->db_irq;
+ } else {
+ edac->db_irq = platform_get_irq(pdev, 1);
+ if (edac->db_irq < 0)
+ return edac->db_irq;
- irq_set_chained_handler_and_data(edac->db_irq,
- altr_edac_a10_irq_handler, edac);
-#endif
+ irq_set_chained_handler_and_data(edac->db_irq,
+ altr_edac_a10_irq_handler, edac);
+ }
for_each_child_of_node(pdev->dev.of_node, child) {
if (!of_device_is_available(child))
@@ -2207,7 +2204,7 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
static const struct of_device_id altr_edac_a10_of_match[] = {
{ .compatible = "altr,socfpga-a10-ecc-manager" },
- { .compatible = "altr,socfpga-s10-ecc-manager" },
+ { .compatible = "altr,socfpga-s10-ecc-manager", .data = (void *)1 },
{},
};
MODULE_DEVICE_TABLE(of, altr_edac_a10_of_match);
diff --git a/drivers/edac/altera_edac.h b/drivers/edac/altera_edac.h
index f3e84172caa9..9387056fd65e 100644
--- a/drivers/edac/altera_edac.h
+++ b/drivers/edac/altera_edac.h
@@ -394,6 +394,7 @@ struct altr_arria10_edac {
struct irq_chip irq_chip;
struct list_head a10_ecc_devices;
struct notifier_block panic_notifier;
+ bool is_s10;
};
#endif /* #ifndef _ALTERA_EDAC_H */
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index c6aa69dbd9fb..475235c402e8 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -4173,6 +4173,8 @@ static int __init amd64_edac_init(void)
goto err_pci;
}
+ request_module_nowait("amd_atl");
+
/* register stuff with EDAC MCE */
if (boot_cpu_data.x86 >= 0x17) {
amd_register_ecc_decoder(decode_umc_error);
diff --git a/drivers/edac/aspeed_edac.c b/drivers/edac/aspeed_edac.c
index dadb8acbee3d..6e069b255595 100644
--- a/drivers/edac/aspeed_edac.c
+++ b/drivers/edac/aspeed_edac.c
@@ -214,10 +214,8 @@ static int config_irq(void *ctx, struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq, mcr_isr, IRQF_TRIGGER_HIGH,
DRV_NAME, ctx);
- if (rc) {
- dev_err(&pdev->dev, "unable to request irq %d\n", irq);
+ if (rc)
return rc;
- }
/* enable interrupts */
regmap_update_bits(aspeed_regmap, ASPEED_MCR_INTR_CTRL,
diff --git a/drivers/edac/debugfs.c b/drivers/edac/debugfs.c
index 8195fc9c9354..447d0c620082 100644
--- a/drivers/edac/debugfs.c
+++ b/drivers/edac/debugfs.c
@@ -1,50 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
-#include <linux/string_choices.h>
-
#include "edac_module.h"
static struct dentry *edac_debugfs;
-static ssize_t edac_fake_inject_write(struct file *file,
- const char __user *data,
- size_t count, loff_t *ppos)
-{
- struct device *dev = file->private_data;
- struct mem_ctl_info *mci = to_mci(dev);
- static enum hw_event_mc_err_type type;
- u16 errcount = mci->fake_inject_count;
-
- if (!errcount)
- errcount = 1;
-
- type = mci->fake_inject_ue ? HW_EVENT_ERR_UNCORRECTED
- : HW_EVENT_ERR_CORRECTED;
-
- printk(KERN_DEBUG
- "Generating %d %s fake error%s to %d.%d.%d to test core handling. NOTE: this won't test the driver-specific decoding logic.\n",
- errcount,
- (type == HW_EVENT_ERR_UNCORRECTED) ? "UE" : "CE",
- str_plural(errcount),
- mci->fake_inject_layer[0],
- mci->fake_inject_layer[1],
- mci->fake_inject_layer[2]
- );
- edac_mc_handle_error(type, mci, errcount, 0, 0, 0,
- mci->fake_inject_layer[0],
- mci->fake_inject_layer[1],
- mci->fake_inject_layer[2],
- "FAKE ERROR", "for EDAC testing only");
-
- return count;
-}
-
-static const struct file_operations debug_fake_inject_fops = {
- .open = simple_open,
- .write = edac_fake_inject_write,
- .llseek = generic_file_llseek,
-};
-
void __init edac_debugfs_init(void)
{
edac_debugfs = debugfs_create_dir("edac", NULL);
@@ -57,29 +16,7 @@ void edac_debugfs_exit(void)
void edac_create_debugfs_nodes(struct mem_ctl_info *mci)
{
- struct dentry *parent;
- char name[80];
- int i;
-
- parent = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
-
- for (i = 0; i < mci->n_layers; i++) {
- sprintf(name, "fake_inject_%s",
- edac_layer_name[mci->layers[i].type]);
- debugfs_create_u8(name, S_IRUGO | S_IWUSR, parent,
- &mci->fake_inject_layer[i]);
- }
-
- debugfs_create_bool("fake_inject_ue", S_IRUGO | S_IWUSR, parent,
- &mci->fake_inject_ue);
-
- debugfs_create_u16("fake_inject_count", S_IRUGO | S_IWUSR, parent,
- &mci->fake_inject_count);
-
- debugfs_create_file("fake_inject", S_IWUSR, parent, &mci->dev,
- &debug_fake_inject_fops);
-
- mci->debugfs = parent;
+ mci->debugfs = debugfs_create_dir(mci->dev.kobj.name, edac_debugfs);
}
/* Create a toplevel dir under EDAC's debugfs hierarchy */
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index cf0d3c2dfc04..638be1f47c59 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -342,14 +342,10 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
}
/*
- * edac_device_reset_delay_period
- *
- * need to stop any outstanding workq queued up at this time
- * because we will be resetting the sleep time.
- * Then restart the workq on the new delay
+ * Stop any outstanding workq queued up at this time because sleep time will
+ * be reset. Then restart the workq on the new delay.
*/
-void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
- unsigned long msec)
+void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec)
{
edac_dev->poll_msec = msec;
edac_dev->delay = msecs_to_jiffies(msec);
diff --git a/drivers/edac/edac_device_sysfs.c b/drivers/edac/edac_device_sysfs.c
index b1c2717cd023..6359007701ba 100644
--- a/drivers/edac/edac_device_sysfs.c
+++ b/drivers/edac/edac_device_sysfs.c
@@ -84,18 +84,23 @@ static ssize_t edac_device_ctl_poll_msec_show(struct edac_device_ctl_info
return sprintf(data, "%u\n", ctl_info->poll_msec);
}
-static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info
- *ctl_info, const char *data,
- size_t count)
+static ssize_t edac_device_ctl_poll_msec_store(struct edac_device_ctl_info *ctl_info,
+ const char *data, size_t count)
{
- unsigned long value;
+ unsigned int value;
+ int ret;
- /* get the value and enforce that it is non-zero, must be at least
- * one millisecond for the delay period, between scans
- * Then cancel last outstanding delay for the work request
- * and set a new one.
+ /*
+ * Get the value, make sure it is non-zero, must be at least one millisecond
+ * for the delay period between scans.
*/
- value = simple_strtoul(data, NULL, 0);
+ ret = kstrtouint(data, 0, &value);
+ if (ret < 0)
+ return ret;
+
+ if (value < 1)
+ return -EINVAL;
+
edac_device_reset_delay_period(ctl_info, value);
return count;
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index c2ed6c696e54..9b4b5582fa9f 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -129,7 +129,7 @@ static ssize_t dimmdev_location_show(struct device *dev,
ssize_t count;
count = edac_dimm_info_location(dimm, data, PAGE_SIZE);
- count += scnprintf(data + count, PAGE_SIZE - count, "\n");
+ count += sysfs_emit_at(data, count, "\n");
return count;
}
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h
index 47593afdc234..eceef5539186 100644
--- a/drivers/edac/edac_module.h
+++ b/drivers/edac/edac_module.h
@@ -52,8 +52,7 @@ bool edac_queue_work(struct delayed_work *work, unsigned long delay);
bool edac_stop_work(struct delayed_work *work);
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
-extern void edac_device_reset_delay_period(struct edac_device_ctl_info
- *edac_dev, unsigned long msec);
+extern void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev, unsigned int msec);
extern void edac_mc_reset_delay_period(unsigned long value);
/*
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c
index a8879d72d064..68d16cc8298d 100644
--- a/drivers/edac/highbank_mc_edac.c
+++ b/drivers/edac/highbank_mc_edac.c
@@ -235,10 +235,8 @@ static int highbank_mc_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
0, dev_name(&pdev->dev), mci);
- if (res < 0) {
- dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
+ if (res < 0)
goto err2;
- }
devres_close_group(&pdev->dev, NULL);
return 0;
diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c
index 94ab80197c2e..6f5fdf43e773 100644
--- a/drivers/edac/ie31200_edac.c
+++ b/drivers/edac/ie31200_edac.c
@@ -416,7 +416,23 @@ static void populate_dimm_info(struct dimm_data *dd, u32 addr_decode, int dimm,
{
dd->size = field_get(cfg->reg_mad_dimm_size_mask[dimm], addr_decode) * cfg->reg_mad_dimm_size_granularity;
dd->ranks = field_get(cfg->reg_mad_dimm_rank_mask[dimm], addr_decode) + 1;
- dd->dtype = field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode) + DEV_X8;
+
+ switch (field_get(cfg->reg_mad_dimm_width_mask[dimm], addr_decode)) {
+ case 0:
+ dd->dtype = DEV_X8;
+ break;
+ case 1:
+ dd->dtype = DEV_X16;
+ break;
+ case 2:
+ dd->dtype = DEV_X32;
+ break;
+ case 3:
+ dd->dtype = DEV_X64;
+ break;
+ default:
+ dd->dtype = DEV_UNKNOWN;
+ }
}
static void ie31200_get_dimm_config(struct mem_ctl_info *mci, void __iomem *window,
diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c
index f1fc20d4ebf6..776c5db2f598 100644
--- a/drivers/edac/igen6_edac.c
+++ b/drivers/edac/igen6_edac.c
@@ -42,7 +42,8 @@
#define GET_BITFIELD(v, lo, hi) (((v) & GENMASK_ULL(hi, lo)) >> (lo))
-#define NUM_IMC 2 /* Max memory controllers */
+/* Probing upper bound, not a hardware capability limit. */
+#define MAX_IMC_TO_PROBE 8
#define NUM_CHANNELS 2 /* Max channels */
#define NUM_DIMMS 2 /* Max DIMMs per channel */
@@ -122,6 +123,43 @@
#define MEM_SLICE_HASH_MASK(v) (GET_BITFIELD(v, 6, 19) << 6)
#define MEM_SLICE_HASH_LSB_MASK_BIT(v) GET_BITFIELD(v, 24, 26)
+/*
+ * A slice represents a portion of memory space participating in an
+ * interleave relationship within the memory hierarchy.
+ *
+ * It can represent in different levels such as:
+ *
+ * - a pair of memory controllers
+ * - a memory controller
+ * - a memory channel
+ * - a memory sub-channel / DIMM
+ *
+ * +--------+
+ * | |
+ * | Zone 1 |
+ * | |
+ * +--------+ +--------+
+ * | | | |
+ * | | | |
+ * | Zone 0 | | Zone 0 |
+ * | | | |
+ * | | | |
+ * +--------+ +--------+
+ *
+ * Slice L Slice S
+ *
+ * Memory space is divided into:
+ *
+ * - Zone 0 : Interleaved region
+ * - Zone 1 : Non-interleaved region (upper part of the large slice).
+ */
+struct slice {
+ /* Slice address. */
+ u64 addr;
+ /* Slice that @addr belongs to. */
+ int id;
+};
+
struct igen6_imc {
int mc;
struct mem_ctl_info *mci;
@@ -175,8 +213,6 @@ static struct res_config {
/* Set imc->dimm_{l_size,s_size,l_map}[chan]. */
void (*set_dimm_params)(struct igen6_imc *imc, int chan);
bool (*ibecc_available)(struct pci_dev *pdev);
- /* Extract error address logged in IBECC */
- u64 (*err_addr)(u64 ecclog);
/* Convert error address logged in IBECC to system physical address */
u64 (*err_addr_to_sys_addr)(u64 eaddr, int mc);
/* Convert error address logged in IBECC to integrated memory controller address */
@@ -184,11 +220,11 @@ static struct res_config {
} *res_cfg;
static struct igen6_pvt {
- struct igen6_imc imc[NUM_IMC];
void __iomem *memss_pma_cr;
u64 ms_hash;
u64 ms_s_size;
int ms_l_map;
+ struct igen6_imc imc[];
} *igen6_pvt;
/* The top of low usable DRAM */
@@ -225,7 +261,8 @@ static char ecclog_buf[ECCLOG_POOL_SIZE];
static struct irq_work ecclog_irq_work;
static struct work_struct ecclog_work;
-/* Compute die IDs for Elkhart Lake with IBECC */
+/* SoC compute die IDs with IBECC capability. */
+/* Elkhart Lake */
#define DID_EHL_SKU5 0x4514
#define DID_EHL_SKU6 0x4528
#define DID_EHL_SKU7 0x452a
@@ -238,22 +275,22 @@ static struct work_struct ecclog_work;
#define DID_EHL_SKU14 0x4534
#define DID_EHL_SKU15 0x4536
-/* Compute die IDs for ICL-NNPI with IBECC */
+/* ICL-NNPI */
#define DID_ICL_SKU8 0x4581
#define DID_ICL_SKU10 0x4585
#define DID_ICL_SKU11 0x4589
#define DID_ICL_SKU12 0x458d
-/* Compute die IDs for Tiger Lake with IBECC */
+/* Tiger Lake */
#define DID_TGL_SKU 0x9a14
-/* Compute die IDs for Alder Lake with IBECC */
+/* Alder Lake */
#define DID_ADL_SKU1 0x4601
#define DID_ADL_SKU2 0x4602
#define DID_ADL_SKU3 0x4621
#define DID_ADL_SKU4 0x4641
-/* Compute die IDs for Alder Lake-N with IBECC */
+/* Alder Lake-N */
#define DID_ADL_N_SKU1 0x4614
#define DID_ADL_N_SKU2 0x4617
#define DID_ADL_N_SKU3 0x461b
@@ -267,38 +304,38 @@ static struct work_struct ecclog_work;
#define DID_ADL_N_SKU11 0x467c
#define DID_ADL_N_SKU12 0x4632
-/* Compute die IDs for Arizona Beach with IBECC */
+/* Arizona Beach */
#define DID_AZB_SKU1 0x4676
-/* Compute did IDs for Amston Lake with IBECC */
+/* Amston Lake */
#define DID_ASL_SKU1 0x464a
#define DID_ASL_SKU2 0x4646
#define DID_ASL_SKU3 0x4652
-/* Compute die IDs for Raptor Lake-P with IBECC */
+/* Raptor Lake-P */
#define DID_RPL_P_SKU1 0xa706
#define DID_RPL_P_SKU2 0xa707
#define DID_RPL_P_SKU3 0xa708
#define DID_RPL_P_SKU4 0xa716
#define DID_RPL_P_SKU5 0xa718
-/* Compute die IDs for Meteor Lake-PS with IBECC */
+/* Meteor Lake-PS */
#define DID_MTL_PS_SKU1 0x7d21
#define DID_MTL_PS_SKU2 0x7d22
#define DID_MTL_PS_SKU3 0x7d23
#define DID_MTL_PS_SKU4 0x7d24
-/* Compute die IDs for Meteor Lake-P with IBECC */
+/* Meteor Lake-P */
#define DID_MTL_P_SKU1 0x7d01
#define DID_MTL_P_SKU2 0x7d02
#define DID_MTL_P_SKU3 0x7d14
-/* Compute die IDs for Arrow Lake-UH with IBECC */
+/* Arrow Lake-UH */
#define DID_ARL_UH_SKU1 0x7d06
#define DID_ARL_UH_SKU2 0x7d20
#define DID_ARL_UH_SKU3 0x7d30
-/* Compute die IDs for Panther Lake-H with IBECC */
+/* Panther Lake-H */
#define DID_PTL_H_SKU1 0xb000
#define DID_PTL_H_SKU2 0xb001
#define DID_PTL_H_SKU3 0xb002
@@ -314,15 +351,114 @@ static struct work_struct ecclog_work;
#define DID_PTL_H_SKU13 0xb02a
#define DID_PTL_H_SKU14 0xb00a
-/* Compute die IDs for Wildcat Lake with IBECC */
+/* Starfire */
+#define DID_STF_SKU1 0xb02b
+
+/* Wildcat Lake */
#define DID_WCL_SKU1 0xfd00
-/* Compute die IDs for Nova Lake-H/HX with IBECC */
+/* Nova Lake-H/HX */
#define DID_NVL_H_SKU1 0xd701
#define DID_NVL_H_SKU2 0xd702
#define DID_NVL_H_SKU3 0xd704
#define DID_NVL_H_SKU4 0xd705
+/* Remove the interleave bit and shift upper part down to fill gap. */
+static u64 squeeze_addr(u64 addr, int intlv_bit)
+{
+ u64 slice_addr;
+
+ slice_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
+ slice_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
+
+ return slice_addr;
+}
+
+/* Shift the upper bits up and insert a zero at the @intlv_bit bit position. */
+static u64 inflate_addr(u64 addr, int intlv_bit)
+{
+ u64 inflated_addr;
+
+ /* Insert a zero at @intlv_bit position. */
+ inflated_addr = GET_BITFIELD(addr, intlv_bit, 63) << (intlv_bit + 1);
+ inflated_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
+
+ return inflated_addr;
+}
+
+static u64 compute_hash(u64 addr, u64 hash_mask, u64 hash_base, int intlv_bit)
+{
+ u64 hash_addr;
+ int i;
+
+ /*
+ * In hash mode, @intlv_bit is the lowest selected bit of @addr
+ * to be XORed. While @mask may or may not include this @intlv_bit,
+ * we enforce that @mask includes @intlv_bit to ensure @intlv_bit is
+ * XORed exactly once.
+ */
+ hash_mask |= BIT_ULL(intlv_bit);
+ hash_addr = addr & hash_mask;
+
+ for (i = 6; i < 20; i++)
+ hash_base ^= (hash_addr >> i) & 1;
+
+ return hash_base;
+}
+
+/*
+ * Converts a higher-level address (system / IMC / channel) into a lower-level
+ * slice address and identifier.
+ */
+static void translate_to_lower_level(u64 addr, u64 hash_mask, u64 hash_base,
+ int intlv_bit, u64 s_size, int l_map,
+ struct slice *slice)
+{
+ /* In non-interleave zone. */
+ if (addr >= 2 * s_size) {
+ slice->addr = addr - s_size;
+ slice->id = l_map;
+ return;
+ }
+
+ /* In interleave zone. */
+ slice->addr = squeeze_addr(addr, intlv_bit);
+
+ /* Non-hash mode. */
+ if (!hash_mask) {
+ slice->id = GET_BITFIELD(addr, intlv_bit, intlv_bit);
+ return;
+ }
+
+ /* Hash mode. */
+ slice->id = compute_hash(addr, hash_mask, hash_base, intlv_bit);
+}
+
+/* Reconstruct address for upper memory hierarchy level. */
+static u64 translate_to_upper_level(u64 addr, u64 hash_mask, u64 hash_base,
+ int intlv_bit, u64 s_size)
+{
+ u64 inflated_addr, hash_val;
+
+ /* In non-interleave zone. */
+ if (addr >= s_size)
+ return addr + s_size;
+
+ /*
+ * In interleave zone.
+ *
+ * Insert a zero at @intlv_bit position.
+ */
+ inflated_addr = inflate_addr(addr, intlv_bit);
+
+ /*
+ * Reconstruct the removed interleave bit and use it to replace
+ * the zero at @intlv_bit position.
+ */
+ hash_val = compute_hash(inflated_addr, hash_mask, hash_base, intlv_bit);
+ return inflated_addr | (hash_val << intlv_bit);
+}
+
static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
{
union {
@@ -354,6 +490,46 @@ static int get_mchbar(struct pci_dev *pdev, u64 *mchbar)
return 0;
}
+/* Check whether the memory controller is absent. */
+static bool imc_absent(void __iomem *window)
+{
+ return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
+}
+
+/* Return MMIO base address of the memory controller if it's present, otherwise return NULL. */
+static void __iomem *map_imc_window(u64 mchbar, int pmc)
+{
+ void __iomem *window;
+
+ window = ioremap(mchbar + pmc * MCHBAR_SIZE, MCHBAR_SIZE);
+ if (!window)
+ return NULL;
+
+ if (imc_absent(window)) {
+ iounmap(window);
+ return NULL;
+ }
+
+ return window;
+}
+
+/* Return the number of present memory controllers. */
+static int get_imc_num(u64 mchbar)
+{
+ void __iomem *window;
+ int lmc, pmc;
+
+ for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
+ window = map_imc_window(mchbar, pmc);
+ if (window) {
+ iounmap(window);
+ lmc++;
+ }
+ }
+
+ return lmc;
+}
+
static bool ehl_ibecc_available(struct pci_dev *pdev)
{
u32 v;
@@ -450,21 +626,9 @@ static u64 mem_addr_to_sys_addr(u64 maddr)
return maddr;
}
-static u64 mem_slice_hash(u64 addr, u64 mask, u64 hash_init, int intlv_bit)
-{
- u64 hash_addr = addr & mask, hash = hash_init;
- u64 intlv = (addr >> intlv_bit) & 1;
- int i;
-
- for (i = 6; i < 20; i++)
- hash ^= (hash_addr >> i) & 1;
-
- return hash ^ intlv;
-}
-
static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
{
- u64 maddr, hash, mask, ms_s_size;
+ u64 mask, ms_s_size;
int intlv_bit;
u32 ms_hash;
@@ -477,12 +641,7 @@ static u64 tgl_err_addr_to_mem_addr(u64 eaddr, int mc)
mask = MEM_SLICE_HASH_MASK(ms_hash);
intlv_bit = MEM_SLICE_HASH_LSB_MASK_BIT(ms_hash) + 6;
- maddr = GET_BITFIELD(eaddr, intlv_bit, 63) << (intlv_bit + 1) |
- GET_BITFIELD(eaddr, 0, intlv_bit - 1);
-
- hash = mem_slice_hash(maddr, mask, mc, intlv_bit);
-
- return maddr | (hash << intlv_bit);
+ return translate_to_upper_level(eaddr, mask, mc, intlv_bit, ms_s_size);
}
static u64 tgl_err_addr_to_sys_addr(u64 eaddr, int mc)
@@ -504,8 +663,9 @@ static u64 adl_err_addr_to_sys_addr(u64 eaddr, int mc)
static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
{
- u64 imc_addr, ms_s_size = igen6_pvt->ms_s_size;
+ u64 ms_s_size = igen6_pvt->ms_s_size;
struct igen6_imc *imc = &igen6_pvt->imc[mc];
+ struct slice slice;
int intlv_bit;
u32 mc_hash;
@@ -516,15 +676,8 @@ static u64 adl_err_addr_to_imc_addr(u64 eaddr, int mc)
intlv_bit = MAC_MC_HASH_LSB(mc_hash) + 6;
- imc_addr = GET_BITFIELD(eaddr, intlv_bit + 1, 63) << intlv_bit |
- GET_BITFIELD(eaddr, 0, intlv_bit - 1);
-
- return imc_addr;
-}
-
-static u64 rpl_p_err_addr(u64 ecclog)
-{
- return field_get(res_cfg->reg_eccerrlog_addr_mask, ecclog);
+ translate_to_lower_level(eaddr, 0, 0, intlv_bit, ms_s_size, 0, &slice);
+ return slice.addr;
}
static enum mem_type ptl_h_get_mem_type(struct igen6_imc *imc)
@@ -686,6 +839,7 @@ static struct res_config tgl_cfg = {
.err_addr_to_imc_addr = tgl_err_addr_to_imc_addr,
};
+/* Shared by Alder Lake, Alder Lake-N, Arizona Beach, Amston Lake, and Raptor Lake-P */
static struct res_config adl_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -701,37 +855,6 @@ static struct res_config adl_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
-static struct res_config adl_n_cfg = {
- .machine_check = true,
- .num_imc = 1,
- .reg_mchbar_mask = GENMASK_ULL(41, 17),
- .reg_tom_mask = GENMASK_ULL(41, 20),
- .reg_touud_mask = GENMASK_ULL(41, 20),
- .reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
- .imc_base = 0xd800,
- .ibecc_base = 0xd400,
- .ibecc_error_log_offset = 0x68,
- .ibecc_available = tgl_ibecc_available,
- .err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
- .err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
-};
-
-static struct res_config rpl_p_cfg = {
- .machine_check = true,
- .num_imc = 2,
- .reg_mchbar_mask = GENMASK_ULL(41, 17),
- .reg_tom_mask = GENMASK_ULL(41, 20),
- .reg_touud_mask = GENMASK_ULL(41, 20),
- .reg_eccerrlog_addr_mask = GENMASK_ULL(45, 5),
- .imc_base = 0xd800,
- .ibecc_base = 0xd400,
- .ibecc_error_log_offset = 0x68,
- .ibecc_available = tgl_ibecc_available,
- .err_addr = rpl_p_err_addr,
- .err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
- .err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
-};
-
static struct res_config mtl_ps_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -749,6 +872,7 @@ static struct res_config mtl_ps_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
+/* Shared by Meteor Lake-P, Arrow Lake-UH, and Wildcat Lake */
static struct res_config mtl_p_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -764,6 +888,7 @@ static struct res_config mtl_p_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
+/* Shared by Panther Lake-H and Starfire */
static struct res_config ptl_h_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -794,21 +919,6 @@ static struct res_config ptl_h_cfg = {
.err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
};
-static struct res_config wcl_cfg = {
- .machine_check = true,
- .num_imc = 1,
- .reg_mchbar_mask = GENMASK_ULL(41, 17),
- .reg_tom_mask = GENMASK_ULL(41, 20),
- .reg_touud_mask = GENMASK_ULL(41, 20),
- .reg_eccerrlog_addr_mask = GENMASK_ULL(38, 5),
- .imc_base = 0xd800,
- .ibecc_base = 0xd400,
- .ibecc_error_log_offset = 0x170,
- .ibecc_available = mtl_p_ibecc_available,
- .err_addr_to_sys_addr = adl_err_addr_to_sys_addr,
- .err_addr_to_imc_addr = adl_err_addr_to_imc_addr,
-};
-
static struct res_config nvl_h_cfg = {
.machine_check = true,
.num_imc = 2,
@@ -861,27 +971,27 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_ADL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_ADL_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_n_cfg },
- { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
- { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
- { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
- { PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
- { PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&rpl_p_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU6), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU7), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU8), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU9), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU10), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU11), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ADL_N_SKU12), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_AZB_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ASL_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ASL_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_ASL_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_RPL_P_SKU1), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_RPL_P_SKU2), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_RPL_P_SKU3), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_RPL_P_SKU4), .driver_data = (kernel_ulong_t)&adl_cfg },
+ { PCI_VDEVICE(INTEL, DID_RPL_P_SKU5), .driver_data = (kernel_ulong_t)&adl_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU1), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU2), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
{ PCI_VDEVICE(INTEL, DID_MTL_PS_SKU3), .driver_data = (kernel_ulong_t)&mtl_ps_cfg },
@@ -892,6 +1002,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU2), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_ARL_UH_SKU3), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
+ { PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&mtl_p_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU2), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU3), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
@@ -906,7 +1017,7 @@ static struct pci_device_id igen6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU12), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU13), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_PTL_H_SKU14), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
- { PCI_VDEVICE(INTEL, DID_WCL_SKU1), .driver_data = (kernel_ulong_t)&wcl_cfg },
+ { PCI_VDEVICE(INTEL, DID_STF_SKU1), .driver_data = (kernel_ulong_t)&ptl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU1), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU2), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
{ PCI_VDEVICE(INTEL, DID_NVL_H_SKU3), .driver_data = (kernel_ulong_t)&nvl_h_cfg },
@@ -1007,55 +1118,13 @@ static void set_dimm_params(struct igen6_imc *imc, int chan)
imc->dimm_s_size[chan] = MAD_DIMM_CH_DIMM_S_SIZE(val);
}
-static int decode_chan_idx(u64 addr, u64 mask, int intlv_bit)
-{
- u64 hash_addr = addr & mask, hash = 0;
- u64 intlv = (addr >> intlv_bit) & 1;
- int i;
-
- for (i = 6; i < 20; i++)
- hash ^= (hash_addr >> i) & 1;
-
- return (int)hash ^ intlv;
-}
-
-static u64 decode_channel_addr(u64 addr, int intlv_bit)
-{
- u64 channel_addr;
-
- /* Remove the interleave bit and shift upper part down to fill gap */
- channel_addr = GET_BITFIELD(addr, intlv_bit + 1, 63) << intlv_bit;
- channel_addr |= GET_BITFIELD(addr, 0, intlv_bit - 1);
-
- return channel_addr;
-}
-
-static void decode_addr(u64 addr, u32 hash, u64 s_size, int l_map,
- int *idx, u64 *sub_addr)
-{
- int intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
-
- if (addr > 2 * s_size) {
- *sub_addr = addr - s_size;
- *idx = l_map;
- return;
- }
-
- if (CHANNEL_HASH_MODE(hash)) {
- *sub_addr = decode_channel_addr(addr, intlv_bit);
- *idx = decode_chan_idx(addr, CHANNEL_HASH_MASK(hash), intlv_bit);
- } else {
- *sub_addr = decode_channel_addr(addr, 6);
- *idx = GET_BITFIELD(addr, 6, 6);
- }
-}
-
static int igen6_decode(struct decoded_addr *res)
{
struct igen6_imc *imc = &igen6_pvt->imc[res->mc];
- u64 addr = res->imc_addr, sub_addr, s_size;
- int idx, l_map;
- u32 hash;
+ u64 addr = res->imc_addr, s_size;
+ int intlv_bit, l_map;
+ u32 hash, hash_mask;
+ struct slice slice;
if (addr >= igen6_tom) {
edac_dbg(0, "Address 0x%llx out of range\n", addr);
@@ -1066,17 +1135,25 @@ static int igen6_decode(struct decoded_addr *res)
hash = readl(imc->window + CHANNEL_HASH_OFFSET);
s_size = imc->ch_s_size;
l_map = imc->ch_l_map;
- decode_addr(addr, hash, s_size, l_map, &idx, &sub_addr);
- res->channel_idx = idx;
- res->channel_addr = sub_addr;
+ hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
+ intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
+
+ translate_to_lower_level(addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
+
+ res->channel_idx = slice.id;
+ res->channel_addr = slice.addr;
/* Decode sub-channel/DIMM */
hash = readl(imc->window + CHANNEL_EHASH_OFFSET);
- s_size = imc->dimm_s_size[idx];
- l_map = imc->dimm_l_map[idx];
- decode_addr(res->channel_addr, hash, s_size, l_map, &idx, &sub_addr);
- res->sub_channel_idx = idx;
- res->sub_channel_addr = sub_addr;
+ s_size = imc->dimm_s_size[res->channel_idx];
+ l_map = imc->dimm_l_map[res->channel_idx];
+ hash_mask = CHANNEL_HASH_MODE(hash) ? CHANNEL_HASH_MASK(hash) : 0;
+ intlv_bit = CHANNEL_HASH_LSB_MASK_BIT(hash) + 6;
+
+ translate_to_lower_level(res->channel_addr, hash_mask, 0, intlv_bit, s_size, l_map, &slice);
+
+ res->sub_channel_idx = slice.id;
+ res->sub_channel_addr = slice.addr;
return 0;
}
@@ -1230,11 +1307,7 @@ static void ecclog_work_cb(struct work_struct *work)
llist_for_each_entry_safe(node, tmp, head, llnode) {
memset(&res, 0, sizeof(res));
- if (res_cfg->err_addr)
- eaddr = res_cfg->err_addr(node->ecclog);
- else
- eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
-
+ eaddr = node->ecclog & res_cfg->reg_eccerrlog_addr_mask;
res.mc = node->mc;
res.sys_addr = res_cfg->err_addr_to_sys_addr(eaddr, res.mc);
res.imc_addr = res_cfg->err_addr_to_imc_addr(eaddr, res.mc);
@@ -1476,18 +1549,27 @@ static struct igen6_pvt *igen6_pvt_setup(struct pci_dev *pdev)
{
void __iomem *memss_pma_cr;
struct igen6_pvt *pvt;
+ int imc_num, rc;
u64 mchbar;
- int rc;
- pvt = kzalloc_obj(*igen6_pvt);
- if (!pvt)
+ rc = get_mchbar(pdev, &mchbar);
+ if (rc)
return NULL;
- rc = get_mchbar(pdev, &mchbar);
- if (rc) {
- kfree(pvt);
+ imc_num = get_imc_num(mchbar);
+ if (!imc_num) {
+ igen6_printk(KERN_ERR, "No mc found.\n");
return NULL;
}
+ edac_dbg(2, "%d mcs found.\n", imc_num);
+
+ /* Use the runtime detected IMC count. */
+ if (res_cfg->num_imc != imc_num)
+ res_cfg->num_imc = imc_num;
+
+ pvt = kzalloc_flex(*pvt, imc, imc_num);
+ if (!pvt)
+ return NULL;
memss_pma_cr = ioremap(mchbar, MCHBAR_SIZE * 2);
if (!memss_pma_cr) {
@@ -1572,12 +1654,6 @@ static void igen6_check(struct mem_ctl_info *mci)
irq_work_queue(&ecclog_irq_work);
}
-/* Check whether the memory controller is absent. */
-static bool igen6_imc_absent(void __iomem *window)
-{
- return readl(window + MAD_INTER_CHANNEL_OFFSET) == ~0;
-}
-
static void imc_release(struct device *dev)
{
/* Nothing to do, the 'imc' owns the 'dev' and will also release it. */
@@ -1689,26 +1765,15 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
{
void __iomem *window;
int lmc, pmc, rc;
- u64 base;
-
- for (lmc = 0, pmc = 0; pmc < NUM_IMC; pmc++) {
- base = mchbar + pmc * MCHBAR_SIZE;
- window = ioremap(base, MCHBAR_SIZE);
- if (!window) {
- igen6_printk(KERN_ERR, "Failed to ioremap 0x%llx for mc%d\n", base, pmc);
- rc = -ENOMEM;
- goto out_unregister_mcis;
- }
- if (igen6_imc_absent(window)) {
- iounmap(window);
- edac_dbg(2, "Skip absent mc%d\n", pmc);
+ for (lmc = 0, pmc = 0; pmc < MAX_IMC_TO_PROBE; pmc++) {
+ window = map_imc_window(mchbar, pmc);
+ if (!window)
continue;
- }
rc = igen6_register_mci(lmc, window, pdev);
if (rc)
- goto out_iounmap;
+ goto err_unregister;
/* Done, if all present MCs are detected and registered. */
if (++lmc >= res_cfg->num_imc)
@@ -1728,10 +1793,8 @@ static int igen6_register_mcis(struct pci_dev *pdev, u64 mchbar)
return 0;
-out_iounmap:
+err_unregister:
iounmap(window);
-
-out_unregister_mcis:
igen6_unregister_mcis();
return rc;
diff --git a/drivers/edac/thunderx_edac.c b/drivers/edac/thunderx_edac.c
index 75c04dfc3962..9c0a1e48f96f 100644
--- a/drivers/edac/thunderx_edac.c
+++ b/drivers/edac/thunderx_edac.c
@@ -729,10 +729,8 @@ static int thunderx_lmc_probe(struct pci_dev *pdev,
thunderx_lmc_err_isr,
thunderx_lmc_threaded_isr, 0,
"[EDAC] ThunderX LMC", mci);
- if (ret) {
- dev_err(&pdev->dev, "Cannot set ISR: %d\n", ret);
+ if (ret)
goto err_free;
- }
lmc->node = FIELD_GET(THUNDERX_NODE, pci_resource_start(pdev, 0));
diff --git a/drivers/edac/xgene_edac.c b/drivers/edac/xgene_edac.c
index 9955396c9a52..62b8166dc287 100644
--- a/drivers/edac/xgene_edac.c
+++ b/drivers/edac/xgene_edac.c
@@ -1924,11 +1924,8 @@ static int xgene_edac_probe(struct platform_device *pdev)
rc = devm_request_irq(&pdev->dev, irq,
xgene_edac_isr, IRQF_SHARED,
dev_name(&pdev->dev), edac);
- if (rc) {
- dev_err(&pdev->dev,
- "Could not request IRQ %d\n", irq);
+ if (rc)
goto out_err;
- }
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
index 87e15e39eb30..ec6e5bde7f80 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
@@ -299,10 +299,10 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p,
amdgpu_res_first(abo->tbo.resource, offset,
coredump->ibs[i].ib_size_dw * 4, &cursor);
while (cursor.remaining) {
- amdgpu_device_mm_access(adev, cursor.start / 4,
- &ib_content[off], cursor.size / 4,
+ amdgpu_device_mm_access(adev, cursor.start,
+ &ib_content[off], cursor.size,
false);
- off += cursor.size;
+ off += cursor.size / 4;
amdgpu_res_next(&cursor, cursor.size);
}
emit_content = true;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 44bed0ba64a3..104d1d2cbad9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -771,6 +771,9 @@ static int amdgpu_device_read_fb_via_bar0(struct amdgpu_device *adev,
if (!buf || !size)
return -EINVAL;
+ if (!amdgpu_sriov_vf(adev))
+ return -EINVAL;
+
flags = pci_resource_flags(adev->pdev, 0);
if ((flags & IORESOURCE_UNSET) || !(flags & IORESOURCE_MEM))
return -EINVAL;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
index b96f94e5169f..1a86a47406b1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
@@ -879,7 +879,7 @@ bool amdgpu_mes_queue_reset_by_mes_supported(struct amdgpu_device *adev)
return (ip_maj == 11 && mes_sched >= 0x8c) ||
((ip_maj == 12 && ip_min == 0) && mes_sched >= 0x8d) ||
- ((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x73);
+ ((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x7b);
}
/* Fix me -- node_id is used to identify the correct MES instances in the future */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index 04f6ebf31cca..42adc8e738d8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@ -396,6 +396,12 @@ static bool psp_get_runtime_db_entry(struct amdgpu_device *adev,
bool ret = false;
int i;
+ /*
+ * Runtime DB is for dGPUs only.
+ */
+ if (adev->flags & AMD_IS_APU)
+ return false;
+
if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) ||
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 0a816b3c5ff9..e43bda0cab3f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -1544,7 +1544,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
struct amdgpu_usermode_queue *queue;
const struct amdgpu_userq_funcs *userq_funcs;
unsigned long queue_id;
- int r = 0;
+ int ret = 0, r;
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) {
@@ -1555,6 +1555,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
r = userq_funcs->map(queue);
if (r) {
dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id);
+ ret = r;
continue;
}
trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
@@ -1562,5 +1563,5 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
}
}
- return r;
+ return ret;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index aedf72c2333e..bb04101b0fb5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -2090,7 +2090,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev,
after->start = eaddr + 1;
after->last = tmp->last;
after->offset = tmp->offset;
- after->offset += (after->start - tmp->start) << PAGE_SHIFT;
+ after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT;
after->flags = tmp->flags;
after->bo_va = tmp->bo_va;
list_add(&after->list, &tmp->bo_va->invalids);
@@ -3122,7 +3122,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m,
id = 0;
seq_puts(m, "\tIdle BOs:\n");
- list_for_each_entry(base, &lists->needs_update, vm_status) {
+ list_for_each_entry(base, &lists->idle, vm_status) {
if (!base->bo)
continue;
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index 69776dbe188d..0ff5a80aa918 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -1651,10 +1651,10 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(11, 0, 2):
case IP_VERSION(11, 0, 3):
if (!adev->gfx.disable_uq &&
- adev->gfx.me_fw_version >= 2420 &&
- adev->gfx.pfp_fw_version >= 2580 &&
- adev->gfx.mec_fw_version >= 2650 &&
- adev->mes.fw_version[0] >= 120) {
+ adev->gfx.me_fw_version >= 3090 &&
+ adev->gfx.pfp_fw_version >= 3190 &&
+ adev->gfx.mec_fw_version >= 3450 &&
+ adev->mes.fw_version[0] >= 147) {
adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs;
adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index 1e5fd1ef8f1d..e2a81a55c63b 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -1436,10 +1436,10 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(12, 0, 0):
case IP_VERSION(12, 0, 1):
if (!adev->gfx.disable_uq &&
- adev->gfx.me_fw_version >= 2780 &&
- adev->gfx.pfp_fw_version >= 2840 &&
- adev->gfx.mec_fw_version >= 3050 &&
- adev->mes.fw_version[0] >= 123) {
+ adev->gfx.me_fw_version >= 3090 &&
+ adev->gfx.pfp_fw_version >= 3190 &&
+ adev->gfx.mec_fw_version >= 3450 &&
+ adev->mes.fw_version[0] >= 147) {
adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs;
adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index 6cf427995078..7f91186ef1d1 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -4546,9 +4546,11 @@ static int gfx_v8_0_mqd_init(struct amdgpu_ring *ring)
/* set static priority for a queue/ring */
gfx_v8_0_mqd_set_priority(ring, mqd);
tmp = RREG32(mmCP_HQD_QUANTUM);
- tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1);
- tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1);
- tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10);
+ if (ring != &adev->gfx.kiq[0].ring) {
+ tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1);
+ tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1);
+ tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10);
+ }
mqd->cp_hqd_quantum = tmp;
/* map_queues packet doesn't need activate the queue,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 4bc947c3bd0d..9811e4e10291 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -1455,6 +1455,14 @@ static int evict_process_queues_cpsch(struct device_queue_manager *dqm,
dqm_evict_mqd_bo(dqm, q);
}
+ /*
+ * Heavy-weight TLB flush after MES removes queues to ensure
+ * in-flight memory accesses complete before memory is freed/migrated.
+ * HWS does this automatically, MES does not.
+ */
+ if (dqm->dev->kfd->shared_resources.enable_mes)
+ kfd_flush_tlb(pdd);
+
if (!dqm->dev->kfd->shared_resources.enable_mes) {
pdd->last_evict_timestamp = get_jiffies_64();
retval = execute_queues_cpsch(dqm,
@@ -3746,8 +3754,11 @@ int suspend_queues(struct kfd_process *p,
if (!per_device_suspended) {
dqm_unlock(dqm);
mutex_unlock(&p->event_mutex);
- if (total_suspended)
+ if (total_suspended) {
amdgpu_amdkfd_debug_mem_fence(dqm->dev->adev);
+ /* Heavy-weight TLB flush after MES suspends queues */
+ kfd_flush_tlb(pdd);
+ }
continue;
}
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 ef1d1cb46152..4fe40e9fcfc8 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -1169,13 +1169,13 @@ int pqm_debugfs_mqds(struct seq_file *m, void *data)
mqd_mgr = q->device->dqm->mqd_mgrs[mqd_type];
size = mqd_mgr->mqd_stride(mqd_mgr,
&q->properties);
- }
- for (xcc = 0; xcc < num_xccs; xcc++) {
- mqd = q->mqd + size * xcc;
- r = mqd_mgr->debugfs_show_mqd(m, mqd);
- if (r != 0)
- break;
+ for (xcc = 0; xcc < num_xccs; xcc++) {
+ mqd = q->mqd + size * xcc;
+ r = mqd_mgr->debugfs_show_mqd(m, mqd);
+ if (r != 0)
+ break;
+ }
}
}
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 ec483276d753..2fe934036e36 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3879,7 +3879,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
continue;
bundle->surface_updates[planes_count].surface = dc_plane;
- if (new_pcrtc_state->color_mgmt_changed) {
+ if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) {
bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction;
bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func;
bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
@@ -5698,6 +5698,10 @@ static bool should_reset_plane(struct drm_atomic_commit *state,
if (new_crtc_state->color_mgmt_changed)
return true;
+ /* Plane color pipeline or its colorop changes. */
+ if (new_plane_state->color_mgmt_changed)
+ return true;
+
/*
* On zpos change, planes need to be reordered by removing and re-adding
* them one by one to the dc state, in order of descending zpos.
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
index e61bbc310f33..b9e90ea449ca 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c
@@ -534,8 +534,12 @@ void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
panel_backlight_quirk->force_pwm)
caps->aux_support = false;
- if (caps->aux_support)
- aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
+ if (caps->aux_support) {
+ if (aconnector->dc_link->dpcd_caps.panel_luminance_control)
+ aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_VESA_AUX;
+ else
+ aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
+ }
luminance_range = &conn_base->display_info.luminance_range;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index 824ef3ce5de0..e13b96358208 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -2208,16 +2208,41 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
if (res)
return res;
- if (plane->type == DRM_PLANE_TYPE_OVERLAY &&
- plane_cap && plane_cap->per_pixel_alpha) {
+ /* Blend mode support varies on DCE generations according to HW caps
+ * and number of planes per CRTC. However, as current driver
+ * implementation only creates one primary and one cursor plane per
+ * CRTC for DCE (overlay is only created if
+ * DC_PLANE_TYPE_DCN_UNIVERSAL), the primary plane blend mode is
+ * ignored across DCE versions. Keep PREMULTI to avoid uAPI
+ * regressions: it was the default/mandatory mode for many years and,
+ * with no overlay plane, primary composes on top of a black
+ * background, where PREMULTI and PIXEL_NONE are equivalent.
+ */
+ if (plane_cap && plane_cap->type != DC_PLANE_TYPE_DCN_UNIVERSAL) {
+ unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
+ BIT(DRM_MODE_BLEND_PREMULTI);
+
+ drm_plane_create_blend_mode_property(plane, blend_caps);
+ } else if ((plane->type == DRM_PLANE_TYPE_OVERLAY ||
+ plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+ plane_cap && plane_cap->per_pixel_alpha) {
unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE);
- drm_plane_create_alpha_property(plane);
drm_plane_create_blend_mode_property(plane, blend_caps);
+
+ if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+ drm_plane_create_alpha_property(plane);
}
+ /* Cursor color format is set to CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA
+ * by default, so only advertise DRM_MODE_BLEND_PREMULTI blend mode for
+ * this type of plane.
+ */
+ if (plane->type == DRM_PLANE_TYPE_CURSOR)
+ drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI));
+
if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
/*
* Allow OVERLAY planes to be used as underlays by assigning an
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index 7eaaf38cd9ab..fc9080f0c093 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -1797,7 +1797,11 @@ bool resource_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx)
* pipe-split, merge together per same height.
*/
for (split_pipe = pipe_ctx->top_pipe; split_pipe;
- split_pipe = split_pipe->top_pipe)
+ split_pipe = split_pipe->top_pipe) {
+
+ if (split_pipe == test_pipe)
+ continue;
+
if (split_pipe->plane_state->layer_index == test_pipe->plane_state->layer_index) {
struct rect r2_half;
@@ -1809,6 +1813,7 @@ bool resource_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx)
r2_bottom = min(r2_bottom, r2_half.y + r2_half.height);
break;
}
+ }
if (r1.x >= r2.x && r1.y >= r2.y && r1_right <= r2_right && r1_bottom <= r2_bottom)
return true;
diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
index 66fe7f313ea3..62ca235cd649 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c
@@ -320,6 +320,8 @@ static struct fixed31_32 interp_tf_pts(const struct fixed31_32 *output_tf_channe
return value;
}
+#define NUM_DEGAMMA_REGIONS 9
+
bool cm3_helper_translate_curve_to_degamma_hw_format(
const struct dc_transfer_func *output_tf,
struct pwl_params *lut_params)
@@ -343,31 +345,15 @@ bool cm3_helper_translate_curve_to_degamma_hw_format(
memset(lut_params, 0, sizeof(struct pwl_params));
memset(seg_distr, 0, sizeof(seg_distr));
- if (output_tf->tf == TRANSFER_FUNCTION_PQ ||
- output_tf->tf == TRANSFER_FUNCTION_SRGB) {
- /* 9 segments
- * segments are from 2^-9 to 0
- */
- const uint8_t SEG_COUNT = 9;
- seg_distr[0] = 0; // Since we only have one point in darkest region
- for (k = 1; k < SEG_COUNT; k++)
- seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases
-
- region_start = -SEG_COUNT;
- region_end = 0;
- } else {
- /* 12 segments
- * segments are from 2^-12 to 2^0
- * There are less than 256 points, for optimization
- */
- const uint8_t SEG_COUNT = 12;
-
- for (i = 0; i < SEG_COUNT; i++)
- seg_distr[i] = 4;
+ /* 9 segments
+ * segments are from 2^-9 to 2^0
+ */
+ seg_distr[0] = 0; // Since we only have one point in darkest region
+ for (k = 1; k < NUM_DEGAMMA_REGIONS; k++)
+ seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases
- region_start = -SEG_COUNT;
- region_end = 0;
- }
+ region_start = -NUM_DEGAMMA_REGIONS;
+ region_end = 0;
for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++)
seg_distr[i] = -1;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c
index 8497eaea012e..297e21e7c68d 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn5_funcs_mode_programming.c
@@ -1128,8 +1128,8 @@ static bool dcn5_mode_programming(struct dml2_core_calcs_mode_programming_ex *in
CalculateWatermarks_params->USRRetrainingSupport = &mode_lib->mp.USRRetrainingSupport;
CalculateWatermarks_params->temp_read_or_ppt_support = mode_lib->mp.temp_read_or_ppt_support;
CalculateWatermarks_params->global_temp_read_or_ppt_supported = &mode_lib->mp.global_temp_read_or_ppt_supported;
- CalculateWatermarks_params->VActiveLatencyHidingMargin = 0;
- CalculateWatermarks_params->VActiveLatencyHidingUs = 0;
+ CalculateWatermarks_params->VActiveLatencyHidingMargin = NULL;
+ CalculateWatermarks_params->VActiveLatencyHidingUs = NULL;
dcn5_calculate_watermarks_and_dram_speed_change_support(&mode_lib->scratch, CalculateWatermarks_params);
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
index 8a371bd1a7a5..28f4a53d0617 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
@@ -1819,8 +1819,8 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param
bool UnboundedRequestEnabled;
unsigned int CompressedBufferSizeInkByte;
bool max_outstanding_when_urgent_expected;
- const unsigned int max_outstanding_requests;
- const unsigned int max_request_size_bytes;
+ unsigned int max_outstanding_requests;
+ unsigned int max_request_size_bytes;
const unsigned int *meta_row_height_l;
const unsigned int *meta_row_height_c;
const enum dml2_pstate_method *uclk_pstate_switch_modes;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c
index 0b884a8661c8..6d6611a6b5a6 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.c
@@ -1414,7 +1414,7 @@ static bool find_shift_for_valid_cache_id_assignment(const int *mcache_boundarie
success = true;
for (pipe_index = 0; pipe_index < pipe_count; pipe_index++) {
if (!calculate_first_second_splitting(mcache_boundaries, num_boundaries, *shift,
- pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], 0, 0)) {
+ pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], NULL, NULL)) {
success = false;
break;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
index 166f10b8862f..c82886323a51 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
@@ -301,6 +301,7 @@ void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, s
out->smn_latency_us = 2;
out->dispclk_dppclk_vco_speed_mhz = 3600;
out->pct_ideal_dram_bw_after_urgent_pixel_only = 65.0;
+ out->gpuvm_min_page_size_kbytes = 4;
break;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
index dd854d992692..f43fc4b78a8d 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
@@ -103,6 +103,11 @@ static int get_estimated_bw(struct dc_link *link)
{
uint8_t bw_estimated_bw = 0;
+ if (link->dpia_bw_alloc_config.bw_granularity == 0) {
+ DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__);
+ return 0;
+ }
+
core_link_read_dpcd(
link,
ESTIMATED_BW,
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index d90d1d7c9cf9..a2ef272e9f27 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -278,7 +278,14 @@ void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state,
plane_state->rotation = DRM_MODE_ROTATE_0;
plane_state->alpha = DRM_BLEND_ALPHA_OPAQUE;
+
plane_state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
+ if (plane->blend_mode_property) {
+ if (!drm_object_property_get_default_value(&plane->base,
+ plane->blend_mode_property,
+ &val))
+ plane_state->pixel_blend_mode = val;
+ }
if (plane->color_encoding_property) {
if (!drm_object_property_get_default_value(&plane->base,
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 1050dddadb17..5ea593b3a98e 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -1449,9 +1449,6 @@ static int prepare_signaling(struct drm_device *dev,
if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
struct drm_pending_vblank_event *e = crtc_state->event;
- if (!file_priv)
- continue;
-
ret = drm_event_reserve_init(dev, file_priv, &e->base,
&e->event.base);
if (ret) {
@@ -1567,6 +1564,8 @@ static void complete_signaling(struct drm_device *dev,
* to prevent a double free in drm_atomic_commit_clear.
*/
if (event && (event->base.fence || event->base.file_priv)) {
+ if (crtc_state->commit && crtc_state->commit->abort_completion)
+ drm_crtc_commit_put(crtc_state->commit);
drm_event_cancel_free(dev, &event->base);
crtc_state->event = NULL;
}
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index 892b325fa99b..a0546955d0b9 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -384,6 +384,58 @@ next:
}
/**
+ * drm_pagemap_migrate_populate_src_pages() - Populate the source page array
+ * @pages: Array of source pages to populate
+ * @src_mpfn: Source array of migrate PFNs
+ * @dst_mpfn: Destination array of migrate PFNs
+ * @npages: Number of pages in the arrays
+ *
+ * Populate @pages with the device pages the copy callback is to read from.
+ *
+ * Entries are normally only populated at the head of each source folio, with
+ * the copy callback deriving the rest of the folio from the order recorded in
+ * the corresponding drm_pagemap_addr. That does not work where
+ * drm_pagemap_migrate_populate_ram_pfn() had to demote a higher-order source
+ * folio to order-0 destination folios: the drm_pagemap_addr entries are then
+ * per-page, and the copy callback needs a source page for each of them.
+ * Populate every entry for those ranges.
+ *
+ * Note that the source folio itself is only split later, by
+ * migrate_vma_pages() / migrate_device_pages(), so its order cannot be used to
+ * detect the demotion - the destination has to be inspected instead.
+ */
+static void drm_pagemap_migrate_populate_src_pages(struct page **pages,
+ unsigned long *src_mpfn,
+ unsigned long *dst_mpfn,
+ unsigned long npages)
+{
+ unsigned long i;
+
+ for (i = 0; i < npages;) {
+ struct page *page = migrate_pfn_to_page(src_mpfn[i]);
+ unsigned int order = 0;
+ unsigned long j, nr;
+
+ if (!page) {
+ i++;
+ continue;
+ }
+
+ order = folio_order(page_folio(page));
+ nr = NR_PAGES(order);
+
+ if (order && !(dst_mpfn[i] & MIGRATE_PFN_COMPOUND)) {
+ for (j = 0; j < nr && i + j < npages; j++)
+ pages[i + j] = folio_page(page_folio(page), j);
+ } else {
+ pages[i] = page;
+ }
+
+ i += nr;
+ }
+}
+
+/**
* drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
* @dev: The device for which the pages were mapped
* @migrate_pfn: Array of migrate pfns set up for the mapped pages. Used to
@@ -875,6 +927,7 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
struct page *page = NULL, *src_page;
struct folio *folio;
unsigned int order = 0;
+ gfp_t gfp = GFP_HIGHUSER;
if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE))
goto next;
@@ -891,11 +944,51 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
order = folio_order(page_folio(src_page));
- /* TODO: Support fallback to single pages if THP allocation fails */
+ /*
+ * A large source folio is always collected whole, at its head
+ * page, PMD aligned and flagged MIGRATE_PFN_COMPOUND: anything
+ * else is split before it reaches us, either by
+ * migrate_vma_collect_pmd() or, for the eviction path, by
+ * migrate_device_pfns(). Both the order-0 fallback below and
+ * drm_pagemap_migrate_populate_src_pages() rely on that, as
+ * they index the folio from @i.
+ */
+ WARN_ON_ONCE(order &&
+ (src_page != folio_page(page_folio(src_page), 0) ||
+ !(src_mpfn[i] & MIGRATE_PFN_COMPOUND)));
+
+ if (order)
+ gfp |= __GFP_NOWARN;
+
if (vas)
- folio = vma_alloc_folio(GFP_HIGHUSER, order, vas, addr);
+ folio = vma_alloc_folio(gfp, order, vas, addr);
else
- folio = folio_alloc(GFP_HIGHUSER, order);
+ folio = folio_alloc(gfp, order);
+
+ if (!folio && order) {
+ /*
+ * Higher-order allocation failed, fall back to
+ * order-0 allocations for the entire range covered
+ * by the original higher-order allocation, without
+ * setting MIGRATE_PFN_COMPOUND, until we move past
+ * that range.
+ */
+ unsigned long nr = NR_PAGES(order);
+ unsigned long j;
+
+ gfp &= ~__GFP_NOWARN;
+ for (j = 0; j < nr && i < npages; j++, i++, addr += PAGE_SIZE) {
+ folio = vas ?
+ vma_alloc_folio(gfp, 0, vas, addr) :
+ folio_alloc(gfp, 0);
+ if (!folio)
+ goto free_pages;
+
+ page = folio_page(folio, 0);
+ mpfn[i] = migrate_pfn(page_to_pfn(page));
+ }
+ continue;
+ }
if (!folio)
goto free_pages;
@@ -940,11 +1033,11 @@ free_pages:
if (!page)
goto next_put;
+ order = folio_order(page_folio(page));
+
put_page(page);
mpfn[i] = 0;
- order = folio_order(page_folio(page));
-
next_put:
i += NR_PAGES(order);
}
@@ -1103,11 +1196,116 @@ void drm_pagemap_put(struct drm_pagemap *dpagemap)
EXPORT_SYMBOL(drm_pagemap_put);
/**
+ * drm_pagemap_page_get_flags() - Read flags from a device-private folio
+ * @page: Pointer to a page of the device-private folio
+ *
+ * Return: The DRM_PAGEMAP_ZDD_FLAG_* bits encoded in zone_device_data.
+ */
+static unsigned long drm_pagemap_page_get_flags(struct page *page)
+{
+ struct folio *folio = page_folio(page);
+
+ return (unsigned long)folio_zone_device_data(folio) &
+ DRM_PAGEMAP_ZDD_FLAG_MASK;
+}
+
+/**
+ * drm_pagemap_page_set_flags() - Set flags on a device-private folio
+ * @page: Pointer to a page of the device-private folio
+ * @flags: DRM_PAGEMAP_ZDD_FLAG_* bits to set
+ *
+ * Preserve any flags already encoded alongside the ZDD pointer.
+ */
+static void drm_pagemap_page_set_flags(struct page *page,
+ unsigned long flags)
+{
+ struct folio *folio = page_folio(page);
+ unsigned long old;
+
+ if (WARN_ON_ONCE(flags & ~DRM_PAGEMAP_ZDD_FLAG_MASK))
+ return;
+
+ old = (unsigned long)folio_zone_device_data(folio);
+ folio_set_zone_device_data(folio, (void *)(old | flags));
+}
+
+/**
+ * drm_pagemap_retire_migrated_pages() - Record migrated device-private folios
+ * @src_pfns: source array after migrate_vma_pages() or migrate_device_pages()
+ * @npages: number of entries in @src_pfns
+ *
+ * Flag device-private folios successfully migrated to RAM before finalize
+ * unlocks the sources. The migrated state is stored in the physical folio, so
+ * it survives later folio splits and subsequent migrations can skip it.
+ */
+static void drm_pagemap_retire_migrated_pages(unsigned long *src_pfns,
+ unsigned long npages)
+{
+ unsigned long i = 0;
+
+ while (i < npages) {
+ struct page *page = migrate_pfn_to_page(src_pfns[i]);
+ unsigned long nr = 1;
+
+ if (!page) {
+ i++;
+ continue;
+ }
+
+ if (src_pfns[i] & MIGRATE_PFN_COMPOUND)
+ nr = folio_nr_pages(page_folio(page));
+
+ if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) &&
+ is_device_private_page(page))
+ drm_pagemap_page_set_flags(page,
+ DRM_PAGEMAP_ZDD_FLAG_MIGRATED);
+
+ i += nr;
+ }
+}
+
+/**
+ * drm_pagemap_skip_retired_pages() - Skip retired device-private folios
+ * @src_pfns: MIGRATE_PFN-encoded source array
+ * @npages: number of entries in @src_pfns
+ *
+ * Skip source folios already migrated to RAM, identified by the migrated flag
+ * stored in the physical folio's zone_device_data.
+ */
+static void drm_pagemap_skip_retired_pages(unsigned long *src_pfns,
+ unsigned long npages)
+{
+ unsigned long i = 0;
+
+ while (i < npages) {
+ struct page *page = migrate_pfn_to_page(src_pfns[i]);
+ unsigned long nr = 1;
+
+ if (!page) {
+ i++;
+ continue;
+ }
+
+ if (src_pfns[i] & MIGRATE_PFN_COMPOUND)
+ nr = folio_nr_pages(page_folio(page));
+
+ if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) &&
+ is_device_private_page(page) &&
+ (drm_pagemap_page_get_flags(page) &
+ DRM_PAGEMAP_ZDD_FLAG_MIGRATED))
+ src_pfns[i] &= ~MIGRATE_PFN_MIGRATE;
+
+ i += nr;
+ }
+}
+
+/**
* drm_pagemap_evict_to_ram() - Evict GPU SVM range to RAM
* @devmem_allocation: Pointer to the device memory allocation
*
- * Similar to __drm_pagemap_migrate_to_ram but does not require mmap lock and
- * migration done via migrate_device_* functions.
+ * Similar to __drm_pagemap_migrate_to_ram(), but uses the
+ * migrate_device_* helpers and does not require the mmap lock.
+ * Device-private PFNs already migrated to RAM by either path are skipped.
*
* Return: 0 on success, negative error code on failure.
*/
@@ -1115,17 +1313,18 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
{
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
struct drm_pagemap_iova_state state = {};
- unsigned long npages, mpages = 0;
+ unsigned long npages, mpages;
struct page **pages;
unsigned long *src, *dst;
struct drm_pagemap_addr *pagemap_addr;
void *buf;
- int i, err = 0;
+ int err = 0;
unsigned int retry_count = 2;
npages = devmem_allocation->size >> PAGE_SHIFT;
retry:
+ mpages = 0;
if (!mmget_not_zero(devmem_allocation->mm))
return -EFAULT;
@@ -1148,6 +1347,8 @@ retry:
if (err)
goto err_free;
+ drm_pagemap_skip_retired_pages(src, npages);
+
err = drm_pagemap_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages,
src, dst, 0);
if (err || !mpages)
@@ -1160,27 +1361,20 @@ retry:
if (err)
goto err_finalize;
- for (i = 0; i < npages;) {
- unsigned int order = 0;
-
- pages[i] = migrate_pfn_to_page(src[i]);
- if (pages[i])
- order = folio_order(page_folio(pages[i]));
-
- i += NR_PAGES(order);
- }
+ drm_pagemap_migrate_populate_src_pages(pages, src, dst, npages);
err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL);
if (err)
goto err_finalize;
err_finalize:
+ drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages,
+ DMA_FROM_DEVICE, &state);
if (err)
drm_pagemap_migration_unlock_put_pages(npages, dst);
migrate_device_pages(src, dst, npages);
+ drm_pagemap_retire_migrated_pages(src, npages);
migrate_device_finalize(src, dst, npages);
- drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages,
- DMA_FROM_DEVICE, &state);
err_free:
kvfree(buf);
@@ -1235,7 +1429,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
struct drm_pagemap_addr *pagemap_addr;
unsigned long start, end;
void *buf;
- int i, err = 0;
+ int err = 0;
zdd = drm_pagemap_page_zone_device_data(page);
if (time_before64(get_jiffies_64(), zdd->devmem_allocation->timeslice_expiration))
@@ -1275,13 +1469,15 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
if (!migrate.cpages)
goto err_free;
+ drm_pagemap_skip_retired_pages(migrate.src, npages);
+
ops = zdd->devmem_allocation->ops;
dev = zdd->devmem_allocation->dev;
err = drm_pagemap_migrate_populate_ram_pfn(vas, page, npages, &mpages,
migrate.src, migrate.dst,
start);
- if (err)
+ if (err || !mpages)
goto err_finalize;
err = drm_pagemap_migrate_map_system_pages(dev, pagemap_addr,
@@ -1290,29 +1486,23 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
if (err)
goto err_finalize;
- for (i = 0; i < npages;) {
- unsigned int order = 0;
-
- pages[i] = migrate_pfn_to_page(migrate.src[i]);
- if (pages[i])
- order = folio_order(page_folio(pages[i]));
-
- i += NR_PAGES(order);
- }
+ drm_pagemap_migrate_populate_src_pages(pages, migrate.src, migrate.dst,
+ npages);
err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL);
if (err)
goto err_finalize;
err_finalize:
- if (err)
- drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
- migrate_vma_pages(&migrate);
- migrate_vma_finalize(&migrate);
if (dev)
drm_pagemap_migrate_unmap_pages(dev, pagemap_addr, migrate.dst,
npages, DMA_FROM_DEVICE,
&state);
+ if (err)
+ drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
+ migrate_vma_pages(&migrate);
+ drm_pagemap_retire_migrated_pages(migrate.src, npages);
+ migrate_vma_finalize(&migrate);
err_free:
kvfree(buf);
err_out:
@@ -1360,13 +1550,19 @@ static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf)
static void drm_pagemap_folio_split(struct folio *orig_folio, struct folio *new_folio)
{
struct drm_pagemap_zdd *zdd;
+ unsigned long orig_data, new_data;
if (!new_folio)
return;
new_folio->pgmap = orig_folio->pgmap;
- zdd = folio_zone_device_data(orig_folio);
- folio_set_zone_device_data(new_folio, drm_pagemap_zdd_get(zdd));
+
+ orig_data = (unsigned long)folio_zone_device_data(orig_folio);
+ zdd = (struct drm_pagemap_zdd *)(orig_data & ~DRM_PAGEMAP_ZDD_FLAG_MASK);
+
+ new_data = (unsigned long)drm_pagemap_zdd_get(zdd);
+ new_data |= orig_data & DRM_PAGEMAP_ZDD_FLAG_MASK;
+ folio_set_zone_device_data(new_folio, (void *)new_data);
}
static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = {
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 9b44c78cd77f..fe3436d1235d 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -524,7 +524,7 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
return PTR_ERR(dmabuf);
}
- fd_install(fd, dmabuf->file);
+ dma_buf_fd_install(dmabuf, fd);
*prime_fd = fd;
return 0;
}
diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
index ea0cca58b7c8..8141c3a1e30a 100644
--- a/drivers/gpu/drm/gud/gud_connector.c
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -396,8 +396,16 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect
}
num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
- for (i = 0; i < num_modes; i++)
- modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+ for (i = 0; i < num_modes; i++) {
+ char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
+
+ if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) {
+ ret = -EIO;
+ goto free;
+ }
+
+ modes[i] = mode;
+ }
ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
free:
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
index 89bd6ca36003..3a1b9e2a2eaa 100644
--- a/drivers/gpu/drm/gud/gud_drv.c
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -289,6 +289,8 @@ static int gud_plane_add_properties(struct gud_device *gdrm)
* but mask out any additions on future devices.
*/
val &= GUD_ROTATION_MASK;
+ if (!(val & GUD_ROTATION_0))
+ continue;
ret = drm_plane_create_rotation_property(&gdrm->plane,
DRM_MODE_ROTATE_0, val);
break;
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index a53d88727177..a1a5720996b7 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -2381,8 +2381,10 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
* dividers both syncing to an active pipe, or asynchronously
* (PIPE_NONE).
*/
- cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display);
- cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
+ if (DISPLAY_VER(display) < 30) {
+ cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display);
+ cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
+ }
if (cdctl != expected) {
if (DISPLAY_VER(display) < 20) {
@@ -2713,8 +2715,8 @@ static void intel_set_cdclk(struct intel_display *display,
}
}
-static bool dg2_power_well_count(struct intel_display *display,
- const struct intel_cdclk_state *cdclk_state)
+static int dg2_power_well_count(struct intel_display *display,
+ const struct intel_cdclk_state *cdclk_state)
{
return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0;
}
diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c
index 0673f16f6fd0..86bb96ac449b 100644
--- a/drivers/gpu/drm/i915/display/intel_cursor.c
+++ b/drivers/gpu/drm/i915/display/intel_cursor.c
@@ -530,13 +530,18 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state,
}
static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,
- struct intel_plane *plane,
- const struct intel_crtc_state *crtc_state)
+ struct intel_plane *plane)
{
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
- if (!crtc_state->enable_psr2_sel_fetch)
+ /*
+ * Clear this whenever the hardware has selective fetch, not just when
+ * the current state uses it. The cursor may have been enabled with
+ * selective fetch earlier and had its enable bit orphaned when the
+ * feature was switched off.
+ */
+ if (!HAS_PSR2_SEL_FETCH(display))
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0);
@@ -586,7 +591,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,
if (crtc_state->enable_psr2_su_region_et)
wa_16021440873(dsb, plane, crtc_state, plane_state);
else
- i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
+ i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
}
}
@@ -695,7 +700,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
if (plane_state)
i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
else
- i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
+ i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
if (plane->cursor.base != base ||
plane->cursor.size != fbc_ctl ||
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c
index 452062417ce9..dbebd7210848 100644
--- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c
@@ -3233,7 +3233,8 @@ static void intel_cx0pll_enable(struct intel_encoder *encoder,
* 8. Program DDI_CLK_VALFREQ to match intended DDI
* clock frequency.
*/
- intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock);
+ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
+ intel_ddi_link_symbol_clock(encoder, port_clock));
/*
* 9. Set PORT_CLOCK_CTL register PCLK PLL Request
@@ -3406,7 +3407,7 @@ void intel_mtl_tbt_pll_enable_clock(struct intel_encoder *encoder, int port_cloc
* clock frequency.
*/
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
- port_clock);
+ intel_ddi_link_symbol_clock(encoder, port_clock));
}
void intel_mtl_pll_enable(struct intel_encoder *encoder,
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index b8985e1e0a81..02a53c9848e1 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -1529,6 +1529,17 @@ int intel_ddi_level(struct intel_encoder *encoder,
return level;
}
+int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock)
+{
+ if (intel_encoder_is_dp(encoder))
+ return intel_dp_link_symbol_clock(clock);
+
+ if (intel_hdmi_is_frl(clock))
+ return DIV_ROUND_CLOSEST(clock * 10, 18);
+
+ return clock;
+}
+
static void
hsw_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state)
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h
index 580ecb09b8b6..239d5a403f91 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.h
+++ b/drivers/gpu/drm/i915/display/intel_ddi.h
@@ -81,6 +81,7 @@ void intel_ddi_sanitize_encoder_pll_mapping(struct intel_encoder *encoder);
int intel_ddi_level(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
int lane);
+int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock);
void intel_ddi_update_active_dpll(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc *crtc);
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 3be1643f8d03..57daed0b0b36 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -765,10 +765,6 @@ static int mst_stream_compute_config(struct intel_atomic_state *state,
if (ret)
return ret;
- ret = intel_pfit_compute_config(pipe_config, conn_state);
- if (ret)
- return ret;
-
for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) {
if (num_joined_pipes > 1)
pipe_config->joiner_pipes = GENMASK(crtc->pipe + num_joined_pipes - 1,
diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c
index 8fc6d230493f..86492651b01d 100644
--- a/drivers/gpu/drm/i915/display/intel_lt_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c
@@ -1976,7 +1976,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
* Change. We handle this step in bxt_set_cdclk().
*/
/* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */
- intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock);
+ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
+ intel_ddi_link_symbol_clock(encoder, port_clock));
/* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */
intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
@@ -2023,7 +2024,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
lane_phy_pulse_status,
lane_phy_pulse_status);
} else {
- intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock);
+ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
+ intel_ddi_link_symbol_clock(encoder, port_clock));
}
/*
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index 07a683293352..5cda1ab90e40 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -879,13 +879,18 @@ skl_plane_disable_arm(struct intel_dsb *dsb,
}
static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
- struct intel_plane *plane,
- const struct intel_crtc_state *crtc_state)
+ struct intel_plane *plane)
{
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
- if (!crtc_state->enable_psr2_sel_fetch)
+ /*
+ * Clear this whenever the hardware has selective fetch, not just when
+ * the current state uses it. The plane may have been enabled with
+ * selective fetch earlier and had its enable bit orphaned when the
+ * feature was switched off.
+ */
+ if (!HAS_PSR2_SEL_FETCH(display))
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
@@ -921,7 +926,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb,
skl_write_plane_wm(dsb, plane, crtc_state);
- icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
+ icl_plane_disable_sel_fetch_arm(dsb, plane);
if (plane_has_normalizer(plane))
intel_de_write_dsb(display, dsb,
@@ -1641,7 +1646,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id),
SEL_FETCH_PLANE_CTL_ENABLE);
else
- icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
+ icl_plane_disable_sel_fetch_arm(dsb, plane);
}
static void
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index 82415af47d54..2f03f95945f1 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -958,6 +958,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
(struct intel_device_info *) ent->driver_data;
int err;
+ if (!intel_info)
+ return -ENODEV;
+
if (intel_info->require_force_probe && !id_forced(pdev->device)) {
dev_info(&pdev->dev,
"Your graphics device %04x is not properly supported by i915 in this\n"
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h
index 7903d7470d19..01145db32c53 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h
@@ -87,4 +87,5 @@ int gp102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct
int gv100_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
int tu102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
int ga102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
+int gb202_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c
index 598513f60449..f142f6310596 100644
--- a/drivers/gpu/drm/nouveau/nouveau_chan.c
+++ b/drivers/gpu/drm/nouveau/nouveau_chan.c
@@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_channel **pchan)
{
struct nouveau_channel *chan = *pchan;
if (chan) {
+ /*
+ * Drop the kill-event subscription first. Its handler
+ * dereferences chan->fence, which the fence context teardown
+ * below frees, so leaving it armed across the teardown leaves
+ * a window for a use-after-free.
+ */
+ nvif_event_dtor(&chan->kill);
+
if (chan->fence)
nouveau_fence(chan->cli->drm)->context_del(chan);
@@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_channel **pchan)
nvif_object_dtor(&chan->nvsw);
nvif_object_dtor(&chan->gart);
nvif_object_dtor(&chan->vram);
- nvif_event_dtor(&chan->kill);
nvif_object_dtor(&chan->user);
nvif_mem_dtor(&chan->mem_userd);
nouveau_vma_del(&chan->sema.vma);
diff --git a/drivers/gpu/drm/nouveau/nouveau_dmem.c b/drivers/gpu/drm/nouveau/nouveau_dmem.c
index 9442ec6e1f6c..ad4570c50be7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dmem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dmem.c
@@ -267,7 +267,7 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf)
nouveau_fence_new(&fence, dmem->migrate.chan);
migrate_vma_pages(&args);
nouveau_dmem_fence_done(&fence);
- dma_unmap_page(drm->dev->dev, dma_info.dma_addr, PAGE_SIZE,
+ dma_unmap_page(drm->dev->dev, dma_info.dma_addr, dma_info.size,
DMA_BIDIRECTIONAL);
done:
migrate_vma_finalize(&args);
@@ -279,11 +279,25 @@ err:
static void nouveau_dmem_folio_split(struct folio *head, struct folio *tail)
{
+ struct nouveau_dmem_chunk *chunk;
+ struct nouveau_dmem *dmem;
+
if (tail == NULL)
return;
tail->pgmap = head->pgmap;
tail->mapping = head->mapping;
folio_set_zone_device_data(tail, folio_zone_device_data(head));
+
+ /*
+ * The split hands out a new independently-freeable folio that will
+ * later be released via nouveau_dmem_folio_free(); account for it so
+ * chunk->callocated stays balanced.
+ */
+ chunk = nouveau_page_to_chunk(&head->page);
+ dmem = chunk->drm->dmem;
+ spin_lock(&dmem->lock);
+ chunk->callocated++;
+ spin_unlock(&dmem->lock);
}
static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = {
@@ -772,7 +786,7 @@ static unsigned long nouveau_dmem_migrate_copy_one(struct nouveau_drm *drm,
return mpfn;
out_dma_unmap:
- dma_unmap_page(dev, dma_info->dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ dma_unmap_page(dev, dma_info->dma_addr, dma_info->size, DMA_BIDIRECTIONAL);
out_free_page:
nouveau_dmem_page_free_locked(drm, dpage);
out:
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
index fa3b4ebf38a8..2bd0376193ae 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buffer_object *bo, uint32_t page_flags)
struct nouveau_sgdma_be *nvbe;
enum ttm_caching caching;
- if (nvbo->force_coherent)
- caching = ttm_uncached;
- else if (drm->agp.bridge)
+ if (nvbo->force_coherent || drm->agp.bridge)
caching = ttm_write_combined;
else
caching = ttm_cached;
diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
index f5e4756b4de4..fc125fd44a9b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
@@ -1319,6 +1319,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->va.range);
if (!op->reg || op->reg->dirty) {
ret = -ENOENT;
+ op->reg = NULL;
goto unwind_continue;
}
@@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->va.range);
if (IS_ERR(op->ops)) {
ret = PTR_ERR(op->ops);
+ op->reg = NULL;
goto unwind_continue;
}
@@ -1473,6 +1475,7 @@ unwind:
op->va.range);
break;
case OP_UNMAP_SPARSE:
+ op->reg->dirty = false;
__nouveau_uvma_region_insert(uvmm, op->reg);
nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new,
op->ops);
@@ -1489,7 +1492,8 @@ unwind:
break;
}
- drm_gpuva_ops_free(&uvmm->base, op->ops);
+ if (!IS_ERR_OR_NULL(op->ops))
+ drm_gpuva_ops_free(&uvmm->base, op->ops);
op->ops = NULL;
op->reg = NULL;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index ea62dc97f118..96c8a5b29999 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -2846,7 +2846,7 @@ nv1b2_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
- .disp = { 0x00000001, ga102_disp_new },
+ .disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@@ -2862,7 +2862,7 @@ nv1b3_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
- .disp = { 0x00000001, ga102_disp_new },
+ .disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@@ -2878,7 +2878,7 @@ nv1b5_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
- .disp = { 0x00000001, ga102_disp_new },
+ .disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@@ -2894,7 +2894,7 @@ nv1b6_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
- .disp = { 0x00000001, ga102_disp_new },
+ .disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@@ -2910,7 +2910,7 @@ nv1b7_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
- .disp = { 0x00000001, ga102_disp_new },
+ .disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
index e1aecd3fe96c..98d6ca5ac311 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
@@ -27,6 +27,7 @@ nvkm-y += nvkm/engine/disp/gp102.o
nvkm-y += nvkm/engine/disp/gv100.o
nvkm-y += nvkm/engine/disp/tu102.o
nvkm-y += nvkm/engine/disp/ga102.o
+nvkm-y += nvkm/engine/disp/gb202.o
nvkm-y += nvkm/engine/disp/udisp.o
nvkm-y += nvkm/engine/disp/uconn.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
index ab0a85c92430..820834b5ee9b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
@@ -144,12 +144,23 @@ ga102_disp = {
},
};
+static const struct nvkm_disp_func
+ga102_gsp_disp = {
+ .uevent = &gv100_disp_chan_uevent,
+ .ramht_size = 0x2000,
+ .gsp.intr = tu102_disp_intr,
+ .gsp.head = &tu102_gsp_head,
+ .gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+ .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+ .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
int
ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
if (nvkm_gsp_rm(device->gsp))
- return r535_disp_new(&ga102_disp, device, type, inst, pdisp);
+ return r535_disp_new(&ga102_gsp_disp, device, type, inst, pdisp);
return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c
new file mode 100644
index 000000000000..d0360610f9fa
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gb202.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2026 Valve Corp.
+ */
+#include "priv.h"
+#include "head.h"
+#include "ior.h"
+
+#include <subdev/timer.h>
+
+/* GB20x (NVD5.0) reorganised the SF HDMI packet units. The AVI unit is
+ * unchanged from GV100, but the legacy VSI unit is gone. Vendor infoframes
+ * are sent through the shared generic infoframe units instead. Register
+ * layout per NVIDIA's clc971.h/clca71.h, programming sequence per
+ * nvhdmipkt_C971.c:programAdvancedInfoframeC971().
+ */
+static void
+gb202_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
+{
+ struct nvkm_device *device = ior->disp->engine.subdev.device;
+ const u32 hoff = head * 0x400;
+ /* Generic infoframe unit 1, the slot NVIDIA's driver uses for the VSI. */
+ const u32 ctrl = 0x6f0138 + hoff;
+ u8 buf[36] = {};
+ int i;
+
+ /* Disable the unit and wait for it to go idle. */
+ nvkm_mask(device, ctrl, 0x00000001, 0x00000000);
+ if (nvkm_msec(device, 2000,
+ if (!(nvkm_rd32(device, ctrl) & 0x00400000))
+ break;
+ ) < 0)
+ return;
+
+ if (!size)
+ return;
+
+ /* Clear SENT status, and point the data port at unit 1's slot. */
+ nvkm_mask(device, ctrl, 0x00800000, 0x00800000);
+ nvkm_wr32(device, 0x6f03f0 + hoff, 0x00000001);
+
+ /* The data port takes the raw packet, except that a zero is inserted
+ * in HB3 after the three header bytes. A slot is 9 dwords (HB0-3 plus
+ * up to 32 payload bytes). An HDMI infoframe carries at most PB0-27,
+ * so the tail stays zero, and we always write the whole slot.
+ */
+ size = min_t(u32, size, 31);
+ memcpy(buf, data, min_t(u32, size, 3));
+ if (size > 3)
+ memcpy(&buf[4], (u8 *)data + 3, size - 3);
+
+ for (i = 0; i < 36; i += 4) {
+ nvkm_wr32(device, 0x6f03f4 + hoff, buf[i + 0] | buf[i + 1] << 8 |
+ buf[i + 2] << 16 |
+ (u32)buf[i + 3] << 24);
+ }
+
+ /* No flip ID or scanline matching. */
+ nvkm_wr32(device, 0x6f013c + hoff, 0x00000000);
+
+ /* ENABLE | RUN_MODE=ALWAYS | LOC=VBLANK | OFFSET=1 | SIZE=0. */
+ nvkm_wr32(device, ctrl, 0x00000041);
+
+ /* Audio priority low (the init value). */
+ nvkm_wr32(device, 0x6f03f8 + hoff, 0x00000002);
+}
+
+/* General Control Packet AVMute bracket. The GCP unit moved to slot 1 on
+ * NVD5.0. Only SB0 (the AVMute bit) is ours to write so we must not do a
+ * full write here: SB1 carries the deep-color CD/PP fields, and SB1_CTRL
+ * (bit 24, new with clc871.h) controls where their generation happens (HW
+ * or driver) on these chips, with the default being HW.
+ */
+static void
+gb202_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 hdmi = head * 0x400;
+
+ nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000000);
+ nvkm_mask(device, 0x6f004c + hdmi, 0x000000ff, !enable ? 0x00000001 :
+ 0x00000010);
+ nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000001);
+}
+
+/* Same core-channel state mirror as gv100_head_state() (assembly at 0x680000,
+ * armed at +0x8000, per-head method offsets unchanged), but NVD5.0 spaces
+ * heads 0x800 apart (see NVCA7D_HEAD_SET_*(a) in clca7d.h).
+ */
+static void
+gb202_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+ const u32 hoff = (state == &head->arm) * 0x8000 + head->id * 0x800;
+ u32 data;
+
+ data = nvkm_rd32(device, 0x682064 + hoff);
+ state->vtotal = (data & 0xffff0000) >> 16;
+ state->htotal = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x682068 + hoff);
+ state->vsynce = (data & 0xffff0000) >> 16;
+ state->hsynce = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x68206c + hoff);
+ state->vblanke = (data & 0xffff0000) >> 16;
+ state->hblanke = (data & 0x0000ffff);
+ data = nvkm_rd32(device, 0x682070 + hoff);
+ state->vblanks = (data & 0xffff0000) >> 16;
+ state->hblanks = (data & 0x0000ffff);
+ /* Bit 31 is ADJ1000DIV1001, not a HERTZ bit. We don't have enough bits
+ * to add the full clock in hz on Blackwell (35 bits), but state->hz
+ * is unused and obsolete under GSP so this is fine.
+ */
+ state->hz = nvkm_rd32(device, 0x68200c + hoff) & 0x7fffffff;
+
+ data = nvkm_rd32(device, 0x682004 + hoff);
+ switch ((data & 0x000000f0) >> 4) {
+ case 5: state->or.depth = 30; break;
+ case 4: state->or.depth = 24; break;
+ case 1: state->or.depth = 18; break;
+ default:
+ state->or.depth = 18;
+ WARN_ON(1);
+ break;
+ }
+}
+
+/* NVD5.0 (GB20x and later) moved the RM head-timing interrupt enable to
+ * the low-latency vector's EN1 block. The event latch is unchanged.
+ */
+static void
+gb202_head_vblank_put(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+
+ nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000000);
+}
+
+static void
+gb202_head_vblank_get(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+
+ nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
+ nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000002);
+}
+
+static irqreturn_t
+gb202_disp_intr(struct nvkm_inth *inth)
+{
+ struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
+ irqreturn_t ret = tu102_disp_intr(inth);
+
+ /* The FE interrupt vectors are message-based on NVD5.0. Re-arm the
+ * low-latency vector so it fires again for any event that latched
+ * while we were servicing.
+ */
+ nvkm_wr32(disp->engine.subdev.device, 0x611f34, 0x00000001);
+ return ret;
+}
+
+static const struct nvkm_head_func
+gb202_gsp_head = {
+ .state = gb202_head_state,
+ .rgpos = gv100_head_rgpos,
+ .vblank_get = gb202_head_vblank_get,
+ .vblank_put = gb202_head_vblank_put,
+};
+
+/* GB20x is GSP-only. This table supplies the register programming the
+ * GSP-RM display path needs from the chip.
+ */
+static const struct nvkm_disp_func
+gb202_gsp_disp = {
+ .uevent = &gv100_disp_chan_uevent,
+ .ramht_size = 0x2000,
+ /* Head timing arrives on the dedicated low-latency vector. */
+ .gsp.intr = gb202_disp_intr,
+ .gsp.intr_low_latency = true,
+ .gsp.head = &gb202_gsp_head,
+ .gsp.hdmi_gcp = gb202_sor_hdmi_gcp,
+ /* The legacy AVI unit is unchanged on GB20x. */
+ .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+ .gsp.hdmi_infoframe_vsi = gb202_sor_hdmi_infoframe_vsi,
+};
+
+int
+gb202_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
+ struct nvkm_disp **pdisp)
+{
+ return r535_disp_new(&gb202_gsp_disp, device, type, inst, pdisp);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
index b642729c254f..5976498da909 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h
@@ -56,6 +56,8 @@ int gv100_head_new(struct nvkm_disp *, int id);
void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state);
void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline);
+extern const struct nvkm_head_func tu102_gsp_head;
+
#define HEAD_MSG(h,l,f,a...) do { \
struct nvkm_head *_h = (h); \
nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
index 3ba04bead2f9..5d682a774f2d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
@@ -194,6 +194,7 @@ void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8);
extern const struct nvkm_ior_func_hda gv100_sor_hda;
+void tu102_sor_hdmi_gcp(struct nvkm_ior *, int, bool);
void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
int nv50_pior_cnt(struct nvkm_disp *, unsigned long *);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
index a3fd7cb7c488..fde321dbd7c8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/priv.h
@@ -5,6 +5,8 @@
#include <engine/disp.h>
#include <core/enum.h>
struct nvkm_head;
+struct nvkm_head_func;
+struct nvkm_ior;
struct nvkm_outp;
struct dcb_output;
@@ -34,6 +36,20 @@ struct nvkm_disp_func {
int (*new)(struct nvkm_disp *, int id);
} wndw, head, dac, sor, pior;
+ /* Register programming that the GSP-RM display path (rm/r535) needs from
+ * the chip, everything else on that path goes through RM. The hooks are
+ * called unconditionally and the head table is handed to nvkm_head_new_().
+ */
+ struct {
+ irqreturn_t (*intr)(struct nvkm_inth *);
+ /* Head-timing interrupts arrive on a second DISP vector. */
+ bool intr_low_latency;
+ const struct nvkm_head_func *head;
+ void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable);
+ void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size);
+ void (*hdmi_infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size);
+ } gsp;
+
u16 ramht_size;
struct nvkm_sclass root;
@@ -72,6 +88,7 @@ int gv100_disp_wndw_cnt(struct nvkm_disp *, unsigned long *);
int gv100_disp_caps_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
int tu102_disp_init(struct nvkm_disp *);
+irqreturn_t tu102_disp_intr(struct nvkm_inth *);
void nv50_disp_dptmds_war_2(struct nvkm_disp *, struct dcb_output *);
void nv50_disp_dptmds_war_3(struct nvkm_disp *, struct dcb_output *);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
index dcb9f8ba374c..f6c163072ff6 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
@@ -30,6 +30,21 @@
#include <nvif/class.h>
+/* General Control Packet: bracket an audio enable/disable with AVMute
+ * through the legacy GCP SF unit. Used by the GSP-RM path, which sends the
+ * equivalent packet via RM as well but keeps the direct write in sync.
+ */
+void
+tu102_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
+{
+ struct nvkm_device *device = sor->disp->engine.subdev.device;
+ const u32 hdmi = head * 0x400;
+
+ nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
+ nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
+ nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
+}
+
void
tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
{
@@ -104,6 +119,64 @@ tu102_sor_new(struct nvkm_disp *disp, int id)
return nvkm_ior_new_(&tu102_sor, disp, SOR, id, hda & BIT(id));
}
+/* The GSP-RM display path leaves head-timing (vblank) interrupts and their
+ * enables to us. These program the RM head-timing line (bit 1 of the
+ * per-head enable, not the bit nvkm's own gv100 path uses).
+ */
+static void
+tu102_head_vblank_put(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+
+ nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000);
+}
+
+static void
+tu102_head_vblank_get(struct nvkm_head *head)
+{
+ struct nvkm_device *device = head->disp->engine.subdev.device;
+
+ nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
+ nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
+}
+
+const struct nvkm_head_func
+tu102_gsp_head = {
+ .state = gv100_head_state,
+ .rgpos = gv100_head_rgpos,
+ .vblank_get = tu102_head_vblank_get,
+ .vblank_put = tu102_head_vblank_put,
+};
+
+static void
+tu102_disp_intr_head_timing(struct nvkm_disp *disp, int head)
+{
+ struct nvkm_subdev *subdev = &disp->engine.subdev;
+ struct nvkm_device *device = subdev->device;
+ u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04));
+
+ if (stat & 0x00000002) {
+ nvkm_disp_vblank(disp, head);
+
+ nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002);
+ }
+}
+
+irqreturn_t
+tu102_disp_intr(struct nvkm_inth *inth)
+{
+ struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
+ struct nvkm_subdev *subdev = &disp->engine.subdev;
+ struct nvkm_device *device = subdev->device;
+ unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff;
+ int head;
+
+ for_each_set_bit(head, &mask, 8)
+ tu102_disp_intr_head_timing(disp, head);
+
+ return IRQ_HANDLED;
+}
+
int
tu102_disp_init(struct nvkm_disp *disp)
{
@@ -230,12 +303,23 @@ tu102_disp = {
},
};
+static const struct nvkm_disp_func
+tu102_gsp_disp = {
+ .uevent = &gv100_disp_chan_uevent,
+ .ramht_size = 0x2000,
+ .gsp.intr = tu102_disp_intr,
+ .gsp.head = &tu102_gsp_head,
+ .gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
+ .gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
+ .gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+};
+
int
tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
if (nvkm_gsp_rm(device->gsp))
- return r535_disp_new(&tu102_disp, device, type, inst, pdisp);
+ return r535_disp_new(&tu102_gsp_disp, device, type, inst, pdisp);
return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
index e77733a5d9c3..f5f22173fc2c 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c
@@ -400,16 +400,16 @@ r535_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
r535_sor_dp_audio_mute(sor, false);
}
-static void
-r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
+static int
+r535_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
{
struct nvkm_disp *disp = sor->disp;
struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
- if (WARN_ON(IS_ERR(ctrl)))
- return;
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);
ctrl->subDeviceInstance = 0;
ctrl->head = head;
@@ -429,12 +429,20 @@ r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u
ctrl->MST.sendACT = 0;
ctrl->MST.singleHeadMSTPipeline = 0;
ctrl->MST.bEnableAudioOverRightPanel = 0;
- WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
+ return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
+}
+
+static void
+r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
+{
+ const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api;
+
+ WARN_ON(rmapi->disp->dp.vcpi(sor, head, slot, slot_nr, pbn, aligned_pbn));
}
static int
-r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
- u32 watermark, u32 hblanksym, u32 vblanksym)
+r535_dp_sst(struct nvkm_ior *sor, int head, bool ef,
+ u32 watermark, u32 hblanksym, u32 vblanksym)
{
struct nvkm_disp *disp = sor->disp;
struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
@@ -461,6 +469,15 @@ r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
}
+static int
+r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
+ u32 watermark, u32 hblanksym, u32 vblanksym)
+{
+ const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api;
+
+ return rmapi->disp->dp.sst(sor, head, ef, watermark, hblanksym, vblanksym);
+}
+
static const struct nvkm_ior_func_dp
r535_sor_dp = {
.sst = r535_sor_dp_sst,
@@ -545,16 +562,21 @@ r535_sor_hdmi_ctrl_audio(struct nvkm_outp *outp, bool enable)
static void
r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable)
{
- struct nvkm_device *device = sor->disp->engine.subdev.device;
- const u32 hdmi = head * 0x400;
-
r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable);
r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable);
+ sor->disp->func->gsp.hdmi_gcp(sor, head, enable);
+}
+
+static void
+r535_sor_hdmi_infoframe_avi(struct nvkm_ior *sor, int head, void *data, u32 size)
+{
+ sor->disp->func->gsp.hdmi_infoframe_avi(sor, head, data, size);
+}
- /* General Control (GCP). */
- nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
- nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
- nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
+static void
+r535_sor_hdmi_infoframe_vsi(struct nvkm_ior *sor, int head, void *data, u32 size)
+{
+ sor->disp->func->gsp.hdmi_infoframe_vsi(sor, head, data, size);
}
static void
@@ -582,8 +604,8 @@ r535_sor_hdmi = {
.ctrl = r535_sor_hdmi_ctrl,
.scdc = r535_sor_hdmi_scdc,
/*TODO: SF_USER -> KMS. */
- .infoframe_avi = gv100_sor_hdmi_infoframe_avi,
- .infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
+ .infoframe_avi = r535_sor_hdmi_infoframe_avi,
+ .infoframe_vsi = r535_sor_hdmi_infoframe_vsi,
.audio = r535_sor_hdmi_audio,
};
@@ -608,31 +630,6 @@ r535_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask)
return 4;
}
-static void
-r535_head_vblank_put(struct nvkm_head *head)
-{
- struct nvkm_device *device = head->disp->engine.subdev.device;
-
- nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000);
-}
-
-static void
-r535_head_vblank_get(struct nvkm_head *head)
-{
- struct nvkm_device *device = head->disp->engine.subdev.device;
-
- nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
- nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
-}
-
-static const struct nvkm_head_func
-r535_head = {
- .state = gv100_head_state,
- .rgpos = gv100_head_rgpos,
- .vblank_get = r535_head_vblank_get,
- .vblank_put = r535_head_vblank_put,
-};
-
static struct nvkm_conn *
r535_conn_new(struct nvkm_disp *disp, u32 id)
{
@@ -1405,35 +1402,6 @@ r535_disp_event = {
};
static void
-r535_disp_intr_head_timing(struct nvkm_disp *disp, int head)
-{
- struct nvkm_subdev *subdev = &disp->engine.subdev;
- struct nvkm_device *device = subdev->device;
- u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04));
-
- if (stat & 0x00000002) {
- nvkm_disp_vblank(disp, head);
-
- nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002);
- }
-}
-
-static irqreturn_t
-r535_disp_intr(struct nvkm_inth *inth)
-{
- struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
- struct nvkm_subdev *subdev = &disp->engine.subdev;
- struct nvkm_device *device = subdev->device;
- unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff;
- int head;
-
- for_each_set_bit(head, &mask, 8)
- r535_disp_intr_head_timing(disp, head);
-
- return IRQ_HANDLED;
-}
-
-static void
r535_disp_fini(struct nvkm_disp *disp, bool suspend)
{
if (!disp->engine.subdev.use.enabled)
@@ -1659,7 +1627,7 @@ r535_disp_oneinit(struct nvkm_disp *disp)
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
for_each_set_bit(i, &disp->head.mask, disp->head.nr) {
- ret = nvkm_head_new_(&r535_head, disp, i);
+ ret = nvkm_head_new_(disp->func->gsp.head, disp, i);
if (ret)
return ret;
}
@@ -1703,12 +1671,20 @@ r535_disp_oneinit(struct nvkm_disp *disp)
if (ret)
return ret;
- ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst);
+ /* Chips that raise head-timing interrupts on a separate low-latency
+ * vector report it as a second DISP interrupt table entry, exposed
+ * as instance 1 by the RM engine-index translation (see
+ * r570_gsp_xlat_mc_engine_idx()). Their high-latency vector
+ * (instance 0) is left unhandled as no event nouveau enables is
+ * routed to it, and without a handler it stays masked.
+ */
+ ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type,
+ disp->func->gsp.intr_low_latency ? 1 : disp->engine.subdev.inst);
if (ret < 0)
return ret;
ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev,
- r535_disp_intr, &disp->engine.subdev.inth);
+ disp->func->gsp.intr, &disp->engine.subdev.inth);
if (ret)
return ret;
@@ -1741,6 +1717,7 @@ r535_disp_new(const struct nvkm_disp_func *hw, struct nvkm_device *device,
rm->uevent = hw->uevent;
rm->sor.cnt = r535_sor_cnt;
rm->sor.new = r535_sor_new;
+ rm->gsp = hw->gsp;
rm->ramht_size = hw->ramht_size;
rm->root.oclass = gpu->disp.class.root;
@@ -1782,6 +1759,8 @@ r535_disp = {
.dp = {
.get_caps = r535_dp_get_caps,
.set_indexed_link_rates = r535_dp_set_indexed_link_rates,
+ .sst = r535_dp_sst,
+ .vcpi = r535_dp_vcpi,
},
.chan = {
.set_pushbuf = r535_disp_chan_set_pushbuf,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c
index a96e31c2d80b..8a23837f356e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/disp.c
@@ -5,6 +5,7 @@
#include <rm/rm.h>
#include <engine/disp.h>
+#include <engine/disp/ior.h>
#include <engine/disp/outp.h>
#include "nvhw/drf.h"
@@ -75,6 +76,67 @@ r570_disp_chan_set_pushbuf(struct nvkm_disp *disp, s32 oclass, int inst, struct
}
static int
+r570_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
+{
+ struct nvkm_disp *disp = sor->disp;
+ NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
+
+ ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
+ NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);
+
+ ctrl->subDeviceInstance = 0;
+ ctrl->head = head;
+ ctrl->sorIndex = sor->id;
+ ctrl->dpLink = sor->asy.link == 2;
+ ctrl->bEnableOverride = 1;
+ ctrl->bMST = 1;
+ ctrl->hBlankSym = 0;
+ ctrl->vBlankSym = 0;
+ ctrl->colorFormat = 0;
+ ctrl->bEnableTwoHeadOneOr = 0;
+ ctrl->singleHeadMultistreamMode = 0;
+ ctrl->MST.slotStart = slot;
+ ctrl->MST.slotEnd = slot + slot_nr - 1;
+ ctrl->MST.PBN = pbn;
+ ctrl->MST.Timeslice = aligned_pbn;
+ ctrl->MST.sendACT = 0;
+ ctrl->MST.singleHeadMSTPipeline = 0;
+ ctrl->MST.bEnableAudioOverRightPanel = 0;
+ return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
+}
+
+static int
+r570_dp_sst(struct nvkm_ior *sor, int head, bool ef,
+ u32 watermark, u32 hblanksym, u32 vblanksym)
+{
+ struct nvkm_disp *disp = sor->disp;
+ NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
+
+ ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
+ NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
+ if (IS_ERR(ctrl))
+ return PTR_ERR(ctrl);
+
+ ctrl->subDeviceInstance = 0;
+ ctrl->head = head;
+ ctrl->sorIndex = sor->id;
+ ctrl->dpLink = sor->asy.link == 2;
+ ctrl->bEnableOverride = 1;
+ ctrl->bMST = 0;
+ ctrl->hBlankSym = hblanksym;
+ ctrl->vBlankSym = vblanksym;
+ ctrl->colorFormat = 0;
+ ctrl->bEnableTwoHeadOneOr = 0;
+ ctrl->SST.bEnhancedFraming = ef;
+ ctrl->SST.tuSize = 64;
+ ctrl->SST.waterMark = watermark;
+ ctrl->SST.bEnableAudioOverRightPanel = 0;
+ return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
+}
+
+static int
r570_dp_set_indexed_link_rates(struct nvkm_outp *outp)
{
NV0073_CTRL_CMD_DP_CONFIG_INDEXED_LINK_RATES_PARAMS *ctrl;
@@ -255,6 +317,8 @@ r570_disp = {
.dp = {
.get_caps = r570_dp_get_caps,
.set_indexed_link_rates = r570_dp_set_indexed_link_rates,
+ .sst = r570_dp_sst,
+ .vcpi = r570_dp_vcpi,
},
.chan = {
.set_pushbuf = r570_disp_chan_set_pushbuf,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c
index 996941c668ba..1488771c63fc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/gsp.c
@@ -44,6 +44,15 @@ r570_gsp_xlat_mc_engine_idx(u32 mc_engine_idx, enum nvkm_subdev_type *ptype, int
*ptype = NVKM_ENGINE_DISP;
*pinst = 0;
return true;
+ case MC_ENGINE_IDX_DISP_LOW:
+ /* GB20x+ report a separate low-latency display vector, used
+ * for head-timing interrupts. Expose it as a second DISP
+ * interrupt instance. r535_disp_oneinit() attaches the
+ * handler to it when the chip's gsp.intr_low_latency is set.
+ */
+ *ptype = NVKM_ENGINE_DISP;
+ *pinst = 1;
+ return true;
case MC_ENGINE_IDX_CE0 ... MC_ENGINE_IDX_CE19:
*ptype = NVKM_ENGINE_CE;
*pinst = mc_engine_idx - MC_ENGINE_IDX_CE0;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h
index 06e972835d77..742b25a2a12d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r570/nvrm/disp.h
@@ -256,6 +256,8 @@ typedef struct NV0073_CTRL_DP_CTRL_PARAMS {
NvU32 eightLaneDpcdBaseAddr;
} NV0073_CTRL_DP_CTRL_PARAMS;
+#define NV0073_CTRL_CMD_DP_CONFIG_STREAM (0x731362U) /* finn: Evaluated from "(FINN_NV04_DISPLAY_COMMON_DP_INTERFACE_ID << 8) | NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS_MESSAGE_ID" */
+
typedef struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS {
NvU32 subDeviceInstance;
NvU32 head;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h
index a9af94adf9ef..fcd0221dcea1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/rm.h
@@ -6,6 +6,7 @@
#ifndef __NVKM_RM_H__
#define __NVKM_RM_H__
#include "handles.h"
+struct nvkm_ior;
struct nvkm_outp;
struct r535_gr;
@@ -93,6 +94,10 @@ struct nvkm_rm_api {
struct {
int (*get_caps)(struct nvkm_disp *, int *link_bw, bool *mst, bool *wm);
int (*set_indexed_link_rates)(struct nvkm_outp *);
+ int (*sst)(struct nvkm_ior *, int head, bool ef,
+ u32 watermark, u32 hblanksym, u32 vblanksym);
+ int (*vcpi)(struct nvkm_ior *, int head,
+ u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn);
} dp;
struct {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c
index f4489efc94a7..22b0fde6ba34 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/nv50.c
@@ -195,6 +195,9 @@ check_io_mapping(struct nv50_instmem *imem)
{
struct nvkm_device *device = imem->base.subdev.device;
+ if (imem->iomap.size)
+ return true;
+
return io_mapping_init_wc(&imem->iomap,
device->func->resource_addr(device, NVKM_BAR2_INST),
device->func->resource_size(device, NVKM_BAR2_INST)) != NULL;
diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c
index 819aed466727..9d60db45139c 100644
--- a/drivers/gpu/drm/sysfb/ofdrm.c
+++ b/drivers/gpu/drm/sysfb/ofdrm.c
@@ -2,6 +2,7 @@
#include <linux/aperture.h>
#include <linux/of_address.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@@ -238,7 +239,7 @@ static bool is_avivo(u32 vendor, u32 device)
/* This will match most R5xx */
return (vendor == PCI_VENDOR_ID_ATI) &&
((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) ||
- (PCI_VENDOR_ID_ATI_R600 >= 0x9400));
+ (device >= PCI_VENDOR_ID_ATI_R600));
}
static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node)
@@ -913,7 +914,10 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
return ERR_PTR(-EINVAL);
}
- fb_size = linebytes * height;
+ if (check_mul_overflow(linebytes, height, &fb_size)) {
+ drm_err(dev, "framebuffer size exceeds maximum\n");
+ return ERR_PTR(-EINVAL);
+ }
/*
* Try to figure out the address of the framebuffer. Unfortunately, Open
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 0b3fcc7011b3..fefc3761a4bc 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -904,6 +904,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
struct tegra_dc *dc)
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
+ unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
struct tegra_plane *plane;
unsigned int num_formats;
@@ -939,6 +940,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
+ drm_plane_create_blend_mode_property(&plane->base, blend_caps);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
err = drm_plane_create_rotation_property(&plane->base,
@@ -1209,6 +1211,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
struct tegra_dc *dc)
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
+ unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
struct tegra_plane *plane;
unsigned int num_formats;
const u32 *formats;
@@ -1252,6 +1255,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
+ drm_plane_create_blend_mode_property(&plane->base, blend_caps);
drm_plane_create_zpos_immutable_property(&plane->base, 255);
return &plane->base;
@@ -1356,6 +1360,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
bool cursor)
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
+ unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
struct tegra_plane *plane;
unsigned int num_formats;
enum drm_plane_type type;
@@ -1394,6 +1399,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
+ drm_plane_create_blend_mode_property(&plane->base, blend_caps);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
err = drm_plane_create_rotation_property(&plane->base,
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
index bd442bfd4540..448f49f3a7d7 100644
--- a/drivers/gpu/drm/tegra/hub.c
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -759,6 +759,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
unsigned int index,
enum drm_plane_type type)
{
+ unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
struct tegra_drm *tegra = drm->dev_private;
struct tegra_display_hub *hub = tegra->hub;
struct tegra_shared_plane *plane;
@@ -797,6 +798,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
+ drm_plane_create_blend_mode_property(p, blend_caps);
drm_plane_create_zpos_property(p, 0, 0, 255);
return p;
diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c
index 075221b431d3..3bf23fcf6574 100644
--- a/drivers/gpu/drm/tiny/cirrus-qemu.c
+++ b/drivers/gpu/drm/tiny/cirrus-qemu.c
@@ -582,6 +582,9 @@ static int cirrus_pci_probe(struct pci_dev *pdev,
struct cirrus_device *cirrus;
int ret;
+ if (pci_resource_len(pdev, 0) < CIRRUS_VRAM_SIZE)
+ return -ENODEV;
+
ret = aperture_remove_conflicting_pci_devices(pdev, cirrus_driver.name);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index 44ffffec550f..a1a875a0c706 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -344,7 +344,7 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev,
if (ret) {
kfree(virtio_gpu_fb);
drm_gem_object_put(obj);
- return NULL;
+ return ERR_PTR(ret);
}
return &virtio_gpu_fb->base;
@@ -378,8 +378,11 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
vgdev->ddev->mode_config.fb_modifiers_not_supported = true;
- for (i = 0 ; i < vgdev->num_scanouts; ++i)
- vgdev_output_init(vgdev, i);
+ for (i = 0; i < vgdev->num_scanouts; ++i) {
+ ret = vgdev_output_init(vgdev, i);
+ if (ret)
+ return ret;
+ }
ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts);
if (ret)
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 17a6a4d26516..9df4c7117341 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -43,6 +43,8 @@
#include <drm/drm_probe_helper.h>
#include <drm/virtgpu_drm.h>
+#include <xen/xen.h>
+
#define DRIVER_NAME "virtio_gpu"
#define DRIVER_DESC "virtio GPU"
@@ -60,6 +62,24 @@
/* See virtio_gpu_ctx_create. One additional character for NULL terminator. */
#define DEBUG_NAME_MAX_LEN 65
+/*
+ * Whether the host must be told about resource backing pages by DMA address
+ * rather than guest-physical address.
+ *
+ * This mirrors vring_use_map_api() in drivers/virtio/virtio_ring.c, including
+ * its xen_domain() case.
+ */
+static inline bool virtio_gpu_use_dma_api(const struct virtio_device *vdev)
+{
+ if (!virtio_has_dma_quirk(vdev))
+ return true;
+
+ if (xen_domain())
+ return true;
+
+ return false;
+}
+
struct virtio_gpu_object_params {
unsigned long size;
bool dumb;
@@ -343,6 +363,7 @@ void virtio_gpu_array_put_free_work(struct work_struct *work);
/* virtgpu_vq.c */
int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev);
void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev);
+void virtio_gpu_reclaim_vbufs(struct virtio_gpu_device *vgdev);
void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo,
struct virtio_gpu_object_params *params,
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 1ffe1e431f65..1d4d3bf46a20 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -352,6 +352,7 @@ void virtio_gpu_deinit(struct drm_device *dev)
flush_work(&vgdev->cursorq.dequeue_work);
flush_work(&vgdev->config_changed_work);
virtio_reset_device(vgdev->vdev);
+ virtio_gpu_reclaim_vbufs(vgdev);
vgdev->vdev->config->del_vqs(vgdev->vdev);
mutex_destroy(&vgdev->obj_restore_lock);
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 9bc0bd68c314..49899485be6f 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -173,7 +173,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
struct virtio_gpu_mem_entry **ents,
unsigned int *nents)
{
- bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
+ bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
struct scatterlist *sg;
struct sg_table *pages;
int si;
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index f2b0ab0f610e..c02c03c10d92 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -208,6 +208,21 @@ static void free_vbuf(struct virtio_gpu_device *vgdev,
kmem_cache_free(vgdev->vbufs, vbuf);
}
+void virtio_gpu_reclaim_vbufs(struct virtio_gpu_device *vgdev)
+{
+ struct virtio_gpu_vbuffer *vbuf;
+
+ while ((vbuf = virtqueue_detach_unused_buf(vgdev->ctrlq.vq))) {
+ if (vbuf->objs)
+ virtio_gpu_array_put_free(vbuf->objs);
+ if (vbuf->resp_cb_data)
+ virtio_gpu_cleanup_object(vbuf->resp_cb_data);
+ free_vbuf(vgdev, vbuf);
+ }
+ while ((vbuf = virtqueue_detach_unused_buf(vgdev->cursorq.vq)))
+ free_vbuf(vgdev, vbuf);
+}
+
static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list)
{
struct virtio_gpu_vbuffer *vbuf;
@@ -764,7 +779,7 @@ int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_to_host_2d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
- bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
+ bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
if (virtio_gpu_is_shmem(bo) && use_dma_api)
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
@@ -795,7 +810,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_to_host_2d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
- bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
+ bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
if (virtio_gpu_is_shmem(bo) && use_dma_api)
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
@@ -1228,7 +1243,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
- bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
+ bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
if (virtio_gpu_is_shmem(bo) && use_dma_api)
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index 9c5384b95c63..ab09dcff5860 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -2435,9 +2435,9 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
oa_config->id = idr_alloc(&oa->metrics_idr, oa_config, 1, 0, GFP_KERNEL);
if (oa_config->id < 0) {
- drm_dbg(&oa->xe->drm, "Failed to create sysfs entry for OA config\n");
+ drm_dbg(&oa->xe->drm, "Failed to allocate id for OA config\n");
err = oa_config->id;
- goto sysfs_err;
+ goto id_alloc_err;
}
id = oa_config->id;
@@ -2448,6 +2448,8 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
return id;
+id_alloc_err:
+ sysfs_remove_group(oa->metrics_kobj, &oa_config->sysfs_metric);
sysfs_err:
mutex_unlock(&oa->metrics_lock);
reg_err:
diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c
index 7b4478fb1697..56cff1e44530 100644
--- a/drivers/gpu/drm/xe/xe_vram.c
+++ b/drivers/gpu/drm/xe/xe_vram.c
@@ -90,6 +90,9 @@ static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset)
offset |= offset_lo << 6; /* HW view bits 31:6 */
offset *= num_enabled; /* convert to SW view */
+ drm_info(&xe->drm, "FLAT_CCS base:%llx, aligned:%s\n", offset,
+ str_yes_no(IS_ALIGNED(offset, SZ_128K)));
+
/*
* Everything below this offset is handed to the VRAM
* allocator, so it has to be the *first* address the
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index aa7fa11a0197..a81bf51cbcf1 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1253,7 +1253,7 @@ config HID_HYPERV_MOUSE
config HID_HYPERV_MOUSE_KUNIT_TEST
bool "KUnit tests for Hyper-V mouse driver" if !KUNIT_ALL_TESTS
- depends on KUNIT && HID_HYPERV_MOUSE
+ depends on KUNIT && (HID_HYPERV_MOUSE = KUNIT || KUNIT = y)
default KUNIT_ALL_TESTS
help
Builds unit tests for the Hyper-V synthetic HID driver.
diff --git a/drivers/hid/bpf/hid_bpf_struct_ops.c b/drivers/hid/bpf/hid_bpf_struct_ops.c
index 702c22fae136..c90b68956cb3 100644
--- a/drivers/hid/bpf/hid_bpf_struct_ops.c
+++ b/drivers/hid/bpf/hid_bpf_struct_ops.c
@@ -62,6 +62,10 @@ struct hid_bpf_offset_write_range {
u32 end;
};
+struct hid_bpf_ctx__safe_trusted {
+ struct hid_device *hid;
+};
+
static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
const struct bpf_reg_state *reg,
int off, int size)
@@ -86,6 +90,8 @@ static int hid_bpf_ops_btf_struct_access(struct bpf_verifier_log *log,
const char *cur = NULL;
int i;
+ BTF_TYPE_EMIT(struct hid_bpf_ctx__safe_trusted);
+
t = btf_type_by_id(reg->btf, reg->btf_id);
for (i = 0; i < ARRAY_SIZE(write_ranges); i++) {
@@ -250,6 +256,11 @@ static void hid_bpf_unreg(void *kdata, struct bpf_link *link)
mutex_lock(&hdev->bpf.prog_list_lock);
+ if (!ops->hdev) {
+ mutex_unlock(&hdev->bpf.prog_list_lock);
+ return;
+ }
+
list_del_rcu(&ops->list);
synchronize_srcu(&hdev->bpf.srcu);
ops->hdev = NULL;
@@ -310,13 +321,17 @@ static struct bpf_struct_ops bpf_hid_bpf_ops = {
void __hid_bpf_ops_destroy_device(struct hid_device *hdev)
{
struct hid_bpf_ops *e;
+ int count = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(e, &hdev->bpf.prog_list, list) {
- hid_put_device(hdev);
+ mutex_lock(&hdev->bpf.prog_list_lock);
+ list_for_each_entry(e, &hdev->bpf.prog_list, list) {
e->hdev = NULL;
+ count++;
}
- rcu_read_unlock();
+ mutex_unlock(&hdev->bpf.prog_list_lock);
+
+ while (count--)
+ hid_put_device(hdev);
}
static int __init hid_bpf_struct_ops_init(void)
diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index 6579bd19da13..cfc061dbdd24 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -687,7 +687,7 @@ static void mousevsc_device_info_valid_descriptor(struct kunit *test)
info->hid_descriptor.bLength = sizeof(struct hid_descriptor);
info->hid_descriptor.rpt_desc.wDescriptorLength = cpu_to_le16(4);
- report = ((u8 *)&info->hid_descriptor) + info->hid_descriptor.bLength;
+ report = (u8 *)(info + 1);
memset(report, 0x42, 4);
mousevsc_on_receive_device_info(input_dev, info, sizeof(*info) + 4);
@@ -713,7 +713,7 @@ static void mousevsc_device_info_report_desc_oob(struct kunit *test)
info->hid_descriptor.bLength = sizeof(struct hid_descriptor);
info->hid_descriptor.rpt_desc.wDescriptorLength = cpu_to_le16(64);
- report = ((u8 *)&info->hid_descriptor) + info->hid_descriptor.bLength;
+ report = (u8 *)(info + 1);
memset(report, 0x42, 8);
mousevsc_on_receive_device_info(input_dev, info, sizeof(*info) + 8);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 341bf587863b..b3aca5aa9176 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -338,6 +338,7 @@
#define I2C_VENDOR_ID_CIRQUE 0x0488
#define I2C_PRODUCT_ID_CIRQUE_1063 0x1063
+#define I2C_PRODUCT_ID_CIRQUE_D0C1 0xD0C1
#define USB_VENDOR_ID_CJTOUCH 0x24b8
#define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 2c41bacab1ca..451c7324e6a0 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -1321,21 +1321,18 @@ static void mt_touch_report(struct hid_device *hid,
* Includes multi-packet support where subsequent
* packets are sent with zero contactcount.
*/
- if (contact_count >= 0) {
+ if (contact_count > 0)
+ app->num_expected = contact_count;
+ else if (app->num_received == 0 && app->prev_scantime != scantime) {
/*
+ * New multi-report frame:
+ *
* For Win8 PTPs the first packet (td->num_received == 0) may
* have a contactcount of 0 if there only is a button event.
- * We double check that this is not a continuation packet
- * of a possible multi-packet frame be checking that the
- * timestamp has changed.
+ *
+ * Some other devices use a sentinel frame with 0 to release all contacts
*/
- if ((app->quirks & MT_QUIRK_WIN8_PTP_BUTTONS) &&
- app->num_received == 0 &&
- app->prev_scantime != scantime)
- app->num_expected = contact_count;
- /* A non 0 contact count always indicates a first packet */
- else if (contact_count)
- app->num_expected = contact_count;
+ app->num_expected = 0;
}
app->prev_scantime = scantime;
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
index 2bd781f1e0f5..ecc19387f6b0 100644
--- a/drivers/hid/hid-rmi.c
+++ b/drivers/hid/hid-rmi.c
@@ -235,7 +235,23 @@ static int rmi_hid_read_block(struct rmi_transport_dev *xport, u16 addr,
break;
}
- read_input_count = data->readReport[1];
+ read_input_count = min_t(int, data->readReport[1],
+ data->input_report_size - 2);
+ if (!read_input_count) {
+ /*
+ * A zero length reply advances neither
+ * bytes_read nor bytes_needed, and because a
+ * reply did arrive the wait above does not
+ * time out either, so a device answering 0
+ * forever would spin here indefinitely with
+ * page_mutex held.
+ */
+ hid_warn(hdev, "%s: zero-length read reply\n",
+ __func__);
+ clear_bit(RMI_READ_DATA_PENDING, &data->flags);
+ ret = -EIO;
+ break;
+ }
memcpy(buf + bytes_read, &data->readReport[2],
min(read_input_count, bytes_needed));
@@ -271,6 +287,11 @@ static int rmi_hid_write_block(struct rmi_transport_dev *xport, u16 addr,
goto exit;
}
+ if (len + 4 > data->output_report_size) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
data->writeReport[0] = RMI_WRITE_REPORT_ID;
data->writeReport[1] = len;
data->writeReport[2] = addr & 0xFF;
@@ -666,8 +687,16 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
- if (id->driver_data)
- data->device_flags = id->driver_data;
+ /*
+ * RMI_DEVICE can only mean "this probe validated the RMI reports and
+ * allocated writeReport": every bail-out to start below skips that
+ * allocation, and device_flags left carrying RMI_DEVICE from
+ * driver_data would send rmi_input_configured() into rmi_set_page()
+ * with writeReport still NULL. A bind through the new_id sysfs
+ * attribute can supply driver_data with the bit set, so do not let
+ * driver_data grant it.
+ */
+ data->device_flags = id->driver_data & ~RMI_DEVICE;
/*
* Check for the RMI specific report ids. If they are misisng
@@ -696,6 +725,17 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
data->output_report_size = hid_report_len(output_report);
+ /*
+ * The write reports built by this driver occupy 6 bytes and the read
+ * handshake looks at the first 3 bytes of an input report, so refuse
+ * to drive a device whose reports cannot hold them.
+ */
+ if (data->output_report_size < 6 || data->input_report_size < 3) {
+ hid_err(hdev, "rmi reports too small (out=%u in=%u)\n",
+ data->output_report_size, data->input_report_size);
+ goto start;
+ }
+
data->device_flags |= RMI_DEVICE;
alloc_size = data->output_report_size + data->input_report_size;
diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c
index 0e725a0f0abe..0ff07fdab442 100644
--- a/drivers/hid/i2c-hid/i2c-hid-core.c
+++ b/drivers/hid/i2c-hid/i2c-hid-core.c
@@ -136,6 +136,8 @@ static const struct i2c_hid_quirks {
I2C_HID_QUIRK_BAD_INPUT_SIZE },
{ I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_1063,
I2C_HID_QUIRK_NO_SLEEP_ON_SUSPEND },
+ { I2C_VENDOR_ID_CIRQUE, I2C_PRODUCT_ID_CIRQUE_D0C1,
+ I2C_HID_QUIRK_NO_IRQ_AFTER_RESET },
/*
* Without additional power on command, at least some QTEC devices send garbage
*/
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index a29bf051ada7..8feb8027be95 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -1550,6 +1550,19 @@ static int wacom_intuos_pro2_bt_irq(struct wacom_wac *wacom, size_t len)
return 0;
}
+ if (wacom->features.type == INTUOSP2_BT ||
+ wacom->features.type == INTUOSP2S_BT) {
+ if (len < 286) {
+ dev_warn(wacom->pen_input->dev.parent,
+ "Pro2 BT report too short: %zu bytes\n", len);
+ return 0;
+ }
+ } else if (len < 46) {
+ dev_warn(wacom->pen_input->dev.parent,
+ "Pro2 BT report too short: %zu bytes\n", len);
+ return 0;
+ }
+
wacom_intuos_pro2_bt_pen(wacom);
if (wacom->features.type == INTUOSP2_BT ||
wacom->features.type == INTUOSP2S_BT) {
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 33dbc71b730f..1a5072425c4a 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -340,8 +340,6 @@ struct cache {
struct list_head invalidation_requests;
sector_t migration_threshold;
- wait_queue_head_t migration_wait;
- atomic_t nr_allocated_migrations;
/*
* The number of in flight migrations that are performing
@@ -397,7 +395,11 @@ struct cache {
bool loaded_mappings:1;
bool loaded_discards:1;
- struct rw_semaphore background_work_lock;
+ /* background work management */
+ bool background_work_allowed;
+ unsigned background_work_nr;
+ spinlock_t background_work_lock;
+ wait_queue_head_t background_work_wait;
struct batcher committer;
struct work_struct commit_ws;
@@ -488,19 +490,13 @@ static struct dm_cache_migration *alloc_migration(struct cache *cache)
memset(mg, 0, sizeof(*mg));
mg->cache = cache;
- atomic_inc(&cache->nr_allocated_migrations);
return mg;
}
static void free_migration(struct dm_cache_migration *mg)
{
- struct cache *cache = mg->cache;
-
- if (atomic_dec_and_test(&cache->nr_allocated_migrations))
- wake_up(&cache->migration_wait);
-
- mempool_free(mg, &cache->migration_pool);
+ mempool_free(mg, &mg->cache->migration_pool);
}
/*----------------------------------------------------------------*/
@@ -1030,34 +1026,39 @@ static void calc_discard_block_range(struct cache *cache, struct bio *bio,
static void prevent_background_work(struct cache *cache)
{
- lockdep_off();
- down_write(&cache->background_work_lock);
- lockdep_on();
+ spin_lock_irq(&cache->background_work_lock);
+ cache->background_work_allowed = false;
+ wait_event_lock_irq(cache->background_work_wait,
+ cache->background_work_nr == 0,
+ cache->background_work_lock);
+ spin_unlock_irq(&cache->background_work_lock);
}
static void allow_background_work(struct cache *cache)
{
- lockdep_off();
- up_write(&cache->background_work_lock);
- lockdep_on();
+ spin_lock_irq(&cache->background_work_lock);
+ cache->background_work_allowed = true;
+ spin_unlock_irq(&cache->background_work_lock);
}
static bool background_work_begin(struct cache *cache)
{
bool r;
- lockdep_off();
- r = down_read_trylock(&cache->background_work_lock);
- lockdep_on();
-
+ spin_lock_irq(&cache->background_work_lock);
+ r = cache->background_work_allowed;
+ if (r)
+ cache->background_work_nr++;
+ spin_unlock_irq(&cache->background_work_lock);
return r;
}
static void background_work_end(struct cache *cache)
{
- lockdep_off();
- up_read(&cache->background_work_lock);
- lockdep_on();
+ spin_lock_irq(&cache->background_work_lock);
+ if (--cache->background_work_nr == 0)
+ wake_up(&cache->background_work_wait);
+ spin_unlock_irq(&cache->background_work_lock);
}
/*----------------------------------------------------------------*/
@@ -1462,6 +1463,9 @@ static void invalidate_complete(struct dm_cache_migration *mg, bool success)
struct bio_list bios;
struct cache *cache = mg->cache;
+ if (success)
+ atomic_inc(&cache->stats.demotion);
+
bio_list_init(&bios);
if (mg->cell) {
if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios))
@@ -1733,7 +1737,6 @@ static int map_bio(struct cache *cache, struct bio *bio, dm_oblock_t block,
if (passthrough_mode(cache)) {
if (bio_data_dir(bio) == WRITE) {
bio_drop_shared_lock(cache, bio);
- atomic_inc(&cache->stats.demotion);
invalidate_start(cache, cblock, block, bio);
return DM_MAPIO_SUBMITTED;
} else
@@ -2507,9 +2510,7 @@ static int cache_create(struct cache_args *ca, struct cache **result)
spin_lock_init(&cache->lock);
bio_list_init(&cache->deferred_bios);
- atomic_set(&cache->nr_allocated_migrations, 0);
atomic_set(&cache->nr_io_migrations, 0);
- init_waitqueue_head(&cache->migration_wait);
r = -ENOMEM;
atomic_set(&cache->nr_dirty, 0);
@@ -2592,8 +2593,10 @@ static int cache_create(struct cache_args *ca, struct cache **result)
issue_op, cache, cache->wq);
dm_iot_init(&cache->tracker);
- init_rwsem(&cache->background_work_lock);
- prevent_background_work(cache);
+ init_waitqueue_head(&cache->background_work_wait);
+ spin_lock_init(&cache->background_work_lock);
+ cache->background_work_allowed = false;
+ cache->background_work_nr = 0;
*result = cache;
return 0;
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 608b617fb817..9e170de50ad3 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1745,7 +1745,6 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
{
struct crypt_config *cc = io->cc;
struct bio *base_bio = io->base_bio;
- blk_status_t error = io->error;
if (!atomic_dec_and_test(&io->io_pending))
return;
@@ -1767,7 +1766,7 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
else
kfree(io->integrity_metadata);
- base_bio->bi_status = error;
+ base_bio->bi_status = io->error;
bio_endio(base_bio);
}
diff --git a/drivers/md/dm-ebs-target.c b/drivers/md/dm-ebs-target.c
index 1e52bde48b91..5d67c6c19d4b 100644
--- a/drivers/md/dm-ebs-target.c
+++ b/drivers/md/dm-ebs-target.c
@@ -265,8 +265,7 @@ static int ebs_ctr(struct dm_target *ti, unsigned int argc, char **argv)
r = -EINVAL;
if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1 ||
- tmp != (sector_t)tmp ||
- (sector_t)tmp >= ti->len) {
+ tmp != (sector_t)tmp) {
ti->error = "Invalid device offset sector";
goto bad;
}
diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index c50feaa98bf9..92970e12267a 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1979,8 +1979,8 @@ static void integrity_metadata(struct work_struct *w)
if (unlikely(dio->op == REQ_OP_DISCARD)) {
unsigned int bi_size = dio->bio_details.bi_iter.bi_size;
- unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : HASH_MAX_DIGESTSIZE;
- unsigned int max_blocks = max_size / ic->tag_size;
+ unsigned int max_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : sizeof(checksums_onstack);
+ unsigned int max_blocks = (max_size - extra_space) / ic->tag_size;
sector_t sector = dio->range.logical_sector;
if (!ic->discard_keyed)
@@ -3875,6 +3875,10 @@ static void dm_integrity_resume(struct dm_target *ti)
r = sync_rw_sb(ic, REQ_OP_READ);
if (r)
dm_integrity_io_error(ic, "reading superblock", r);
+
+ if (ic->mode == 'R')
+ goto skip_writes;
+
if ((ic->sb->flags & flags) != flags) {
ic->sb->flags |= flags;
r = sync_rw_sb(ic, REQ_OP_WRITE | REQ_FUA);
@@ -3984,6 +3988,7 @@ static void dm_integrity_resume(struct dm_target *ti)
}
}
+skip_writes:
ic->reboot_notifier.notifier_call = dm_integrity_reboot;
ic->reboot_notifier.next = NULL;
ic->reboot_notifier.priority = INT_MAX - 1; /* be notified after md and before hardware drivers */
@@ -4130,6 +4135,10 @@ static void dm_integrity_io_hints(struct dm_target *ti, struct queue_limits *lim
limits->dma_alignment = limits->logical_block_size - 1;
limits->discard_granularity = ic->sectors_per_block << SECTOR_SHIFT;
+ if (ic->internal_hash &&
+ (ic->mode == 'D' || ic->mode == 'B' || ic->mode == 'I'))
+ limits->features |= BLK_FEAT_STABLE_WRITES;
+
if (!ic->internal_hash) {
struct blk_integrity *bi = &limits->integrity;
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 90fd669636ec..d4fac2caca86 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -1822,24 +1822,20 @@ static int fastrpc_dmabuf_alloc(struct fastrpc_user *fl, char __user *argp)
return err;
}
- bp.fd = dma_buf_fd(buf->dmabuf, O_ACCMODE);
+ bp.fd = get_unused_fd_flags(O_ACCMODE);
if (bp.fd < 0) {
dma_buf_put(buf->dmabuf);
- return -EINVAL;
+ return bp.fd;
}
if (copy_to_user(argp, &bp, sizeof(bp))) {
- /*
- * The usercopy failed, but we can't do much about it, as
- * dma_buf_fd() already called fd_install() and made the
- * file descriptor accessible for the current process. It
- * might already be closed and dmabuf no longer valid when
- * we reach this point. Therefore "leak" the fd and rely on
- * the process exit path to do any required cleanup.
- */
+ put_unused_fd(bp.fd);
+ dma_buf_put(buf->dmabuf);
return -EFAULT;
}
+ dma_buf_fd_install(buf->dmabuf, bp.fd);
+
return 0;
}
diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c
index 839f7482dc18..654f051d0023 100644
--- a/drivers/net/bonding/bond_alb.c
+++ b/drivers/net/bonding/bond_alb.c
@@ -875,7 +875,7 @@ static int rlb_initialize(struct bonding *bond)
spin_unlock_bh(&bond->mode_lock);
/* register to receive ARPs */
- bond->recv_probe = rlb_arp_recv;
+ WRITE_ONCE(bond->recv_probe, rlb_arp_recv);
return 0;
}
@@ -1281,10 +1281,10 @@ unwind:
}
/* determine if the packet is NA or NS */
-static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
+static bool alb_determine_nd(struct sk_buff *skb)
{
- struct ipv6hdr *ip6hdr;
- struct icmp6hdr *hdr;
+ const struct ipv6hdr *ip6hdr;
+ const struct icmp6hdr *hdr;
if (!pskb_network_may_pull(skb, sizeof(*ip6hdr)))
return true;
@@ -1296,7 +1296,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr)))
return true;
- hdr = icmp6_hdr(skb);
+ ip6hdr = ipv6_hdr(skb);
+ hdr = (const struct icmp6hdr *)(ip6hdr + 1);
return hdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT ||
hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION;
}
@@ -1381,7 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,
if (!is_multicast_ether_addr(eth_data->h_dest)) {
switch (skb->protocol) {
case htons(ETH_P_IPV6):
- if (alb_determine_nd(skb, bond))
+ if (alb_determine_nd(skb))
break;
fallthrough;
case htons(ETH_P_IP):
@@ -1467,7 +1468,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,
break;
}
- if (alb_determine_nd(skb, bond)) {
+ if (alb_determine_nd(skb)) {
do_tx_balance = false;
break;
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index ef9eb0c53c66..a9bff7663eec 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1245,7 +1245,7 @@ static void bond_peer_notify_may_events(struct bonding *bond, bool force)
}
if (notified || force)
- bond->send_peer_notif--;
+ WRITE_ONCE(bond->send_peer_notif, bond->send_peer_notif - 1);
}
/**
@@ -2284,7 +2284,7 @@ skip_mac_set:
}
}
- bond->slave_cnt++;
+ WRITE_ONCE(bond->slave_cnt, bond->slave_cnt + 1);
netdev_compute_master_upper_features(bond->dev, true);
bond_set_carrier(bond);
@@ -2517,9 +2517,7 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_alb_deinit_slave(bond, slave);
}
- if (all) {
- RCU_INIT_POINTER(bond->curr_active_slave, NULL);
- } else if (oldcurrent == slave) {
+ if (!all && oldcurrent == slave) {
/* Note that we hold RTNL over this sequence, so there
* is no concern that another slave add/remove event
* will interfere.
@@ -2533,7 +2531,7 @@ static int __bond_release_one(struct net_device *bond_dev,
unblock_netpoll_tx();
synchronize_rcu();
- bond->slave_cnt--;
+ WRITE_ONCE(bond->slave_cnt, bond->slave_cnt - 1);
if (!bond_has_slaves(bond)) {
call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
@@ -4385,13 +4383,13 @@ static int bond_open(struct net_device *bond_dev)
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
queue_delayed_work(bond->wq, &bond->arp_work, 0);
- bond->recv_probe = bond_rcv_validate;
+ WRITE_ONCE(bond->recv_probe, bond_rcv_validate);
}
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
queue_delayed_work(bond->wq, &bond->ad_work, 0);
/* register to receive LACPDUs */
- bond->recv_probe = bond_3ad_lacpdu_recv;
+ WRITE_ONCE(bond->recv_probe, bond_3ad_lacpdu_recv);
bond_3ad_initiate_agg_selection(bond, 1);
bond_for_each_slave(bond, slave, iter)
@@ -4413,7 +4411,7 @@ static int bond_close(struct net_device *bond_dev)
struct slave *slave;
bond_work_cancel_all(bond);
- bond->send_peer_notif = 0;
+ WRITE_ONCE(bond->send_peer_notif, 0);
WRITE_ONCE(bond->recv_probe, NULL);
/* Wait for any in-flight RX handlers */
@@ -5118,7 +5116,7 @@ static void bond_skip_slave(struct bond_up_slave *slaves,
if (skipslave == slaves->arr[idx]) {
slaves->arr[idx] =
slaves->arr[slaves->count - 1];
- slaves->count--;
+ WRITE_ONCE(slaves->count, slaves->count - 1);
break;
}
}
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 36b8d89387ee..9efadeff6a22 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -1147,11 +1147,11 @@ static int bond_option_arp_interval_set(struct bonding *bond,
*/
if (!newval->value) {
if (bond->params.arp_validate)
- bond->recv_probe = NULL;
+ WRITE_ONCE(bond->recv_probe, NULL);
cancel_delayed_work_sync(&bond->arp_work);
} else {
/* arp_validate can be set only in active-backup mode */
- bond->recv_probe = bond_rcv_validate;
+ WRITE_ONCE(bond->recv_probe, bond_rcv_validate);
cancel_delayed_work_sync(&bond->mii_work);
queue_delayed_work(bond->wq, &bond->arp_work, 0);
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index fa9a8edce22f..8277c1c87bb3 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -538,7 +538,7 @@ struct airoha_wdma_info {
/* RX queue to IRQ mapping: BIT(q) in IRQ(n) */
#define RX_IRQ0_BANK_PIN_MASK 0x839f
-#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000
+#define RX_IRQ1_BANK_PIN_MASK 0xffe00000
#define RX_IRQ2_BANK_PIN_MASK 0x20
#define RX_IRQ3_BANK_PIN_MASK 0x40
#define RX_IRQ_BANK_PIN_MASK(_n) \
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index 2de974213090..e2e850c1b90b 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -3400,7 +3400,7 @@ static void xgbe_enable_rx(struct xgbe_prv_data *pdata)
XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val);
/* Enable MAC Rx */
- XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1);
+ XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1);
@@ -3411,7 +3411,6 @@ static void xgbe_disable_rx(struct xgbe_prv_data *pdata)
unsigned int i;
/* Disable MAC Rx */
- XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0);
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0);
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 1e1f52285a39..d6931c41f39d 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -968,6 +968,8 @@ struct macb_dma_desc_ptp {
* of the frame
* @mapping: DMA address of the skb's fragment buffer
* @size: size of the DMA mapped buffer
+ * @fcs_len: FCS bytes appended in software, 0 or ETH_FCS_LEN, only
+ * set for the last buffer of the frame
* @mapped_as_page: true when buffer was mapped with skb_frag_dma_map(),
* false when buffer was mapped with dma_map_single()
*/
@@ -975,6 +977,7 @@ struct macb_tx_skb {
struct sk_buff *skb;
dma_addr_t mapping;
size_t size;
+ u8 fcs_len;
bool mapped_as_page;
};
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 76ee4f506033..b1939da4c95a 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -1322,8 +1322,8 @@ static void macb_tx_error_task(struct work_struct *work)
bp->netdev->stats.tx_packets++;
queue->stats.tx_packets++;
packets++;
- bp->netdev->stats.tx_bytes += skb->len;
- queue->stats.tx_bytes += skb->len;
+ bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len;
+ queue->stats.tx_bytes += skb->len - tx_skb->fcs_len;
bytes += skb->len;
}
} else {
@@ -1450,8 +1450,8 @@ static int macb_tx_complete(struct macb_queue *queue, int budget)
skb->data);
bp->netdev->stats.tx_packets++;
queue->stats.tx_packets++;
- bp->netdev->stats.tx_bytes += skb->len;
- queue->stats.tx_bytes += skb->len;
+ bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len;
+ queue->stats.tx_bytes += skb->len - tx_skb->fcs_len;
packets++;
bytes += skb->len;
}
@@ -2199,7 +2199,8 @@ static void macb_poll_controller(struct net_device *netdev)
static unsigned int macb_tx_map(struct macb *bp,
struct macb_queue *queue,
struct sk_buff *skb,
- unsigned int hdrlen)
+ unsigned int hdrlen,
+ u8 fcs_len)
{
unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags;
unsigned int len, i, tx_head = queue->tx_head;
@@ -2284,6 +2285,7 @@ static unsigned int macb_tx_map(struct macb *bp,
/* This is the last buffer of the frame: save socket buffer */
tx_skb->skb = skb;
+ tx_skb->fcs_len = fcs_len;
/* Update TX ring: update buffer descriptors in reverse order
* to avoid race condition
@@ -2417,6 +2419,7 @@ static inline int macb_clear_csum(struct sk_buff *skb)
return 0;
}
+/* Returns a negative errno, or the FCS bytes appended (0 or ETH_FCS_LEN). */
static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev)
{
bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) ||
@@ -2465,7 +2468,7 @@ add_fcs:
skb_put_u8(*skb, (fcs >> 16) & 0xff);
skb_put_u8(*skb, (fcs >> 24) & 0xff);
- return 0;
+ return ETH_FCS_LEN;
}
static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
@@ -2478,6 +2481,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
netdev_tx_t ret = NETDEV_TX_OK;
unsigned int hdrlen;
unsigned long flags;
+ int fcs_len;
bool is_lso;
if (macb_clear_csum(skb)) {
@@ -2485,7 +2489,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
return ret;
}
- if (macb_pad_and_fcs(&skb, netdev)) {
+ fcs_len = macb_pad_and_fcs(&skb, netdev);
+ if (fcs_len < 0) {
dev_kfree_skb_any(skb);
return ret;
}
@@ -2548,7 +2553,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
}
/* Map socket buffer for DMA transfer */
- if (macb_tx_map(bp, queue, skb, hdrlen)) {
+ if (macb_tx_map(bp, queue, skb, hdrlen, fcs_len)) {
dev_kfree_skb_any(skb);
goto unlock;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
index 74c041ab5280..937b085582b5 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c
@@ -3468,6 +3468,8 @@ err:
return ret;
}
+#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8
+
static int rvu_enable_sriov(struct rvu *rvu)
{
struct pci_dev *pdev = rvu->pdev;
@@ -3486,24 +3488,27 @@ static int rvu_enable_sriov(struct rvu *rvu)
return 0;
pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &rvu->vf_devid);
- chans = rvu_get_num_lbk_chans();
- if (chans < 0)
- return chans;
-
vfs = pci_sriov_get_totalvfs(pdev);
-
- /* Limit VFs in case we have more VFs than LBK channels available. */
- if (vfs > chans)
- vfs = chans;
-
if (!vfs)
return 0;
- /* LBK channel number 63 is used for switching packets between
- * CGX mapped VFs. Hence limit LBK pairs till 62 only.
- */
- if (vfs > 62)
- vfs = 62;
+ if (rvu->vf_devid == PCI_DEVID_OCTEONTX2_RVU_AFVF) {
+ chans = rvu_get_num_lbk_chans();
+ if (chans < 0)
+ return chans;
+
+ /* The last LBK channel is reserved for switching packets between
+ * CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF
+ * count to available LBK channels minus 2.
+ */
+ vfs = min(vfs, chans - 2);
+
+ if (vfs <= 0) {
+ dev_warn(&pdev->dev,
+ "Skipping SRIOV enablement, not enough LBK channels available\n");
+ return 0;
+ }
+ }
/* Save VFs number for reference in VF interrupts handlers.
* Since interrupts might start arriving during SRIOV enablement
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
index 60922944675b..c34f8d86cc8a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
@@ -2957,10 +2957,9 @@ static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
}
}
- /* Disable the entry */
- npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
-
if (!cn20k_dft_rl) {
+ /* Disable the entry */
+ npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP;
/* Free the entry in bitmap */
npc_mcam_clear_bit(mcam, index);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c
index 4f984f6a2cb9..55ec6387ab28 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/rx.c
@@ -3,6 +3,7 @@
#include "rx.h"
#include "en/xdp.h"
+#include <linux/bitmap.h>
#include <net/xdp_sock_drv.h>
#include <linux/filter.h>
@@ -156,6 +157,7 @@ err_reuse_batch:
xsk_buff_free(xsk_buffs[batch]);
err:
+ bitmap_fill(wi->skip_release_bitmap, rq->mpwqe.pages_per_wqe);
rq->stats->buff_alloc_err++;
return -ENOMEM;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 206cf9db3466..7bd0606a5253 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -410,8 +410,11 @@ static inline void mlx5e_free_rx_wqe(struct mlx5e_rq *rq,
static void mlx5e_xsk_free_rx_wqe(struct mlx5e_wqe_frag_info *wi)
{
- if (!(wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE)))
- xsk_buff_free(*wi->xskp);
+ if (wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE))
+ return;
+
+ xsk_buff_free(*wi->xskp);
+ wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE);
}
static void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix)
diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 417c15d1ff42..6fcc5f561d56 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -83,7 +83,7 @@ struct oa_tc6 {
u16 spi_data_tx_buf_offset;
u16 tx_credits;
u8 rx_chunks_available;
- bool rx_buf_overflow;
+ bool wait_until_start_valid;
bool int_flag;
bool disable_traffic;
bool prot_ctrl;
@@ -455,7 +455,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
{
struct oa_tc6 *tc6 = bus->priv;
u32 regval;
- bool ret;
+ int ret;
ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE |
(regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
@@ -693,6 +693,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)
return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, value);
}
+/* Called when a frame that is meant to be transmitted, is dropped. */
+static void oa_tc6_drop_tx_skb(struct oa_tc6 *tc6, struct sk_buff *skb)
+{
+ if (skb) {
+ tc6->netdev->stats.tx_dropped++;
+ dev_kfree_skb_any(skb);
+ }
+}
+
+static struct sk_buff *oa_tc6_detach_waiting_tx_skb(struct oa_tc6 *tc6)
+{
+ struct sk_buff *skb;
+
+ lockdep_assert_held(&tc6->tx_skb_lock);
+ skb = tc6->waiting_tx_skb;
+ tc6->waiting_tx_skb = NULL;
+
+ return skb;
+}
+
static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)
{
if (tc6->rx_skb) {
@@ -704,29 +724,39 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)
static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6)
{
- if (tc6->ongoing_tx_skb) {
- tc6->netdev->stats.tx_dropped++;
- kfree_skb(tc6->ongoing_tx_skb);
- tc6->ongoing_tx_skb = NULL;
- }
+ oa_tc6_drop_tx_skb(tc6, tc6->ongoing_tx_skb);
+ tc6->ongoing_tx_skb = NULL;
}
static void oa_tc6_cleanup_waiting_tx_skb(struct oa_tc6 *tc6)
{
- if (tc6->waiting_tx_skb) {
- tc6->netdev->stats.tx_dropped++;
- kfree_skb(tc6->waiting_tx_skb);
- tc6->waiting_tx_skb = NULL;
- }
+ struct sk_buff *skb;
+
+ spin_lock_bh(&tc6->tx_skb_lock);
+ skb = oa_tc6_detach_waiting_tx_skb(tc6);
+ spin_unlock_bh(&tc6->tx_skb_lock);
+
+ oa_tc6_drop_tx_skb(tc6, skb);
}
-static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
+static void oa_tc6_free_ongoing_skbs(struct oa_tc6 *tc6)
{
oa_tc6_cleanup_ongoing_tx_skb(tc6);
oa_tc6_cleanup_ongoing_rx_skb(tc6);
+}
+
+static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
+{
+ oa_tc6_free_ongoing_skbs(tc6);
oa_tc6_cleanup_waiting_tx_skb(tc6);
}
+static void oa_tc6_look_for_new_frame(struct oa_tc6 *tc6)
+{
+ tc6->wait_until_start_valid = true;
+ oa_tc6_cleanup_ongoing_rx_skb(tc6);
+}
+
/* If the failure is at SPI interface level, masking and clearing
* the interrupt of the device won't work. Since SPI interrupt is
* disabled, it should stop the repeated interrupts.
@@ -734,9 +764,19 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
static void oa_tc6_disable_traffic(struct oa_tc6 *tc6)
{
u32 regval = OA_TC6_INT_MASK0_ALL_INTERRUPTS;
+ struct sk_buff *skb;
+ spin_lock_bh(&tc6->tx_skb_lock);
tc6->disable_traffic = true;
- oa_tc6_free_pending_skbs(tc6);
+ skb = oa_tc6_detach_waiting_tx_skb(tc6);
+ spin_unlock_bh(&tc6->tx_skb_lock);
+
+ /* disable_traffic, when set, is a point of no return to
+ * working state. Keeping the TX queues disabled.
+ */
+ netif_tx_disable(tc6->netdev);
+ oa_tc6_drop_tx_skb(tc6, skb);
+ oa_tc6_free_ongoing_skbs(tc6);
oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval);
oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, &regval);
oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval);
@@ -755,6 +795,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
return ret;
}
+ /* This function is called for each chunk received in a given SPI
+ * transaction. In case, extended status bit is set in more than
+ * one chunk, skip the write, if status0 is already cleared.
+ */
+ if (!value)
+ return 0;
+
/* Clear the error interrupts status */
ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value);
if (ret) {
@@ -764,8 +811,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
}
if (FIELD_GET(OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) {
- tc6->rx_buf_overflow = true;
- oa_tc6_cleanup_ongoing_rx_skb(tc6);
+ oa_tc6_look_for_new_frame(tc6);
net_err_ratelimited("%s: Receive buffer overflow error\n",
tc6->netdev->name);
return -EAGAIN;
@@ -791,6 +837,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
{
+ int ret = 0;
+
/* Process rx chunk footer for the following,
* 1. tx credits
* 2. errors if any from MAC-PHY
@@ -801,9 +849,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
footer);
if (FIELD_GET(OA_TC6_DATA_FOOTER_EXTENDED_STS, footer)) {
- int ret = oa_tc6_process_extended_status(tc6);
-
- if (ret)
+ ret = oa_tc6_process_extended_status(tc6);
+ /* EAGAIN error is recoverable. Move on to check
+ * HEADER and SYNC errors before returning.
+ */
+ if (ret && ret != -EAGAIN)
return ret;
}
@@ -821,7 +871,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
return -ENODEV;
}
- return 0;
+ return ret;
}
static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
@@ -846,13 +896,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
tc6->rx_skb = NULL;
}
-static void oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
+/* On oversubscribed traffic condition, particularly with overwhelming rx
+ * buffer overflow errors, there could be data chunk loss. If tail + length
+ * goes beyond end pointer, that is an indication that the data chunk with
+ * end_valid bit is lost. Time to look for a data chunk with start_valid bit.
+ *
+ * If rx_skb is NULL, it is time to start looking for data chunk with
+ * start_bit.
+ */
+static int oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
{
+ if (!tc6->rx_skb ||
+ skb_tailroom(tc6->rx_skb) < length) {
+ oa_tc6_look_for_new_frame(tc6);
+ return -EAGAIN;
+ }
+
memcpy(skb_put(tc6->rx_skb, length), payload, length);
+ return 0;
}
+/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is
+ * possible that we get two data chunks with start_valid bit set, without
+ * end_valid bit set in between. In this case, rx_skb would have a valid
+ * buffer pointer. We should release, if a valid pointer is found before
+ * allocating a new one.
+ */
static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6)
{
+ oa_tc6_cleanup_ongoing_rx_skb(tc6);
tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu +
ETH_HLEN + ETH_FCS_LEN);
if (!tc6->rx_skb) {
@@ -872,7 +944,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,
if (ret)
return ret;
- oa_tc6_update_rx_skb(tc6, payload, size);
+ ret = oa_tc6_update_rx_skb(tc6, payload, size);
+ if (ret)
+ return ret;
oa_tc6_submit_rx_skb(tc6);
@@ -887,22 +961,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)
if (ret)
return ret;
- oa_tc6_update_rx_skb(tc6, payload, size);
-
- return 0;
+ return oa_tc6_update_rx_skb(tc6, payload, size);
}
-static void oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size)
+static int oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size)
{
- oa_tc6_update_rx_skb(tc6, payload, size);
+ int ret;
- oa_tc6_submit_rx_skb(tc6);
+ ret = oa_tc6_update_rx_skb(tc6, payload, size);
+ if (!ret)
+ oa_tc6_submit_rx_skb(tc6);
+ return ret;
}
-static void oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload,
- u32 footer)
+static int oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload,
+ u32 footer)
{
- oa_tc6_update_rx_skb(tc6, payload, OA_TC6_CHUNK_PAYLOAD_SIZE);
+ return oa_tc6_update_rx_skb(tc6, payload,
+ OA_TC6_CHUNK_PAYLOAD_SIZE);
}
static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
@@ -917,10 +993,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
u16 size;
/* Restart the new rx frame after receiving rx buffer overflow error */
- if (start_valid && tc6->rx_buf_overflow)
- tc6->rx_buf_overflow = false;
+ if (start_valid && tc6->wait_until_start_valid)
+ tc6->wait_until_start_valid = false;
- if (tc6->rx_buf_overflow)
+ if (tc6->wait_until_start_valid)
return 0;
/* Process the chunk with complete rx frame */
@@ -942,8 +1018,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
/* Process the chunk with only rx frame end */
if (end_valid && !start_valid) {
size = end_byte_offset + 1;
- oa_tc6_prcs_rx_frame_end(tc6, data, size);
- return 0;
+ return oa_tc6_prcs_rx_frame_end(tc6, data, size);
}
/* Process the chunk with previous rx frame end and next rx frame
@@ -957,6 +1032,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
if (tc6->rx_skb) {
size = end_byte_offset + 1;
oa_tc6_prcs_rx_frame_end(tc6, data, size);
+
+ /* Return value from oa_tc6_prcs_rx_frame_end is not
+ * checked. If it returned an error, it is to make
+ * the code to look for new frame. At this stage,
+ * code below is going to process a new frame. So,
+ * error condition is set to false, in case it is
+ * set before proceeding.
+ */
+ tc6->wait_until_start_valid = false;
}
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset;
return oa_tc6_prcs_rx_frame_start(tc6,
@@ -965,9 +1049,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
}
/* Process the chunk with ongoing rx frame data */
- oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
-
- return 0;
+ return oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
}
static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)
@@ -983,8 +1065,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)
static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
{
u16 no_of_rx_chunks = length / OA_TC6_CHUNK_SIZE;
+ bool retry = false;
+ int ret = 0;
u32 footer;
- int ret;
/* All the rx chunks in the receive SPI data buffer are examined here */
for (int i = 0; i < no_of_rx_chunks; i++) {
@@ -993,8 +1076,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
OA_TC6_CHUNK_PAYLOAD_SIZE);
ret = oa_tc6_process_rx_chunk_footer(tc6, footer);
- if (ret)
- return ret;
+ if (ret) {
+ if (ret != -EAGAIN)
+ return ret;
+ retry = true;
+ }
/* If there is a data valid chunks then process it for the
* information needed to determine the validity and the location
@@ -1006,12 +1092,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
ret = oa_tc6_prcs_rx_chunk_payload(tc6, payload,
footer);
- if (ret)
- return ret;
+ if (ret) {
+ if (ret != -ENOMEM && ret != -EAGAIN)
+ return ret;
+ retry = true;
+ }
}
}
- return 0;
+ /* Not bailing out on recoverable error codes, -EAGAIN and
+ * -ENOMEM. If subsequent loop iterations, if any, succeeds,
+ * error code would be overwritten. retry flag helps to
+ * make the caller to continue and retry. Since recovery
+ * action for -ENOMEM and -EAGAIN are same, we are returning
+ * one of the error codes, that is -EAGAIN.
+ *
+ * Successful recovery depends on how small the frames are,
+ * how many chunks, among the received chunks triggered the
+ * error, whether data is intact even with error conditions.
+ * As a result, there is no single, best method to recover
+ * most data when error conditions hit. We do our best by
+ * processing all the chunks with good "footer header" and
+ * "data valid" bit set.
+ */
+ if (retry) {
+ ret = -EAGAIN;
+ oa_tc6_look_for_new_frame(tc6);
+ }
+
+ return ret;
}
static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
@@ -1173,12 +1282,9 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)
}
ret = oa_tc6_process_spi_data_rx_buf(tc6, spi_len);
- if (ret) {
- if (ret == -EAGAIN)
- continue;
- oa_tc6_cleanup_ongoing_tx_skb(tc6);
- oa_tc6_cleanup_ongoing_rx_skb(tc6);
+ if (ret && ret != -EAGAIN) {
+ oa_tc6_free_ongoing_skbs(tc6);
netdev_err(tc6->netdev, "Device error: %d\n", ret);
return ret;
}
@@ -1200,15 +1306,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data)
* no need to attempt spi transfer, once it fails. Pending skbs
* are already freed.
*/
- if (!tc6->disable_traffic) {
- while (tc6->int_flag ||
- (tc6->waiting_tx_skb && tc6->tx_credits)) {
- ret = oa_tc6_try_spi_transfer(tc6);
- if (ret) {
- disable_irq_nosync(tc6->spi->irq);
- oa_tc6_disable_traffic(tc6);
- break;
- }
+ spin_lock_bh(&tc6->tx_skb_lock);
+ if (tc6->disable_traffic) {
+ spin_unlock_bh(&tc6->tx_skb_lock);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_bh(&tc6->tx_skb_lock);
+
+ while (tc6->int_flag ||
+ (tc6->waiting_tx_skb && tc6->tx_credits)) {
+ ret = oa_tc6_try_spi_transfer(tc6);
+ if (ret) {
+ disable_irq_nosync(tc6->spi->irq);
+ oa_tc6_disable_traffic(tc6);
+ break;
}
}
@@ -1287,23 +1398,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);
* @tc6: oa_tc6 struct.
* @skb: socket buffer in which the ethernet frame is stored.
*
- * Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q
- * otherwise returns NETDEV_TX_BUSY.
+ * Return: NETDEV_TX_OK either on successful queueing of the packet for
+ * transmission, or on packet getting dropped. Packet can be dropped due to
+ * failure in linearizing the buffer or disable_traffic is set due to
+ * earlier fatal error. Returns NETDEV_TX_BUSY when there is no room
+ * to queue the packet.
*/
netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb)
{
- if (tc6->disable_traffic || tc6->waiting_tx_skb) {
- netif_stop_queue(tc6->netdev);
- return NETDEV_TX_BUSY;
- }
-
if (skb_linearize(skb)) {
- dev_kfree_skb_any(skb);
- tc6->netdev->stats.tx_dropped++;
+ oa_tc6_drop_tx_skb(tc6, skb);
return NETDEV_TX_OK;
}
spin_lock_bh(&tc6->tx_skb_lock);
+ if (tc6->waiting_tx_skb) {
+ netif_stop_queue(tc6->netdev);
+ spin_unlock_bh(&tc6->tx_skb_lock);
+ return NETDEV_TX_BUSY;
+ }
+ if (tc6->disable_traffic) {
+ spin_unlock_bh(&tc6->tx_skb_lock);
+ oa_tc6_drop_tx_skb(tc6, skb);
+ return NETDEV_TX_OK;
+ }
tc6->waiting_tx_skb = skb;
spin_unlock_bh(&tc6->tx_skb_lock);
@@ -1462,8 +1580,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);
*/
void oa_tc6_exit(struct oa_tc6 *tc6)
{
- tc6->disable_traffic = true;
disable_irq(tc6->spi->irq);
+ spin_lock_bh(&tc6->tx_skb_lock);
+ tc6->disable_traffic = true;
+ spin_unlock_bh(&tc6->tx_skb_lock);
oa_tc6_phy_exit(tc6);
oa_tc6_free_pending_skbs(tc6);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f2fc89176654..24656b35350b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -3676,6 +3676,14 @@ static int stmmac_hw_setup(struct net_device *dev)
/* Initialize MTL*/
stmmac_mtl_configuration(priv);
+ /* Apply the RX packet parser table */
+ if (priv->tc_entries) {
+ ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
+ priv->tc_entries_max);
+ if (ret)
+ return ret;
+ }
+
/* Initialize Safety Features */
stmmac_safety_feat_configuration(priv);
@@ -4319,6 +4327,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
/**
* stmmac_tso_allocator - close entry point of the driver
* @priv: driver private structure
+ * @entry: TX queue buffer index
* @des: buffer start address
* @total_len: total length to fill in descriptors
* @last_segment: condition for the last descriptor
@@ -4327,8 +4336,9 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
* This function fills descriptor and request new descriptors according to
* buffer length to fill
*/
-static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
- int total_len, bool last_segment, u32 queue)
+static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
+ dma_addr_t des, int total_len,
+ bool last_segment, u32 queue)
{
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
struct dma_desc *desc;
@@ -4340,14 +4350,13 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
while (tmp_len > 0) {
dma_addr_t curr_addr;
- tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
- priv->dma_conf.dma_tx_size);
- WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
+ *entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size);
+ WARN_ON(tx_q->tx_skbuff[*entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
+ desc = &tx_q->dma_entx[*entry].basic;
else
- desc = &tx_q->dma_tx[tx_q->cur_tx];
+ desc = &tx_q->dma_tx[*entry];
curr_addr = des + (total_len - tmp_len);
stmmac_set_desc_addr(priv, desc, curr_addr);
@@ -4486,7 +4495,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dma_desc *desc, *first, *mss_desc = NULL;
struct stmmac_priv *priv = netdev_priv(dev);
- unsigned int first_entry, tx_packets;
+ unsigned int first_entry, entry, tx_packets;
struct stmmac_txq_stats *txq_stats;
struct stmmac_tx_queue *tx_q;
bool set_ic, is_last_segment;
@@ -4549,22 +4558,24 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
}
first_entry = tx_q->cur_tx;
- WARN_ON(tx_q->tx_skbuff[first_entry]);
+ entry = first_entry;
+
+ WARN_ON(tx_q->tx_skbuff[entry]);
if (tx_q->tbs & STMMAC_TBS_AVAIL)
- desc = &tx_q->dma_entx[first_entry].basic;
+ desc = &tx_q->dma_entx[entry].basic;
else
- desc = &tx_q->dma_tx[first_entry];
+ desc = &tx_q->dma_tx[entry];
first = desc;
/* first descriptor: fill Headers on Buf1 */
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
+ goto error;
stmmac_set_desc_addr(priv, first, des);
- stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len,
+ stmmac_tso_allocator(priv, &entry, des + proto_hdr_len, pay_len,
(nfrags == 0), queue);
/* In case two or more DMA transmit descriptors are allocated for this
@@ -4579,8 +4590,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
* this DMA buffer right after the DMA engine completely finishes the
* full buffer transmission.
*/
- stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des, skb_headlen(skb),
- false);
+ stmmac_set_tx_skb_dma_entry(tx_q, entry, des, skb_headlen(skb), false);
/* Prepare fragments */
for (i = 0; i < nfrags; i++) {
@@ -4590,14 +4600,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
skb_frag_size(frag),
DMA_TO_DEVICE);
if (dma_mapping_error(priv->device, des))
- goto dma_map_err;
+ goto error_dma_unmap;
- stmmac_tso_allocator(priv, des, skb_frag_size(frag),
+ stmmac_tso_allocator(priv, &entry, des, skb_frag_size(frag),
(i == nfrags - 1), queue);
- stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des,
+ stmmac_set_tx_skb_dma_entry(tx_q, entry, des,
skb_frag_size(frag), true);
}
+ tx_q->cur_tx = entry;
stmmac_set_tx_dma_last_segment(tx_q, tx_q->cur_tx);
@@ -4702,7 +4713,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_OK;
-dma_map_err:
+error_dma_unmap:
+ for (;;) {
+ desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
+ stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
+ stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
+ first_entry);
+ if (first_entry == entry)
+ break;
+
+ first_entry = STMMAC_NEXT_ENTRY(first_entry,
+ priv->dma_conf.dma_tx_size);
+ }
+error:
dev_err(priv->device, "Tx dma map failed\n");
dev_kfree_skb(skb);
priv->xstats.tx_dropped++;
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 298efc76a56b..69fe5717846b 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -318,6 +318,11 @@ static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen,
static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
unsigned int hdrlen, unsigned int role, __u16 inner_proto)
{
+ if (skb_is_gso(skb)) {
+ netdev_dbg(pctx->dev, "GSO is not supported in GTP\n");
+ goto err;
+ }
+
if (!gtp_check_ms(skb, pctx, hdrlen, role, inner_proto)) {
netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
return 1;
diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c
index 4939cf67b336..f29864db662a 100644
--- a/drivers/net/ipvlan/ipvlan_main.c
+++ b/drivers/net/ipvlan/ipvlan_main.c
@@ -848,7 +848,6 @@ static int ipvlan_device_event(struct notifier_block *unused,
__ipvlan_link_delete(net, ipvlan->dev, &lst_kill);
}
- unregister_netdevice_many(&lst_kill);
break;
}
case NETDEV_FEAT_CHANGE:
@@ -899,6 +898,9 @@ static int ipvlan_device_event(struct notifier_block *unused,
mutex_unlock(&port->pnodes_lock);
+ /* Avoid invoking nested netdevice notifiers under pnodes_lock. */
+ unregister_netdevice_many(&lst_kill);
+
ipvlan_port_put(port);
return ret;
diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c
index 9c171697e762..2c04be6d61a8 100644
--- a/drivers/net/ntb_netdev.c
+++ b/drivers/net/ntb_netdev.c
@@ -127,8 +127,10 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
{
struct ntb_netdev_queue *q = qp_data;
struct ntb_netdev *dev = q->ntdev;
+ struct pcpu_sw_netstats *tstats;
struct sk_buff *skb, *new_skb;
struct net_device *ndev;
+ unsigned long flags;
int rc;
ndev = dev->ndev;
@@ -139,17 +141,20 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len);
if (len < 0) {
- ndev->stats.rx_errors++;
- ndev->stats.rx_length_errors++;
+ DEV_STATS_INC(ndev, rx_errors);
+ DEV_STATS_INC(ndev, rx_length_errors);
goto enqueue_again;
}
- ndev->stats.rx_packets++;
- ndev->stats.rx_bytes += len;
+ tstats = this_cpu_ptr(ndev->tstats);
+ flags = u64_stats_update_begin_irqsave(&tstats->syncp);
+ u64_stats_inc(&tstats->rx_packets);
+ u64_stats_add(&tstats->rx_bytes, len);
+ u64_stats_update_end_irqrestore(&tstats->syncp, flags);
new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
if (!new_skb) {
- ndev->stats.rx_dropped++;
+ DEV_STATS_INC(ndev, rx_dropped);
goto enqueue_again;
}
@@ -166,8 +171,8 @@ enqueue_again:
rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN);
if (rc) {
dev_kfree_skb_any(skb);
- ndev->stats.rx_errors++;
- ndev->stats.rx_fifo_errors++;
+ DEV_STATS_INC(ndev, rx_errors);
+ DEV_STATS_INC(ndev, rx_fifo_errors);
}
}
@@ -210,25 +215,39 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
{
struct ntb_netdev_queue *q = qp_data;
struct ntb_netdev *dev = q->ntdev;
+ struct pcpu_sw_netstats *tstats;
struct net_device *ndev;
struct sk_buff *skb;
+ unsigned long flags;
+ bool registered;
ndev = dev->ndev;
skb = data;
if (!skb || !ndev)
return;
+ rcu_read_lock();
+ registered = READ_ONCE(ndev->reg_state) == NETREG_REGISTERED;
+ if (!registered)
+ goto free_skb;
+
if (len > 0) {
- ndev->stats.tx_packets++;
- ndev->stats.tx_bytes += skb->len;
+ /* The memcpy kthread can migrate, so pin the per-CPU update. */
+ tstats = get_cpu_ptr(ndev->tstats);
+ flags = u64_stats_update_begin_irqsave(&tstats->syncp);
+ u64_stats_inc(&tstats->tx_packets);
+ u64_stats_add(&tstats->tx_bytes, skb->len);
+ u64_stats_update_end_irqrestore(&tstats->syncp, flags);
+ put_cpu_ptr(ndev->tstats);
} else {
- ndev->stats.tx_errors++;
- ndev->stats.tx_aborted_errors++;
+ DEV_STATS_INC(ndev, tx_errors);
+ DEV_STATS_INC(ndev, tx_aborted_errors);
}
+free_skb:
dev_kfree_skb_any(skb);
- if (ntb_transport_tx_free_entry(qp) >= tx_start) {
+ if (registered && ntb_transport_tx_free_entry(qp) >= tx_start) {
/* Make sure anybody stopping the queue after this sees the new
* value of ntb_transport_tx_free_entry()
*/
@@ -237,6 +256,7 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
ntb_transport_link_query(q->qp))
netif_wake_subqueue(ndev, q->qid);
}
+ rcu_read_unlock();
}
static const struct ntb_queue_handlers ntb_netdev_handlers = {
@@ -277,7 +297,7 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
drop:
dev_kfree_skb_any(skb);
- ndev->stats.tx_dropped++;
+ DEV_STATS_INC(ndev, tx_dropped);
return NETDEV_TX_OK;
}
@@ -647,6 +667,7 @@ static int ntb_netdev_probe(struct device *client_dev)
}
ndev->features = NETIF_F_HIGHDMA;
+ ndev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
diff --git a/drivers/net/ppp/ppp_async.c b/drivers/net/ppp/ppp_async.c
index 583426d06381..ea7fe9608ffd 100644
--- a/drivers/net/ppp/ppp_async.c
+++ b/drivers/net/ppp/ppp_async.c
@@ -63,8 +63,6 @@ struct asyncppp {
struct tasklet_struct tsk;
- refcount_t refcnt;
- struct completion dead;
struct ppp_channel chan; /* interface to generic ppp layer */
unsigned char obuf[OBUFSIZE];
};
@@ -115,38 +113,6 @@ static const struct ppp_channel_ops async_ops = {
*/
/*
- * We have a potential race on dereferencing tty->disc_data,
- * because the tty layer provides no locking at all - thus one
- * cpu could be running ppp_asynctty_receive while another
- * calls ppp_asynctty_close, which zeroes tty->disc_data and
- * frees the memory that ppp_asynctty_receive is using. The best
- * way to fix this is to use a rwlock in the tty struct, but for now
- * we use a single global rwlock for all ttys in ppp line discipline.
- *
- * FIXME: this is no longer true. The _close path for the ldisc is
- * now guaranteed to be sane.
- */
-static DEFINE_RWLOCK(disc_data_lock);
-
-static struct asyncppp *ap_get(struct tty_struct *tty)
-{
- struct asyncppp *ap;
-
- read_lock(&disc_data_lock);
- ap = tty->disc_data;
- if (ap != NULL)
- refcount_inc(&ap->refcnt);
- read_unlock(&disc_data_lock);
- return ap;
-}
-
-static void ap_put(struct asyncppp *ap)
-{
- if (refcount_dec_and_test(&ap->refcnt))
- complete(&ap->dead);
-}
-
-/*
* Called when a tty is put into PPP line discipline. Called in process
* context.
*/
@@ -180,9 +146,6 @@ ppp_asynctty_open(struct tty_struct *tty)
skb_queue_head_init(&ap->rqueue);
tasklet_setup(&ap->tsk, ppp_async_process);
- refcount_set(&ap->refcnt, 1);
- init_completion(&ap->dead);
-
ap->chan.private = ap;
ap->chan.ops = &async_ops;
ap->chan.mtu = PPP_MRU;
@@ -203,34 +166,18 @@ ppp_asynctty_open(struct tty_struct *tty)
}
/*
- * Called when the tty is put into another line discipline
- * or it hangs up. We have to wait for any cpu currently
- * executing in any of the other ppp_asynctty_* routines to
- * finish before we can call ppp_unregister_channel and free
- * the asyncppp struct. This routine must be called from
- * process context, not interrupt or softirq context.
+ * Called when the tty is put into another line discipline or it hangs up.
+ * This call is serialized against other ldisc functions.
*/
static void
ppp_asynctty_close(struct tty_struct *tty)
{
- struct asyncppp *ap;
+ struct asyncppp *ap = tty->disc_data;
- write_lock_irq(&disc_data_lock);
- ap = tty->disc_data;
- tty->disc_data = NULL;
- write_unlock_irq(&disc_data_lock);
if (!ap)
return;
- /*
- * We have now ensured that nobody can start using ap from now
- * on, but we have to wait for all existing users to finish.
- * Note that ppp_unregister_channel ensures that no calls to
- * our channel ops (i.e. ppp_async_send/ioctl) are in progress
- * by the time it returns.
- */
- if (!refcount_dec_and_test(&ap->refcnt))
- wait_for_completion(&ap->dead);
+ tty->disc_data = NULL;
tasklet_kill(&ap->tsk);
ppp_unregister_channel(&ap->chan);
@@ -241,17 +188,6 @@ ppp_asynctty_close(struct tty_struct *tty)
}
/*
- * Called on tty hangup in process context.
- *
- * Wait for I/O to driver to complete and unregister PPP channel.
- * This is already done by the close routine, so just call that.
- */
-static void ppp_asynctty_hangup(struct tty_struct *tty)
-{
- ppp_asynctty_close(tty);
-}
-
-/*
* Read does nothing - no data is ever available this way.
* Pppd reads and writes packets via /dev/ppp instead.
*/
@@ -281,7 +217,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf,
static int
ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
- struct asyncppp *ap = ap_get(tty);
+ struct asyncppp *ap = tty->disc_data;
int err, val;
int __user *p = (int __user *)arg;
@@ -322,7 +258,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
err = tty_mode_ioctl(tty, cmd, arg);
}
- ap_put(ap);
return err;
}
@@ -331,7 +266,7 @@ static void
ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
size_t count)
{
- struct asyncppp *ap = ap_get(tty);
+ struct asyncppp *ap = tty->disc_data;
unsigned long flags;
if (!ap)
@@ -341,21 +276,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
spin_unlock_irqrestore(&ap->recv_lock, flags);
if (!skb_queue_empty(&ap->rqueue))
tasklet_schedule(&ap->tsk);
- ap_put(ap);
tty_unthrottle(tty);
}
static void
ppp_asynctty_wakeup(struct tty_struct *tty)
{
- struct asyncppp *ap = ap_get(tty);
+ struct asyncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (!ap)
return;
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
tasklet_schedule(&ap->tsk);
- ap_put(ap);
}
@@ -365,7 +298,6 @@ static struct tty_ldisc_ops ppp_ldisc = {
.name = "ppp",
.open = ppp_asynctty_open,
.close = ppp_asynctty_close,
- .hangup = ppp_asynctty_hangup,
.read = ppp_asynctty_read,
.write = ppp_asynctty_write,
.ioctl = ppp_asynctty_ioctl,
diff --git a/drivers/net/ppp/ppp_synctty.c b/drivers/net/ppp/ppp_synctty.c
index 0b1bd1635c39..f87d43faeeab 100644
--- a/drivers/net/ppp/ppp_synctty.c
+++ b/drivers/net/ppp/ppp_synctty.c
@@ -38,11 +38,9 @@
#include <linux/ppp-ioctl.h>
#include <linux/ppp_channel.h>
#include <linux/spinlock.h>
-#include <linux/completion.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-#include <linux/refcount.h>
#include <linux/unaligned.h>
#include <linux/uaccess.h>
@@ -67,8 +65,6 @@ struct syncppp {
struct tasklet_struct tsk;
- refcount_t refcnt;
- struct completion dead_cmp;
struct ppp_channel chan; /* interface to generic ppp layer */
};
@@ -117,37 +113,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)
*/
/*
- * We have a potential race on dereferencing tty->disc_data,
- * because the tty layer provides no locking at all - thus one
- * cpu could be running ppp_synctty_receive while another
- * calls ppp_synctty_close, which zeroes tty->disc_data and
- * frees the memory that ppp_synctty_receive is using. The best
- * way to fix this is to use a rwlock in the tty struct, but for now
- * we use a single global rwlock for all ttys in ppp line discipline.
- *
- * FIXME: Fixed in tty_io nowadays.
- */
-static DEFINE_RWLOCK(disc_data_lock);
-
-static struct syncppp *sp_get(struct tty_struct *tty)
-{
- struct syncppp *ap;
-
- read_lock(&disc_data_lock);
- ap = tty->disc_data;
- if (ap != NULL)
- refcount_inc(&ap->refcnt);
- read_unlock(&disc_data_lock);
- return ap;
-}
-
-static void sp_put(struct syncppp *ap)
-{
- if (refcount_dec_and_test(&ap->refcnt))
- complete(&ap->dead_cmp);
-}
-
-/*
* Called when a tty is put into sync-PPP line discipline.
*/
static int
@@ -177,9 +142,6 @@ ppp_sync_open(struct tty_struct *tty)
skb_queue_head_init(&ap->rqueue);
tasklet_setup(&ap->tsk, ppp_sync_process);
- refcount_set(&ap->refcnt, 1);
- init_completion(&ap->dead_cmp);
-
ap->chan.private = ap;
ap->chan.ops = &sync_ops;
ap->chan.mtu = PPP_MRU;
@@ -201,34 +163,18 @@ ppp_sync_open(struct tty_struct *tty)
}
/*
- * Called when the tty is put into another line discipline
- * or it hangs up. We have to wait for any cpu currently
- * executing in any of the other ppp_synctty_* routines to
- * finish before we can call ppp_unregister_channel and free
- * the syncppp struct. This routine must be called from
- * process context, not interrupt or softirq context.
+ * Called when the tty is put into another line discipline or it hangs up.
+ * This call is serialized against other ldisc functions.
*/
static void
ppp_sync_close(struct tty_struct *tty)
{
- struct syncppp *ap;
+ struct syncppp *ap = tty->disc_data;
- write_lock_irq(&disc_data_lock);
- ap = tty->disc_data;
- tty->disc_data = NULL;
- write_unlock_irq(&disc_data_lock);
if (!ap)
return;
- /*
- * We have now ensured that nobody can start using ap from now
- * on, but we have to wait for all existing users to finish.
- * Note that ppp_unregister_channel ensures that no calls to
- * our channel ops (i.e. ppp_sync_send/ioctl) are in progress
- * by the time it returns.
- */
- if (!refcount_dec_and_test(&ap->refcnt))
- wait_for_completion(&ap->dead_cmp);
+ tty->disc_data = NULL;
tasklet_kill(&ap->tsk);
ppp_unregister_channel(&ap->chan);
@@ -238,17 +184,6 @@ ppp_sync_close(struct tty_struct *tty)
}
/*
- * Called on tty hangup in process context.
- *
- * Wait for I/O to driver to complete and unregister PPP channel.
- * This is already done by the close routine, so just call that.
- */
-static void ppp_sync_hangup(struct tty_struct *tty)
-{
- ppp_sync_close(tty);
-}
-
-/*
* Read does nothing - no data is ever available this way.
* Pppd reads and writes packets via /dev/ppp instead.
*/
@@ -273,7 +208,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf,
static int
ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
{
- struct syncppp *ap = sp_get(tty);
+ struct syncppp *ap = tty->disc_data;
int __user *p = (int __user *)arg;
int err, val;
@@ -314,7 +249,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
break;
}
- sp_put(ap);
return err;
}
@@ -323,7 +257,7 @@ static void
ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
size_t count)
{
- struct syncppp *ap = sp_get(tty);
+ struct syncppp *ap = tty->disc_data;
unsigned long flags;
if (!ap)
@@ -333,21 +267,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
spin_unlock_irqrestore(&ap->recv_lock, flags);
if (!skb_queue_empty(&ap->rqueue))
tasklet_schedule(&ap->tsk);
- sp_put(ap);
tty_unthrottle(tty);
}
static void
ppp_sync_wakeup(struct tty_struct *tty)
{
- struct syncppp *ap = sp_get(tty);
+ struct syncppp *ap = tty->disc_data;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (!ap)
return;
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
tasklet_schedule(&ap->tsk);
- sp_put(ap);
}
@@ -357,7 +289,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = {
.name = "pppsync",
.open = ppp_sync_open,
.close = ppp_sync_close,
- .hangup = ppp_sync_hangup,
.read = ppp_sync_read,
.write = ppp_sync_write,
.ioctl = ppp_synctty_ioctl,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 8178a8758cd3..fdfdcf24ddcf 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -1446,6 +1446,7 @@ static const struct usb_device_id products[] = {
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0316, 3)}, /* Quectel RG255C */
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0112, 0)}, /* Fibocom FG132 */
+ {QMI_QUIRK_SET_DTR(0x04b7, 0x8217, 8)}, /* Compal EXM-G1x */
{QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */
{QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/
{QMI_FIXED_INTF(0x2692, 0x9025, 4)}, /* Cellient MPL200 (rebranded Qualcomm 05c6:9025) */
diff --git a/drivers/net/vxlan/vxlan_mdb.c b/drivers/net/vxlan/vxlan_mdb.c
index d71e1925ecfd..841f42ffecb9 100644
--- a/drivers/net/vxlan/vxlan_mdb.c
+++ b/drivers/net/vxlan/vxlan_mdb.c
@@ -354,6 +354,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed");
return false;
}
+ if (ipv4_is_zeronet(nla_get_in_addr(attr))) {
+ NL_SET_ERR_MSG_MOD(extack, "IPv4 all-zeros source address is not allowed");
+ return false;
+ }
break;
#if IS_ENABLED(CONFIG_IPV6)
case htons(ETH_P_IPV6): {
@@ -368,6 +372,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed");
return false;
}
+ if (ipv6_addr_any(&src)) {
+ NL_SET_ERR_MSG_MOD(extack, "IPv6 all-zeros source address is not allowed");
+ return false;
+ }
break;
}
#endif
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 1322c678f4eb..758245c799a1 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -155,8 +155,6 @@ static const struct class nvme_ns_chr_class = {
};
static void nvme_put_subsystem(struct nvme_subsystem *subsys);
-static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
- unsigned nsid);
static void nvme_update_keep_alive(struct nvme_ctrl *ctrl,
struct nvme_command *cmd);
static int nvme_get_log_lsi(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page,
@@ -1612,7 +1610,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl,
}
if (nvme_multi_css(ctrl) && !csi_seen) {
- dev_warn(ctrl->device, "Command set not reported for nsid:%d\n",
+ dev_warn(ctrl->device, "Command set not reported for nsid:%u\n",
info->nsid);
status = -EINVAL;
}
@@ -2341,14 +2339,6 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info)
size_t size;
int i, ret;
- /*
- * The FDP configuration is static for the lifetime of the namespace,
- * so return immediately if we've already registered this namespace's
- * streams.
- */
- if (head->nr_plids)
- return 0;
-
ret = nvme_get_features(ctrl, NVME_FEAT_FDP, info->endgid, NULL, 0,
&fdp);
if (ret) {
@@ -2395,6 +2385,7 @@ static int nvme_query_fdp_info(struct nvme_ns *ns, struct nvme_ns_info *info)
for (i = 0; i < head->nr_plids; i++)
head->plids[i] = le16_to_cpu(ruhs->ruhsd[i].pid);
+ head->write_stream_granularity = min(info->runs, U32_MAX);
free:
kfree(ruhs);
return ret;
@@ -2442,12 +2433,6 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
goto out;
}
- if (ns->ctrl->ctratt & NVME_CTRL_ATTR_FDPS) {
- ret = nvme_query_fdp_info(ns, info);
- if (ret < 0)
- goto out;
- }
-
if (nvme_invalid_lba_sz(le64_to_cpu(id->nsze),
id->lbaf[lbaf].ds - SECTOR_SHIFT, &capacity)) {
dev_warn_once(ns->ctrl->device,
@@ -2468,9 +2453,26 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
if (!nvme_update_disk_info(ns, id, nvm, &lim))
capacity = 0;
+ /*
+ * A failed zone info query leaves zi zero-initialized, so skip the
+ * zoned limits update instead of configuring the queue from it.
+ * During a revalidation that keeps the zone geometry the queue was
+ * last validated with; on a first scan the namespace is registered
+ * without zoned limits, so that it is still available as a handle
+ * for admin commands.
+ */
if (IS_ENABLED(CONFIG_BLK_DEV_ZONED) &&
- ns->head->ids.csi == NVME_CSI_ZNS)
- nvme_update_zone_info(ns, &lim, &zi);
+ ns->head->ids.csi == NVME_CSI_ZNS) {
+ if (zi.zone_size)
+ nvme_update_zone_info(ns, &lim, &zi);
+ else
+ dev_warn(ns->ctrl->device,
+ "zone info query failed for nsid %u, %s\n",
+ ns->head->ns_id,
+ blk_queue_is_zoned(ns->disk->queue) ?
+ "keeping the previous zone limits" :
+ "not enabling zoned mode");
+ }
if ((ns->ctrl->vwc & NVME_CTRL_VWC_PRESENT) && !info->no_vwc)
lim.features |= BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA;
@@ -2490,10 +2492,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
capacity = 0;
lim.max_write_streams = ns->head->nr_plids;
- if (lim.max_write_streams)
- lim.write_stream_granularity = min(info->runs, U32_MAX);
- else
- lim.write_stream_granularity = 0;
+ lim.write_stream_granularity = ns->head->write_stream_granularity;
/*
* Only set the DEAC bit if the device guarantees that reads from
@@ -4001,10 +4000,11 @@ static void nvme_add_ns_cdev(struct nvme_ns *ns)
set_bit(NVME_NS_CDEV_LIVE, &ns->flags);
}
-static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
+static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ns *ns,
struct nvme_ns_info *info)
- __must_hold(&ctrl->subsys->lock)
+ __must_hold(&ns->ctrl->subsys->lock)
{
+ struct nvme_ctrl *ctrl = ns->ctrl;
struct nvme_ns_head *head;
size_t size = sizeof(*head);
int ret = -ENOMEM;
@@ -4032,6 +4032,7 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
ratelimit_state_init(&head->rs_nuse, 5 * HZ, 1);
ratelimit_set_flags(&head->rs_nuse, RATELIMIT_MSG_ON_RELEASE);
kref_init(&head->ref);
+ ns->head = head;
if (head->ids.csi) {
ret = nvme_get_effects_log(ctrl, head->ids.csi, &head->effects);
@@ -4040,21 +4041,30 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl,
} else
head->effects = ctrl->effects;
+ if (ctrl->ctratt & NVME_CTRL_ATTR_FDPS) {
+ ret = nvme_query_fdp_info(ns, info);
+ if (ret < 0)
+ goto out_cleanup_srcu;
+ }
+
ret = nvme_mpath_alloc_disk(ctrl, head);
if (ret)
- goto out_cleanup_srcu;
+ goto out_cleanup_fdp;
list_add_tail(&head->entry, &ctrl->subsys->nsheads);
kref_get(&ctrl->subsys->ref);
return head;
+out_cleanup_fdp:
+ kfree(head->plids);
out_cleanup_srcu:
cleanup_srcu_struct(&head->srcu);
out_ida_remove:
ida_free(&ctrl->subsys->ns_ida, head->instance);
out_free_head:
kfree(head);
+ ns->head = NULL;
out:
if (ret > 0)
ret = blk_status_to_errno(nvme_error_status(ret));
@@ -4116,13 +4126,13 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
((ns->ctrl->subsys->cmic & NVME_CTRL_CMIC_MULTI_CTRL) &&
info->is_shared)) {
dev_err(ctrl->device,
- "ignoring nsid %d because of duplicate IDs\n",
+ "ignoring nsid %u because of duplicate IDs\n",
info->nsid);
return ret;
}
dev_err(ctrl->device,
- "clearing duplicate IDs for nsid %d\n", info->nsid);
+ "clearing duplicate IDs for nsid %u\n", info->nsid);
dev_err(ctrl->device,
"use of /dev/disk/by-id/ may cause data corruption\n");
memset(&info->ids.nguid, 0, sizeof(info->ids.nguid));
@@ -4137,11 +4147,11 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
ret = nvme_subsys_check_duplicate_ids(ctrl->subsys, &info->ids);
if (ret) {
dev_err(ctrl->device,
- "duplicate IDs in subsystem for nsid %d\n",
+ "duplicate IDs in subsystem for nsid %u\n",
info->nsid);
goto out_unlock;
}
- head = nvme_alloc_ns_head(ctrl, info);
+ head = nvme_alloc_ns_head(ns, info);
if (IS_ERR(head)) {
ret = PTR_ERR(head);
goto out_unlock;
@@ -4151,20 +4161,20 @@ static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info)
if ((!info->is_shared || !head->shared) &&
!list_empty(&head->list)) {
dev_err(ctrl->device,
- "Duplicate unshared namespace %d\n",
+ "Duplicate unshared namespace %u\n",
info->nsid);
goto out_put_ns_head;
}
if (!nvme_ns_ids_equal(&head->ids, &info->ids)) {
dev_err(ctrl->device,
- "IDs don't match for shared namespace %d\n",
+ "IDs don't match for shared namespace %u\n",
info->nsid);
goto out_put_ns_head;
}
if (!multipath) {
dev_warn(ctrl->device,
- "Found shared namespace %d, but multipathing not supported.\n",
+ "Found shared namespace %u, but multipathing not supported.\n",
info->nsid);
dev_warn_once(ctrl->device,
"Shared namespace support requires core_nvme.multipath=Y.\n");
@@ -4333,6 +4343,9 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info)
last_path = true;
}
mutex_unlock(&ctrl->subsys->lock);
+
+ /* guarantee not available in head->list */
+ synchronize_srcu(&ns->head->srcu);
if (last_path)
nvme_put_ns_head(ns->head);
nvme_put_ns_head(ns->head);
@@ -4410,7 +4423,7 @@ static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_info *info)
if (!nvme_ns_ids_equal(&ns->head->ids, &info->ids)) {
dev_err(ns->ctrl->device,
- "identifiers changed for nsid %d\n", ns->head->ns_id);
+ "identifiers changed for nsid %u\n", ns->head->ns_id);
goto out;
}
@@ -4437,7 +4450,7 @@ static void nvme_scan_ns(struct nvme_ctrl *ctrl, unsigned nsid)
if (info.ids.csi != NVME_CSI_NVM && !nvme_multi_css(ctrl)) {
dev_warn(ctrl->device,
- "command set not reported for nsid: %d\n", nsid);
+ "command set not reported for nsid: %u\n", nsid);
return;
}
@@ -4501,15 +4514,16 @@ static void nvme_scan_ns_async(void *data, async_cookie_t cookie)
nvme_scan_ns(scan_info->ctrl, nsid);
}
-static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
- unsigned nsid)
+static void nvme_remove_nsid_range(struct nvme_ctrl *ctrl, u32 start, u32 end)
{
struct nvme_ns *ns, *next;
LIST_HEAD(rm_list);
mutex_lock(&ctrl->namespaces_lock);
list_for_each_entry_safe(ns, next, &ctrl->namespaces, list) {
- if (ns->head->ns_id > nsid) {
+ if (ns->head->ns_id >= end)
+ break;
+ if (ns->head->ns_id > start) {
list_del_rcu(&ns->list);
synchronize_srcu(&ctrl->srcu);
list_add_tail_rcu(&ns->list, &rm_list);
@@ -4559,13 +4573,14 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl)
goto out;
async_schedule_domain(nvme_scan_ns_async, &scan_info,
&domain);
- while (++prev < nsid)
- nvme_ns_remove_by_nsid(ctrl, prev);
+ if (prev + 1 < nsid)
+ nvme_remove_nsid_range(ctrl, prev, nsid);
+ prev = max(prev + 1, nsid);
}
async_synchronize_full_domain(&domain);
}
out:
- nvme_remove_invalid_namespaces(ctrl, prev);
+ nvme_remove_nsid_range(ctrl, prev, UINT_MAX);
free:
async_synchronize_full_domain(&domain);
kfree(ns_list);
@@ -4585,7 +4600,7 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl)
for (i = 1; i <= nn; i++)
nvme_scan_ns(ctrl, i);
- nvme_remove_invalid_namespaces(ctrl, nn);
+ nvme_remove_nsid_range(ctrl, nn, UINT_MAX);
}
static void nvme_clear_changed_ns_log(struct nvme_ctrl *ctrl)
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index fd5abd04e080..59f823dfbbcc 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -1028,6 +1028,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
}
if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
pr_err("Invalid DH-CHAP secret %s\n", p);
+ kfree_sensitive(p);
ret = -EINVAL;
goto out;
}
@@ -1042,6 +1043,7 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts,
}
if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) {
pr_err("Invalid DH-CHAP secret %s\n", p);
+ kfree_sensitive(p);
ret = -EINVAL;
goto out;
}
diff --git a/drivers/nvme/host/fault_inject.c b/drivers/nvme/host/fault_inject.c
index 105d6cb41c72..783e1999fef4 100644
--- a/drivers/nvme/host/fault_inject.c
+++ b/drivers/nvme/host/fault_inject.c
@@ -42,9 +42,11 @@ void nvme_fault_inject_init(struct nvme_fault_inject *fault_inj,
}
fault_inj->parent = parent;
- /* create debugfs for status code and dont_retry */
+ /* create debugfs for opcode, status code, and dont_retry */
+ fault_inj->opcode = 0xffff;
fault_inj->status = NVME_SC_INVALID_OPCODE;
fault_inj->dont_retry = true;
+ debugfs_create_x16("opcode", 0600, dir, &fault_inj->opcode);
debugfs_create_x16("status", 0600, dir, &fault_inj->status);
debugfs_create_bool("dont_retry", 0600, dir, &fault_inj->dont_retry);
}
@@ -59,6 +61,7 @@ void nvme_should_fail(struct request *req)
{
struct gendisk *disk = req->q->disk;
struct nvme_fault_inject *fault_inject = NULL;
+ struct nvme_command *cmd = nvme_req(req)->cmd;
u16 status;
if (disk) {
@@ -72,7 +75,14 @@ void nvme_should_fail(struct request *req)
fault_inject = &nvme_req(req)->ctrl->fault_inject;
}
- if (fault_inject && should_fail(&fault_inject->attr, 1)) {
+ if (!fault_inject)
+ return;
+
+ if (fault_inject->opcode <= 0xff &&
+ fault_inject->opcode != cmd->common.opcode)
+ return;
+
+ if (should_fail(&fault_inject->attr, 1)) {
/* inject status code and DNR bit */
status = fault_inject->status;
if (fault_inject->dont_retry)
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 023710e08e0d..48454cb7a0fc 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2364,9 +2364,15 @@ nvme_fc_ctrl_free(struct kref *ref)
struct nvme_fc_ctrl *ctrl =
container_of(ref, struct nvme_fc_ctrl, ref);
unsigned long flags;
+ bool owns_opts;
- /* remove from rport list */
+ /*
+ * Presence on the rport list means nvme_fc_init_ctrl() completed,
+ * and with it ownership of the fabrics options passed to it. If it
+ * failed instead, the options still belong to nvmf_create_ctrl().
+ */
spin_lock_irqsave(&ctrl->rport->lock, flags);
+ owns_opts = !list_empty(&ctrl->ctrl_list);
list_del(&ctrl->ctrl_list);
spin_unlock_irqrestore(&ctrl->rport->lock, flags);
@@ -2376,7 +2382,7 @@ nvme_fc_ctrl_free(struct kref *ref)
nvme_fc_rport_put(ctrl->rport);
ida_free(&nvme_fc_ctrl_cnt, ctrl->cnum);
- if (ctrl->ctrl.opts)
+ if (owns_opts)
nvmf_free_options(ctrl->ctrl.opts);
kfree(ctrl);
}
@@ -3575,14 +3581,14 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) {
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: failed to init ctrl state\n", ctrl->cnum);
- goto fail_ctrl;
+ goto fail_unlist;
}
if (!queue_delayed_work(nvme_wq, &ctrl->connect_work, 0)) {
dev_err(ctrl->ctrl.device,
"NVME-FC{%d}: failed to schedule initial connect\n",
ctrl->cnum);
- goto fail_ctrl;
+ goto fail_unlist;
}
flush_delayed_work(&ctrl->connect_work);
@@ -3593,14 +3599,22 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
return &ctrl->ctrl;
+fail_unlist:
+ /*
+ * Leaving the list hands the options back to nvmf_create_ctrl();
+ * see nvme_fc_ctrl_free(). Re-init so that list_empty() there
+ * reports the controller as unlisted.
+ */
+ spin_lock_irqsave(&rport->lock, flags);
+ list_del_init(&ctrl->ctrl_list);
+ spin_unlock_irqrestore(&rport->lock, flags);
+
fail_ctrl:
nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING);
cancel_work_sync(&ctrl->ioerr_work);
cancel_work_sync(&ctrl->ctrl.reset_work);
cancel_delayed_work_sync(&ctrl->connect_work);
- ctrl->ctrl.opts = NULL;
-
if (ctrl->ctrl.admin_tagset)
nvme_remove_admin_tag_set(&ctrl->ctrl);
/* initiate nvme ctrl ref counting teardown */
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 75e5d5a8a77c..2cff9fcbf740 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -323,6 +323,7 @@ struct nvme_fault_inject {
#ifdef CONFIG_FAULT_INJECTION_DEBUG_FS
struct fault_attr attr;
struct dentry *parent;
+ u16 opcode;
bool dont_retry; /* DNR, do not retry */
u16 status; /* status code */
#endif
@@ -571,6 +572,7 @@ struct nvme_ns_head {
u16 nr_plids;
u16 *plids;
+ u32 write_stream_granularity;
#ifdef CONFIG_NVME_MULTIPATH
struct bio_list requeue_list
__guarded_by(&requeue_lock);
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 538385138e45..9cb811a2ce1f 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -2036,7 +2036,7 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
struct ib_device *dev;
bool queue_ready = test_bit(NVME_RDMA_Q_LIVE, &queue->flags);
blk_status_t ret;
- int err;
+ int err = 0;
WARN_ON_ONCE(rq->tag < 0);
@@ -2092,16 +2092,18 @@ static blk_status_t nvme_rdma_queue_rq(struct blk_mq_hw_ctx *hctx,
err_unmap:
nvme_rdma_unmap_data(queue, rq);
err:
- if (err == -EIO)
- ret = nvme_host_path_error(rq);
- else if (err == -ENOMEM || err == -EAGAIN)
- ret = BLK_STS_RESOURCE;
- else
- ret = BLK_STS_IOERR;
- nvme_cleanup_cmd(rq);
+ if (err != -EIO) {
+ nvme_cleanup_cmd(rq);
+ if (err == -ENOMEM || err == -EAGAIN)
+ ret = BLK_STS_RESOURCE;
+ else
+ ret = BLK_STS_IOERR;
+ }
unmap_qe:
ib_dma_unmap_single(dev, req->sqe.dma, sizeof(struct nvme_command),
DMA_TO_DEVICE);
+ if (err == -EIO)
+ return nvme_host_path_error(rq);
return ret;
}
diff --git a/drivers/nvme/host/sysfs.c b/drivers/nvme/host/sysfs.c
index abf8edaae371..02a2490a9ed7 100644
--- a/drivers/nvme/host/sysfs.c
+++ b/drivers/nvme/host/sysfs.c
@@ -166,7 +166,7 @@ static DEVICE_ATTR_RO(eui);
static ssize_t nsid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- return sysfs_emit(buf, "%d\n", dev_to_ns_head(dev)->ns_id);
+ return sysfs_emit(buf, "%u\n", dev_to_ns_head(dev)->ns_id);
}
static DEVICE_ATTR_RO(nsid);
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 5fda9661bdb7..921934028e0b 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -413,8 +413,13 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,
* if we're the first on the send_list and we can try to send
* directly, otherwise queue io_work. Also, only do that if we
* are on the same cpu, so we don't introduce contention.
+ *
+ * TLS kTLS send takes ctx->tx_lock while blk_mq holds set->srcu.
+ * lockdep reports circular locking via elevator_lock. Defer TLS
+ * sends to the io workqueue instead of inline from this path.
*/
if (queue->io_cpu == raw_smp_processor_id() &&
+ !nvme_tcp_queue_tls(queue) &&
empty && mutex_trylock(&queue->send_mutex)) {
nvme_tcp_send_all(queue);
mutex_unlock(&queue->send_mutex);
@@ -684,6 +689,13 @@ static int nvme_tcp_handle_c2h_data(struct nvme_tcp_queue *queue,
return -ENOENT;
}
+ if (rq_data_dir(rq) != READ) {
+ dev_err(queue->ctrl->ctrl.device,
+ "queue %d tag %#x unexpected data for a write\n",
+ nvme_tcp_queue_id(queue), rq->tag);
+ return -EPROTO;
+ }
+
req = blk_mq_rq_to_pdu(rq);
if (!blk_rq_payload_bytes(rq) || !req->curr_bio || !req->data_len) {
dev_err(queue->ctrl->ctrl.device,
diff --git a/drivers/nvme/host/zns.c b/drivers/nvme/host/zns.c
index 2a152e87bd76..e31ec6f4f94f 100644
--- a/drivers/nvme/host/zns.c
+++ b/drivers/nvme/host/zns.c
@@ -48,12 +48,12 @@ int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf,
NVME_CMD_EFFECTS_CSUPP)) {
if (test_and_clear_bit(NVME_NS_FORCE_RO, &ns->flags))
dev_warn(ns->ctrl->device,
- "Zone Append supported for zoned namespace:%d. Remove read-only mode\n",
+ "Zone Append supported for zoned namespace:%u. Remove read-only mode\n",
ns->head->ns_id);
} else {
set_bit(NVME_NS_FORCE_RO, &ns->flags);
dev_warn(ns->ctrl->device,
- "Zone Append not supported for zoned namespace:%d. Forcing to read-only mode\n",
+ "Zone Append not supported for zoned namespace:%u. Forcing to read-only mode\n",
ns->head->ns_id);
}
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index edb9627d97b0..a55319bcdbd1 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -238,6 +238,12 @@ void nvmet_auth_sq_free(struct nvmet_sq *sq)
sq->dhchap_skey = NULL;
}
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq)
+{
+ cancel_delayed_work_sync(&sq->auth_expired_work);
+ nvmet_auth_sq_free(sq);
+}
+
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl)
{
ctrl->shash_id = 0;
diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
index 413ee2d16d29..6286e38436dd 100644
--- a/drivers/nvme/target/configfs.c
+++ b/drivers/nvme/target/configfs.c
@@ -814,7 +814,7 @@ static ssize_t nvmet_ns_resv_enable_store(struct config_item *item,
mutex_lock(&ns->subsys->lock);
if (ns->enabled) {
- pr_err("the ns:%d is already enabled.\n", ns->nsid);
+ pr_err("the ns:%u is already enabled.\n", ns->nsid);
mutex_unlock(&ns->subsys->lock);
return -EINVAL;
}
@@ -880,7 +880,7 @@ static struct config_group *nvmet_ns_make(struct config_group *group,
goto out;
config_group_init_type_name(&ns->group, name, &nvmet_ns_type);
- pr_info("adding nsid %d to subsystem %s\n", nsid, subsys->subsysnqn);
+ pr_info("adding nsid %u to subsystem %s\n", nsid, subsys->subsysnqn);
return &ns->group;
out:
diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c
index d74c01c98f19..43871a8f56ca 100644
--- a/drivers/nvme/target/core.c
+++ b/drivers/nvme/target/core.c
@@ -558,7 +558,7 @@ static void nvmet_p2pmem_ns_add_p2p(struct nvmet_ctrl *ctrl,
if (ret < 0)
pci_dev_put(p2p_dev);
- pr_info("using p2pmem on %s for nsid %d\n", pci_name(p2p_dev),
+ pr_info("using p2pmem on %s for nsid %u\n", pci_name(p2p_dev),
ns->nsid);
}
@@ -591,6 +591,11 @@ int nvmet_ns_enable(struct nvmet_ns *ns)
if (ns->enabled)
goto out_unlock;
+ if (!ns->device_path) {
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
ret = nvmet_bdev_ns_enable(ns);
if (ret == -ENOTBLK)
ret = nvmet_file_ns_enable(ns);
@@ -980,7 +985,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq)
wait_for_completion(&sq->confirm_done);
wait_for_completion(&sq->free_done);
percpu_ref_exit(&sq->ref);
- nvmet_auth_sq_free(sq);
+ nvmet_auth_sq_destroy(sq);
nvmet_cq_put(sq->cq);
/*
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index e362d7913a38..dbda55895f4f 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -924,6 +924,7 @@ u8 nvmet_setup_auth(struct nvmet_ctrl *ctrl, struct nvmet_sq *sq, bool reset);
void nvmet_auth_sq_init(struct nvmet_sq *sq);
void nvmet_destroy_auth(struct nvmet_ctrl *ctrl);
void nvmet_auth_sq_free(struct nvmet_sq *sq);
+void nvmet_auth_sq_destroy(struct nvmet_sq *sq);
int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id);
bool nvmet_check_auth_status(struct nvmet_req *req);
int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
@@ -950,6 +951,7 @@ static inline void nvmet_auth_sq_init(struct nvmet_sq *sq)
}
static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {};
static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {};
+static inline void nvmet_auth_sq_destroy(struct nvmet_sq *sq) {};
static inline bool nvmet_check_auth_status(struct nvmet_req *req)
{
return true;
diff --git a/drivers/nvme/target/pr.c b/drivers/nvme/target/pr.c
index 0948a690a1c0..09d8c63f5680 100644
--- a/drivers/nvme/target/pr.c
+++ b/drivers/nvme/target/pr.c
@@ -145,7 +145,7 @@ static void nvmet_pr_add_resv_log(struct nvmet_ctrl *ctrl, u8 log_type,
log.nsid = cpu_to_le32(nsid);
if (!kfifo_put(&log_mgr->log_queue, log)) {
- pr_info("a reservation log lost, cntlid:%d, log_type:%d, nsid:%d\n",
+ pr_info("a reservation log lost, cntlid:%d, log_type:%d, nsid:%u\n",
ctrl->cntlid, log_type, nsid);
log_mgr->lost_count++;
}
diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index 49cba690914a..ffa1e172c259 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1631,19 +1631,13 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
mutex_unlock(&nvmet_rdma_queue_mutex);
if (pending > NVMET_RDMA_BACKLOG) {
ret = NVME_SC_CONNECT_CTRL_BUSY;
- goto put_device;
+ goto free_queue;
}
}
ret = nvmet_rdma_cm_accept(cm_id, queue, &event->param.conn);
- if (ret) {
- /*
- * Don't destroy the cm_id in free path, as we implicitly
- * destroy the cm_id here with non-zero ret code.
- */
- queue->cm_id = NULL;
+ if (ret)
goto free_queue;
- }
mutex_lock(&nvmet_rdma_queue_mutex);
list_add_tail(&queue->queue_list, &nvmet_rdma_queue_list);
@@ -1652,6 +1646,11 @@ static int nvmet_rdma_queue_connect(struct rdma_cm_id *cm_id,
return 0;
free_queue:
+ /*
+ * Don't destroy the cm_id in free path, as we implicitly
+ * destroy the cm_id here with non-zero ret code.
+ */
+ queue->cm_id = NULL;
nvmet_rdma_free_queue(queue);
put_device:
kref_put(&ndev->ref, nvmet_rdma_free_dev);
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index e4f603b2ace7..e59810175262 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -103,6 +103,7 @@ enum nvmet_tcp_recv_state {
enum {
NVMET_TCP_F_INIT_FAILED = (1 << 0),
+ NVMET_TCP_F_R2T_SENT = (1 << 1),
};
struct nvmet_tcp_cmd {
@@ -776,6 +777,7 @@ static int nvmet_try_send_r2t(struct nvmet_tcp_cmd *cmd, bool last_in_batch)
return -EAGAIN;
cmd->queue->snd_cmd = NULL;
+ cmd->flags |= NVMET_TCP_F_R2T_SENT;
return 1;
}
@@ -1009,6 +1011,12 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
cmd = &queue->connect;
}
+ if (unlikely(!(cmd->flags & NVMET_TCP_F_R2T_SENT))) {
+ pr_err("queue %d: unsolicited H2CData (ttag %u)\n",
+ queue->idx, data->ttag);
+ goto err_proto;
+ }
+
if (le32_to_cpu(data->data_offset) != cmd->rbytes_done) {
pr_err("ttag %u unexpected data offset %u (expected %u)\n",
data->ttag, le32_to_cpu(data->data_offset),
@@ -1244,6 +1252,8 @@ recv:
}
queue->left = hdr->hlen - queue->offset + hdgst;
+ if (queue->left > sizeof(queue->pdu) - queue->offset)
+ return -EPROTO;
goto recv;
}
diff --git a/drivers/pmdomain/mediatek/Kconfig b/drivers/pmdomain/mediatek/Kconfig
index 8923e6516441..a2eb704a773c 100644
--- a/drivers/pmdomain/mediatek/Kconfig
+++ b/drivers/pmdomain/mediatek/Kconfig
@@ -43,9 +43,12 @@ config MTK_MFG_PM_DOMAIN
This driver is required for the Mali GPU to work at all on MT8196 and
MT6991.
+endmenu
+
config AIROHA_CPU_PM_DOMAIN
tristate "Airoha CPU power domain"
default ARCH_AIROHA
+ depends on ARCH_AIROHA || COMPILE_TEST
depends on HAVE_ARM_SMCCC
depends on PM
select PM_GENERIC_DOMAINS
@@ -54,5 +57,3 @@ config AIROHA_CPU_PM_DOMAIN
CPU frequency and power is controlled by ATF with SMC command to
set performance states.
-
-endmenu
diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c
index 96e4bd2f5a14..90743275942d 100644
--- a/drivers/pmdomain/qcom/rpmhpd.c
+++ b/drivers/pmdomain/qcom/rpmhpd.c
@@ -241,13 +241,9 @@ static struct rpmhpd *eliza_rpmhpds[] = {
[RPMHPD_GFX] = &gfx,
[RPMHPD_LCX] = &lcx,
[RPMHPD_LMX] = &lmx,
- [RPMHPD_MMCX] = &mmcx,
- [RPMHPD_MMCX_AO] = &mmcx_ao,
[RPMHPD_MSS] = &mss,
[RPMHPD_MX] = &mx,
[RPMHPD_MX_AO] = &mx_ao,
- [RPMHPD_MXC] = &mxc,
- [RPMHPD_MXC_AO] = &mxc_ao,
[RPMHPD_NSP] = &nsp,
};
diff --git a/drivers/ras/amd/atl/core.c b/drivers/ras/amd/atl/core.c
index 0f7cd6dab0b0..d77dacdd4f56 100644
--- a/drivers/ras/amd/atl/core.c
+++ b/drivers/ras/amd/atl/core.c
@@ -190,7 +190,6 @@ static const struct x86_cpu_id amd_atl_cpuids[] = {
X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
{ }
};
-MODULE_DEVICE_TABLE(x86cpu, amd_atl_cpuids);
static int __init amd_atl_init(void)
{
diff --git a/drivers/ras/amd/atl/map.c b/drivers/ras/amd/atl/map.c
index 24a05af747d5..4ec9333ef745 100644
--- a/drivers/ras/amd/atl/map.c
+++ b/drivers/ras/amd/atl/map.c
@@ -771,9 +771,5 @@ int get_address_map(struct addr_ctx *ctx)
dump_address_map(&ctx->map);
- ret = validate_address_map(ctx);
- if (ret)
- return ret;
-
- return ret;
+ return validate_address_map(ctx);
}
diff --git a/drivers/s390/crypto/zcrypt_cca_key.h b/drivers/s390/crypto/zcrypt_cca_key.h
index f5907b67db29..8a69eed75040 100644
--- a/drivers/s390/crypto/zcrypt_cca_key.h
+++ b/drivers/s390/crypto/zcrypt_cca_key.h
@@ -219,6 +219,7 @@ static inline int zcrypt_type6_crt_key(struct ica_rsa_modexpo_crt *crt, void *p)
copy_from_user(key->key_parts + 2 * long_len + 2 * short_len,
crt->u_mult_inv, long_len))
return -EFAULT;
+ memset(key->key_parts + 3 * long_len + 2 * short_len, 0, pad_len);
memset(key->key_parts + 3 * long_len + 2 * short_len + pad_len,
0xff, crt->inputdatalength);
pub = (struct cca_public_sec *)(key->key_parts + key_len);
diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c
index d4ce6352b5b2..19909bf43dc9 100644
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -1158,8 +1158,21 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain,
/* do not check the key here, it may be incomplete */
- /* copy the vlsc key token back */
+ /*
+ * Copy the vlsc key token back.
+ * The available space in the destination (key_token) and the source
+ * (t) buffer is always larger as the valid range of prepparm->kb.len.
+ * Validate t->len by comparing it with the length information in the
+ * param block of the request (prepparm->kb.len)
+ * The value range of prepparm->kb.len has been checked above.
+ */
t = (struct cipherkeytoken *)prepparm->kb.tlv1.key_token;
+ if (t->len != prepparm->kb.len - 3 * sizeof(uint16_t)) {
+ ZCRYPT_DBF_ERR("%s reply with invalid key_token length %u\n",
+ __func__, t->len);
+ rc = -EIO;
+ goto out;
+ }
memcpy(key_token, t, t->len);
*key_token_size = t->len;
diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c
index 08e36685e578..61c88fe853c5 100644
--- a/drivers/s390/net/ctcm_mpc.c
+++ b/drivers/s390/net/ctcm_mpc.c
@@ -826,7 +826,7 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
fsm_deltimer(&grp->timer);
- if (grp->saved_xid2->xid2_flag2 == 0x40) {
+ if (priv->xid->xid2_flag2 == 0x40) {
priv->xid->xid2_flag2 = 0x00;
if (grp->estconnfunc) {
grp->estconnfunc(grp->port_num, 1,
@@ -1636,7 +1636,6 @@ done:
"The XID used in the MPC protocol is not valid, "
"rc = %d\n", rc);
priv->xid->xid2_flag2 = 0x40;
- grp->saved_xid2->xid2_flag2 = 0x40;
}
return rc;
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index a091f77cedaf..c4a35547dcc6 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -6600,11 +6600,13 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
doutc(cl, "tpath '%s', mask %d, caller_uid %d, caller_gid %d\n",
tpath, mask, caller_uid, caller_gid);
+ mutex_lock(&mdsc->mutex);
for (i = 0; i < mdsc->s_cap_auths_num; i++) {
struct ceph_mds_cap_auth *s = &mdsc->s_cap_auths[i];
err = ceph_mds_auth_match(mdsc, s, cred, tpath);
if (err < 0) {
+ mutex_unlock(&mdsc->mutex);
put_cred(cred);
return err;
} else if (err > 0) {
@@ -6626,6 +6628,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
doutc(cl, "root_squash_perms %d, rw_perms_s %p\n", root_squash_perms,
rw_perms_s);
if (root_squash_perms && rw_perms_s == NULL) {
+ mutex_unlock(&mdsc->mutex);
doutc(cl, "access allowed\n");
return 0;
}
@@ -6640,6 +6643,7 @@ int ceph_mds_check_access(struct ceph_mds_client *mdsc, char *tpath, int mask)
!!(mask & MAY_READ), !!(mask & MAY_WRITE));
}
doutc(cl, "access denied\n");
+ mutex_unlock(&mdsc->mutex);
return -EACCES;
}
diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h
index 3c62e3c3530b..e7a262c9c2ab 100644
--- a/fs/ceph/mds_client.h
+++ b/fs/ceph/mds_client.h
@@ -604,6 +604,7 @@ struct ceph_mds_client {
struct rw_semaphore pool_perm_rwsem;
struct rb_root pool_perm_tree;
+ /* protected by mutex */
u32 s_cap_auths_num;
struct ceph_mds_cap_auth *s_cap_auths;
diff --git a/fs/ceph/super.c b/fs/ceph/super.c
index 15edea30dc8b..72935f665f11 100644
--- a/fs/ceph/super.c
+++ b/fs/ceph/super.c
@@ -1420,6 +1420,11 @@ static int ceph_reconfigure_fc(struct fs_context *fc)
else
ceph_clear_mount_opt(fsc, SPARSEREAD);
+ if (fsopt->flags & CEPH_MOUNT_OPT_NEARFULL_SYNC)
+ ceph_set_mount_opt(fsc, NEARFULL_SYNC);
+ else
+ ceph_clear_mount_opt(fsc, NEARFULL_SYNC);
+
if (strcmp_null(fsc->mount_options->mon_addr, fsopt->mon_addr)) {
kfree(fsc->mount_options->mon_addr);
fsc->mount_options->mon_addr = fsopt->mon_addr;
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index 4929f3431189..d8cac1cbf3bd 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -9,6 +9,7 @@
*/
#include <linux/fs.h>
+#include <linux/magic.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/fs_context.h>
@@ -19,9 +20,6 @@
#include <linux/configfs.h>
#include "configfs_internal.h"
-/* Random magic number */
-#define CONFIGFS_MAGIC 0x62656570
-
static struct vfsmount *configfs_mount = NULL;
struct kmem_cache *configfs_dir_cachep;
static int configfs_mnt_count = 0;
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 60264833bb63..848a0d338b89 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1737,8 +1737,8 @@ static struct attr_def *ntfs_attr_find_in_attrdef(const struct ntfs_volume *vol,
struct attr_def *ad;
WARN_ON(!type);
- for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef <
- vol->attrdef_size && ad->type; ++ad) {
+ for (ad = vol->attrdef; (u8 *)ad - (u8 *)vol->attrdef <=
+ vol->attrdef_size - (s32)sizeof(*ad) && ad->type; ++ad) {
/* We have not found it yet, carry on searching. */
if (likely(le32_to_cpu(ad->type) < le32_to_cpu(type)))
continue;
@@ -2500,7 +2500,7 @@ int ntfs_resident_attr_record_add(struct ntfs_inode *ni, __le32 type,
return offset;
put_err_out:
ntfs_attr_put_search_ctx(ctx);
- return -EIO;
+ return err;
}
/*
@@ -2639,7 +2639,7 @@ static int ntfs_non_resident_attr_record_add(struct ntfs_inode *ni, __le32 type,
return offset;
put_err_out:
ntfs_attr_put_search_ctx(ctx);
- return -1;
+ return err;
}
/*
@@ -5704,12 +5704,12 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
lcn << vol->cluster_size_bits,
alloc_cnt <<
vol->cluster_size_bits);
- if (err > 0)
+ if (err)
goto out;
}
if (signal_pending(current))
- goto out;
+ goto signal_out;
vcn += alloc_cnt;
try_alloc_cnt -= alloc_cnt;
@@ -5730,7 +5730,7 @@ int ntfs_attr_fallocate(struct ntfs_inode *ni, loff_t start, loff_t byte_len, bo
up_write(&ni->runlist.lock);
mutex_unlock(&ni->mrec_lock);
if (err || signal_pending(current))
- goto out;
+ goto signal_out;
vcn += alloc_cnt;
try_alloc_cnt -= alloc_cnt;
@@ -5756,4 +5756,8 @@ out_unmap:
mutex_unlock(&ni->mrec_lock);
out:
return err >= 0 ? 0 : err;
+signal_out:
+ if (!err)
+ err = -EINTR;
+ goto out;
}
diff --git a/fs/ntfs/bdev-io.c b/fs/ntfs/bdev-io.c
index 86db4d9298ed..4f27eed3b072 100644
--- a/fs/ntfs/bdev-io.c
+++ b/fs/ntfs/bdev-io.c
@@ -34,7 +34,7 @@ int ntfs_bdev_read(struct block_device *bdev, char *data, loff_t start, size_t s
int error;
struct bio *bio;
blk_opf_t op;
- sector_t sector = start >> SECTOR_SHIFT;
+ sector_t sector = ntfs_bytes_to_bio_sector(start);
if (start & (SECTOR_SIZE - 1))
return -EINVAL;
diff --git a/fs/ntfs/bitmap.c b/fs/ntfs/bitmap.c
index b1436b3151b9..1840b7d84c62 100644
--- a/fs/ntfs/bitmap.c
+++ b/fs/ntfs/bitmap.c
@@ -64,7 +64,7 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range)
end = start_buf;
while (end < end_buf) {
- u64 aligned_start, aligned_count;
+ u64 aligned_start, aligned_end, aligned_count;
u64 start = find_next_zero_bit(bitmap, end_buf - start_buf,
end - start_buf) + start_buf;
if (start >= end_buf)
@@ -74,8 +74,10 @@ int ntfs_trim_fs(struct ntfs_volume *vol, struct fstrim_range *range)
start - start_buf) + start_buf;
aligned_start = ALIGN(ntfs_cluster_to_bytes(vol, start), dq);
- aligned_count =
- ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end - start), dq);
+ aligned_end = ALIGN_DOWN(ntfs_cluster_to_bytes(vol, end), dq);
+ if (aligned_start >= aligned_end)
+ continue;
+ aligned_count = aligned_end - aligned_start;
if (aligned_count >= range->minlen) {
ret = blkdev_issue_discard(vol->sb->s_bdev, aligned_start >> 9,
aligned_count >> 9, GFP_NOFS);
diff --git a/fs/ntfs/compress.c b/fs/ntfs/compress.c
index 2225630b19d7..197d8607fc63 100644
--- a/fs/ntfs/compress.c
+++ b/fs/ntfs/compress.c
@@ -1414,7 +1414,7 @@ static int ntfs_write_cb(struct ntfs_inode *ni, loff_t pos, struct page **pages,
bio_pos = ntfs_cluster_to_bytes(vol, bio_lcn);
bio = bio_alloc(vol->sb->s_bdev, DIV_ROUND_UP(bio_size, PAGE_SIZE),
REQ_OP_WRITE, GFP_NOIO);
- bio->bi_iter.bi_sector = ntfs_bytes_to_sector(vol, bio_pos);
+ bio->bi_iter.bi_sector = ntfs_bytes_to_bio_sector(bio_pos);
for (i = 0; bio_size; i++) {
unsigned int len = min_t(unsigned int, bio_size, PAGE_SIZE);
diff --git a/fs/ntfs/ea.c b/fs/ntfs/ea.c
index cdd306933d73..3f4ba7667522 100644
--- a/fs/ntfs/ea.c
+++ b/fs/ntfs/ea.c
@@ -404,10 +404,12 @@ alloc_new_ea:
*packed_ea_size = p_ea_info->ea_length;
mark_mft_record_dirty(ni);
out:
- if (ea_info_qsize > 0)
- NInoSetHasEA(ni);
- else
- NInoClearHasEA(ni);
+ if (!err) {
+ if (ea_info_qsize > 0)
+ NInoSetHasEA(ni);
+ else
+ NInoClearHasEA(ni);
+ }
kvfree(ea_buf);
kvfree(old_ea_buf);
@@ -615,7 +617,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler,
if (!buffer) {
err = sizeof(u8);
} else if (size < sizeof(u8)) {
- err = -ENODATA;
+ err = -ERANGE;
} else {
err = sizeof(u8);
*(u8 *)buffer = (u8)(le32_to_cpu(ni->flags) & 0x3F);
@@ -628,7 +630,7 @@ static int ntfs_getxattr(const struct xattr_handler *handler,
if (!buffer) {
err = sizeof(u32);
} else if (size < sizeof(u32)) {
- err = -ENODATA;
+ err = -ERANGE;
} else {
err = sizeof(u32);
*(u32 *)buffer = le32_to_cpu(ni->flags);
@@ -753,18 +755,39 @@ static int ntfs_new_attr_flags(struct ntfs_inode *ni, __le32 fattr)
old_arec_size = le32_to_cpu(a->length);
/*
- * Move payloads before shrinking the record. Otherwise resizing moves
+ * Move payloads before shrinking the record. Otherwise resizing moves
* the following attribute over the old payload before it can be copied.
+ *
+ * When offsets increase, move mapping_pairs first to avoid name
+ * overwriting the start of mapping_pairs.
*/
if (arec_size < old_arec_size) {
- if (a->name_length && name_ofs != old_name_ofs)
- memmove((u8 *)a + name_ofs, (u8 *)a + old_name_ofs,
- a->name_length * sizeof(__le16));
- if (mp_ofs != old_mp_ofs)
- memmove((u8 *)a + mp_ofs, (u8 *)a + old_mp_ofs, mp_size);
+ if (name_ofs > old_name_ofs) {
+ /* Payload offsets increased: move mapping pairs first. */
+ if (mp_ofs != old_mp_ofs)
+ memmove((u8 *)a + mp_ofs,
+ (u8 *)a + old_mp_ofs,
+ mp_size);
+ if (a->name_length && name_ofs != old_name_ofs)
+ memmove((u8 *)a + name_ofs,
+ (u8 *)a + old_name_ofs,
+ a->name_length *
+ sizeof(__le16));
+ } else {
+ /* Payload offsets decreased or unchanged: move name first. */
+ if (a->name_length && name_ofs != old_name_ofs)
+ memmove((u8 *)a + name_ofs,
+ (u8 *)a + old_name_ofs,
+ a->name_length *
+ sizeof(__le16));
+ if (mp_ofs != old_mp_ofs)
+ memmove((u8 *)a + mp_ofs,
+ (u8 *)a + old_mp_ofs,
+ mp_size);
+ }
}
- err = ntfs_attr_record_resize(m, a, arec_size);
+ err = ntfs_attr_record_resize(ctx->mrec, a, arec_size);
if (unlikely(err))
goto err_out;
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 88747217ba61..8164326b7812 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -270,18 +270,25 @@ static int ntfs_setattr_size(struct inode *vi, struct iattr *attr)
return err;
inode_dio_wait(vi);
+
+ /*
+ * Serialize with page faults and pagecache instantiation so that
+ * readers cannot observe the size change until the attribute
+ * updates below have completed.
+ */
+ filemap_invalidate_lock(vi->i_mapping);
if (attr->ia_size > old_size) {
truncate_pagecache(vi, old_size);
i_size_write(vi, attr->ia_size);
pagecache_isize_extended(vi, old_size, attr->ia_size);
- } else
+ } else {
truncate_setsize(vi, attr->ia_size);
+ }
err = ntfs_truncate_vfs(vi, attr->ia_size, old_size);
- if (err) {
+ if (err)
i_size_write(vi, old_size);
- return err;
- }
+ filemap_invalidate_unlock(vi->i_mapping);
return err;
}
@@ -669,6 +676,7 @@ out_lock:
static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf)
{
struct inode *inode = file_inode(vmf->vma->vm_file);
+ struct address_space *mapping = inode->i_mapping;
vm_fault_t ret;
if (NInoWofCompressed(NTFS_I(inode)))
@@ -677,7 +685,14 @@ static vm_fault_t ntfs_filemap_page_mkwrite(struct vm_fault *vmf)
sb_start_pagefault(inode->i_sb);
file_update_time(vmf->vma->vm_file);
+ /*
+ * Serialize against truncate/fallocate which hold the lock
+ * exclusively while invalidating pagecache and changing extents.
+ */
+ filemap_invalidate_lock_shared(mapping);
ret = iomap_page_mkwrite(vmf, &ntfs_page_mkwrite_iomap_ops, NULL);
+ filemap_invalidate_unlock_shared(mapping);
+
sb_end_pagefault(inode->i_sb);
return ret;
}
@@ -1116,7 +1131,6 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le
struct ntfs_volume *vol = ni->vol;
int err = 0;
loff_t old_size;
- bool map_locked = false;
if (mode & ~(NTFS_FALLOC_FL_SUPPORTED))
return -EOPNOTSUPP;
@@ -1148,16 +1162,13 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le
inode_lock(vi);
if (NInoCompressed(ni) || NInoEncrypted(ni) || NInoWofCompressed(ni)) {
- err = -EOPNOTSUPP;
- goto out;
+ inode_unlock(vi);
+ return -EOPNOTSUPP;
}
inode_dio_wait(vi);
- if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE |
- FALLOC_FL_INSERT_RANGE)) {
- filemap_invalidate_lock(vi->i_mapping);
- map_locked = true;
- }
+ /* Take invalidate_lock for all fallocate operations to prevent races */
+ filemap_invalidate_lock(vi->i_mapping);
switch (mode & FALLOC_FL_MODE_MASK) {
case FALLOC_FL_ALLOCATE_RANGE:
@@ -1182,14 +1193,15 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t offset, loff_t le
err = file_modified(file);
out:
- if (map_locked)
- filemap_invalidate_unlock(vi->i_mapping);
+ if (!err && mode == 0 && NInoNonResident(ni) &&
+ offset > old_size) {
+ truncate_pagecache(vi, old_size);
+ pagecache_isize_extended(vi, old_size, offset);
+ }
+
+ filemap_invalidate_unlock(vi->i_mapping);
+
if (!err) {
- if (mode == 0 && NInoNonResident(ni) &&
- offset > old_size) {
- truncate_pagecache(vi, old_size);
- pagecache_isize_extended(vi, old_size, offset);
- }
NInoSetFileNameDirty(ni);
inode_set_mtime_to_ts(vi, inode_set_ctime_current(vi));
mark_inode_dirty(vi);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 32edb4045178..5aedc045f65a 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -1852,7 +1852,7 @@ int ntfs_read_inode_mount(struct inode *vi)
struct mft_record *m = NULL;
struct attr_record *a;
struct ntfs_attr_search_ctx *ctx;
- unsigned int i, nr_blocks;
+ unsigned int i;
int err;
size_t new_rl_count;
@@ -1896,11 +1896,6 @@ int ntfs_read_inode_mount(struct inode *vi)
goto err_out;
}
- /* Determine the first block of the $MFT/$DATA attribute. */
- nr_blocks = ntfs_bytes_to_sector(vol, vol->mft_record_size);
- if (!nr_blocks)
- nr_blocks = 1;
-
/* Load $MFT/$DATA's first mft record. */
err = ntfs_bdev_read(sb->s_bdev, (char *)m,
ntfs_cluster_to_bytes(vol, vol->mft_lcn), i);
@@ -3780,8 +3775,7 @@ static s64 __ntfs_inode_non_resident_attr_pwrite(struct inode *vi,
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE,
GFP_NOIO);
bio->bi_iter.bi_sector =
- ntfs_bytes_to_sector(vol,
- ntfs_cluster_to_bytes(vol, lcn) +
+ ntfs_bytes_to_bio_sector(ntfs_cluster_to_bytes(vol, lcn) +
lcn_folio_off);
length = min_t(unsigned long,
diff --git a/fs/ntfs/lcnalloc.c b/fs/ntfs/lcnalloc.c
index aa2e017a4384..0d6cd08ee2e7 100644
--- a/fs/ntfs/lcnalloc.c
+++ b/fs/ntfs/lcnalloc.c
@@ -53,10 +53,10 @@ int ntfs_cluster_free_from_rl_nolock(struct ntfs_volume *vol,
if (rl->lcn < 0)
continue;
err = ntfs_bitmap_clear_run(lcnbmp_vi, rl->lcn, rl->length);
- if (unlikely(err && (!ret || ret == -ENOMEM) && ret != err))
- ret = err;
- else
+ if (likely(!err))
nr_freed += rl->length;
+ else if (!ret || ret == -ENOMEM)
+ ret = err;
}
ntfs_inc_free_clusters(vol, nr_freed);
ntfs_debug("Done.");
@@ -1045,8 +1045,9 @@ err_out:
"Failed to rollback (error %i). Leaving inconsistent metadata! Unmount and run chkdsk.",
(int)delta);
NVolSetErrors(vol);
+ } else {
+ ntfs_dec_free_clusters(vol, delta);
}
- ntfs_dec_free_clusters(vol, delta);
up_write(&vol->lcnbmp_lock);
memalloc_nofs_restore(memalloc_flags);
ntfs_error(vol->sb, "Aborting (error %i).", err);
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
index 984a0827f9ac..7e58c99f1728 100644
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -499,8 +499,8 @@ int ntfs_sync_mft_mirror(struct ntfs_volume *vol, const u64 mft_no,
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
bio->bi_iter.bi_sector =
- NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
- lcn_folio_off + folio_ofs);
+ ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, vol->mftmirr_lcn) +
+ lcn_folio_off + folio_ofs);
if (bio_add_folio(bio, folio, vol->mft_record_size, folio_ofs))
err = submit_bio_wait(bio);
@@ -580,7 +580,7 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn
err = pre_write_mst_fixup((struct ntfs_record *)fixup_m, vol->mft_record_size);
if (err) {
ntfs_error(vol->sb, "Failed to apply mst fixups!");
- goto err_out;
+ goto unmap_err_out;
}
folio_size = vol->mft_record_size / ni->mft_lcn_count;
@@ -592,8 +592,8 @@ int write_mft_record_nolock(struct ntfs_inode *ni, struct mft_record *m, int syn
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE, GFP_NOIO);
bio->bi_iter.bi_sector =
- NTFS_B_TO_SECTOR(vol, NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) +
- clu_off);
+ ntfs_bytes_to_bio_sector(NTFS_CLU_TO_B(vol, ni->mft_lcn[i]) +
+ clu_off);
if (!bio_add_folio(bio, folio, folio_size,
ni->folio_ofs + offset)) {
@@ -645,6 +645,8 @@ done:
return 0;
put_bio_out:
bio_put(bio);
+unmap_err_out:
+ kunmap_local(kaddr);
err_out:
/*
* The caller should mark the base inode as bad so no more I/O
@@ -2740,8 +2742,8 @@ flush_bio:
bio = bio_alloc(vol->sb->s_bdev, 1, REQ_OP_WRITE,
GFP_NOIO);
bio->bi_iter.bi_sector =
- ntfs_bytes_to_sector(vol,
- ntfs_cluster_to_bytes(vol, lcn) + off);
+ ntfs_bytes_to_bio_sector(
+ ntfs_cluster_to_bytes(vol, lcn) + off);
}
if (vol->cluster_size == NTFS_BLOCK_SIZE &&
diff --git a/fs/ntfs/ntfs.h b/fs/ntfs/ntfs.h
index df5a75d506f6..45f77848a9cf 100644
--- a/fs/ntfs/ntfs.h
+++ b/fs/ntfs/ntfs.h
@@ -19,6 +19,7 @@
#include <linux/nls.h>
#include <linux/smp.h>
#include <linux/pagemap.h>
+#include <linux/blk_types.h>
#include <linux/uidgid.h>
#include "volume.h"
@@ -71,8 +72,6 @@
#define NTFS_CLU_TO_POFS(vol, clu) (((u64)(clu) << (vol)->cluster_size_bits) & \
~PAGE_MASK)
-#define NTFS_B_TO_SECTOR(vol, b) ((b) >> ((vol)->sb)->s_blocksize_bits)
-
enum {
NTFS_BLOCK_SIZE = 512,
NTFS_BLOCK_SIZE_BITS = 9,
@@ -154,11 +153,10 @@ static inline u64 ntfs_cluster_to_poff(const struct ntfs_volume *vol,
return (clu << vol->cluster_size_bits) & ~PAGE_MASK;
}
-/* Convert byte offset to sector (block) number. */
-static inline sector_t ntfs_bytes_to_sector(const struct ntfs_volume *vol,
- u64 bytes)
+/* Convert a byte offset on the volume to a bio sector number. */
+static inline sector_t ntfs_bytes_to_bio_sector(u64 bytes)
{
- return bytes >> vol->sb->s_blocksize_bits;
+ return bytes >> SECTOR_SHIFT;
}
/* Global variables. */
diff --git a/fs/ntfs/reparse.c b/fs/ntfs/reparse.c
index 5e483a2f9060..1a6073e22677 100644
--- a/fs/ntfs/reparse.c
+++ b/fs/ntfs/reparse.c
@@ -405,7 +405,7 @@ unsigned int ntfs_reparse_tag_dt_types(struct ntfs_volume *vol, unsigned long mr
vi = ntfs_iget(vol->sb, mref);
if (IS_ERR(vi))
- return PTR_ERR(vi);
+ return DT_UNKNOWN;
reparse_attr = (struct reparse_point *)ntfs_attr_readall(NTFS_I(vi),
AT_REPARSE_POINT, NULL, 0, &attr_size);
@@ -694,8 +694,9 @@ static int update_reparse_data(struct ntfs_inode *ni, struct ntfs_index_context
goto put_rp_inode;
}
- if (set_reparse_index(ni, xr, ((const struct reparse_point *)value)->reparse_tag) &&
- oldsize > 0) {
+ err = set_reparse_index(ni, xr,
+ ((const struct reparse_point *)value)->reparse_tag);
+ if (err && oldsize > 0) {
/*
* If cannot index, try to remove the reparse
* data and log the error. There will be an
diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c
index 30481e5d5dd4..60d43339c590 100644
--- a/fs/ntfs/super.c
+++ b/fs/ntfs/super.c
@@ -557,8 +557,8 @@ static bool is_boot_sector_ntfs(const struct super_block *sb,
* Check sectors per cluster value is valid and the cluster size
* is not above the maximum (2MB).
*/
- if (b->bpb.sectors_per_cluster > 0x80 &&
- b->bpb.sectors_per_cluster < 0xf4)
+ if (b->bpb.sectors_per_cluster < 0xf4 &&
+ !is_power_of_2(b->bpb.sectors_per_cluster))
goto not_ntfs;
/* Check reserved/unused fields are really zero. */
@@ -695,7 +695,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
* = -log2(mft_record_size) bytes. mft_record_size normaly is
* 1024 bytes, which is encoded as 0xF6 (-10 in decimal).
*/
- vol->mft_record_size = 1 << -clusters_per_mft_record;
+ vol->mft_record_size = 1U << -clusters_per_mft_record;
vol->mft_record_size_mask = vol->mft_record_size - 1;
vol->mft_record_size_bits = ffs(vol->mft_record_size) - 1;
ntfs_debug("vol->mft_record_size = %i (0x%x)", vol->mft_record_size,
@@ -732,7 +732,7 @@ static bool parse_ntfs_boot_sector(struct ntfs_volume *vol,
* index_record_size normaly equals 4096 bytes, which is
* encoded as 0xF4 (-12 in decimal).
*/
- vol->index_record_size = 1 << -clusters_per_index_record;
+ vol->index_record_size = 1U << -clusters_per_index_record;
vol->index_record_size_mask = vol->index_record_size - 1;
vol->index_record_size_bits = ffs(vol->index_record_size) - 1;
ntfs_debug("vol->index_record_size = %i (0x%x)",
@@ -1241,9 +1241,9 @@ static bool load_and_init_attrdef(struct ntfs_volume *vol)
goto failed;
}
NInoSetSparseDisabled(NTFS_I(ino));
- /* The size of FILE_AttrDef must be above 0 and fit inside 31 bits. */
+ /* FILE_AttrDef must hold at least one entry and fit inside 31 bits. */
i_size = i_size_read(ino);
- if (i_size <= 0 || i_size > 0x7fffffff)
+ if (i_size < (s64)sizeof(struct attr_def) || i_size > 0x7fffffff)
goto iput_failed;
vol->attrdef = kvzalloc(i_size, GFP_NOFS);
if (!vol->attrdef)
@@ -1862,7 +1862,8 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
return 0;
/* If there are some dirty buffers in the bdev inode */
- if (ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) {
+ if (!NVolErrors(vol) &&
+ ntfs_clear_volume_flags(vol, VOLUME_IS_DIRTY)) {
ntfs_warning(sb, "Failed to clear dirty bit in volume information flags. Run chkdsk.");
err = -EIO;
}
diff --git a/fs/ntfs/wof.c b/fs/ntfs/wof.c
index 8f84c2212eee..9847259e5b1a 100644
--- a/fs/ntfs/wof.c
+++ b/fs/ntfs/wof.c
@@ -39,8 +39,6 @@ struct ntfs_wof_workspace {
struct mutex *lock;
const struct ntfs_codec_ops *codec;
u32 comp_unit;
- void *input;
- size_t input_size;
void *output;
void *scratch;
};
@@ -97,30 +95,36 @@ static struct ntfs_wof_workspace *ntfs_wof_workspace(u8 block_size_bits)
}
}
+/*
+ * Size of the buffer a chunk is read into. A chunk is read straight off the
+ * device, so the buffer has to hold @comp_unit bytes plus the leading partial
+ * sector.
+ */
+static size_t ntfs_wof_input_size(const struct ntfs_wof_workspace *ws)
+{
+ return round_up((size_t)ws->comp_unit + 511, 512);
+}
+
static int ntfs_wof_workspace_prepare(struct ntfs_wof_workspace *ws)
{
- void *input, *output, *scratch;
+ void *output, *scratch;
size_t scratch_size;
- if (ws->input)
+ if (ws->output)
return 0;
- ws->input_size = round_up((size_t)ws->comp_unit + 511, 512);
scratch_size = ws->codec->scratch_size(ws->comp_unit);
if (!scratch_size)
return -EINVAL;
- input = kvmalloc(ws->input_size, GFP_NOFS);
output = kvmalloc(ws->comp_unit, GFP_NOFS);
scratch = kvzalloc(scratch_size, GFP_NOFS);
- if (!input || !output || !scratch) {
- kvfree(input);
+ if (!output || !scratch) {
kvfree(output);
kvfree(scratch);
return -ENOMEM;
}
- ws->input = input;
ws->output = output;
ws->scratch = scratch;
return 0;
@@ -134,10 +138,8 @@ void ntfs_wof_free_workspaces(void)
struct ntfs_wof_workspace *ws = ntfs_wof_workspaces[i];
mutex_lock(ws->lock);
- kvfree(ws->input);
kvfree(ws->output);
kvfree(ws->scratch);
- ws->input = NULL;
ws->output = NULL;
ws->scratch = NULL;
mutex_unlock(ws->lock);
@@ -602,6 +604,51 @@ static int ntfs_wof_try_direct(struct ntfs_wof_workspace *ws,
chunk_end, src, src_len, dst_len);
}
+/*
+ * Decompress one chunk into @folio. Only this step needs the workspace, so it
+ * is the only step that takes the workspace lock.
+ */
+static int ntfs_wof_decompress_chunk(struct ntfs_wof_workspace *ws,
+ struct ntfs_volume *vol,
+ struct address_space *mapping,
+ struct folio *folio, loff_t folio_start,
+ loff_t folio_end, u64 chunk_file_offset,
+ char *chunk_mem, u32 chunk_size,
+ u32 decomp_size)
+{
+ loff_t chunk_end = chunk_file_offset + decomp_size;
+ loff_t copy_start, copy_end;
+ int err;
+
+ mutex_lock(ws->lock);
+ err = ntfs_wof_workspace_prepare(ws);
+ if (err)
+ goto out_unlock;
+
+ err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset,
+ chunk_end, chunk_mem, chunk_size,
+ decomp_size);
+ if (err != -EAGAIN)
+ goto out_unlock;
+
+ err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output,
+ decomp_size);
+ if (err) {
+ ntfs_error(vol->sb, "Decompression failed: %d", err);
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
+ copy_start = max_t(loff_t, folio_start, chunk_file_offset);
+ copy_end = min_t(loff_t, folio_end, chunk_file_offset + decomp_size);
+ memcpy_to_folio(folio, copy_start - folio_start,
+ ws->output + copy_start - chunk_file_offset,
+ copy_end - copy_start);
+out_unlock:
+ mutex_unlock(ws->lock);
+ return err;
+}
+
int ntfs_read_wof_compressed_block(struct folio *folio)
{
struct address_space *mapping = folio->mapping;
@@ -613,6 +660,8 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
loff_t folio_start = folio_pos(folio);
loff_t folio_end = folio_next_pos(folio);
char *chunk_mem;
+ void *input;
+ size_t input_size;
u32 decomp_size;
u64 chunk_count, chunk_idx, last_chunk, chunk_offset;
int err = 0;
@@ -652,10 +701,12 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
goto out_iput;
}
- mutex_lock(ws->lock);
- err = ntfs_wof_workspace_prepare(ws);
- if (err)
- goto out_unlock_ws;
+ input_size = ntfs_wof_input_size(ws);
+ input = kvmalloc(input_size, GFP_NOFS);
+ if (!input) {
+ err = -ENOMEM;
+ goto out_iput;
+ }
chunk_idx = div_u64(folio_start, ws->comp_unit);
last_chunk =
@@ -663,55 +714,35 @@ int ntfs_read_wof_compressed_block(struct folio *folio)
chunk_count = DIV_ROUND_UP_ULL(i_size, ws->comp_unit);
for (; chunk_idx <= last_chunk; chunk_idx++) {
u32 chunk_size;
- u64 chunk_file_offset;
- loff_t chunk_end, copy_start, copy_end;
decomp_size = chunk_idx + 1 == chunk_count ?
i_size - chunk_idx * ws->comp_unit :
ws->comp_unit;
err = parse_wof_chunk_table(ni, wof_ni, chunk_idx, chunk_count,
decomp_size, &chunk_offset,
- &chunk_size, ws->input,
- ws->input_size);
+ &chunk_size, input, input_size);
if (err)
- goto out_unlock_ws;
+ goto out_free_input;
err = ntfs_read_wof_chunk(vol, wof_ni, chunk_offset, chunk_size,
- ws->input, ws->input_size,
- &chunk_mem);
+ input, input_size, &chunk_mem);
if (err)
- goto out_unlock_ws;
-
- chunk_file_offset = chunk_idx * ws->comp_unit;
- chunk_end = chunk_file_offset + decomp_size;
- err = ntfs_wof_try_direct(ws, mapping, folio, chunk_file_offset,
- chunk_end, chunk_mem, chunk_size,
- decomp_size);
- if (!err)
- continue;
- if (err != -EAGAIN)
- goto out_unlock_ws;
+ goto out_free_input;
- err = ntfs_wof_decode(ws, chunk_mem, chunk_size, ws->output,
- decomp_size);
- if (err) {
- ntfs_error(vol->sb, "Decompression failed: %d", err);
- err = -EINVAL;
- goto out_unlock_ws;
- }
- copy_start = max_t(loff_t, folio_start, chunk_file_offset);
- copy_end = min_t(loff_t, folio_end,
- chunk_file_offset + decomp_size);
- memcpy_to_folio(folio, copy_start - folio_start,
- ws->output + copy_start - chunk_file_offset,
- copy_end - copy_start);
+ err = ntfs_wof_decompress_chunk(ws, vol, mapping, folio,
+ folio_start, folio_end,
+ chunk_idx * ws->comp_unit,
+ chunk_mem, chunk_size,
+ decomp_size);
+ if (err)
+ goto out_free_input;
}
if (folio_end > i_size)
folio_zero_segment(folio, i_size - folio_start,
folio_size(folio));
-out_unlock_ws:
- mutex_unlock(ws->lock);
+out_free_input:
+ kvfree(input);
out_iput:
iput(wof_inode);
out:
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 204afc5e984b..1c78c695d0dd 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1240,7 +1240,7 @@ static int ignore_hardlimit(struct dquot *dquot)
{
struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];
- return capable(CAP_SYS_RESOURCE) &&
+ return capable_noaudit(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
!(info->dqi_flags & DQF_ROOT_SQUASH));
}
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index f5aad5f61dce..f8aa9e7b4bc6 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3555,6 +3555,7 @@ int cifs_do_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
int rc = 0;
int bytes_returned = 0;
__u16 params, byte_count, data_count, param_offset, offset;
+ size_t cifs_acl_size, bytes_available;
cifs_dbg(FYI, "In SetPosixACL (Unix) for path %s\n", fileName);
setAclRetry:
@@ -3574,8 +3575,7 @@ setAclRetry:
}
params = 6 + name_len;
pSMB->MaxParameterCount = cpu_to_le16(2);
- /* BB find max SMB size from sess */
- pSMB->MaxDataCount = cpu_to_le16(1000);
+ pSMB->MaxDataCount = cpu_to_le16(min_t(unsigned int, CIFSMaxBufSize, USHRT_MAX));
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
pSMB->Flags = 0;
@@ -3587,6 +3587,15 @@ setAclRetry:
parm_data = ((char *)pSMB) + offset;
pSMB->ParameterOffset = cpu_to_le16(param_offset);
+ /* make sure we can fit the larger cifs_posix_aces in the buffer */
+ cifs_acl_size = sizeof(struct cifs_posix_acl) +
+ (acl->a_count * sizeof(struct cifs_posix_ace));
+ bytes_available = (CIFSMaxBufSize + MAX_HEADER_SIZE(tcon->ses->server)) - offset;
+ if (cifs_acl_size > bytes_available || cifs_acl_size > USHRT_MAX) {
+ rc = -E2BIG;
+ goto setACLerrorExit;
+ }
+
/* convert to on the wire format for POSIX ACL */
data_count = posix_acl_to_cifs(parm_data, acl, acl_type);
@@ -6325,8 +6334,10 @@ CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
int name_len;
int rc = 0;
int bytes_returned = 0;
- __u16 params, param_offset, byte_count, offset, count;
+ __u16 params, param_offset;
+ unsigned int byte_count, offset, count;
int remap = cifs_remap(cifs_sb);
+ unsigned int total_len;
cifs_dbg(FYI, "In SetEA\n");
SetEARetry:
@@ -6378,6 +6389,13 @@ SetEARetry:
pSMB->Reserved3 = 0;
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
byte_count = 3 /* pad */ + params + count;
+ if (check_add_overflow(in_len, byte_count, &total_len) ||
+ byte_count > U16_MAX ||
+ total_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
+ cifs_dbg(VFS, "EA request too large: %u bytes\n", total_len);
+ cifs_buf_release(pSMB);
+ return -E2BIG;
+ }
pSMB->DataCount = cpu_to_le16(count);
parm_data->list_len = cpu_to_le32(count);
parm_data->list.EA_flags = 0;
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index bcd7f1ae99ba..b6e98eb31673 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -4189,14 +4189,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
return rc;
}
-static int
-cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
+static int set_fs_context_auth(struct smb3_fs_context *ctx,
+ struct cifs_ses *ses)
{
ctx->sectype = ses->sectype;
- /* krb5 is special, since we don't need username or pw */
- if (ctx->sectype == Kerberos)
+ /*
+ * krb5 is special as we might need to pass username (passwordless) down
+ * to cifs.upcall(8) for keytab.
+ */
+ if (ctx->sectype == Kerberos) {
+ if (ses->user_name && ses->user_name[0]) {
+ ctx->username = kstrndup(ses->user_name,
+ CIFS_MAX_USERNAME_LEN,
+ GFP_KERNEL);
+ if (!ctx->username)
+ return -ENOMEM;
+ }
return 0;
+ }
return cifs_set_cifscreds(ctx, ses);
}
@@ -4236,7 +4247,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
ctx->dfs_root_ses = master_tcon->ses->dfs_root_ses;
ctx->unicode = master_tcon->ses->unicode;
- rc = cifs_set_vol_auth(ctx, master_tcon->ses);
+ rc = set_fs_context_auth(ctx, master_tcon->ses);
if (rc) {
tcon = ERR_PTR(rc);
goto out;
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index bdcd54157e6c..d7b0a9512dfa 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -999,26 +999,50 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry)
struct cifs_tcon *tcon;
int rc;
- rc = filemap_write_and_wait(inode->i_mapping);
- if (is_interrupt_error(rc))
+ rc = inode_lock_killable(inode);
+ if (rc)
return -ERESTARTSYS;
+
+ filemap_invalidate_lock(inode->i_mapping);
+
+ rc = filemap_write_and_wait(inode->i_mapping);
+ if (is_interrupt_error(rc)) {
+ rc = -ERESTARTSYS;
+ goto out;
+ }
mapping_set_error(inode->i_mapping, rc);
cfile = find_writable_file(cinode, FIND_FSUID_ONLY);
rc = cifs_file_flush(xid, inode, cfile);
if (!rc) {
if (cfile) {
+ struct netfs_inode *ictx = netfs_inode(inode);
+
tcon = tlink_tcon(cfile->tlink);
server = tcon->ses->server;
+ netfs_wb_begin(ictx, false);
rc = server->ops->set_file_size(xid, tcon,
cfile, 0, false);
- }
- if (!rc) {
- netfs_resize_file(&cinode->netfs, 0, true);
- cifs_setsize(inode, 0);
+ if (!rc) {
+ netfs_resize_file(&cinode->netfs, 0, true);
+ cifs_setsize(inode, 0);
+ cifs_invalidate_cache(inode, 0);
+ }
+ netfs_wb_end(ictx);
+ } else {
+ /*
+ * No cached handle; evict stale pages so they can't
+ * be served after the file is later extended; let
+ * the server's O_TRUNC open response set the i_size
+ */
+ truncate_inode_pages(inode->i_mapping, 0);
cifs_invalidate_cache(inode, 0);
}
}
+
+out:
+ filemap_invalidate_unlock(inode->i_mapping);
+ inode_unlock(inode);
if (cfile)
cifsFileInfo_put(cfile);
return rc;
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 7d6738ffcb80..cb4fd09f996e 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1839,31 +1839,31 @@ free_vars:
*
* @tcon: destination file tcon
* @bytes_left: how many bytes are left to copy
+ * @chunk_size: maximum size of a single chunk
*
* Return: maximum number of chunks with which Chunks[] can be filled.
*/
static inline u32
-calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
+calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left, u32 chunk_size)
{
u32 max_chunks = READ_ONCE(tcon->max_chunks);
u32 max_bytes_copy = READ_ONCE(tcon->max_bytes_copy);
- u32 max_bytes_chunk = READ_ONCE(tcon->max_bytes_chunk);
u64 need;
u32 allowed;
- if (!max_bytes_chunk || !max_bytes_copy || !max_chunks)
+ if (!chunk_size || !max_bytes_copy || !max_chunks)
return 0;
/* chunks needed for the remaining bytes */
- need = DIV_ROUND_UP_ULL(bytes_left, max_bytes_chunk);
+ need = DIV_ROUND_UP_ULL(bytes_left, chunk_size);
/* chunks allowed per cc request */
- allowed = DIV_ROUND_UP(max_bytes_copy, max_bytes_chunk);
+ allowed = DIV_ROUND_UP(max_bytes_copy, chunk_size);
return (u32)umin(need, umin(max_chunks, allowed));
}
/**
- * smb2_copychunk_range - server-side copy of data range
+ * __smb2_copychunk_range - server-side copy of data range
*
* @xid: transaction id
* @src_file: source file
@@ -1875,15 +1875,15 @@ calc_chunk_count(struct cifs_tcon *tcon, u64 bytes_left)
* Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
* IOCTLs, splitting the request into chunks limited by tcon->max_*.
*
- * Return: @len on success; negative errno on failure.
+ * Return: 0 on success; negative errno on failure.
*/
-static ssize_t
-smb2_copychunk_range(const unsigned int xid,
- struct cifsFileInfo *src_file,
- struct cifsFileInfo *dst_file,
- u64 src_off,
- u64 len,
- u64 dst_off)
+static int
+__smb2_copychunk_range(const unsigned int xid,
+ struct cifsFileInfo *src_file,
+ struct cifsFileInfo *dst_file,
+ u64 src_off,
+ u64 len,
+ u64 dst_off)
{
int rc = 0;
unsigned int ret_data_len = 0;
@@ -1891,12 +1891,14 @@ smb2_copychunk_range(const unsigned int xid,
struct copychunk_ioctl_rsp *cc_rsp = NULL;
struct cifs_tcon *tcon;
struct srv_copychunk *chunk;
- u32 chunks, chunk_count, chunk_bytes;
+ u32 chunks, chunk_count, chunk_bytes, chunk_size;
u32 copy_bytes, copy_bytes_left;
u32 chunks_written, bytes_written;
u64 total_bytes_left = len;
u64 src_off_prev, dst_off_prev;
+ u64 max_chunk = 0;
u32 retries = 0;
+ bool reverse = false;
tcon = tlink_tcon(dst_file->tlink);
@@ -1904,8 +1906,50 @@ smb2_copychunk_range(const unsigned int xid,
dst_file->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dst_off, len);
+ /*
+ * Same-file left shifts are safe in forward order. For a right shift,
+ * let L be the copy length, delta the distance between the source and
+ * destination, and C the normal chunk size:
+ *
+ * delta >= L: copy forwards using C
+ * delta < L:
+ * delta >= C: copy backwards using C
+ * delta < C: copy backwards with chunks limited to delta
+ *
+ * Copying backwards prevents one chunk from overwriting data needed by
+ * a later chunk. Limiting the chunk size to delta prevents an individual
+ * chunk from overlapping itself.
+ * This limit can be removed once all supported servers handle overlapping
+ * descriptors safely.
+ *
+ * A small right shift over a large range may therefore require many
+ * chunks.
+ */
+ if (src_file == dst_file && dst_off > src_off) {
+ u64 delta = dst_off - src_off;
+
+ if (delta < len) {
+ reverse = true;
+ max_chunk = delta;
+ }
+ }
+
+ /*
+ * A backward copy walks the offsets down from the end of the range.
+ * Do this once, outside the retry loop, so a retry does not move the
+ * offsets again.
+ */
+ if (reverse) {
+ src_off += len;
+ dst_off += len;
+ }
+
retry:
- chunk_count = calc_chunk_count(tcon, total_bytes_left);
+ chunk_size = READ_ONCE(tcon->max_bytes_chunk);
+ if (max_chunk && max_chunk < chunk_size)
+ chunk_size = (u32)max_chunk;
+
+ chunk_count = calc_chunk_count(tcon, total_bytes_left, chunk_size);
if (!chunk_count) {
rc = -EOPNOTSUPP;
goto out;
@@ -1946,16 +1990,21 @@ retry:
while (copy_bytes_left > 0 && chunks < chunk_count) {
chunk = &cc_req->Chunks[chunks++];
+ chunk_bytes = umin(copy_bytes_left, chunk_size);
+ if (reverse) {
+ src_off -= chunk_bytes;
+ dst_off -= chunk_bytes;
+ }
+
chunk->SourceOffset = cpu_to_le64(src_off);
chunk->TargetOffset = cpu_to_le64(dst_off);
-
- chunk_bytes = umin(copy_bytes_left, tcon->max_bytes_chunk);
-
chunk->Length = cpu_to_le32(chunk_bytes);
/* Buffer is zeroed, no need to set chunk->Reserved = 0 */
- src_off += chunk_bytes;
- dst_off += chunk_bytes;
+ if (!reverse) {
+ src_off += chunk_bytes;
+ dst_off += chunk_bytes;
+ }
copy_bytes_left -= chunk_bytes;
copy_bytes += chunk_bytes;
@@ -2003,6 +2052,18 @@ retry:
goto out;
}
+ /*
+ * A successful COPYCHUNK should copy every descriptor (MS-SMB2
+ * 3.3.5.15.6). Reject a short backward copy because the rewind
+ * below only supports forward copying.
+ */
+ if (unlikely(reverse && bytes_written < copy_bytes)) {
+ cifs_tcon_dbg(VFS, "Copychunk short write %u/%u (reverse)\n",
+ bytes_written, copy_bytes);
+ rc = -EIO;
+ goto out;
+ }
+
/* Partial write: rewind */
if (bytes_written < copy_bytes) {
u32 delta = copy_bytes - bytes_written;
@@ -2064,10 +2125,27 @@ out:
trace_smb3_copychunk_done(xid, src_file->fid.volatile_fid,
dst_file->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dst_off, len);
- return len;
+ return 0;
}
}
+static ssize_t
+smb2_copychunk_range(const unsigned int xid,
+ struct cifsFileInfo *src_file,
+ struct cifsFileInfo *dst_file,
+ u64 src_off,
+ u64 len,
+ u64 dst_off)
+{
+ int rc;
+
+ rc = __smb2_copychunk_range(xid, src_file, dst_file, src_off, len,
+ dst_off);
+ if (rc)
+ return rc;
+ return len;
+}
+
static int
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *fid)
@@ -2218,7 +2296,7 @@ smb2_duplicate_extents(const unsigned int xid,
trgtfile->fid.volatile_fid, tcon->tid,
tcon->ses->Suid, src_off, dest_off, len);
inode = d_inode(trgtfile->dentry);
- if (inode->i_size < dest_off + len) {
+ if (i_size_read(inode) < dest_off + len) {
rc = smb2_set_file_size(xid, tcon, trgtfile, dest_off + len, false);
if (rc)
goto duplicate_extents_out;
@@ -2235,7 +2313,10 @@ smb2_duplicate_extents(const unsigned int xid,
if (ret_data_len > 0)
cifs_dbg(FYI, "Non-zero response length in duplicate extents\n");
- if (rc == 0) {
+ if (rc) {
+ CIFS_I(inode)->time = 0; /* force reval */
+ cifs_invalidate_cache(inode, 0);
+ } else {
qrc = SMB2_query_info(xid, tcon, trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid, &file_inf);
spin_lock(&inode->i_lock);
@@ -3441,6 +3522,13 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid,
ses->Suid, offset, len);
+ new_size = offset + len;
+ if (!keep_size && i_size_read(inode) < new_size) {
+ rc = inode_newsize_ok(inode, new_size);
+ if (rc)
+ goto out;
+ }
+
filemap_invalidate_lock(inode->i_mapping);
netfs_read_sizes(inode, &i_size, &remote_i_size, &zero_point);
@@ -3464,6 +3552,9 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
if (keep_size == false && !CIFS_CACHE_READ(cifsi))
goto zero_range_exit;
+ fscache_invalidate(cifs_inode_cookie(inode), NULL,
+ i_size_read(inode), 0);
+
rc = smb3_zero_data(file, tcon, offset, len, xid);
if (rc < 0)
goto zero_range_exit;
@@ -3471,7 +3562,6 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
/*
* do we also need to change the size of the file?
*/
- new_size = offset + len;
if (keep_size == false && (unsigned long long)i_size_read(inode) < new_size) {
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_size);
@@ -3488,6 +3578,7 @@ static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon,
zero_range_exit:
filemap_invalidate_unlock(inode->i_mapping);
+ out:
free_xid(xid);
if (rc)
trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,
@@ -3533,6 +3624,8 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
*/
truncate_pagecache_range(inode, offset, offset + len - 1);
netfs_wait_for_outstanding_io(inode);
+ fscache_invalidate(cifs_inode_cookie(inode), NULL,
+ i_size_read(inode), 0);
cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len);
@@ -3938,18 +4031,26 @@ static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
}
filemap_invalidate_lock(inode->i_mapping);
- rc = filemap_write_and_wait_range(inode->i_mapping, off, old_eof - 1);
+ rc = filemap_write_and_wait_range(inode->i_mapping,
+ round_down(off, PAGE_SIZE),
+ old_eof - 1);
if (rc < 0)
goto out_2;
- truncate_pagecache_range(inode, off, old_eof);
+ netfs_wait_for_outstanding_io(inode);
+ /*
+ * Invalidate cached folios from the page containing off to EOF before
+ * moving data on the server, so subsequent reads do not see stale data.
+ */
+ truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
+ fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
+
spin_lock(&inode->i_lock);
netfs_write_zero_point(inode, old_eof);
spin_unlock(&inode->i_lock);
- netfs_wait_for_outstanding_io(inode);
- rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
- old_eof - off - len, off);
+ rc = __smb2_copychunk_range(xid, cfile, cfile, off + len,
+ old_eof - off - len, off);
if (rc < 0)
goto out_2;
@@ -3982,7 +4083,7 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
struct cifsFileInfo *cfile = file->private_data;
struct inode *inode = file_inode(file);
struct cifsInodeInfo *cifsi = CIFS_I(inode);
- __u64 count, old_eof, new_eof;
+ loff_t old_eof, new_eof;
xid = get_xid();
@@ -3992,15 +4093,32 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
goto out;
}
- count = old_eof - off;
- new_eof = old_eof + len;
+ if (check_add_overflow(old_eof, len, &new_eof)) {
+ rc = -EFBIG;
+ goto out;
+ }
+ rc = inode_newsize_ok(inode, new_eof);
+ if (rc)
+ goto out;
+
+ /* SET_ZERO_DATA creates a hole only in a sparse file. */
+ rc = smb2_set_sparse(xid, tcon, cfile, inode, true);
+ if (rc)
+ goto out;
filemap_invalidate_lock(inode->i_mapping);
- rc = filemap_write_and_wait_range(inode->i_mapping, off, new_eof - 1);
+ rc = filemap_write_and_wait_range(inode->i_mapping,
+ round_down(off, PAGE_SIZE),
+ old_eof - 1);
if (rc < 0)
goto out_2;
- truncate_pagecache_range(inode, off, old_eof);
netfs_wait_for_outstanding_io(inode);
+ /*
+ * Invalidate cached folios from the page containing off to EOF before
+ * moving data on the server, so subsequent reads do not see stale data.
+ */
+ truncate_pagecache_range(inode, round_down(off, PAGE_SIZE), -1);
+ fscache_invalidate(cifs_inode_cookie(inode), NULL, old_eof, 0);
rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
cfile->fid.volatile_fid, cfile->pid, new_eof);
@@ -4013,7 +4131,12 @@ static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
spin_unlock(&inode->i_lock);
fscache_resize_cookie(cifs_inode_cookie(inode), i_size_read(inode));
- rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
+ /*
+ * Move [off, old_eof) right by len. The helper copies backwards if the
+ * source and destination ranges overlap.
+ */
+ rc = __smb2_copychunk_range(xid, cfile, cfile, off, old_eof - off,
+ off + len);
if (rc < 0)
goto out_2;
spin_lock(&inode->i_lock);
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index fdf4e50c27ce..e266859818a4 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -101,12 +101,11 @@ void __release_mid(struct TCP_Server_Info *server, struct mid_q_entry *midEntry)
trace_smb3_slow_rsp(smb_cmd, midEntry->mid, midEntry->pid,
midEntry->when_sent, midEntry->when_received);
if (cifsFYI & CIFS_TIMER) {
- pr_debug("slow rsp: cmd %d mid %llu",
- midEntry->command, midEntry->mid);
- cifs_info("A: 0x%lx S: 0x%lx R: 0x%lx\n",
- now - midEntry->when_alloc,
- now - midEntry->when_sent,
- now - midEntry->when_received);
+ pr_debug("slow rsp: cmd %d mid %llu A: 0x%lx S: 0x%lx R: 0x%lx\n",
+ midEntry->command, midEntry->mid,
+ now - midEntry->when_alloc,
+ now - midEntry->when_sent,
+ now - midEntry->when_received);
}
}
#endif
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index 91fdd1ddc61f..4cb92d6599ee 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -13,6 +13,7 @@
#include "mgmt/ksmbd_ida.h"
#include "mgmt/user_session.h"
#include "connection.h"
+#include "vfs_cache.h"
#include "compress.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
@@ -384,12 +385,12 @@ static void ksmbd_conn_cancel_async_requests(struct ksmbd_conn *conn)
spin_lock(&conn->request_lock);
list_for_each_entry_safe(work, tmp, &conn->async_requests,
async_request_entry) {
- if (work->state != KSMBD_WORK_ACTIVE)
+ if (cmpxchg(&work->state, KSMBD_WORK_ACTIVE,
+ KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
continue;
ksmbd_debug(CONN, "Cancel async request id %d\n",
work->async_id);
- work->state = KSMBD_WORK_CANCELLED;
if (work->cancel_fn)
work->cancel_fn(work->cancel_argv);
}
@@ -473,6 +474,9 @@ retry_idle:
if (retry_count >= max_timeout)
return -EIO;
+ /* A blocked byte-range lock cannot drain until teardown wakes it. */
+ ksmbd_wake_session_blocked_works(sess);
+
down_read(&conn_list_lock);
hash_for_each(conn_list, bkt, conn, hlist) {
if (ksmbd_session_is_bound_to_conn(sess, conn)) {
diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h
index 5f1d3ebab4fb..0844aa929f55 100644
--- a/fs/smb/server/ksmbd_work.h
+++ b/fs/smb/server/ksmbd_work.h
@@ -82,7 +82,7 @@ struct ksmbd_work {
/* Contiguous SMB2 compression transform owned by this work item. */
void *compress_buf;
- unsigned char state;
+ unsigned int state;
/* No response for cancelled request */
bool send_no_response:1;
/* Request is encrypted */
diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c
index b2d9580bddc6..cc9f18ede80d 100644
--- a/fs/smb/server/mgmt/share_config.c
+++ b/fs/smb/server/mgmt/share_config.c
@@ -146,9 +146,9 @@ static struct ksmbd_share_config *__share_lookup(const char *name)
static int parse_veto_list(struct ksmbd_share_config *share,
char *veto_list,
- int veto_list_sz)
+ size_t veto_list_sz)
{
- int sz = 0;
+ size_t sz;
if (!veto_list_sz)
return 0;
@@ -156,7 +156,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
while (veto_list_sz > 0) {
struct ksmbd_veto_pattern *p;
- sz = strlen(veto_list);
+ sz = strnlen(veto_list, veto_list_sz);
if (!sz)
break;
@@ -164,7 +164,7 @@ static int parse_veto_list(struct ksmbd_share_config *share,
if (!p)
return -ENOMEM;
- p->pattern = kstrdup(veto_list, KSMBD_DEFAULT_GFP);
+ p->pattern = kstrndup(veto_list, sz, KSMBD_DEFAULT_GFP);
if (!p->pattern) {
kfree(p);
return -ENOMEM;
@@ -172,6 +172,9 @@ static int parse_veto_list(struct ksmbd_share_config *share,
list_add(&p->list, &share->veto_list);
+ if (sz == veto_list_sz)
+ break;
+
veto_list += sz + 1;
veto_list_sz -= (sz + 1);
}
@@ -224,17 +227,28 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
}
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
- int path_len = PATH_MAX;
-
- if (resp->payload_sz)
- path_len = resp->payload_sz - resp->veto_list_sz;
+ size_t path_len;
- share->path = kstrndup(ksmbd_share_config_path(resp), path_len,
- KSMBD_DEFAULT_GFP);
- if (!share->path) {
- ret = -ENOMEM;
+ if (resp->payload_sz <= resp->veto_list_sz) {
+ ret = -EINVAL;
} else {
- ret = 0;
+ path_len = resp->payload_sz - resp->veto_list_sz;
+ if (resp->veto_list_sz)
+ path_len--;
+
+ if (!path_len) {
+ ret = -EINVAL;
+ } else {
+ share->path = kstrndup(
+ ksmbd_share_config_path(resp),
+ path_len, KSMBD_DEFAULT_GFP);
+ if (!share->path)
+ ret = -ENOMEM;
+ else
+ ret = 0;
+ }
+ }
+ if (share->path) {
share->path_sz = strlen(share->path);
while (share->path_sz > 1 &&
share->path[share->path_sz - 1] == '/')
diff --git a/fs/smb/server/mgmt/tree_connect.c b/fs/smb/server/mgmt/tree_connect.c
index 5f63e236267a..dd1db3554cae 100644
--- a/fs/smb/server/mgmt/tree_connect.c
+++ b/fs/smb/server/mgmt/tree_connect.c
@@ -82,6 +82,8 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
down_write(&sess->tree_conns_lock);
ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
KSMBD_DEFAULT_GFP));
+ if (!ret)
+ atomic_inc(&tree_conn->refcount);
up_write(&sess->tree_conns_lock);
if (ret) {
status.ret = -ENOMEM;
@@ -129,6 +131,12 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *tree_conn)
{
down_write(&sess->tree_conns_lock);
+ if (tree_conn->t_state == TREE_DISCONNECTED ||
+ xa_load(&sess->tree_conns, tree_conn->id) != tree_conn) {
+ up_write(&sess->tree_conns_lock);
+ return -ENOENT;
+ }
+ tree_conn->t_state = TREE_DISCONNECTED;
xa_erase(&sess->tree_conns, tree_conn->id);
up_write(&sess->tree_conns_lock);
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index 7022d5d656b4..2eb8f730e99e 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -666,10 +666,21 @@ void destroy_previous_session(struct ksmbd_conn *conn,
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
goto out;
+ down_write(&prev_sess->chann_lock);
+ if (prev_sess->tearing_down) {
+ up_write(&prev_sess->chann_lock);
+ goto out;
+ }
+ prev_sess->tearing_down = true;
+ up_write(&prev_sess->chann_lock);
+
ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_RECONNECT);
err = ksmbd_conn_wait_idle_sess(conn, prev_sess);
if (err) {
- ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_NEED_SETUP);
+ down_write(&prev_sess->chann_lock);
+ prev_sess->tearing_down = false;
+ up_write(&prev_sess->chann_lock);
+ ksmbd_all_conn_set_status(prev_sess, KSMBD_SESS_GOOD);
goto out;
}
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index f8a24c33f7fe..3e52d4cc1324 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -42,6 +42,7 @@ struct ksmbd_session {
bool sign;
bool enc;
+ bool tearing_down;
int state;
__u8 *Preauth_HashValue;
diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c
index 58af0fddf39f..1b8c3482d1e4 100644
--- a/fs/smb/server/oplock.c
+++ b/fs/smb/server/oplock.c
@@ -924,31 +924,69 @@ out:
ksmbd_conn_put(conn);
}
+/*
+ * Select and pin the connection used for an oplock break before doing any
+ * allocations which may sleep. The caller of oplock_break() holds a live
+ * reference on ci (a file being opened, a file being operated on, or an
+ * explicit ksmbd_inode_lookup_lock() reference in the parent lease break
+ * paths), so the inode cannot be freed during the call and its lock is
+ * reachable without dereferencing opinfo->o_fp, which is not pinned by
+ * the oplock reference and may be freed by a concurrent close.
+ *
+ * opinfo->conn is cleared under ci->m_lock by session_fd_check() when the
+ * durable handle owning the oplock is disconnected, reassigned by
+ * ksmbd_reopen_durable_fd() under the same lock, and the last
+ * ksmbd_conn_put() of the old connection frees it. Holding the read lock
+ * excludes both writers, so the connection cannot be freed while it is
+ * selected.
+ */
+static struct ksmbd_conn *smb2_oplock_break_conn_get(struct oplock_info *opinfo,
+ struct ksmbd_inode *ci)
+{
+ struct ksmbd_conn *conn;
+
+ down_read(&ci->m_lock);
+ conn = READ_ONCE(opinfo->conn);
+ if (conn && !ksmbd_conn_releasing(conn))
+ conn = ksmbd_conn_get(conn);
+ else
+ conn = NULL;
+ up_read(&ci->m_lock);
+
+ return conn;
+}
+
/**
* smb2_oplock_break_noti() - send smb2 exclusive/batch to level2 oplock
* break command from server to client
* @opinfo: oplock info object
+ * @ci: inode owning the break target's oplock list, pinned by
+ * the caller
*
* Return: 0 on success, otherwise error
*/
-static int smb2_oplock_break_noti(struct oplock_info *opinfo)
+static int smb2_oplock_break_noti(struct oplock_info *opinfo,
+ struct ksmbd_inode *ci)
{
struct ksmbd_conn *conn;
struct oplock_break_info *br_info;
int ret = 0;
struct ksmbd_work *work;
- conn = READ_ONCE(opinfo->conn);
+ conn = smb2_oplock_break_conn_get(opinfo, ci);
if (!conn)
return ksmbd_invalidate_durable_fd(opinfo->fid);
work = ksmbd_alloc_work_struct();
- if (!work)
+ if (!work) {
+ ksmbd_conn_put(conn);
return -ENOMEM;
+ }
br_info = kmalloc_obj(struct oplock_break_info, KSMBD_DEFAULT_GFP);
if (!br_info) {
ksmbd_free_work_struct(work);
+ ksmbd_conn_put(conn);
return -ENOMEM;
}
@@ -957,7 +995,8 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
br_info->open_trunc = opinfo->open_trunc;
work->request_buf = (char *)br_info;
- work->conn = ksmbd_conn_get(conn);
+ /* Transfer the reference acquired by smb2_oplock_break_conn_get(). */
+ work->conn = conn;
work->sess = opinfo->sess;
ksmbd_conn_r_count_inc(conn);
@@ -1154,9 +1193,9 @@ static void wait_lease_breaking(struct oplock_info *opinfo)
}
}
-static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
- struct ksmbd_work *in_work, bool share_break,
- bool sync_lease_break)
+static int oplock_break(struct oplock_info *brk_opinfo, struct ksmbd_inode *ci,
+ int req_op_level, struct ksmbd_work *in_work,
+ bool share_break, bool sync_lease_break)
{
int err = 0;
bool sent_interim = false;
@@ -1298,7 +1337,7 @@ again:
}
}
- err = smb2_oplock_break_noti(brk_opinfo);
+ err = smb2_oplock_break_noti(brk_opinfo, ci);
ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level);
if (brk_opinfo->op_state == OPLOCK_CLOSING)
@@ -1326,13 +1365,14 @@ static int oplock_break_add(struct list_head *head, struct oplock_info *opinfo)
return 0;
}
-static void oplock_break_drain_none(struct list_head *head)
+static void oplock_break_drain_none(struct list_head *head,
+ struct ksmbd_inode *ci)
{
struct oplock_break_entry *ent, *tmp;
list_for_each_entry_safe(ent, tmp, head, list) {
- oplock_break(ent->opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false,
- false);
+ oplock_break(ent->opinfo, ci, SMB2_OPLOCK_LEVEL_NONE, NULL,
+ false, false);
list_del(&ent->list);
opinfo_put(ent->opinfo);
kfree(ent);
@@ -1481,7 +1521,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
}
up_read(&p_ci->m_lock);
- oplock_break_drain_none(&brk_list);
+ oplock_break_drain_none(&brk_list, p_ci);
ksmbd_inode_put(p_ci);
}
@@ -1525,7 +1565,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
}
up_read(&p_ci->m_lock);
- oplock_break_drain_none(&brk_list);
+ oplock_break_drain_none(&brk_list, p_ci);
ksmbd_inode_put(p_ci);
}
@@ -1665,7 +1705,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
prev_durable_detached = prev_op_snapshot.durable_detached;
prev_fid = prev_op_snapshot.fid;
- err = oplock_break(prev_opinfo, break_level, work,
+ err = oplock_break(prev_opinfo, ci, break_level, work,
share_ret < 0 && prev_opinfo->is_lease, false);
if (prev_durable_detached || (prev_durable_open && err == -ENOENT))
ksmbd_invalidate_durable_fd(prev_fid);
@@ -1771,7 +1811,8 @@ static bool smb_break_all_write_oplock(struct ksmbd_work *work,
}
brk_opinfo->open_trunc = is_trunc;
- oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work, false, false);
+ oplock_break(brk_opinfo, fp->f_ci, SMB2_OPLOCK_LEVEL_II, work, false,
+ false);
sent_break = true;
opinfo_put(brk_opinfo);
@@ -1863,7 +1904,7 @@ next:
brk_op->op_state = OPLOCK_STATE_NONE;
spin_unlock(&brk_op->state_lock);
} else {
- oplock_break(brk_op,
+ oplock_break(brk_op, ci,
brk_op->is_lease && !is_trunc ?
SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE,
send_interim && !sent_interim ? work : NULL,
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index a8046f477d54..0ecc52fde69c 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -97,6 +97,11 @@ static int register_session_channel(struct ksmbd_session *sess,
int rc = 0;
down_write(&sess->chann_lock);
+ if (sess->tearing_down) {
+ rc = -ESHUTDOWN;
+ goto out;
+ }
+
if (xa_load(&sess->ksmbd_chann_list, (long)conn))
goto out;
@@ -873,7 +878,8 @@ int smb2_allocate_rsp_buf(struct ksmbd_work *work)
req = smb_get_msg(work->request_buf);
if ((req->InfoType == SMB2_O_INFO_FILE &&
(req->FileInfoClass == FILE_FULL_EA_INFORMATION ||
- req->FileInfoClass == FILE_ALL_INFORMATION)) ||
+ req->FileInfoClass == FILE_ALL_INFORMATION ||
+ req->FileInfoClass == FILE_NORMALIZED_NAME_INFORMATION)) ||
req->InfoType == SMB2_O_INFO_SECURITY)
sz = large_sz;
}
@@ -2784,6 +2790,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
struct ksmbd_session *sess = work->sess;
char *treename = NULL, *name = NULL;
struct ksmbd_tree_conn_status status;
+ struct ksmbd_tree_connect *tree_conn = NULL;
struct ksmbd_share_config *share = NULL;
int rc = -EINVAL;
@@ -2811,6 +2818,7 @@ int smb2_tree_connect(struct ksmbd_work *work)
status = ksmbd_tree_conn_connect(work, name);
if (status.ret == KSMBD_TREE_CONN_STATUS_OK) {
+ tree_conn = status.tree_conn;
rsp->hdr.Id.SyncId.TreeId = cpu_to_le32(status.tree_conn->id);
share = status.tree_conn->share_conf;
@@ -2854,8 +2862,15 @@ int smb2_tree_connect(struct ksmbd_work *work)
status.tree_conn->posix_extensions = true;
down_write(&sess->tree_conns_lock);
- status.tree_conn->t_state = TREE_CONNECTED;
+ if (status.tree_conn->t_state == TREE_DISCONNECTED) {
+ status.ret = KSMBD_TREE_CONN_STATUS_ERROR;
+ share = NULL;
+ } else {
+ status.tree_conn->t_state = TREE_CONNECTED;
+ }
up_write(&sess->tree_conns_lock);
+ if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
+ goto out_err1;
rsp->StructureSize = cpu_to_le16(16);
out_err1:
/*
@@ -2882,9 +2897,6 @@ out_err1:
rc = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_tree_connect_rsp));
if (rc) {
if (status.ret == KSMBD_TREE_CONN_STATUS_OK) {
- down_write(&sess->tree_conns_lock);
- status.tree_conn->t_state = TREE_DISCONNECTED;
- up_write(&sess->tree_conns_lock);
ksmbd_tree_conn_disconnect(sess, status.tree_conn);
status.tree_conn = NULL;
}
@@ -2925,6 +2937,9 @@ out_err1:
if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
smb2_set_err_rsp(work);
+ if (tree_conn)
+ ksmbd_tree_connect_put(tree_conn);
+
return rc;
}
@@ -3028,17 +3043,6 @@ int smb2_tree_disconnect(struct ksmbd_work *work)
ksmbd_close_tree_conn_fds(work);
- down_write(&sess->tree_conns_lock);
- if (tcon->t_state == TREE_DISCONNECTED) {
- up_write(&sess->tree_conns_lock);
- rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
- err = -ENOENT;
- goto err_out;
- }
-
- tcon->t_state = TREE_DISCONNECTED;
- up_write(&sess->tree_conns_lock);
-
err = ksmbd_tree_conn_disconnect(sess, tcon);
if (err) {
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
@@ -3086,17 +3090,41 @@ int smb2_session_logoff(struct ksmbd_work *work)
smb2_set_err_rsp(work);
return -ENOENT;
}
+
+ down_write(&sess->chann_lock);
+ if (sess->tearing_down) {
+ up_write(&sess->chann_lock);
+ ksmbd_conn_unlock(conn);
+ rsp->hdr.Status = STATUS_USER_SESSION_DELETED;
+ smb2_set_err_rsp(work);
+ return -ENOENT;
+ }
+ sess->tearing_down = true;
+ up_write(&sess->chann_lock);
+
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_RECONNECT);
ksmbd_conn_unlock(conn);
+ err = ksmbd_conn_wait_idle_sess(conn, sess);
+ if (err) {
+ down_write(&sess->chann_lock);
+ sess->tearing_down = false;
+ up_write(&sess->chann_lock);
+ ksmbd_all_conn_set_status(sess, KSMBD_SESS_GOOD);
+ rsp->hdr.Status = STATUS_UNEXPECTED_IO_ERROR;
+ smb2_set_err_rsp(work);
+ return err;
+ }
+
ksmbd_close_session_fds(work);
- ksmbd_conn_wait_idle(conn);
if (ksmbd_tree_conn_session_logoff(sess)) {
ksmbd_debug(SMB, "Invalid tid %d\n", req->hdr.Id.SyncId.TreeId);
rsp->hdr.Status = STATUS_NETWORK_NAME_DELETED;
smb2_set_err_rsp(work);
- return -ENOENT;
+ err = -ENOENT;
+ } else {
+ err = 0;
}
down_write(&conn->session_lock);
@@ -3106,6 +3134,9 @@ int smb2_session_logoff(struct ksmbd_work *work)
ksmbd_all_conn_set_status(sess, KSMBD_SESS_NEED_SETUP);
+ if (err)
+ return err;
+
rsp->StructureSize = cpu_to_le16(4);
err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
if (err) {
@@ -6757,7 +6788,7 @@ static int get_file_normalized_name_info(struct ksmbd_work *work,
{
struct smb2_file_alt_name_info *file_info;
char *filename, *normalized, *stream_name;
- int conv_len, filename_len;
+ int buf_free_len, conv_len, filename_len;
if (work->conn->dialect < SMB311_PROT_ID) {
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
@@ -6781,6 +6812,14 @@ static int get_file_normalized_name_info(struct ksmbd_work *work,
return -ENOMEM;
filename_len = strlen(normalized);
+ buf_free_len = smb2_resp_buf_len(work, sizeof(*rsp) +
+ sizeof(*file_info));
+ if (buf_free_len < 0 ||
+ (size_t)buf_free_len < (filename_len + 1) * sizeof(__le16)) {
+ kfree(normalized);
+ return -EINVAL;
+ }
+
file_info = (struct smb2_file_alt_name_info *)rsp->Buffer;
conv_len = smbConvertToUTF16((__le16 *)file_info->FileName,
normalized, filename_len,
@@ -7444,6 +7483,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
struct object_id_info *info;
info = (struct object_id_info *)(rsp->Buffer);
+ memset(info, 0, sizeof(*info));
if (path.mnt->mnt_sb->s_uuid_len == 16)
memcpy(info->objid, path.mnt->mnt_sb->s_uuid.b,
@@ -7499,6 +7539,7 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
info->FreeSpaceStopFiltering = 0;
info->DefaultQuotaThreshold = cpu_to_le64(SMB2_NO_FID);
info->DefaultQuotaLimit = cpu_to_le64(SMB2_NO_FID);
+ info->FileSystemControlFlags = 0;
info->Padding = 0;
rsp->OutputBufferLength = cpu_to_le32(48);
fixed_len = 48;
@@ -7521,6 +7562,9 @@ static int smb2_get_info_filesystem(struct ksmbd_work *work,
info->UserBlocksAvail = cpu_to_le64(stfs.f_bavail);
info->TotalFileNodes = cpu_to_le64(stfs.f_files);
info->FreeFileNodes = cpu_to_le64(stfs.f_ffree);
+ info->FileSysIdentifier =
+ cpu_to_le64((u64)(u32)stfs.f_fsid.val[1] << 32 |
+ (u32)stfs.f_fsid.val[0]);
rsp->OutputBufferLength = cpu_to_le32(56);
fixed_len = 56;
}
@@ -8620,13 +8664,18 @@ static noinline int smb2_read_pipe(struct ksmbd_work *work)
}
aux_payload_buf =
- kvmalloc(rpc_resp->payload_sz, KSMBD_DEFAULT_GFP);
+ kvmalloc(ALIGN(rpc_resp->payload_sz, 8),
+ KSMBD_DEFAULT_GFP);
if (!aux_payload_buf) {
err = -ENOMEM;
goto out;
}
memcpy(aux_payload_buf, rpc_resp->payload, rpc_resp->payload_sz);
+ if (rpc_resp->payload_sz & 7)
+ memset(aux_payload_buf + rpc_resp->payload_sz, 0,
+ ALIGN(rpc_resp->payload_sz, 8) -
+ rpc_resp->payload_sz);
nbytes = rpc_resp->payload_sz;
err = ksmbd_iov_pin_rsp_read(work, (void *)rsp,
@@ -9680,14 +9729,14 @@ int smb2_cancel(struct ksmbd_work *work)
* still on conn->async_requests with a live cancel_fn
* pointing at the freed file_lock.
*/
- if (iter->state != KSMBD_WORK_ACTIVE)
+ if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE,
+ KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
break;
ksmbd_debug(SMB,
"smb2 with AsyncId %llu cancelled command = 0x%x\n",
le64_to_cpu(hdr->Id.AsyncId),
le16_to_cpu(chdr->Command));
- iter->state = KSMBD_WORK_CANCELLED;
if (iter->cancel_fn == smb2_notify_cancel_fn)
cancelled_notify =
smb2_notify_cancel_claim(iter->cancel_argv);
@@ -9716,11 +9765,16 @@ int smb2_cancel(struct ksmbd_work *work)
iter == work)
continue;
+ if (cmpxchg(&iter->state, KSMBD_WORK_ACTIVE,
+ KSMBD_WORK_CANCELLED) != KSMBD_WORK_ACTIVE)
+ break;
+
ksmbd_debug(SMB,
"smb2 with mid %llu cancelled command = 0x%x\n",
le64_to_cpu(hdr->MessageId),
le16_to_cpu(chdr->Command));
- iter->state = KSMBD_WORK_CANCELLED;
+ if (iter->cancel_fn)
+ iter->cancel_fn(iter->cancel_argv);
break;
}
spin_unlock(&conn->request_lock);
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index 8ad2e5a5cca8..1fad6ccf3a72 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -383,10 +383,10 @@ void free_acl_state(struct posix_acl_state *state)
kfree(state->groups);
}
-static void parse_dacl(struct mnt_idmap *idmap,
- struct smb_acl *pdacl, char *end_of_acl,
- struct smb_sid *pownersid, struct smb_sid *pgrpsid,
- struct smb_fattr *fattr)
+static int parse_dacl(struct mnt_idmap *idmap,
+ struct smb_acl *pdacl, char *end_of_acl,
+ struct smb_sid *pownersid, struct smb_sid *pgrpsid,
+ struct smb_fattr *fattr)
{
int i, ret;
u16 num_aces = 0;
@@ -400,13 +400,13 @@ static void parse_dacl(struct mnt_idmap *idmap,
bool owner_found = false, group_found = false, others_found = false;
if (!pdacl)
- return;
+ return 0;
/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
pr_err("ACL too small to parse DACL\n");
- return;
+ return -EINVAL;
}
ksmbd_debug(SMB, "DACL revision %d size %d num aces %d\n",
@@ -418,31 +418,31 @@ static void parse_dacl(struct mnt_idmap *idmap,
num_aces = le16_to_cpu(pdacl->num_aces);
if (num_aces <= 0)
- return;
+ return 0;
dacl_size = le16_to_cpu(pdacl->size);
if (dacl_size < sizeof(struct smb_acl))
- return;
+ return -EINVAL;
if (num_aces > (dacl_size - sizeof(struct smb_acl)) /
(offsetof(struct smb_ace, sid) +
offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
- return;
+ return -EINVAL;
ret = init_acl_state(&acl_state, num_aces);
if (ret)
- return;
+ return ret;
ret = init_acl_state(&default_acl_state, num_aces);
if (ret) {
free_acl_state(&acl_state);
- return;
+ return ret;
}
ppace = kmalloc_objs(struct smb_ace *, num_aces, KSMBD_DEFAULT_GFP);
if (!ppace) {
free_acl_state(&default_acl_state);
free_acl_state(&acl_state);
- return;
+ return -ENOMEM;
}
/*
@@ -451,8 +451,10 @@ static void parse_dacl(struct mnt_idmap *idmap,
* user/group/other have no permissions
*/
for (i = 0; i < num_aces; ++i) {
- if (end_of_acl - acl_base < acl_size)
- break;
+ if (end_of_acl - acl_base < acl_size) {
+ ret = -EINVAL;
+ goto out;
+ }
ppace[i] = (struct smb_ace *)(acl_base + acl_size);
acl_base = (char *)ppace[i];
@@ -465,8 +467,10 @@ static void parse_dacl(struct mnt_idmap *idmap,
(end_of_acl - acl_base <
acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
(le16_to_cpu(ppace[i]->size) <
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
- break;
+ acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth)) {
+ ret = -EINVAL;
+ goto out;
+ }
acl_size = le16_to_cpu(ppace[i]->size);
ppace[i]->access_req =
@@ -524,8 +528,8 @@ static void parse_dacl(struct mnt_idmap *idmap,
temp_fattr.cf_uid = INVALID_UID;
ret = sid_to_id(idmap, &ppace[i]->sid, SIDOWNER, &temp_fattr);
if (ret || uid_eq(temp_fattr.cf_uid, INVALID_UID)) {
- pr_err("%s: Error %d mapping Owner SID to uid\n",
- __func__, ret);
+ pr_err_ratelimited("%s: Error %d mapping Owner SID to uid\n",
+ __func__, ret);
continue;
}
@@ -541,7 +545,6 @@ static void parse_dacl(struct mnt_idmap *idmap,
((acl_mode & 0700) >> 6) | 0004;
}
}
- kfree(ppace);
if (owner_found) {
/* The owner must be set to at least read-only. */
@@ -584,10 +587,12 @@ static void parse_dacl(struct mnt_idmap *idmap,
fattr->cf_acls =
posix_acl_alloc(acl_state.users->n +
acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
- if (fattr->cf_acls) {
- cf_pace = fattr->cf_acls->a_entries;
- posix_state_to_acl(&acl_state, cf_pace);
+ if (!fattr->cf_acls) {
+ ret = -ENOMEM;
+ goto out;
}
+ cf_pace = fattr->cf_acls->a_entries;
+ posix_state_to_acl(&acl_state, cf_pace);
}
}
@@ -598,14 +603,20 @@ static void parse_dacl(struct mnt_idmap *idmap,
fattr->cf_dacls =
posix_acl_alloc(default_acl_state.users->n +
default_acl_state.groups->n + 4, KSMBD_DEFAULT_GFP);
- if (fattr->cf_dacls) {
- cf_pdace = fattr->cf_dacls->a_entries;
- posix_state_to_acl(&default_acl_state, cf_pdace);
+ if (!fattr->cf_dacls) {
+ ret = -ENOMEM;
+ goto out;
}
+ cf_pdace = fattr->cf_dacls->a_entries;
+ posix_state_to_acl(&default_acl_state, cf_pdace);
}
}
+ ret = 0;
+out:
+ kfree(ppace);
free_acl_state(&acl_state);
free_acl_state(&default_acl_state);
+ return ret;
}
static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
@@ -966,8 +977,10 @@ int parse_sec_desc(struct mnt_idmap *idmap, struct smb_ntsd *pntsd,
if (dacloffset < sizeof(struct smb_ntsd))
return -EINVAL;
- parse_dacl(idmap, dacl_ptr, end_of_acl,
- owner_sid_ptr, group_sid_ptr, fattr);
+ rc = parse_dacl(idmap, dacl_ptr, end_of_acl,
+ owner_sid_ptr, group_sid_ptr, fattr);
+ if (rc)
+ return rc;
}
return 0;
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 4b0b572a3e1b..e550aa41ad2c 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -532,14 +532,21 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
if (entry->msg_sz < sizeof(struct ksmbd_share_config_response))
return -EINVAL;
- if (resp->payload_sz) {
- if (resp->payload_sz < resp->veto_list_sz)
- return -EINVAL;
+ if (strnlen(resp->share_name, sizeof(resp->share_name)) ==
+ sizeof(resp->share_name))
+ return -EINVAL;
- if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
- resp->payload_sz, &msg_sz))
- return -EINVAL;
- }
+ if (resp->veto_list_sz > resp->payload_sz)
+ return -EINVAL;
+
+ if (resp->flags != KSMBD_SHARE_FLAG_INVALID &&
+ !(resp->flags & KSMBD_SHARE_FLAG_PIPE) &&
+ resp->payload_sz <= resp->veto_list_sz)
+ return -EINVAL;
+
+ if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
+ resp->payload_sz, &msg_sz))
+ return -EINVAL;
break;
}
case KSMBD_EVENT_LOGIN_REQUEST_EXT:
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index 832e93084605..4968cfc1a572 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -39,6 +39,7 @@ struct tcp_transport {
static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
static void tcp_stop_kthread(struct task_struct *kthread);
+static void ksmbd_tcp_stop_listener(struct interface *iface);
static struct interface *alloc_iface(char *ifname);
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
@@ -321,13 +322,20 @@ static int ksmbd_tcp_run_kthread(struct interface *iface)
int rc;
struct task_struct *kthread;
- kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
- iface->name);
+ kthread = kthread_create(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
+ iface->name);
if (IS_ERR(kthread)) {
rc = PTR_ERR(kthread);
return rc;
}
+
+ /*
+ * The listener can exit after its socket is shutdown, so keep the
+ * task_struct alive until the caller has stopped it.
+ */
+ get_task_struct(kthread);
iface->ksmbd_kthread = kthread;
+ wake_up_process(kthread);
return 0;
}
@@ -598,12 +606,7 @@ static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
if (iface && iface->state == IFACE_STATE_CONFIGURED) {
ksmbd_debug(CONN, "netdev-down event: netdev(%s) is going down\n",
iface->name);
- kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
- tcp_stop_kthread(iface->ksmbd_kthread);
- iface->ksmbd_kthread = NULL;
- sock_release(iface->ksmbd_socket);
- iface->ksmbd_socket = NULL;
-
+ ksmbd_tcp_stop_listener(iface);
iface->state = IFACE_STATE_DOWN;
break;
}
@@ -631,11 +634,25 @@ static void tcp_stop_kthread(struct task_struct *kthread)
if (!kthread)
return;
- ret = kthread_stop(kthread);
+ ret = kthread_stop_put(kthread);
if (ret)
pr_err("failed to stop forker thread\n");
}
+static void ksmbd_tcp_stop_listener(struct interface *iface)
+{
+ if (iface->ksmbd_socket)
+ kernel_sock_shutdown(iface->ksmbd_socket, SHUT_RDWR);
+
+ tcp_stop_kthread(iface->ksmbd_kthread);
+ iface->ksmbd_kthread = NULL;
+
+ if (iface->ksmbd_socket) {
+ sock_release(iface->ksmbd_socket);
+ iface->ksmbd_socket = NULL;
+ }
+}
+
void ksmbd_tcp_destroy(void)
{
struct interface *iface, *tmp;
@@ -643,6 +660,7 @@ void ksmbd_tcp_destroy(void)
unregister_netdevice_notifier(&ksmbd_netdev_notifier);
list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
+ ksmbd_tcp_stop_listener(iface);
list_del(&iface->entry);
kfree(iface->name);
kfree(iface);
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index d2b524f79cbe..c2c9aaa5de1b 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -2007,6 +2007,11 @@ out:
return ret;
}
+static bool ksmbd_vfs_copy_range_valid(loff_t offset, size_t len)
+{
+ return offset >= 0 && (loff_t)len <= MAX_LFS_FILESIZE - offset;
+}
+
int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
struct ksmbd_file *src_fp,
struct ksmbd_file *dst_fp,
@@ -2042,6 +2047,10 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
dst_off = le64_to_cpu(chunks[i].TargetOffset);
len = le32_to_cpu(chunks[i].Length);
+ if (!ksmbd_vfs_copy_range_valid(src_off, len) ||
+ !ksmbd_vfs_copy_range_valid(dst_off, len))
+ return -E2BIG;
+
if (check_lock_range(src_fp->filp, src_off,
src_off + len - 1, READ))
return -EAGAIN;
@@ -2134,7 +2143,8 @@ int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
len = le32_to_cpu(chunks[i].Length);
copy_len = len;
- if (src_off < 0)
+ if (!ksmbd_vfs_copy_range_valid(src_off, len) ||
+ !ksmbd_vfs_copy_range_valid(dst_off, len))
return -E2BIG;
if (src_off > src_file_size || len > src_file_size - src_off) {
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 81626d204249..fd2c595f0486 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -846,12 +846,25 @@ static void set_close_state_blocked_works(struct ksmbd_file *fp)
spin_lock(&fp->f_lock);
list_for_each_entry(cancel_work, &fp->blocked_works,
fp_entry) {
- cancel_work->state = KSMBD_WORK_CLOSED;
- cancel_work->cancel_fn(cancel_work->cancel_argv);
+ if (xchg(&cancel_work->state, KSMBD_WORK_CLOSED) ==
+ KSMBD_WORK_ACTIVE)
+ cancel_work->cancel_fn(cancel_work->cancel_argv);
}
spin_unlock(&fp->f_lock);
}
+void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess)
+{
+ struct ksmbd_file_table *ft = &sess->file_table;
+ struct ksmbd_file *fp;
+ unsigned int id;
+
+ read_lock(&ft->lock);
+ idr_for_each_entry(ft->idr, fp, id)
+ set_close_state_blocked_works(fp);
+ read_unlock(&ft->lock);
+}
+
int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
{
struct ksmbd_file *fp;
diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h
index 502efb16f05f..1884f6deb9d0 100644
--- a/fs/smb/server/vfs_cache.h
+++ b/fs/smb/server/vfs_cache.h
@@ -226,6 +226,7 @@ void ksmbd_stop_durable_scavenger(void);
bool ksmbd_durable_scavenger_active(void);
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
void ksmbd_close_session_fds(struct ksmbd_work *work);
+void ksmbd_wake_session_blocked_works(struct ksmbd_session *sess);
int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
int ksmbd_init_global_file_table(void);
void ksmbd_free_global_file_table(void);
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 9f7133e02576..399a207f2d0e 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -91,6 +91,7 @@ xfs-y += xfs_aops.o \
xfs_healthmon.o \
xfs_icache.o \
xfs_ioctl.o \
+ xfs_ioend.o \
xfs_iomap.o \
xfs_iops.o \
xfs_inode.o \
diff --git a/fs/xfs/libxfs/xfs_rtgroup.h b/fs/xfs/libxfs/xfs_rtgroup.h
index c0b9f9f2c413..fca2eb74908c 100644
--- a/fs/xfs/libxfs/xfs_rtgroup.h
+++ b/fs/xfs/libxfs/xfs_rtgroup.h
@@ -359,7 +359,11 @@ static inline int xfs_initialize_rtgroups(struct xfs_mount *mp,
# define xfs_rtgroup_unlock(rtg, gf) ((void)0)
# define xfs_rtgroup_trans_join(tp, rtg, gf) ((void)0)
# define xfs_update_rtsb(bp, sb_bp) ((void)0)
-# define xfs_log_rtsb(tp, sb_bp) (NULL)
+static inline struct xfs_buf *xfs_log_rtsb(struct xfs_trans *tp,
+ const struct xfs_buf *sb_bp)
+{
+ return NULL;
+}
# define xfs_rtgroup_get_geometry(rtg, rgeo) (-EOPNOTSUPP)
#endif /* CONFIG_XFS_RT */
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index 75f2a021ee6d..f0341adbb879 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -1470,36 +1470,33 @@ xfs_sync_sb_buf(
bool update_rtsb)
{
struct xfs_trans *tp;
- struct xfs_buf *bp;
- struct xfs_buf *rtsb_bp = NULL;
int error;
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
if (error)
return error;
- bp = xfs_trans_getsb(tp);
xfs_log_sb(tp);
- xfs_trans_bhold(tp, bp);
- if (update_rtsb) {
- rtsb_bp = xfs_log_rtsb(tp, bp);
- if (rtsb_bp)
- xfs_trans_bhold(tp, rtsb_bp);
- }
+ if (update_rtsb)
+ xfs_log_rtsb(tp, xfs_trans_getsb(tp));
xfs_trans_set_sync(tp);
error = xfs_trans_commit(tp);
if (error)
- goto out;
- /*
- * write out the sb buffer to get the changes to disk
- */
- error = xfs_bwrite(bp);
- if (!error && rtsb_bp)
- error = xfs_bwrite(rtsb_bp);
-out:
- if (rtsb_bp)
- xfs_buf_relse(rtsb_bp);
- xfs_buf_relse(bp);
+ return error;
+
+ /* Re-acquire and write the sb and rtsb to disk. */
+ xfs_buf_lock(mp->m_sb_bp);
+ error = xfs_bwrite(mp->m_sb_bp);
+ xfs_buf_unlock(mp->m_sb_bp);
+ if (error)
+ return error;
+
+ if (update_rtsb && mp->m_rtsb_bp) {
+ xfs_buf_lock(mp->m_rtsb_bp);
+ error = xfs_bwrite(mp->m_rtsb_bp);
+ xfs_buf_unlock(mp->m_rtsb_bp);
+ }
+
return error;
}
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 74a6089abadf..8b6119776fb3 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -20,6 +20,7 @@
#include "xfs_errortag.h"
#include "xfs_error.h"
#include "xfs_icache.h"
+#include "xfs_ioend.h"
#include "xfs_zone_alloc.h"
#include "xfs_rtgroup.h"
#include <linux/bio-integrity.h>
@@ -37,15 +38,6 @@ XFS_WPC(struct iomap_writepage_ctx *ctx)
}
/*
- * Fast and loose check if this write could update the on-disk inode size.
- */
-static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
-{
- return ioend->io_offset + ioend->io_size >
- XFS_I(ioend->io_inode)->i_disk_size;
-}
-
-/*
* Update on-disk file size now that data has been written to disk.
*/
int
@@ -80,175 +72,6 @@ xfs_setfilesize(
return xfs_trans_commit(tp);
}
-static void
-xfs_ioend_put_open_zones(
- struct iomap_ioend *ioend)
-{
- struct iomap_ioend *tmp;
-
- /*
- * Put the open zone for all ioends merged into this one (if any).
- */
- list_for_each_entry(tmp, &ioend->io_list, io_list)
- xfs_open_zone_put(tmp->io_private);
-
- /*
- * The main ioend might not have an open zone if the submission failed
- * before xfs_zone_alloc_and_submit got called.
- */
- if (ioend->io_private)
- xfs_open_zone_put(ioend->io_private);
-}
-
-/*
- * IO write completion.
- */
-STATIC void
-xfs_end_ioend_write(
- struct iomap_ioend *ioend)
-{
- struct xfs_inode *ip = XFS_I(ioend->io_inode);
- struct xfs_mount *mp = ip->i_mount;
- bool is_zoned = xfs_is_zoned_inode(ip);
- xfs_off_t offset = ioend->io_offset;
- size_t size = ioend->io_size;
- unsigned int nofs_flag;
- int error;
-
- /*
- * We can allocate memory here while doing writeback on behalf of
- * memory reclaim. To avoid memory allocation deadlocks set the
- * task-wide nofs context for the following operations.
- */
- nofs_flag = memalloc_nofs_save();
-
- /*
- * Just clean up the in-memory structures if the fs has been shut down.
- */
- if (xfs_is_shutdown(mp)) {
- error = -EIO;
- goto done;
- }
-
- /*
- * Clean up all COW blocks and underlying data fork delalloc blocks on
- * I/O error. The delalloc punch is required because this ioend was
- * mapped to blocks in the COW fork and the associated pages are no
- * longer dirty. If we don't remove delalloc blocks here, they become
- * stale and can corrupt free space accounting on unmount.
- */
- error = blk_status_to_errno(ioend->io_bio.bi_status);
- if (unlikely(error)) {
- /*
- * Zoned writes update the in-core open zone accounting before
- * I/O submission. A failed write leaves that state
- * inconsistent, so shut down the filesystem instead of letting
- * later writers wait forever for open zone space to become
- * available.
- */
- if (is_zoned) {
- xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
- goto done;
- }
- if (ioend->io_flags & IOMAP_IOEND_SHARED) {
- ASSERT(!is_zoned);
- xfs_reflink_cancel_cow_range(ip, offset, size, true);
- xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
- offset + size, NULL);
- }
- goto done;
- }
-
- /*
- * Success: commit the COW or unwritten blocks if needed.
- */
- if (is_zoned)
- error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
- ioend->io_private, NULLFSBLOCK);
- else if (ioend->io_flags & IOMAP_IOEND_SHARED)
- error = xfs_reflink_end_cow(ip, offset, size);
- else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
- error = xfs_iomap_write_unwritten(ip, offset, size, false);
-
- if (!error &&
- !(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
- xfs_ioend_is_append(ioend))
- error = xfs_setfilesize(ip, offset, size);
-done:
- if (is_zoned)
- xfs_ioend_put_open_zones(ioend);
- iomap_finish_ioends(ioend, error);
- memalloc_nofs_restore(nofs_flag);
-}
-
-/*
- * Finish all pending IO completions that require transactional modifications.
- *
- * We try to merge physical and logically contiguous ioends before completion to
- * minimise the number of transactions we need to perform during IO completion.
- * Both unwritten extent conversion and COW remapping need to iterate and modify
- * one physical extent at a time, so we gain nothing by merging physically
- * discontiguous extents here.
- *
- * The ioend chain length that we can be processing here is largely unbound in
- * length and we may have to perform significant amounts of work on each ioend
- * to complete it. Hence we have to be careful about holding the CPU for too
- * long in this loop.
- */
-void
-xfs_end_io(
- struct work_struct *work)
-{
- struct xfs_inode *ip =
- container_of(work, struct xfs_inode, i_ioend_work);
- struct iomap_ioend *ioend;
- struct list_head tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&ip->i_ioend_lock, flags);
- list_replace_init(&ip->i_ioend_list, &tmp);
- spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
-
- iomap_sort_ioends(&tmp);
- while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
- io_list))) {
- list_del_init(&ioend->io_list);
- iomap_ioend_try_merge(ioend, &tmp);
- if (bio_op(&ioend->io_bio) == REQ_OP_READ)
- iomap_finish_ioends(ioend,
- blk_status_to_errno(ioend->io_bio.bi_status));
- else
- xfs_end_ioend_write(ioend);
- cond_resched();
- }
-}
-
-void
-xfs_end_bio(
- struct bio *bio)
-{
- struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
- struct xfs_inode *ip = XFS_I(ioend->io_inode);
- struct xfs_mount *mp = ip->i_mount;
- unsigned long flags;
-
- /*
- * For Appends record the actually written block number and set the
- * boundary flag if needed.
- */
- if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
- ioend->io_sector = bio->bi_iter.bi_sector;
- xfs_mark_rtg_boundary(ioend);
- }
-
- spin_lock_irqsave(&ip->i_ioend_lock, flags);
- if (list_empty(&ip->i_ioend_list))
- WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
- &ip->i_ioend_work));
- list_add_tail(&ioend->io_list, &ip->i_ioend_list);
- spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
-}
-
/*
* We cannot cancel the ioend directly on error. We may have already set other
* pages under writeback and hence we have to run I/O completion to mark the
@@ -631,13 +454,8 @@ xfs_zoned_map_blocks(
XFS_BMAPI_REMAP);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
- wpc->iomap.type = IOMAP_MAPPED;
- wpc->iomap.flags = IOMAP_F_DIRTY;
- wpc->iomap.bdev = mp->m_rtdev_targp->bt_bdev;
- wpc->iomap.offset = offset;
- wpc->iomap.length = XFS_FSB_TO_B(mp, count_fsb);
- wpc->iomap.flags = IOMAP_F_ANON_WRITE;
-
+ xfs_iomap_set_anon_write(ip, &wpc->iomap, offset,
+ XFS_FSB_TO_B(mp, count_fsb));
trace_xfs_zoned_map_blocks(ip, offset, wpc->iomap.length);
return 0;
}
diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
index 5a7a0f1a0b49..d5ae5c9d4c26 100644
--- a/fs/xfs/xfs_aops.h
+++ b/fs/xfs/xfs_aops.h
@@ -10,6 +10,5 @@ extern const struct address_space_operations xfs_address_space_operations;
extern const struct address_space_operations xfs_dax_aops;
int xfs_setfilesize(struct xfs_inode *ip, xfs_off_t offset, size_t size);
-void xfs_end_bio(struct bio *bio);
#endif /* __XFS_AOPS_H__ */
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 7bff07e31cbd..426a67b813a7 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -25,7 +25,7 @@
#include "xfs_iomap.h"
#include "xfs_reflink.h"
#include "xfs_file.h"
-#include "xfs_aops.h"
+#include "xfs_ioend.h"
#include "xfs_zone_alloc.h"
#include "xfs_error.h"
#include "xfs_errortag.h"
diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c
index b6a3bc9f143c..041bb2105ec6 100644
--- a/fs/xfs/xfs_fsmap.c
+++ b/fs/xfs/xfs_fsmap.c
@@ -1174,8 +1174,7 @@ xfs_getfsmap(
if (!xfs_getfsmap_check_keys(&head->fmh_keys[0], &head->fmh_keys[1]))
return -EINVAL;
- use_rmap = xfs_has_rmapbt(mp) &&
- has_capability_noaudit(current, CAP_SYS_ADMIN);
+ use_rmap = xfs_has_rmapbt(mp) && capable_noaudit(CAP_SYS_ADMIN);
head->fmh_entries = 0;
/* Set up our device handlers. */
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 9d8dd30bd927..a857b8aa255c 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -82,24 +82,20 @@ static inline xa_mark_t ici_tag_to_mark(unsigned int tag)
/*
* Allocate and initialise an xfs_inode.
+ *
+ * This can happen in context of already dirtied transactions, so the memory
+ * allocations must not fail.
*/
struct xfs_inode *
xfs_inode_alloc(
struct xfs_mount *mp,
xfs_ino_t ino)
{
+ gfp_t gfp = GFP_KERNEL | __GFP_NOFAIL;
struct xfs_inode *ip;
- /*
- * XXX: If this didn't occur in transactions, we could drop GFP_NOFAIL
- * and return NULL here on ENOMEM.
- */
- ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, GFP_KERNEL | __GFP_NOFAIL);
-
- if (inode_init_always(mp->m_super, VFS_I(ip))) {
- kmem_cache_free(xfs_inode_cache, ip);
- return NULL;
- }
+ ip = alloc_inode_sb(mp->m_super, xfs_inode_cache, gfp);
+ inode_init_always_gfp(mp->m_super, VFS_I(ip), gfp);
VFS_I(ip)->i_ino = ino;
/* VFS doesn't initialise i_mode! */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 1b53701bebea..96ca3e480cb9 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -647,7 +647,7 @@ xfs_ioctl_setattr_get_trans(
goto out_error;
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
- has_capability_noaudit(current, CAP_FOWNER), &tp);
+ capable_noaudit(CAP_FOWNER), &tp);
if (error)
goto out_error;
diff --git a/fs/xfs/xfs_ioend.c b/fs/xfs/xfs_ioend.c
new file mode 100644
index 000000000000..40695d18dac0
--- /dev/null
+++ b/fs/xfs/xfs_ioend.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2025 Christoph Hellwig.
+ * All Rights Reserved.
+ */
+#include "xfs_platform.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_iomap.h"
+#include "xfs_trace.h"
+#include "xfs_bmap_util.h"
+#include "xfs_reflink.h"
+#include "xfs_zone_alloc.h"
+#include "xfs_ioend.h"
+
+static void
+xfs_ioend_put_open_zones(
+ struct iomap_ioend *ioend)
+{
+ struct iomap_ioend *tmp;
+
+ /*
+ * Put the open zone for all ioends merged into this one (if any).
+ */
+ list_for_each_entry(tmp, &ioend->io_list, io_list)
+ xfs_open_zone_put(tmp->io_private);
+
+ /*
+ * The main ioend might not have an open zone if the submission failed
+ * before xfs_zone_alloc_and_submit got called.
+ */
+ if (ioend->io_private)
+ xfs_open_zone_put(ioend->io_private);
+}
+
+static void
+xfs_end_ioend_write(
+ struct iomap_ioend *ioend)
+{
+ struct xfs_inode *ip = XFS_I(ioend->io_inode);
+ struct xfs_mount *mp = ip->i_mount;
+ bool is_zoned = xfs_is_zoned_inode(ip);
+ xfs_off_t offset = ioend->io_offset;
+ size_t size = ioend->io_size;
+ unsigned int nofs_flag;
+ int error;
+
+ /*
+ * We can allocate memory here while doing writeback on behalf of
+ * memory reclaim. To avoid memory allocation deadlocks set the
+ * task-wide nofs context for the following operations.
+ */
+ nofs_flag = memalloc_nofs_save();
+
+ /*
+ * Just clean up the in-memory structures if the fs has been shut down.
+ */
+ if (xfs_is_shutdown(mp)) {
+ error = -EIO;
+ goto done;
+ }
+
+ /*
+ * Clean up all COW blocks and underlying data fork delalloc blocks on
+ * I/O error. The delalloc punch is required because this ioend was
+ * mapped to blocks in the COW fork and the associated pages are no
+ * longer dirty. If we don't remove delalloc blocks here, they become
+ * stale and can corrupt free space accounting on unmount.
+ */
+ error = blk_status_to_errno(ioend->io_bio.bi_status);
+ if (unlikely(error)) {
+ /*
+ * Zoned writes update the in-core open zone accounting before
+ * I/O submission. A failed write leaves that state
+ * inconsistent, so shut down the filesystem instead of letting
+ * later writers wait forever for open zone space to become
+ * available.
+ */
+ if (is_zoned) {
+ xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR);
+ goto done;
+ }
+ if (ioend->io_flags & IOMAP_IOEND_SHARED) {
+ ASSERT(!is_zoned);
+ xfs_reflink_cancel_cow_range(ip, offset, size, true);
+ xfs_bmap_punch_delalloc_range(ip, XFS_DATA_FORK, offset,
+ offset + size, NULL);
+ }
+ goto done;
+ }
+
+ /*
+ * Success: commit the COW or unwritten blocks if needed.
+ */
+ if (is_zoned)
+ error = xfs_zoned_end_io(ip, offset, size, ioend->io_sector,
+ ioend->io_private, NULLFSBLOCK);
+ else if (ioend->io_flags & IOMAP_IOEND_SHARED)
+ error = xfs_reflink_end_cow(ip, offset, size);
+ else if (ioend->io_flags & IOMAP_IOEND_UNWRITTEN)
+ error = xfs_iomap_write_unwritten(ip, offset, size, false);
+
+ if (!error &&
+ !(ioend->io_flags & IOMAP_IOEND_DIRECT) &&
+ xfs_ioend_is_append(ioend))
+ error = xfs_setfilesize(ip, offset, size);
+done:
+ if (is_zoned)
+ xfs_ioend_put_open_zones(ioend);
+ iomap_finish_ioends(ioend, error);
+ memalloc_nofs_restore(nofs_flag);
+}
+
+/*
+ * Finish all pending IO completions that require transactional modifications.
+ *
+ * We try to merge physical and logically contiguous ioends before completion to
+ * minimise the number of transactions we need to perform during IO completion.
+ * Both unwritten extent conversion and COW remapping need to iterate and modify
+ * one physical extent at a time, so we gain nothing by merging physically
+ * discontiguous extents here.
+ *
+ * The ioend chain length that we can be processing here is largely unbound in
+ * length and we may have to perform significant amounts of work on each ioend
+ * to complete it. Hence we have to be careful about holding the CPU for too
+ * long in this loop.
+ */
+void
+xfs_end_io(
+ struct work_struct *work)
+{
+ struct xfs_inode *ip =
+ container_of(work, struct xfs_inode, i_ioend_work);
+ struct iomap_ioend *ioend;
+ struct list_head tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip->i_ioend_lock, flags);
+ list_replace_init(&ip->i_ioend_list, &tmp);
+ spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
+
+ iomap_sort_ioends(&tmp);
+ while ((ioend = list_first_entry_or_null(&tmp, struct iomap_ioend,
+ io_list))) {
+ list_del_init(&ioend->io_list);
+ iomap_ioend_try_merge(ioend, &tmp);
+ if (bio_op(&ioend->io_bio) == REQ_OP_READ)
+ iomap_finish_ioends(ioend,
+ blk_status_to_errno(ioend->io_bio.bi_status));
+ else
+ xfs_end_ioend_write(ioend);
+ cond_resched();
+ }
+}
+
+void
+xfs_end_bio(
+ struct bio *bio)
+{
+ struct iomap_ioend *ioend = iomap_ioend_from_bio(bio);
+ struct xfs_inode *ip = XFS_I(ioend->io_inode);
+ struct xfs_mount *mp = ip->i_mount;
+ unsigned long flags;
+
+ /*
+ * For Appends record the actually written block number and set the
+ * boundary flag if needed.
+ */
+ if (IS_ENABLED(CONFIG_XFS_RT) && bio_is_zone_append(bio)) {
+ ioend->io_sector = bio->bi_iter.bi_sector;
+ xfs_mark_rtg_boundary(ioend);
+ }
+
+ spin_lock_irqsave(&ip->i_ioend_lock, flags);
+ if (list_empty(&ip->i_ioend_list))
+ WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
+ &ip->i_ioend_work));
+ list_add_tail(&ioend->io_list, &ip->i_ioend_list);
+ spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
+}
diff --git a/fs/xfs/xfs_ioend.h b/fs/xfs/xfs_ioend.h
new file mode 100644
index 000000000000..525865767fca
--- /dev/null
+++ b/fs/xfs/xfs_ioend.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __XFS_IOEND_H
+#define __XFS_IOEND_H
+
+/*
+ * Fast and loose check if this write could update the on-disk inode size.
+ */
+static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
+{
+ return ioend->io_offset + ioend->io_size >
+ XFS_I(ioend->io_inode)->i_disk_size;
+}
+
+void xfs_end_bio(struct bio *bio);
+
+#endif /* __XFS_IOEND_H */
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 71c45be8c652..7c6238fed61e 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -1083,12 +1083,7 @@ xfs_zoned_direct_write_iomap_begin(
return error;
}
- iomap->type = IOMAP_MAPPED;
- iomap->flags = IOMAP_F_DIRTY;
- iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
- iomap->offset = offset;
- iomap->length = length;
- iomap->flags = IOMAP_F_ANON_WRITE;
+ xfs_iomap_set_anon_write(ip, iomap, offset, length);
return 0;
}
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index cffcec532ea6..c906c62d46f3 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -29,6 +29,20 @@ int xfs_zero_range(struct xfs_inode *ip, loff_t pos, loff_t len,
int xfs_truncate_page(struct xfs_inode *ip, loff_t pos,
struct xfs_zone_alloc_ctx *ac, bool *did_zero);
+static inline void
+xfs_iomap_set_anon_write(
+ struct xfs_inode *ip,
+ struct iomap *iomap,
+ loff_t offset,
+ loff_t length)
+{
+ iomap->type = IOMAP_MAPPED;
+ iomap->bdev = ip->i_mount->m_rtdev_targp->bt_bdev;
+ iomap->offset = offset;
+ iomap->length = length;
+ iomap->flags = IOMAP_F_ANON_WRITE | IOMAP_F_DIRTY;
+}
+
static inline xfs_filblks_t
xfs_aligned_fsb_count(
xfs_fileoff_t offset_fsb,
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 4a3299abf774..d1306e723899 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -834,7 +834,7 @@ xfs_setattr_nonsize(
}
error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
- has_capability_noaudit(current, CAP_FOWNER), &tp);
+ capable_noaudit(CAP_FOWNER), &tp);
if (error)
goto out_dqrele;
diff --git a/fs/xfs/xfs_platform.h b/fs/xfs/xfs_platform.h
index 59a33c60e0ca..5d542e95fe44 100644
--- a/fs/xfs/xfs_platform.h
+++ b/fs/xfs/xfs_platform.h
@@ -289,15 +289,4 @@ int xfs_rw_bdev(struct block_device *bdev, sector_t sector, unsigned int count,
# define PTR_FMT "%p"
#endif
-/*
- * Helper for IO routines to grab backing pages from allocated kernel memory.
- */
-static inline struct page *
-kmem_to_page(void *addr)
-{
- if (is_vmalloc_addr(addr))
- return vmalloc_to_page(addr);
- return virt_to_page(addr);
-}
-
#endif /* _XFS_PLATFORM_H */
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 4b2eeb7783f7..b24db75eaedc 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -445,7 +445,7 @@ xfs_shutdown_devices(
blkdev_issue_flush(mp->m_logdev_targp->bt_bdev);
invalidate_bdev(mp->m_logdev_targp->bt_bdev);
}
- if (mp->m_rtdev_targp) {
+ if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) {
blkdev_issue_flush(mp->m_rtdev_targp->bt_bdev);
invalidate_bdev(mp->m_rtdev_targp->bt_bdev);
}
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 1e025848811a..a5d25b703dfc 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -521,7 +521,8 @@ xfs_trans_log_buf(
{
struct xfs_buf_log_item *bip = bp->b_log_item;
- ASSERT(first <= last && last < BBTOB(bp->b_length));
+ ASSERT(first <= last);
+ ASSERT(last < BBTOB(bp->b_length));
ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED));
xfs_trans_dirty_buf(tp, bp);
diff --git a/fs/xfs/xfs_zone_alloc.c b/fs/xfs/xfs_zone_alloc.c
index 7d13fa7ab30a..bdbb60cc5d5b 100644
--- a/fs/xfs/xfs_zone_alloc.c
+++ b/fs/xfs/xfs_zone_alloc.c
@@ -793,17 +793,35 @@ xfs_get_cached_zone(
rcu_read_lock();
oz = VFS_I(ip)->i_private;
- if (oz) {
- /*
- * GC only steals open zones at mount time, so no GC zones
- * should end up in the cache.
- */
- ASSERT(!oz->oz_is_gc);
- if (!atomic_inc_not_zero(&oz->oz_ref))
+ if (!oz)
+ goto out_unlock;
+
+ /*
+ * GC only steals open zones at mount time, so no GC zones should end up
+ * in the cache.
+ */
+ ASSERT(!oz->oz_is_gc);
+
+ /*
+ * Drop the old cached open zone if it is full.
+ */
+ if (oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
+ spin_lock(&ip->i_flags_lock);
+ oz = VFS_I(ip)->i_private;
+ if (oz && oz->oz_allocated == rtg_blocks(oz->oz_rtg)) {
+ VFS_I(ip)->i_private = NULL;
+ spin_unlock(&ip->i_flags_lock);
+ xfs_open_zone_put(oz);
oz = NULL;
+ goto out_unlock;
+ }
+ spin_unlock(&ip->i_flags_lock);
}
- rcu_read_unlock();
+ if (!atomic_inc_not_zero(&oz->oz_ref))
+ oz = NULL;
+out_unlock:
+ rcu_read_unlock();
return oz;
}
@@ -818,18 +836,41 @@ xfs_get_cached_zone(
* that were every written to, but significantly simplifies the cached zone
* lookup. Because the open_zone is clearly marked as full when all data
* in the underlying RTG was written, the caching is always safe.
+ *
+ * Called with a reference on @oz held. And returns two references on the
+ * returned zone: one for the caller and one for pinning the zone in
+ * inode->i_private.
*/
-static void
+static struct xfs_open_zone *
xfs_set_cached_zone(
struct xfs_inode *ip,
struct xfs_open_zone *oz)
{
struct xfs_open_zone *old_oz;
+ /*
+ * If the open zone cached in the inode still has free space, use that
+ * instead of the new open zone just selected. This can happen when
+ * multiple threads race to perform zone selection for an inode.
+ * io_uring worker threads seem to be good way to trigger this.
+ *
+ * We need to grab an extra reference to this open zone as the caller
+ * owns a reference in addition to the i_private pointer.
+ */
+ spin_lock(&ip->i_flags_lock);
+ old_oz = VFS_I(ip)->i_private;
+ if (old_oz && old_oz->oz_allocated < rtg_blocks(old_oz->oz_rtg) &&
+ atomic_inc_not_zero(&old_oz->oz_ref)) {
+ spin_unlock(&ip->i_flags_lock);
+ xfs_open_zone_put(oz);
+ return old_oz;
+ }
+ VFS_I(ip)->i_private = oz;
atomic_inc(&oz->oz_ref);
- old_oz = xchg(&VFS_I(ip)->i_private, oz);
+ spin_unlock(&ip->i_flags_lock);
if (old_oz)
xfs_open_zone_put(old_oz);
+ return oz;
}
static void
@@ -873,14 +914,13 @@ xfs_zone_alloc_and_submit(
* the inode is still associated with a zone and use that if so.
*/
if (!*oz)
+select_zone:
*oz = xfs_get_cached_zone(ip);
-
if (!*oz) {
-select_zone:
*oz = xfs_select_zone(mp, write_hint, pack_tight);
if (!*oz)
goto out_error;
- xfs_set_cached_zone(ip, *oz);
+ *oz = xfs_set_cached_zone(ip, *oz);
}
alloc_len = xfs_zone_alloc_blocks(*oz, XFS_B_TO_FSB(mp, ioend->io_size),
diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c
index d0b85179a3d2..5fdcf98a2133 100644
--- a/fs/xfs/xfs_zone_gc.c
+++ b/fs/xfs/xfs_zone_gc.c
@@ -869,6 +869,11 @@ xfs_zone_gc_write_chunk(
WRITE_ONCE(chunk->state, XFS_GC_BIO_NEW);
list_move_tail(&chunk->entry, &data->writing);
+ /*
+ * If we run on top of stacked block device, the read I/O might have
+ * reset bi_bdev, restore it to the one we want.
+ */
+ bio_set_dev(&chunk->bio, mp->m_rtdev_targp->bt_bdev);
bio_reuse(&chunk->bio, REQ_OP_WRITE);
while ((split_chunk = xfs_zone_gc_split_write(data, chunk)))
xfs_zone_gc_submit_write(data, split_chunk);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 1a45e0d521d8..a10a591c18b2 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -202,12 +202,8 @@ struct acpi_device_dir {
/* Plug and Play */
-#define MAX_ACPI_DEVICE_NAME_LEN 40
-#define MAX_ACPI_CLASS_NAME_LEN 20
typedef char acpi_bus_id[8];
typedef u64 acpi_bus_address;
-typedef char acpi_device_name[MAX_ACPI_DEVICE_NAME_LEN];
-typedef char acpi_device_class[MAX_ACPI_CLASS_NAME_LEN];
struct acpi_hardware_id {
struct list_head list;
@@ -229,16 +225,12 @@ struct acpi_device_pnp {
acpi_bus_address bus_address; /* _ADR */
char *unique_id; /* _UID */
struct list_head ids; /* _HID and _CIDs */
- acpi_device_name device_name; /* Driver-determined */
- acpi_device_class device_class; /* " */
};
#define acpi_device_bid(d) ((d)->pnp.bus_id)
#define acpi_device_adr(d) ((d)->pnp.bus_address)
const char *acpi_device_hid(struct acpi_device *device);
#define acpi_device_uid(d) ((d)->pnp.unique_id)
-#define acpi_device_name(d) ((d)->pnp.device_name)
-#define acpi_device_class(d) ((d)->pnp.device_class)
/* Power Management */
@@ -578,6 +570,9 @@ int acpi_dev_for_each_child_reverse(struct acpi_device *adev,
* ------
*/
+#define MAX_ACPI_CLASS_NAME_LEN 20
+typedef char acpi_device_class[MAX_ACPI_CLASS_NAME_LEN];
+
struct acpi_bus_event {
struct list_head node;
acpi_device_class device_class;
diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h
index 95eb4b66b057..ebbd3b0ddf36 100644
--- a/include/drm/drm_pagemap.h
+++ b/include/drm/drm_pagemap.h
@@ -2,6 +2,7 @@
#ifndef _DRM_PAGEMAP_H_
#define _DRM_PAGEMAP_H_
+#include <linux/bits.h>
#include <linux/dma-direction.h>
#include <linux/hmm.h>
#include <linux/memremap.h>
@@ -339,6 +340,9 @@ struct drm_pagemap_migrate_details {
#if IS_ENABLED(CONFIG_ZONE_DEVICE)
+#define DRM_PAGEMAP_ZDD_FLAG_MIGRATED BIT(0)
+#define DRM_PAGEMAP_ZDD_FLAG_MASK DRM_PAGEMAP_ZDD_FLAG_MIGRATED
+
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
@@ -373,7 +377,9 @@ static inline struct drm_pagemap_zdd *drm_pagemap_page_zone_device_data(struct p
{
struct folio *folio = page_folio(page);
- return folio_zone_device_data(folio);
+ return (struct drm_pagemap_zdd *)
+ ((unsigned long)folio_zone_device_data(folio) &
+ ~DRM_PAGEMAP_ZDD_FLAG_MASK);
}
#else
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 37db92b3d6f8..f8532d92fcad 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -145,6 +145,7 @@ extern bool has_capability_noaudit(struct task_struct *t, int cap);
extern bool has_ns_capability_noaudit(struct task_struct *t,
struct user_namespace *ns, int cap);
extern bool capable(int cap);
+bool capable_noaudit(int cap);
extern bool ns_capable(struct user_namespace *ns, int cap);
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
@@ -167,6 +168,10 @@ static inline bool capable(int cap)
{
return true;
}
+static inline bool capable_noaudit(int cap)
+{
+ return true;
+}
static inline bool ns_capable(struct user_namespace *ns, int cap)
{
return true;
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index 7a631a257613..3754d697854b 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -527,7 +527,10 @@ struct cgroup {
int nr_threaded_children; /* # of live threaded child cgroups */
- /* sequence number for cgroup.kill, serialized by css_set_lock. */
+ /*
+ * Sequence number for cgroup.kill. Incremented with both cgroup_mutex
+ * and css_set_lock held. Readers hold either one.
+ */
unsigned int kill_seq;
struct kernfs_node *kn; /* cgroup kernfs entry */
diff --git a/include/linux/device-id/ap.h b/include/linux/device-id/ap.h
index 0992333a34db..e050abebbf3d 100644
--- a/include/linux/device-id/ap.h
+++ b/include/linux/device-id/ap.h
@@ -4,7 +4,6 @@
#ifdef __KERNEL__
#include <linux/types.h>
-typedef unsigned long kernel_ulong_t;
#endif
#define AP_DEVICE_ID_MATCH_CARD_TYPE 0x01
@@ -14,7 +13,6 @@ typedef unsigned long kernel_ulong_t;
struct ap_device_id {
__u16 match_flags; /* which fields to match against */
__u8 dev_type; /* device type */
- kernel_ulong_t driver_info;
};
#endif /* ifndef LINUX_DEVICE_ID_AP_H */
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index d1203da56fc5..d15b2b31d3c9 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -567,6 +567,7 @@ void dma_buf_unpin(struct dma_buf_attachment *attach);
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info);
int dma_buf_fd(struct dma_buf *dmabuf, int flags);
+void dma_buf_fd_install(struct dma_buf *dmabuf, int fd);
struct dma_buf *dma_buf_get(int fd);
void dma_buf_put(struct dma_buf *dmabuf);
diff --git a/include/linux/dma-fence-array.h b/include/linux/dma-fence-array.h
index 1b1d87579c38..0c49d7ccefb6 100644
--- a/include/linux/dma-fence-array.h
+++ b/include/linux/dma-fence-array.h
@@ -28,7 +28,6 @@ struct dma_fence_array_cb {
/**
* struct dma_fence_array - fence to represent an array of fences
* @base: fence base class
- * @lock: spinlock for fence handling
* @num_fences: number of fences in the array
* @num_pending: fences in the array still pending
* @fences: array of the fences
diff --git a/include/linux/dma-fence-chain.h b/include/linux/dma-fence-chain.h
index df3beadf1515..705c4394ac0d 100644
--- a/include/linux/dma-fence-chain.h
+++ b/include/linux/dma-fence-chain.h
@@ -20,7 +20,6 @@
* @prev: previous fence of the chain
* @prev_seqno: original previous seqno before garbage collection
* @fence: encapsulated fence
- * @lock: spinlock for fence handling
*/
struct dma_fence_chain {
struct dma_fence base;
@@ -81,9 +80,8 @@ dma_fence_chain_contained(struct dma_fence *fence)
}
/**
- * dma_fence_chain_alloc
- *
- * Returns a new struct dma_fence_chain object or NULL on failure.
+ * dma_fence_chain_alloc - Returns a new &struct dma_fence_chain object or
+ * %NULL on failure.
*
* This specialized allocator has to be a macro for its allocations to be
* accounted separately (to have a separate alloc_tag). The typecast is
@@ -93,7 +91,8 @@ dma_fence_chain_contained(struct dma_fence *fence)
kmalloc_obj(struct dma_fence_chain)
/**
- * dma_fence_chain_free
+ * dma_fence_chain_free - Frees an allocated but not used
+ * &struct dma_fence_chain object.
* @chain: chain node to free
*
* Frees up an allocated but not used struct dma_fence_chain object. This
diff --git a/include/linux/edac.h b/include/linux/edac.h
index e6b4e51130e5..f7a8218f9cc0 100644
--- a/include/linux/edac.h
+++ b/include/linux/edac.h
@@ -598,9 +598,6 @@ struct mem_ctl_info {
int op_state;
struct dentry *debugfs;
- u8 fake_inject_layer[EDAC_MAX_LAYERS];
- bool fake_inject_ue;
- u16 fake_inject_count;
/*
* Memory Controller hierarchy
diff --git a/include/linux/igmp.h b/include/linux/igmp.h
index 3a2d35a9f307..a0cf0398519f 100644
--- a/include/linux/igmp.h
+++ b/include/linux/igmp.h
@@ -57,20 +57,21 @@ struct ip_mc_socklist {
};
struct ip_sf_list {
- struct ip_sf_list *sf_next;
+ struct ip_sf_list __rcu *sf_next;
unsigned long sf_count[2]; /* include/exclude counts */
__be32 sf_inaddr;
unsigned char sf_gsresp; /* include in g & s response? */
unsigned char sf_oldin; /* change state */
unsigned char sf_crcount; /* retrans. left to send */
+ struct rcu_head rcu;
};
struct ip_mc_list {
struct in_device *interface;
__be32 multiaddr;
unsigned int sfmode;
- struct ip_sf_list *sources;
- struct ip_sf_list *tomb;
+ struct ip_sf_list __rcu *sources;
+ struct ip_sf_list __rcu *tomb;
unsigned long sfcount[2];
union {
struct ip_mc_list *next;
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index 8c4f3bb24429..e6de7ae55bda 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -181,6 +181,7 @@ struct kprobe_blacklist_entry {
struct list_head list;
unsigned long start_addr;
unsigned long end_addr;
+ struct rcu_head rcu;
};
#ifdef CONFIG_KPROBES
diff --git a/include/linux/nvme-tcp.h b/include/linux/nvme-tcp.h
index e435250fcb4d..859338da8573 100644
--- a/include/linux/nvme-tcp.h
+++ b/include/linux/nvme-tcp.h
@@ -77,7 +77,7 @@ struct nvme_tcp_hdr {
__le32 plen;
};
-/**
+/*
* struct nvme_tcp_icreq_pdu - nvme tcp initialize connection request pdu
*
* @hdr: pdu generic header
@@ -95,7 +95,7 @@ struct nvme_tcp_icreq_pdu {
__u8 rsvd2[112];
};
-/**
+/*
* struct nvme_tcp_icresp_pdu - nvme tcp initialize connection response pdu
*
* @hdr: pdu common header
@@ -113,12 +113,13 @@ struct nvme_tcp_icresp_pdu {
__u8 rsvd[112];
};
-/**
+/*
* struct nvme_tcp_term_pdu - nvme tcp terminate connection pdu
*
* @hdr: pdu common header
* @fes: fatal error status
- * @fei: fatal error information
+ * @feil: fatal error information (low 16 bits)
+ * @feih: fatal error information (high 16 bits)
*/
struct nvme_tcp_term_pdu {
struct nvme_tcp_hdr hdr;
@@ -128,7 +129,7 @@ struct nvme_tcp_term_pdu {
__u8 rsvd[10];
};
-/**
+/*
* struct nvme_tcp_cmd_pdu - nvme tcp command capsule pdu
*
* @hdr: pdu common header
@@ -139,10 +140,9 @@ struct nvme_tcp_cmd_pdu {
struct nvme_command cmd;
};
-/**
+/*
* struct nvme_tcp_rsp_pdu - nvme tcp response capsule pdu
*
- * @hdr: pdu common header
* @hdr: nvme-tcp generic header
* @cqe: nvme completion queue entry
*/
@@ -151,7 +151,7 @@ struct nvme_tcp_rsp_pdu {
struct nvme_completion cqe;
};
-/**
+/*
* struct nvme_tcp_r2t_pdu - nvme tcp ready-to-transfer pdu
*
* @hdr: pdu common header
@@ -169,7 +169,7 @@ struct nvme_tcp_r2t_pdu {
__u8 rsvd[4];
};
-/**
+/*
* struct nvme_tcp_data_pdu - nvme tcp data pdu
*
* @hdr: pdu common header
diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h
index 4cc52698e214..8d7e5521f7cd 100644
--- a/include/linux/sched/user.h
+++ b/include/linux/sched/user.h
@@ -25,7 +25,8 @@ struct user_struct {
#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \
defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \
- defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD)
+ defined(CONFIG_VFIO_PCI_ZDEV_KVM) || IS_ENABLED(CONFIG_IOMMUFD) || \
+ defined(CONFIG_SECRETMEM)
atomic_long_t locked_vm;
#endif
#ifdef CONFIG_WATCH_QUEUE
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 671c13494566..421f6fc45451 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3082,6 +3082,11 @@ static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
return skb->transport_header != (typeof(skb->transport_header))~0U;
}
+static inline void skb_unset_transport_header(struct sk_buff *skb)
+{
+ skb->transport_header = (typeof(skb->transport_header))~0U;
+}
+
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
DEBUG_NET_WARN_ON_ONCE(!skb_transport_header_was_set(skb));
diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h
index 3357ee62d10b..5549298c1ec6 100644
--- a/include/net/af_vsock.h
+++ b/include/net/af_vsock.h
@@ -229,6 +229,9 @@ struct sock *vsock_find_bound_socket_net(struct sockaddr_vm *addr,
struct sock *vsock_find_connected_socket_net(struct sockaddr_vm *src,
struct sockaddr_vm *dst,
struct net *net);
+bool vsock_check_source(const struct vsock_sock *vsk,
+ const struct vsock_transport *transport,
+ const struct sockaddr_vm *src);
void vsock_remove_sock(struct vsock_sock *vsk);
void vsock_for_each_connected_socket(struct vsock_transport *transport,
void (*fn)(struct sock *sk));
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 238ad3349456..795fb41b45f5 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -88,8 +88,6 @@ struct ip6_sf_socklist {
struct in6_addr sl_addr[] __counted_by(sl_max);
};
-#define IP6_SFBLOCK 10 /* allocate this many at once */
-
struct ipv6_mc_socklist {
struct in6_addr addr;
int ifindex;
diff --git a/include/net/ip.h b/include/net/ip.h
index a8f57b4f4aa2..6f602df72ee6 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -704,7 +704,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,
static __inline__ void inet_reset_saddr(struct sock *sk)
{
- inet_sk(sk)->inet_rcv_saddr = inet_sk(sk)->inet_saddr = 0;
+ inet_sk(sk)->inet_saddr = 0;
+ WRITE_ONCE(inet_sk(sk)->inet_rcv_saddr, 0);
#if IS_ENABLED(CONFIG_IPV6)
if (sk->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(sk);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 670c20876f26..436495ff2271 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -765,8 +765,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue,
void tcp_send_probe0(struct sock *);
int tcp_write_wakeup(struct sock *, int mib);
void tcp_send_fin(struct sock *sk);
-void tcp_send_active_reset(struct sock *sk, gfp_t priority,
- enum sk_rst_reason reason);
+void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason);
int tcp_send_synack(struct sock *);
void tcp_push_one(struct sock *, unsigned int mss_now);
void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags);
diff --git a/include/trace/events/icmp.h b/include/trace/events/icmp.h
index 09ae115099df..6937b778ae54 100644
--- a/include/trace/events/icmp.h
+++ b/include/trace/events/icmp.h
@@ -27,17 +27,20 @@ TRACE_EVENT(icmp_send,
TP_fast_assign(
struct iphdr *iph = ip_hdr(skb);
- struct udphdr *uh = udp_hdr(skb);
- int proto_4 = iph->protocol;
+ struct udphdr _uh, *uh = NULL;
__be32 *p32;
__entry->skbaddr = skb;
__entry->type = type;
__entry->code = code;
- if (proto_4 != IPPROTO_UDP || (u8 *)uh < skb->head ||
- (u8 *)uh + sizeof(struct udphdr)
- > skb_tail_pointer(skb)) {
+ if (iph->protocol == IPPROTO_UDP)
+ uh = skb_header_pointer(skb,
+ skb_network_offset(skb) +
+ (iph->ihl << 2),
+ sizeof(_uh), &_uh);
+
+ if (!uh) {
__entry->sport = 0;
__entry->dport = 0;
__entry->ulen = 0;
diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h
index b32c72a662b6..42a5fa8ad6b0 100644
--- a/include/uapi/drm/amdgpu_drm.h
+++ b/include/uapi/drm/amdgpu_drm.h
@@ -1512,6 +1512,7 @@ struct drm_amdgpu_info_device {
__u64 high_va_max;
/* gfx10 pa_sc_tile_steering_override */
__u32 pa_sc_tile_steering_override;
+ __u32 pad;
/* disabled TCCs */
__u64 tcc_disabled_mask;
__u64 min_engine_clock;
@@ -1536,7 +1537,6 @@ struct drm_amdgpu_info_device {
__u32 csa_alignment;
/* Userq IP mask (1 << AMDGPU_HW_IP_*) */
__u32 userq_ip_mask;
- __u32 pad;
};
struct drm_amdgpu_info_hw_ip {
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index fd5f0e95648e..66a91c8b1cb9 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -8,6 +8,7 @@
#define AUTOFS_SUPER_MAGIC 0x0187
#define CEPH_SUPER_MAGIC 0x00c36400
#define CODA_SUPER_MAGIC 0x73757245
+#define CONFIGFS_MAGIC 0x62656570 /* some random number */
#define CRAMFS_MAGIC 0x28cd3d45 /* some random number */
#define CRAMFS_MAGIC_WEND 0x453dcd28 /* magic number with the wrong endianess */
#define DEBUGFS_MAGIC 0x64626720
diff --git a/kernel/capability.c b/kernel/capability.c
index 829f49ae07b9..90e6ab62f6db 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -326,7 +326,6 @@ bool has_capability_noaudit(struct task_struct *t, int cap)
{
return has_ns_capability_noaudit(t, &init_user_ns, cap);
}
-EXPORT_SYMBOL(has_capability_noaudit);
static bool ns_capable_common(struct user_namespace *ns,
int cap,
@@ -416,6 +415,24 @@ bool capable(int cap)
return ns_capable(&init_user_ns, cap);
}
EXPORT_SYMBOL(capable);
+
+/**
+ * capable_noaudit - Determine if the current task has a superior
+ * capability in effect by checking the process's effective
+ * capabilities (unaudited).
+ * @cap: The capability to be tested for
+ *
+ * This is the same as capable(), except it uses CAP_OPT_NOAUDIT as to prevent
+ * issuing spurious audit messages.
+ *
+ * This sets PF_SUPERPRIV on the task if the capability is available on the
+ * assumption that it's about to be used.
+ */
+bool capable_noaudit(int cap)
+{
+ return ns_capable_noaudit(&init_user_ns, cap);
+}
+EXPORT_SYMBOL(capable_noaudit);
#endif /* CONFIG_MULTIUSER */
/**
diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c
index c3a12fee7528..2d532bf2c0c7 100644
--- a/kernel/cgroup/cgroup.c
+++ b/kernel/cgroup/cgroup.c
@@ -6873,10 +6873,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
spin_lock_irq(&css_set_lock);
cset = task_css_set(current);
get_css_set(cset);
- if (kargs->cgrp)
- kargs->kill_seq = kargs->cgrp->kill_seq;
- else
- kargs->kill_seq = cset->dfl_cgrp->kill_seq;
+ kargs->kill_seq = cset->dfl_cgrp->kill_seq;
spin_unlock_irq(&css_set_lock);
if (!(kargs->flags & CLONE_INTO_CGROUP)) {
@@ -6940,6 +6937,7 @@ static int cgroup_css_set_fork(struct kernel_clone_args *kargs)
put_css_set(cset);
kargs->cgrp = dst_cgrp;
+ kargs->kill_seq = dst_cgrp->kill_seq;
return ret;
err:
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index d100634fa12b..2538faac9aba 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1259,6 +1259,28 @@ static void reset_partition_data(struct cpuset *cs)
cpumask_copy(cs->effective_cpus, parent->effective_cpus);
}
+/* Return true if isolated_cpus changes. */
+static bool isolated_cpu_update(int new_prs, int cpu)
+{
+ lockdep_assert_held(&callback_lock);
+ lockdep_assert_held(&cpuset_mutex);
+
+ if (new_prs == PRS_ISOLATED) {
+ if (cpumask_test_cpu(cpu, isolated_cpus))
+ return false;
+ cpumask_set_cpu(cpu, isolated_cpus);
+ return true;
+ }
+
+ /* CPUs isolated at boot must remain isolated. */
+ if (!cpumask_test_cpu(cpu,
+ housekeeping_cpumask(HK_TYPE_DOMAIN_BOOT)) ||
+ !cpumask_test_cpu(cpu, isolated_cpus))
+ return false;
+ cpumask_clear_cpu(cpu, isolated_cpus);
+ return true;
+}
+
/*
* isolated_cpus_update - Update the isolated_cpus mask
* @old_prs: old partition_root_state
@@ -1267,19 +1289,16 @@ static void reset_partition_data(struct cpuset *cs)
*/
static void isolated_cpus_update(int old_prs, int new_prs, struct cpumask *xcpus)
{
+ bool updated = false;
+ int cpu;
+
WARN_ON_ONCE(old_prs == new_prs);
lockdep_assert_held(&callback_lock);
lockdep_assert_held(&cpuset_mutex);
- if (new_prs == PRS_ISOLATED) {
- if (cpumask_subset(xcpus, isolated_cpus))
- return;
- cpumask_or(isolated_cpus, isolated_cpus, xcpus);
- } else {
- if (!cpumask_intersects(xcpus, isolated_cpus))
- return;
- cpumask_andnot(isolated_cpus, isolated_cpus, xcpus);
- }
- update_housekeeping = true;
+ for_each_cpu(cpu, xcpus)
+ updated |= isolated_cpu_update(new_prs, cpu);
+ if (updated)
+ update_housekeeping = true;
}
/*
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index bfc89083daa9..6337da5cab9e 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1447,8 +1447,14 @@ static bool __within_kprobe_blacklist(unsigned long addr)
/*
* If 'kprobe_blacklist' is defined, check the address and
* reject any probe registration in the prohibited area.
+ * Note: this can return true during transition period where
+ * (start_addr, end_addr) in the black list is shrinking
+ * but old entry has not been removed yet. This is acceptable
+ * because the worst case is that we reject more probes than
+ * we should.
*/
- list_for_each_entry(ent, &kprobe_blacklist, list) {
+ guard(rcu)();
+ list_for_each_entry_rcu(ent, &kprobe_blacklist, list) {
if (addr >= ent->start_addr && addr < ent->end_addr)
return true;
}
@@ -2509,7 +2515,7 @@ int kprobe_add_ksym_blacklist(unsigned long entry)
ent->start_addr = entry;
ent->end_addr = entry + size;
INIT_LIST_HEAD(&ent->list);
- list_add_tail(&ent->list, &kprobe_blacklist);
+ list_add_tail_rcu(&ent->list, &kprobe_blacklist);
return (int)size;
}
@@ -2603,8 +2609,8 @@ static void kprobe_remove_area_blacklist(unsigned long start, unsigned long end)
list_for_each_entry_safe(ent, n, &kprobe_blacklist, list) {
if (ent->start_addr < start || ent->start_addr >= end)
continue;
- list_del(&ent->list);
- kfree(ent);
+ list_del_rcu(&ent->list);
+ kfree_rcu(ent, rcu);
}
}
diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c
index 10af28a9f2c0..713aa26b2828 100644
--- a/kernel/sched/ext/ext.c
+++ b/kernel/sched/ext/ext.c
@@ -876,9 +876,9 @@ struct task_struct *scx_task_iter_next_locked(struct scx_task_iter *iter)
* unloading. The init_tasks ("swappers") should be excluded
* from the iteration because:
*
- * - It's unsafe to use __setschduler_prio() on an init_task to
- * determine the sched_class to use as it won't preserve its
- * idle_sched_class.
+ * - It's unsafe to use __setscheduler_class() on an init_task
+ * to determine the sched_class to use as it won't preserve
+ * its idle_sched_class.
*
* - ops.init/exit_task() can easily be confused if called with
* init_tasks as they, e.g., share PID 0.
@@ -2806,6 +2806,8 @@ static void dispatch_to_local_dsq(struct scx_sched *sch, struct rq *rq,
* @p: task to finish dispatching
* @qseq_at_dispatch: qseq when @p started getting dispatched
* @dsq_id: destination DSQ ID
+ * @slice: slice carried by the insert verdict, 0 keeps the current value
+ * @vtime: vtime carried by the insert verdict, committed on PRIQ inserts
* @enq_flags: %SCX_ENQ_*
*
* Dispatching to local DSQs may need to wait for queueing to complete or
@@ -5514,7 +5516,7 @@ static const struct kset_uevent_ops scx_uevent_ops = {
};
/*
- * Used by sched_fork() and __setscheduler_prio() to pick the matching
+ * Used by sched_fork() and __setscheduler_class() to pick the matching
* sched_class. dl/rt are already handled.
*/
bool task_should_scx(int policy)
@@ -7694,7 +7696,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
/*
* Enable ops for every task. Fork is excluded by scx_fork_rwsem
* preventing new tasks from being added. No need to exclude tasks
- * leaving as sched_ext_free() can handle both prepped and enabled
+ * leaving as sched_ext_dead() can handle both prepped and enabled
* tasks. Prep all tasks first and then enable them with preemption
* disabled.
*
@@ -7786,7 +7788,7 @@ static void scx_root_enable_workfn(struct kthread_work *work)
/*
* We're fully committed and can't fail. The task READY -> ENABLED
- * transitions here are synchronized against sched_ext_free() through
+ * transitions here are synchronized against sched_ext_dead() through
* scx_tasks_lock.
*/
percpu_down_write(&scx_fork_rwsem);
@@ -8079,6 +8081,7 @@ static int bpf_scx_check_member(const struct btf_type *t,
case offsetof(struct sched_ext_ops, cgroup_init):
case offsetof(struct sched_ext_ops, cgroup_exit):
case offsetof(struct sched_ext_ops, cgroup_prep_move):
+ case offsetof(struct sched_ext_ops, cgroup_set_bandwidth):
#endif
case offsetof(struct sched_ext_ops, cpu_online):
case offsetof(struct sched_ext_ops, cpu_offline):
@@ -9003,12 +9006,6 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
if (unlikely(READ_ONCE(sch->aborting)))
return false;
- if (unlikely(!scx_task_on_sched(sch, p))) {
- scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
- p->comm, p->pid);
- return false;
- }
-
/*
* Can be called from either ops.dispatch() holding the dispatched rq's
* lock or any context where no rq lock is held. If latter, lock @p's
@@ -9040,6 +9037,17 @@ static bool scx_dsq_move(struct bpf_iter_scx_dsq_kern *kit,
goto out;
}
+ /*
+ * @p has been on $src_dsq and can't move anymore. If @p is not on @sch,
+ * the caller didn't have authority over @p at the time of the call.
+ */
+ if (unlikely(!scx_task_on_sched(sch, p))) {
+ scx_error(sch, "scx_bpf_dsq_move[_vtime]() on %s[%d] but the task belongs to a different scheduler",
+ p->comm, p->pid);
+ raw_spin_unlock(&src_dsq->lock);
+ goto out;
+ }
+
/* @p is still on $src_dsq and stable, determine the destination */
dst_dsq = find_dsq_for_dispatch(sch, locked_rq ?: this_rq(), dsq_id, task_cpu(p));
@@ -9765,7 +9773,7 @@ __bpf_kfunc struct task_struct *bpf_iter_scx_dsq_next(struct bpf_iter_scx_dsq *i
* bpf_iter_scx_dsq_destroy - Destroy a DSQ iterator
* @it: iterator to destroy
*
- * Undo scx_iter_scx_dsq_new().
+ * Undo bpf_iter_scx_dsq_new().
*/
__bpf_kfunc void bpf_iter_scx_dsq_destroy(struct bpf_iter_scx_dsq *it)
{
@@ -11041,3 +11049,16 @@ static int __init scx_init(void)
return 0;
}
__initcall(scx_init);
+
+/*
+ * Compatibility markers for userspace. Existence of a marker function
+ * represents that the kernel supports that sched-ext feature.
+ */
+
+/*
+ * scx_compat_marker_cgroup_set_bandwidth_may_sleep: advertises that
+ * ops.cgroup_set_bandwidth() may be implemented as a sleepable callback.
+ */
+#ifdef CONFIG_EXT_GROUP_SCHED
+DEFINE_SCX_COMPAT_MARKER(cgroup_set_bandwidth_may_sleep);
+#endif /* CONFIG_EXT_GROUP_SCHED */
diff --git a/kernel/sched/ext/internal.h b/kernel/sched/ext/internal.h
index 27bbf5e04d90..0967b99a4948 100644
--- a/kernel/sched/ext/internal.h
+++ b/kernel/sched/ext/internal.h
@@ -442,7 +442,7 @@ struct sched_ext_ops {
*
* Note that this callback may be called from a CPU other than the
* one the task is going to run on. This can happen when a task
- * property is changed (i.e., affinity), since scx_next_task_scx(),
+ * property is changed (i.e., affinity), since set_next_task_scx(),
* which triggers this callback, may run on a CPU different from
* the task's assigned CPU.
*
@@ -753,7 +753,7 @@ struct sched_ext_ops {
* @burst_us: bandwidth control burst
*
* Update @cgrp's bandwidth control parameters. This is from the cpu.max
- * cgroup interface.
+ * cgroup interface. This operation may block.
*
* @quota_us / @period_us determines the CPU bandwidth @cgrp is entitled
* to. For example, if @period_us is 1_000_000 and @quota_us is
@@ -2001,6 +2001,27 @@ struct scx_bstr_buf {
char line[SCX_EXIT_MSG_LEN];
};
+/* Internal helper for DEFINE_SCX_COMPAT_MARKER(). */
+#define DECLARE_SCX_COMPAT_MARKER(func) \
+ extern void scx_compat_marker_##func(void)
+
+/**
+ * DEFINE_SCX_COMPAT_MARKER() - define a userspace capability marker
+ * @func: marker suffix; the defined symbol is scx_compat_marker_@func
+ *
+ * Emit an empty, callerless function that is retained in the kernel's BTF.
+ * Its presence is part of the kernel<->userspace contract: userspace probes
+ * scx_compat_marker_@func (e.g. via BTF) to detect that this kernel supports
+ * the corresponding feature.
+ *
+ * The leading declaration suppresses the missing-prototype warning; the
+ * trailing declaration consumes the semicolon at the use site.
+ */
+#define DEFINE_SCX_COMPAT_MARKER(func) \
+ DECLARE_SCX_COMPAT_MARKER(func); \
+ __used __retain void scx_compat_marker_##func(void) {} \
+ DECLARE_SCX_COMPAT_MARKER(func)
+
extern struct scx_sched __rcu *scx_root;
DECLARE_PER_CPU(struct rq *, scx_locked_rq_state);
diff --git a/kernel/trace/trace_btf.c b/kernel/trace/trace_btf.c
index 00172f301f25..ee7a04886bf6 100644
--- a/kernel/trace/trace_btf.c
+++ b/kernel/trace/trace_btf.c
@@ -61,47 +61,50 @@ struct btf_anon_stack {
/*
* Find a member of data structure/union by name and return it.
- * Return NULL if not found, or -EINVAL if parameter is invalid.
- * If the member is an member of anonymous union/structure, the offset
- * of that anonymous union/structure is stored into @anon_offset. Caller
- * can calculate the correct offset from the root data structure by
- * adding anon_offset to the member's offset.
+ * Return NULL if not found, or ERR_PTR(-EINVAL) if parameter is invalid.
+ * If the member is a member of an anonymous union/structure, the bit offset
+ * of that anonymous union/structure is stored into @anon_offset.
+ * If @member_type is non-NULL, the actual containing structure/union type
+ * of the found member is stored into @member_type.
*/
const struct btf_member *btf_find_struct_member(struct btf *btf,
const struct btf_type *type,
const char *member_name,
- u32 *anon_offset)
+ u32 *anon_offset,
+ const struct btf_type **member_type)
{
struct btf_anon_stack *anon_stack;
const struct btf_member *member;
+ const struct btf_type *mtype;
u32 tid, cur_offset = 0;
const char *name;
int i, top = 0;
+ if (!btf_type_is_struct(type))
+ return ERR_PTR(-EINVAL);
+
anon_stack = kzalloc_objs(*anon_stack, BTF_ANON_STACK_MAX);
if (!anon_stack)
return ERR_PTR(-ENOMEM);
retry:
- if (!btf_type_is_struct(type)) {
- member = ERR_PTR(-EINVAL);
- goto out;
- }
-
for_each_member(i, type, member) {
if (!member->name_off) {
/* Anonymous union/struct: push it for later use */
- if (btf_type_skip_modifiers(btf, member->type, &tid) &&
+ mtype = btf_type_skip_modifiers(btf, member->type, &tid);
+ if (mtype && btf_type_is_struct(mtype) &&
top < BTF_ANON_STACK_MAX) {
anon_stack[top].tid = tid;
- anon_stack[top++].offset =
- cur_offset + member->offset;
+ anon_stack[top++].offset = cur_offset +
+ __btf_member_bit_offset(type, member);
}
} else {
name = btf_name_by_offset(btf, member->name_off);
if (name && !strcmp(member_name, name)) {
if (anon_offset)
*anon_offset = cur_offset;
+ if (member_type)
+ *member_type = type;
goto out;
}
}
diff --git a/kernel/trace/trace_btf.h b/kernel/trace/trace_btf.h
index 4bc44bc261e6..4bd26bceae23 100644
--- a/kernel/trace/trace_btf.h
+++ b/kernel/trace/trace_btf.h
@@ -8,4 +8,5 @@ const struct btf_param *btf_get_func_param(const struct btf_type *func_proto,
const struct btf_member *btf_find_struct_member(struct btf *btf,
const struct btf_type *type,
const char *member_name,
- u32 *anon_offset);
+ u32 *anon_offset,
+ const struct btf_type **member_type);
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index c4163904ba74..804442b2f7d2 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -625,6 +625,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
{
const struct btf_type *type = *ptype;
const struct btf_member *field;
+ const struct btf_type *mtype;
struct btf *btf = ctx_btf(ctx);
char *fieldname = *pfieldname;
int bitoffs = 0;
@@ -640,7 +641,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
anon_offs = 0;
field = btf_find_struct_member(btf, type, fieldname,
- &anon_offs);
+ &anon_offs, &mtype);
if (IS_ERR(field)) {
trace_probe_log_err(ctx->offset, BAD_BTF_TID);
return PTR_ERR(field);
@@ -653,7 +654,7 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
bitoffs += anon_offs;
/* Accumulate the bit-offsets of the dot-connected fields */
- if (btf_type_kflag(type)) {
+ if (btf_type_kflag(mtype)) {
bitoffs += BTF_MEMBER_BIT_OFFSET(field->offset);
ctx->last_bitsize = BTF_MEMBER_BITFIELD_SIZE(field->offset);
} else {
@@ -661,11 +662,11 @@ static int get_bitoffset_of_field(char **pfieldname, const struct btf_type **pty
ctx->last_bitsize = 0;
}
- type = btf_type_skip_modifiers(btf, field->type, NULL);
- if (!type) {
- trace_probe_log_err(ctx->offset, BAD_BTF_TID);
- return -EINVAL;
- }
+ type = btf_type_skip_modifiers(btf, field->type, NULL);
+ if (!type) {
+ trace_probe_log_err(ctx->offset, BAD_BTF_TID);
+ return -EINVAL;
+ }
if (next)
ctx->offset += next - fieldname;
@@ -2552,19 +2553,60 @@ int traceprobe_set_print_fmt(struct trace_probe *tp, enum probe_print_type ptype
int traceprobe_define_arg_fields(struct trace_event_call *event_call,
size_t offset, struct trace_probe *tp)
{
+ struct trace_probe_event *tpe = trace_probe_event_from_call(event_call);
int ret, i;
+ /*
+ * A field created by trace_define_field() only stores the name and
+ * type pointers, it does not copy the strings. Here they point into
+ * the probe_arg of @tp, which is freed when @tp is removed. For an
+ * event with multiple probes attached, the field list is defined
+ * once by the first probe but kept alive by the surviving siblings,
+ * so removing that first probe would leave the fields referencing
+ * freed memory. Duplicate the strings and anchor the copies on the
+ * trace_probe_event, which lives as long as the field list itself.
+ *
+ * event_define_fields() ignores the return value of this hook, so
+ * if a previous attempt failed before creating any field, it may
+ * call here again. Release duplicates left behind by such an
+ * attempt before starting over.
+ */
+ for (i = 0; i < tpe->nr_field_strings; i++)
+ kfree(tpe->field_strings[i]);
+ kfree(tpe->field_strings);
+ tpe->field_strings = NULL;
+ tpe->nr_field_strings = 0;
+
+ if (tp->nr_args) {
+ tpe->field_strings = kcalloc(tp->nr_args * 2, sizeof(char *),
+ GFP_KERNEL);
+ if (!tpe->field_strings)
+ return -ENOMEM;
+ }
+
/* Set argument names as fields */
for (i = 0; i < tp->nr_args; i++) {
struct probe_arg *parg = &tp->args[i];
const char *fmt = parg->type->fmttype;
int size = parg->type->size;
+ char *name, *type;
if (parg->fmt)
fmt = parg->fmt;
if (parg->count)
size *= parg->count;
- ret = trace_define_field(event_call, fmt, parg->name,
+
+ name = kstrdup(parg->name, GFP_KERNEL);
+ type = kstrdup(fmt, GFP_KERNEL);
+ if (!name || !type) {
+ kfree(name);
+ kfree(type);
+ return -ENOMEM;
+ }
+ tpe->field_strings[tpe->nr_field_strings++] = name;
+ tpe->field_strings[tpe->nr_field_strings++] = type;
+
+ ret = trace_define_field(event_call, type, name,
offset + parg->offset, size,
parg->type->is_signed,
FILTER_OTHER);
@@ -2576,6 +2618,11 @@ int traceprobe_define_arg_fields(struct trace_event_call *event_call,
static void trace_probe_event_free(struct trace_probe_event *tpe)
{
+ int i;
+
+ for (i = 0; i < tpe->nr_field_strings; i++)
+ kfree(tpe->field_strings[i]);
+ kfree(tpe->field_strings);
kfree(tpe->class.system);
kfree(tpe->call.name);
kfree(tpe->call.print_fmt);
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index fba1af092a9b..d1fb3520700f 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -264,6 +264,8 @@ struct trace_probe_event {
struct trace_event_call call;
struct list_head files;
struct list_head probes;
+ char **field_strings;
+ int nr_field_strings;
struct trace_uprobe_filter filter[];
};
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 861d857adadb..22cc3c8181b8 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -368,7 +368,7 @@ error:
static void free_trace_uprobe(struct trace_uprobe *tu)
{
- if (!tu)
+ if (IS_ERR_OR_NULL(tu))
return;
path_put(&tu->path);
@@ -533,7 +533,7 @@ static int register_trace_uprobe(struct trace_uprobe *tu)
return ret;
}
-DEFINE_FREE(free_trace_uprobe, struct trace_uprobe *, if (_T) free_trace_uprobe(_T))
+DEFINE_FREE(free_trace_uprobe, struct trace_uprobe *, free_trace_uprobe(_T))
/*
* Argument syntax:
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 3c034cbc5bb3..1ae3732a2c51 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -3197,7 +3197,16 @@ restart:
#ifdef CONFIG_PREEMPT_RT
static void worker_lock_callback(struct worker_pool *pool)
{
- spin_lock(&pool->cb_lock);
+ /*
+ * SINGLE_DEPTH_NESTING is for a dead pool's bh_worker() running from
+ * drain_dead_softirq_workfn() inside a live pool's bh_worker(). The
+ * unlocked read is stable: the flag is only set while @pool's CPU is
+ * dead, inside a serialized hotplug operation. data_race() as the value
+ * only affects the lockdep annotation and the read can be elided when
+ * lockdep is disabled.
+ */
+ spin_lock_nested(&pool->cb_lock,
+ data_race(pool->flags) & POOL_BH_DRAINING ? SINGLE_DEPTH_NESTING : 0);
}
static void worker_unlock_callback(struct worker_pool *pool)
@@ -5285,12 +5294,6 @@ static void pwq_release_workfn(struct kthread_work *work)
mutex_unlock(&wq->mutex);
}
- if (!is_percpu_pool(pool)) {
- mutex_lock(&wq_pool_mutex);
- put_unbound_pool(pool);
- mutex_unlock(&wq_pool_mutex);
- }
-
if (!list_empty(&pwq->pending_node)) {
struct wq_node_nr_active *nna =
wq_node_nr_active(pwq->wq, pwq->pool->node);
@@ -5300,6 +5303,12 @@ static void pwq_release_workfn(struct kthread_work *work)
raw_spin_unlock_irq(&nna->lock);
}
+ if (!is_percpu_pool(pool)) {
+ mutex_lock(&wq_pool_mutex);
+ put_unbound_pool(pool);
+ mutex_unlock(&wq_pool_mutex);
+ }
+
kfree_rcu(pwq, rcu);
/*
@@ -8050,6 +8059,9 @@ static int wq_watchdog_param_set_thresh(const char *val,
if (ret)
return ret;
+ if (thresh > MAX_JIFFY_OFFSET / HZ)
+ return -ERANGE;
+
if (system_percpu_wq)
wq_watchdog_set_thresh(thresh);
else
@@ -8080,12 +8092,12 @@ static inline void wq_watchdog_init(void) { }
static void bh_pool_kick_normal(struct irq_work *irq_work)
{
- raise_softirq_irqoff(TASKLET_SOFTIRQ);
+ raise_softirq(TASKLET_SOFTIRQ);
}
static void bh_pool_kick_highpri(struct irq_work *irq_work)
{
- raise_softirq_irqoff(HI_SOFTIRQ);
+ raise_softirq(HI_SOFTIRQ);
}
static void __init restrict_unbound_cpumask(const char *name, const struct cpumask *mask)
diff --git a/lib/once.c b/lib/once.c
index d801bfa945e6..0a0a919156e0 100644
--- a/lib/once.c
+++ b/lib/once.c
@@ -93,6 +93,6 @@ void __do_once_sleepable_done(bool *done, struct static_key_true *once_key,
{
*done = true;
mutex_unlock(&once_mutex);
- static_branch_disable(once_key);
+ once_disable_jump(once_key, mod);
}
EXPORT_SYMBOL(__do_once_sleepable_done);
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index ced400f72d43..afbb5974bd22 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2449,6 +2449,8 @@ static void zap_huge_pmd_folio(struct mm_struct *mm, struct vm_area_struct *vma,
add_mm_counter(mm, mm_counter_file(folio),
-HPAGE_PMD_NR);
+ if (is_present && pmd_dirty(pmdval))
+ folio_mark_dirty(folio);
if (is_present && pmd_young(pmdval) &&
likely(vma_has_recency(vma)))
folio_mark_accessed(folio);
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 785772845795..4f6f58bf3db6 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1992,7 +1992,8 @@ retry:
if (h->surplus_huge_pages_node[folio_nid(folio)])
adjust_surplus = true;
remove_hugetlb_folio(h, folio, adjust_surplus);
- h->max_huge_pages--;
+ if (!adjust_surplus)
+ h->max_huge_pages--;
spin_unlock_irq(&hugetlb_lock);
/*
@@ -2012,7 +2013,8 @@ retry:
if (rc) {
spin_lock_irq(&hugetlb_lock);
add_hugetlb_folio(h, folio, adjust_surplus);
- h->max_huge_pages++;
+ if (!adjust_surplus)
+ h->max_huge_pages++;
goto out;
}
} else {
@@ -7330,14 +7332,14 @@ void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
* There is no need to transfer the per-node surplus state
* when we do not cross the node.
*/
- if (new_nid == old_nid)
- return;
- spin_lock_irq(&hugetlb_lock);
- if (h->surplus_huge_pages_node[old_nid]) {
- h->surplus_huge_pages_node[old_nid]--;
- h->surplus_huge_pages_node[new_nid]++;
+ if (new_nid != old_nid) {
+ spin_lock_irq(&hugetlb_lock);
+ if (h->surplus_huge_pages_node[old_nid]) {
+ h->surplus_huge_pages_node[old_nid]--;
+ h->surplus_huge_pages_node[new_nid]++;
+ }
+ spin_unlock_irq(&hugetlb_lock);
}
- spin_unlock_irq(&hugetlb_lock);
}
/*
diff --git a/mm/hugetlb_cgroup.c b/mm/hugetlb_cgroup.c
index e0083de1ca82..ecb6e0b7819a 100644
--- a/mm/hugetlb_cgroup.c
+++ b/mm/hugetlb_cgroup.c
@@ -97,6 +97,7 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup,
struct page_counter *fault, *fault_parent = NULL;
struct page_counter *rsvd, *rsvd_parent = NULL;
unsigned long limit;
+ int ret;
if (parent_h_cgroup) {
fault_parent = hugetlb_cgroup_counter_from_cgroup(
@@ -118,8 +119,10 @@ static void hugetlb_cgroup_init(struct hugetlb_cgroup *h_cgroup,
limit = round_down(PAGE_COUNTER_MAX,
pages_per_huge_page(&hstates[idx]));
- VM_BUG_ON(page_counter_set_max(fault, limit));
- VM_BUG_ON(page_counter_set_max(rsvd, limit));
+ ret = page_counter_set_max(fault, limit);
+ VM_WARN_ON_ONCE(ret);
+ ret = page_counter_set_max(rsvd, limit);
+ VM_WARN_ON_ONCE(ret);
}
}
diff --git a/mm/hugetlb_cma.c b/mm/hugetlb_cma.c
index db0680e82847..95fd2d190f0d 100644
--- a/mm/hugetlb_cma.c
+++ b/mm/hugetlb_cma.c
@@ -3,6 +3,7 @@
#include <linux/mm.h>
#include <linux/cma.h>
#include <linux/compiler.h>
+#include <linux/cpuset.h>
#include <linux/mm_inline.h>
#include <asm/page.h>
@@ -55,15 +56,25 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
int node;
struct folio *folio;
struct page *page = NULL;
+ const nodemask_t *nmask;
+ unsigned int cpuset_mems_cookie;
if (!hugetlb_cma_size)
return NULL;
- if (hugetlb_cma[nid])
+retry_cpuset:
+ if (!nodemask) {
+ cpuset_mems_cookie = read_mems_allowed_begin();
+ nmask = &cpuset_current_mems_allowed;
+ } else {
+ nmask = nodemask;
+ }
+
+ if (hugetlb_cma[nid] && node_isset(nid, *nmask))
page = cma_alloc_frozen_compound(hugetlb_cma[nid], order);
if (!page && !(gfp_mask & __GFP_THISNODE)) {
- for_each_node_mask(node, *nodemask) {
+ for_each_node_mask(node, *nmask) {
if (node == nid || !hugetlb_cma[node])
continue;
@@ -73,8 +84,12 @@ struct folio *hugetlb_cma_alloc_frozen_folio(int order, gfp_t gfp_mask,
}
}
- if (!page)
+ if (!page) {
+ if (!nodemask &&
+ unlikely(read_mems_allowed_retry(cpuset_mems_cookie)))
+ goto retry_cpuset;
return NULL;
+ }
folio = page_folio(page);
folio_set_hugetlb_cma(folio);
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 11ff98d55c76..75639298efc2 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -454,12 +454,6 @@ int hugepage_madvise(struct vm_area_struct *vma,
case MADV_HUGEPAGE:
*vm_flags &= ~VM_NOHUGEPAGE;
*vm_flags |= VM_HUGEPAGE;
- /*
- * If the vma become good for khugepaged to scan,
- * register it here without waiting a page fault that
- * may not happen any time soon.
- */
- khugepaged_enter_vma(vma, *vm_flags);
break;
case MADV_NOHUGEPAGE:
*vm_flags &= ~VM_HUGEPAGE;
diff --git a/mm/madvise.c b/mm/madvise.c
index 96f2387b2f46..eeee82cf2b3f 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -178,6 +178,14 @@ static int madvise_update_vma(vm_flags_t new_flags,
/* vm_flags is protected by the mmap_lock held in write mode. */
vma_start_write(vma);
vma->flags = new_vma_flags;
+ /*
+ * If the vma become good for khugepaged to scan,
+ * register it here without waiting a page fault that
+ * may not happen any time soon.
+ */
+ if (vma_flags_test(&new_vma_flags, VMA_HUGEPAGE_BIT))
+ khugepaged_enter_vma(vma, vma_flags_to_legacy(new_vma_flags));
+
if (set_new_anon_name)
return replace_anon_vma_name(vma, anon_name);
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index 835fc8e51184..05ef55cae4dc 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -96,7 +96,6 @@ enum {
RES_LIMIT,
RES_MAX_USAGE,
RES_FAILCNT,
- RES_SOFT_LIMIT,
};
#ifdef CONFIG_LOCKDEP
@@ -1888,6 +1887,30 @@ static int mem_cgroup_hierarchy_write(struct cgroup_subsys_state *css,
return -EINVAL;
}
+static u64 mem_cgroup_soft_limit_read(struct cgroup_subsys_state *css,
+ struct cftype *cft)
+{
+ return (u64)PAGE_COUNTER_MAX * PAGE_SIZE;
+}
+
+static ssize_t mem_cgroup_soft_limit_write(struct kernfs_open_file *of,
+ char *buf, size_t nbytes, loff_t off)
+{
+ unsigned long nr_pages;
+ int ret;
+
+ ret = page_counter_memparse(strstrip(buf), "-1", &nr_pages);
+ if (ret)
+ return ret;
+
+ pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. "
+ "Writing any value to this file has no effect. "
+ "Please report your usecase to linux-mm@kvack.org if you "
+ "depend on this functionality.\n");
+
+ return nbytes;
+}
+
static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,
struct cftype *cft)
{
@@ -1924,8 +1947,6 @@ static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css,
return (u64)counter->watermark * PAGE_SIZE;
case RES_FAILCNT:
return counter->failcnt;
- case RES_SOFT_LIMIT:
- return (u64)READ_ONCE(memcg->soft_limit) * PAGE_SIZE;
default:
BUG();
}
@@ -2020,17 +2041,6 @@ static ssize_t mem_cgroup_write(struct kernfs_open_file *of,
break;
}
break;
- case RES_SOFT_LIMIT:
- if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
- ret = -EOPNOTSUPP;
- } else {
- pr_warn_once("soft_limit_in_bytes is deprecated and will be removed. "
- "Please report your usecase to linux-mm@kvack.org if you "
- "depend on this functionality.\n");
- WRITE_ONCE(memcg->soft_limit, nr_pages);
- ret = 0;
- }
- break;
}
return ret ?: nbytes;
}
@@ -2384,9 +2394,8 @@ struct cftype mem_cgroup_legacy_files[] = {
},
{
.name = "soft_limit_in_bytes",
- .private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT),
- .write = mem_cgroup_write,
- .read_u64 = mem_cgroup_read_u64,
+ .write = mem_cgroup_soft_limit_write,
+ .read_u64 = mem_cgroup_soft_limit_read,
},
{
.name = "failcnt",
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 3498a5651d50..79053ece02cd 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -2679,7 +2679,7 @@ static unsigned long alloc_pages_bulk_weighted_interleave(gfp_t gfp,
prev_node = node;
/* create a local copy of node weights to operate on outside rcu */
- weights = kzalloc(nr_node_ids, GFP_KERNEL);
+ weights = kmalloc(nr_node_ids, gfp & GFP_RECLAIM_MASK);
if (!weights)
return total_allocated;
diff --git a/mm/migrate_device.c b/mm/migrate_device.c
index 762c5cee8fec..009bfa8b212d 100644
--- a/mm/migrate_device.c
+++ b/mm/migrate_device.c
@@ -1423,6 +1423,15 @@ int migrate_device_range(unsigned long *src_pfns, unsigned long start,
src_pfns[i] = migrate_device_pfn_lock(pfn);
nr = folio_nr_pages(folio);
+ if (nr > npages - i) {
+ if (src_pfns[i] & MIGRATE_PFN_MIGRATE) {
+ folio_unlock(folio);
+ folio_put(folio);
+ }
+ memset(&src_pfns[i], 0,
+ (npages - i) * sizeof(*src_pfns));
+ break;
+ }
if (nr > 1) {
src_pfns[i] |= MIGRATE_PFN_COMPOUND;
for (j = 1; j < nr; j++)
@@ -1457,6 +1466,15 @@ int migrate_device_pfns(unsigned long *src_pfns, unsigned long npages)
src_pfns[i] = migrate_device_pfn_lock(src_pfns[i]);
nr = folio_nr_pages(folio);
+ if (nr > npages - i) {
+ if (src_pfns[i] & MIGRATE_PFN_MIGRATE) {
+ folio_unlock(folio);
+ folio_put(folio);
+ }
+ memset(&src_pfns[i], 0,
+ (npages - i) * sizeof(*src_pfns));
+ break;
+ }
if (nr > 1) {
src_pfns[i] |= MIGRATE_PFN_COMPOUND;
for (j = 1; j < nr; j++)
diff --git a/mm/mremap.c b/mm/mremap.c
index e8df5cdb0ac9..2b4b523a86b8 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1331,18 +1331,30 @@ static void dontunmap_complete(struct vma_remap_struct *vrm,
{
unsigned long start = vrm->addr;
unsigned long end = vrm->addr + vrm->old_len;
- unsigned long old_start = vrm->vma->vm_start;
- unsigned long old_end = vrm->vma->vm_end;
+ struct vm_area_struct *vma = vrm->vma;
+ unsigned long old_start = vma->vm_start;
+ unsigned long old_end = vma->vm_end;
/* We always clear VMA_LOCKED[ONFAULT]_BIT on the old VMA. */
- vma_clear_flags_mask(vrm->vma, VMA_LOCKED_MASK);
+ vma_clear_flags_mask(vma, VMA_LOCKED_MASK);
/*
* anon_vma links of the old vma is no longer needed after its page
* table has been moved.
*/
- if (new_vma != vrm->vma && start == old_start && end == old_end)
- unlink_anon_vmas(vrm->vma);
+ if (new_vma != vma && start == old_start && end == old_end) {
+ const pgoff_t pgoff_unfaulted = vma->vm_start >> PAGE_SHIFT;
+
+ unlink_anon_vmas(vma);
+ /*
+ * The VMA is now unfaulted and it is an invariant that
+ * unfaulted anonymous VMAs have page offset equal to
+ * vma->vm_start >> PAGE_SHIFT.
+ */
+ vma_set_anon_pgoff(vma, pgoff_unfaulted);
+ if (vma_is_anonymous(vma) && !vma->vm_file)
+ vma_set_pgoff(vma, pgoff_unfaulted);
+ }
/* Because we won't unmap we don't need to touch locked_vm. */
}
diff --git a/mm/secretmem.c b/mm/secretmem.c
index d29865075b6e..384f5cfc457f 100644
--- a/mm/secretmem.c
+++ b/mm/secretmem.c
@@ -18,6 +18,8 @@
#include <linux/secretmem.h>
#include <linux/set_memory.h>
#include <linux/sched/signal.h>
+#include <linux/sched/user.h>
+#include <linux/cred.h>
#include <uapi/linux/magic.h>
@@ -47,10 +49,69 @@ bool secretmem_active(void)
return !!atomic_read(&secretmem_users);
}
+struct secretmem_inode_state {
+ struct user_struct *user;
+ atomic_long_t nr_pages_accounted;
+};
+
+static bool __secretmem_account_pages(struct user_struct *user,
+ unsigned long nr_pages)
+{
+ unsigned long page_limit, cur_pages, new_pages;
+
+ if (!nr_pages)
+ return true;
+
+ page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+
+ cur_pages = atomic_long_read(&user->locked_vm);
+ do {
+ new_pages = cur_pages + nr_pages;
+ if (new_pages > page_limit)
+ return false;
+ } while (!atomic_long_try_cmpxchg(&user->locked_vm,
+ &cur_pages, new_pages));
+ return true;
+}
+
+static bool secretmem_account_folio(struct secretmem_inode_state *state,
+ const struct folio *folio)
+{
+ const unsigned long nr_pages = folio_nr_pages(folio);
+
+ if (!__secretmem_account_pages(state->user, nr_pages))
+ return false;
+
+ atomic_long_add(nr_pages, &state->nr_pages_accounted);
+ return true;
+}
+
+static void __secretmem_unaccount_pages(struct secretmem_inode_state *state,
+ unsigned long nr_pages)
+{
+ atomic_long_sub(nr_pages, &state->user->locked_vm);
+ atomic_long_sub(nr_pages, &state->nr_pages_accounted);
+}
+
+static void secretmem_unaccount_folio(struct secretmem_inode_state *state,
+ struct folio *folio)
+{
+ __secretmem_unaccount_pages(state, folio_nr_pages(folio));
+}
+
+static void secretmem_unaccount_all_folios(struct secretmem_inode_state *state)
+{
+ const unsigned long nr_pages_accounted =
+ atomic_long_read(&state->nr_pages_accounted);
+
+ __secretmem_unaccount_pages(state, nr_pages_accounted);
+}
+
static vm_fault_t secretmem_fault(struct vm_fault *vmf)
{
struct address_space *mapping = vmf->vma->vm_file->f_mapping;
struct inode *inode = file_inode(vmf->vma->vm_file);
+ struct secretmem_inode_state *state = inode->i_private;
pgoff_t offset = vmf->pgoff;
gfp_t gfp = vmf->gfp_mask;
unsigned long addr;
@@ -72,8 +133,15 @@ retry:
goto out;
}
+ if (!secretmem_account_folio(state, folio)) {
+ folio_put(folio);
+ ret = VM_FAULT_SIGBUS;
+ goto out;
+ }
+
err = set_direct_map_invalid_noflush(folio_page(folio, 0));
if (err) {
+ secretmem_unaccount_folio(state, folio);
folio_put(folio);
ret = vmf_error(err);
goto out;
@@ -82,6 +150,7 @@ retry:
__folio_mark_uptodate(folio);
err = filemap_add_folio(mapping, folio, offset, gfp);
if (unlikely(err)) {
+ secretmem_unaccount_folio(state, folio);
/*
* If a split of large page was required, it
* already happened when we marked the page invalid
@@ -112,22 +181,30 @@ static const struct vm_operations_struct secretmem_vm_ops = {
.fault = secretmem_fault,
};
+static void secretmem_destroy_inode_priv(struct inode *inode)
+{
+ struct secretmem_inode_state *state = inode->i_private;
+
+ secretmem_unaccount_all_folios(state);
+ free_uid(state->user);
+ kfree(state);
+ inode->i_private = NULL;
+}
+
static int secretmem_release(struct inode *inode, struct file *file)
{
atomic_dec(&secretmem_users);
+ secretmem_destroy_inode_priv(inode);
+
return 0;
}
static int secretmem_mmap_prepare(struct vm_area_desc *desc)
{
- const unsigned long len = vma_desc_size(desc);
-
if (!vma_desc_test_any(desc, VMA_SHARED_BIT, VMA_MAYSHARE_BIT))
return -EINVAL;
- vma_desc_set_flags(desc, VMA_LOCKED_BIT, VMA_DONTDUMP_BIT);
- if (!mlock_future_ok(desc->mm, /*is_vma_locked=*/ true, len))
- return -EAGAIN;
+ vma_desc_set_flags(desc, VMA_DONTDUMP_BIT);
desc->vm_ops = &secretmem_vm_ops;
return 0;
@@ -187,20 +264,40 @@ static const struct inode_operations secretmem_iops = {
static struct vfsmount *secretmem_mnt;
+static int secretmem_init_inode_priv(struct inode *inode)
+{
+ struct secretmem_inode_state *state;
+
+ state = kzalloc_obj(*state);
+ if (!state)
+ return -ENOMEM;
+
+ state->user = get_uid(current_user());
+ inode->i_private = state;
+ return 0;
+}
+
static struct file *secretmem_file_create(unsigned long flags)
{
struct file *file;
struct inode *inode;
const char *anon_name = "[secretmem]";
+ int err;
inode = anon_inode_make_secure_inode(secretmem_mnt->mnt_sb, anon_name, NULL);
if (IS_ERR(inode))
return ERR_CAST(inode);
+ err = secretmem_init_inode_priv(inode);
+ if (err)
+ goto err_free_inode;
+
file = alloc_file_pseudo(inode, secretmem_mnt, "secretmem",
O_RDWR | O_LARGEFILE, &secretmem_fops);
- if (IS_ERR(file))
- goto err_free_inode;
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto err_free_priv;
+ }
mapping_set_gfp_mask(inode->i_mapping, GFP_USER);
mapping_set_unevictable(inode->i_mapping);
@@ -215,10 +312,11 @@ static struct file *secretmem_file_create(unsigned long flags)
atomic_inc(&secretmem_users);
return file;
-
+err_free_priv:
+ secretmem_destroy_inode_priv(inode);
err_free_inode:
iput(inode);
- return file;
+ return ERR_PTR(err);
}
SYSCALL_DEFINE1(memfd_secret, unsigned int, flags)
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 23fb68fce000..74f04c323c50 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -2171,8 +2171,10 @@ static ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start,
}
if (err) {
- if (err == -EAGAIN)
+ if (err == -EAGAIN) {
+ err = 0;
continue;
+ }
break;
}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 35a1be57e386..d7355c73f93e 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -2632,11 +2632,11 @@ int hci_register_dev(struct hci_dev *hdev)
if (error)
BT_WARN("register suspend notifier failed error:%d\n", error);
- queue_work(hdev->req_workqueue, &hdev->power_on);
-
idr_init(&hdev->adv_monitors_idr);
msft_register(hdev);
+ queue_work(hdev->req_workqueue, &hdev->power_on);
+
return id;
err_wqueue:
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ee459dd411f5..644e31160d55 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1337,7 +1337,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
struct l2cap_ecred_conn_data {
struct {
struct l2cap_ecred_conn_req_hdr req;
- __le16 scid[5];
+ __le16 scid[L2CAP_ECRED_CONN_SCID_MAX];
} __packed pdu;
struct l2cap_chan *chan;
struct pid *pid;
@@ -1352,7 +1352,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
if (chan == conn->chan)
return;
- if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
+ if (!test_bit(FLAG_DEFER_SETUP, &chan->flags))
return;
pid = chan->ops->get_peer_pid(chan);
@@ -1362,9 +1362,16 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)
return;
+ if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
+ return;
+
if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
return;
+ /* Unreachable, checked in l2cap_connect (+timer drops it if reached) */
+ if (WARN_ON_ONCE(conn->count >= ARRAY_SIZE(conn->pdu.scid)))
+ return;
+
l2cap_ecred_init(chan, 0);
/* Set the same ident so we can match on the rsp */
@@ -3894,6 +3901,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
struct l2cap_ecred_conn_rsp *rsp_flex =
container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr);
+ if (chan->mode != L2CAP_MODE_EXT_FLOWCTL)
+ return;
+
/* Check if channel for outgoing connection or if it wasn't deferred
* since in those cases it must be skipped.
*/
@@ -3904,6 +3914,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
/* Reset ident so only one response is sent */
chan->ident = 0;
+ /* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */
+ if (WARN_ON_ONCE(rsp->count >= ARRAY_SIZE(rsp->pdu.scid)))
+ rsp->pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_NO_MEM);
+
/* Include all channels pending with the same ident */
if (!rsp->pdu.rsp.result)
rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid);
@@ -5063,6 +5077,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
chan->ident = cmd->ident;
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
l2cap_state_change(chan, BT_CONNECT2);
@@ -7369,6 +7384,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
goto done;
}
+ mutex_lock(&conn->lock);
+ l2cap_chan_lock(chan);
+
if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
struct l2cap_chan_data data;
@@ -7376,19 +7394,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
data.pid = chan->ops->get_peer_pid(chan);
data.count = 1;
- l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
+ __l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
+
+ /* Leave room for non-deferred channel that ends the group. */
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
+ data.count += 1;
/* Check if there isn't too many channels being connected */
if (data.count > L2CAP_ECRED_CONN_SCID_MAX) {
hci_conn_drop(hcon);
err = -EPROTO;
- goto done;
+ goto chan_unlock;
}
}
- mutex_lock(&conn->lock);
- l2cap_chan_lock(chan);
-
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
hci_conn_drop(hcon);
err = -EBUSY;
diff --git a/net/bluetooth/msft.c b/net/bluetooth/msft.c
index ded68568e6c9..d9dd722db3eb 100644
--- a/net/bluetooth/msft.c
+++ b/net/bluetooth/msft.c
@@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev)
INIT_LIST_HEAD(&msft->handle_map);
INIT_LIST_HEAD(&msft->address_filters);
- hdev->msft_data = msft;
mutex_init(&msft->filter_lock);
+ hdev->msft_data = msft;
}
void msft_release(struct hci_dev *hdev)
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 3ef5d8bbf552..3e9b10f8abf1 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
struct hlist_node *tmp;
rcu_assign_pointer(*pp, pg->next);
- hlist_del_init(&pg->mglist);
+ /* Keep ->next (held under multicast_lock, freed later by the GC work):
+ * a port->mglist teardown walk may have latched this node as its next,
+ * and deleting other groups of the same port must not truncate it.
+ */
+ hlist_del_init_rcu(&pg->mglist);
br_multicast_eht_clean_sets(pg);
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
br_multicast_del_group_src(ent, false);
@@ -835,6 +839,13 @@ static void br_multicast_find_del_pg(struct net_bridge *br,
struct net_bridge_mdb_entry *mp;
struct net_bridge_port_group *p;
+ /* A teardown walk over port->mglist can reach a group that an earlier
+ * iteration already deleted as a side effect. It is off mp->ports by
+ * now, so skip it instead of falling through to the WARN_ON() below.
+ */
+ if (hlist_unhashed(&pg->mglist))
+ return;
+
mp = br_mdb_ip_get(br, &pg->key.addr);
if (WARN_ON(!mp))
return;
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 9c1b6cf8c36f..212e7797f9e4 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -1003,7 +1003,6 @@ static struct page *ceph_msg_data_iter_next(struct ceph_msg_data_cursor *cursor,
* we'll get an iov_iter_get_pages2 variant that doesn't take
* page refs. Until then, just put the page ref.
*/
- VM_BUG_ON_PAGE(!PageWriteback(page) && page_count(page) < 2, page);
put_page(page);
*length = min_t(size_t, len, cursor->resid);
diff --git a/net/core/dev.c b/net/core/dev.c
index 38336858c168..290e0f099e6b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12703,7 +12703,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
const char *pat, int new_ifindex,
struct netlink_ext_ack *extack)
{
- struct netdev_name_node *name_node;
+ struct netdev_name_node *name_node, *tmp;
struct net *net_old = dev_net(dev);
char new_name[IFNAMSIZ] = {};
int err, new_nsid;
@@ -12749,13 +12749,19 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
}
/* Check that none of the altnames conflicts. */
err = -EEXIST;
- netdev_for_each_altname(dev, name_node) {
- if (netdev_name_in_use(net, name_node->name)) {
- NL_SET_ERR_MSG_FMT(extack,
- "An interface with the altname %s exists in the target netns",
- name_node->name);
- goto out;
+ netdev_for_each_altname_safe(dev, name_node, tmp) {
+ if (!netdev_name_in_use(net, name_node->name))
+ continue;
+
+ if (!check_net(net_old)) {
+ __netdev_name_node_alt_destroy(name_node);
+ continue;
}
+
+ NL_SET_ERR_MSG_FMT(extack,
+ "An interface with the altname %s exists in the target netns",
+ name_node->name);
+ goto out;
}
/* Check that new_ifindex isn't used yet. */
@@ -13210,7 +13216,6 @@ static struct pernet_operations __net_initdata netdev_net_ops = {
static void __net_exit default_device_exit_net(struct net *net)
{
- struct netdev_name_node *name_node, *tmp;
struct net_device *dev, *aux;
/*
* Push all migratable network devices back to the
@@ -13234,10 +13239,6 @@ static void __net_exit default_device_exit_net(struct net *net)
if (netdev_name_in_use(&init_net, fb_name))
snprintf(fb_name, IFNAMSIZ, "dev%%d");
- netdev_for_each_altname_safe(dev, name_node, tmp)
- if (netdev_name_in_use(&init_net, name_node->name))
- __netdev_name_node_alt_destroy(name_node);
-
err = dev_change_net_namespace(dev, &init_net, fb_name);
if (err) {
pr_emerg("%s: failed to move %s to init_net: %d\n",
diff --git a/net/core/gro_cells.c b/net/core/gro_cells.c
index 1b84385c04bd..d8c0a2867120 100644
--- a/net/core/gro_cells.c
+++ b/net/core/gro_cells.c
@@ -22,6 +22,8 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
if (unlikely(!(dev->flags & IFF_UP)))
goto drop;
+ skb_unset_transport_header(skb);
+
if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
res = netif_rx(skb);
goto unlock;
diff --git a/net/core/page_pool.c b/net/core/page_pool.c
index 8f8956fb061b..08d7f35cf608 100644
--- a/net/core/page_pool.c
+++ b/net/core/page_pool.c
@@ -1073,7 +1073,8 @@ netmem_ref page_pool_alloc_frag_netmem(struct page_pool *pool,
if (WARN_ON(size > max_size))
return 0;
- size = ALIGN(size, dma_get_cache_alignment());
+ size = ALIGN(size, max_t(unsigned int, dma_get_cache_alignment(),
+ __alignof__(struct skb_shared_info)));
*offset = pool->frag_offset;
if (netmem && *offset + size > max_size) {
diff --git a/net/core/sock.c b/net/core/sock.c
index 1ad41904db25..fa60b7494c58 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2494,6 +2494,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
#ifdef CONFIG_BPF_SYSCALL
RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
#endif
+#if IS_ENABLED(CONFIG_INET_PSP)
+ RCU_INIT_POINTER(newsk->psp_assoc, NULL);
+#endif
/* SANITY */
if (likely(newsk->sk_net_refcnt)) {
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 0483519b7fb0..7a362f2e2c2b 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -874,7 +874,7 @@ static void fib_rebalance(struct fib_info *fi)
change_nexthops(fi) {
int upper_bound;
- if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
+ if (!total || nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
upper_bound = -1;
} else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) &&
nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) {
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index b80b8a92f46e..d56355aca797 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -188,6 +188,10 @@ static void ip_ma_put(struct ip_mc_list *im)
}
}
+#define pmc_dereference(e, pmc) \
+ rcu_dereference_protected(e, lockdep_is_held(&(pmc)->lock) || \
+ lockdep_is_held(&(pmc)->interface->mc_tomb_lock))
+
#define for_each_pmc_rcu(in_dev, pmc) \
for (pmc = rcu_dereference(in_dev->mc_list); \
pmc != NULL; \
@@ -198,13 +202,28 @@ static void ip_ma_put(struct ip_mc_list *im)
pmc != NULL; \
pmc = rtnl_dereference(pmc->next_rcu))
+#define for_each_psf_mclock(pmc, psf) \
+ for (psf = pmc_dereference((pmc)->sources, pmc); \
+ psf; \
+ psf = pmc_dereference(psf->sf_next, pmc))
+
+#define for_each_psf_rcu(im, psf) \
+ for (psf = rcu_dereference((im)->sources); \
+ psf; \
+ psf = rcu_dereference(psf->sf_next))
+
+#define for_each_psf_tomb(pmc, psf) \
+ for (psf = pmc_dereference((pmc)->tomb, pmc); \
+ psf; \
+ psf = pmc_dereference(psf->sf_next, pmc))
+
static void ip_sf_list_clear_all(struct ip_sf_list *psf)
{
struct ip_sf_list *next;
while (psf) {
- next = psf->sf_next;
- kfree(psf);
+ next = rcu_dereference_protected(psf->sf_next, 1);
+ kfree_rcu(psf, rcu);
psf = next;
}
}
@@ -349,7 +368,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
struct ip_sf_list *psf;
int scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (!is_in(pmc, psf, type, gdeleted, sdeleted))
continue;
scount++;
@@ -494,7 +513,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
struct net *net = dev_net(dev);
struct igmpv3_report *pih;
struct igmpv3_grec *pgr = NULL;
- struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
+ struct ip_sf_list *psf, *psf_next, *psf_prev;
+ struct ip_sf_list __rcu **psf_list;
int scount, stotal, first, isquery, truncate;
unsigned int mtu;
@@ -517,7 +537,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
- if (!*psf_list)
+ if (!rcu_access_pointer(*psf_list))
goto empty_source;
pih = skb ? igmpv3_report_hdr(skb) : NULL;
@@ -533,10 +553,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
}
first = 1;
psf_prev = NULL;
- for (psf = *psf_list; psf; psf = psf_next) {
+ for (psf = pmc_dereference(*psf_list, pmc);
+ psf;
+ psf = psf_next) {
__be32 *psrc;
- psf_next = psf->sf_next;
+ psf_next = pmc_dereference(psf->sf_next, pmc);
if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
psf_prev = psf;
@@ -583,10 +605,12 @@ decrease_sf_crcount:
psf->sf_crcount--;
if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next,
+ psf_next);
else
- *psf_list = psf->sf_next;
- kfree(psf);
+ rcu_assign_pointer(*psf_list,
+ psf_next);
+ kfree_rcu(psf, rcu);
continue;
}
}
@@ -655,28 +679,29 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
/*
* remove zero-count source records from a source filter list
*/
-static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
+static void igmpv3_clear_zeros(struct ip_sf_list __rcu **ppsf)
{
struct ip_sf_list *psf_prev, *psf_next, *psf;
psf_prev = NULL;
- for (psf = *ppsf; psf; psf = psf_next) {
- psf_next = psf->sf_next;
+ for (psf = rcu_dereference_protected(*ppsf, 1); psf; psf = psf_next) {
+ psf_next = rcu_dereference_protected(psf->sf_next, 1);
if (psf->sf_crcount == 0) {
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next, psf_next);
else
- *ppsf = psf->sf_next;
- kfree(psf);
- } else
+ rcu_assign_pointer(*ppsf, psf_next);
+ kfree_rcu(psf, rcu);
+ } else {
psf_prev = psf;
+ }
}
}
static void kfree_pmc(struct ip_mc_list *pmc)
{
- ip_sf_list_clear_all(pmc->sources);
- ip_sf_list_clear_all(pmc->tomb);
+ ip_sf_list_clear_all(rcu_dereference_protected(pmc->sources, 1));
+ ip_sf_list_clear_all(rcu_dereference_protected(pmc->tomb, 1));
kfree(pmc);
}
@@ -710,7 +735,8 @@ static void igmpv3_send_cr(struct in_device *in_dev)
igmpv3_clear_zeros(&pmc->sources);
}
}
- if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
+ if (pmc->crcount == 0 && !rcu_access_pointer(pmc->tomb) &&
+ !rcu_access_pointer(pmc->sources)) {
if (pmc_prev)
pmc_prev->next = pmc_next;
else
@@ -896,7 +922,7 @@ static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
int i, scount;
scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++) {
@@ -927,7 +953,7 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
/* mark INCLUDE-mode sources */
scount = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (scount == nsrcs)
break;
for (i = 0; i < nsrcs; i++)
@@ -1228,11 +1254,12 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
if (pmc->sfmode == MCAST_INCLUDE) {
struct ip_sf_list *psf;
+ for_each_psf_mclock(im, psf)
+ psf->sf_crcount = pmc->crcount;
pmc->tomb = im->tomb;
pmc->sources = im->sources;
- im->tomb = im->sources = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next)
- psf->sf_crcount = pmc->crcount;
+ RCU_INIT_POINTER(im->tomb, NULL);
+ RCU_INIT_POINTER(im->sources, NULL);
}
spin_unlock_bh(&im->lock);
@@ -1271,9 +1298,18 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
if (pmc) {
im->interface = pmc->interface;
if (im->sfmode == MCAST_INCLUDE) {
- swap(im->tomb, pmc->tomb);
- swap(im->sources, pmc->sources);
- for (psf = im->sources; psf; psf = psf->sf_next)
+ struct ip_sf_list *sources, *tomb;
+
+ tomb = rcu_replace_pointer(im->tomb,
+ rcu_dereference_protected(pmc->tomb, 1),
+ lockdep_is_held(&im->lock));
+ rcu_assign_pointer(pmc->tomb, tomb);
+
+ sources = rcu_replace_pointer(im->sources,
+ rcu_dereference_protected(pmc->sources, 1),
+ lockdep_is_held(&im->lock));
+ rcu_assign_pointer(pmc->sources, sources);
+ for_each_psf_mclock(im, psf)
psf->sf_crcount = in_dev->mr_qrv ?:
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
} else {
@@ -1310,8 +1346,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
struct ip_sf_list *psf;
spin_lock_bh(&pmc->lock);
- psf = pmc->tomb;
- pmc->tomb = NULL;
+ psf = pmc_dereference(pmc->tomb, pmc);
+ RCU_INIT_POINTER(pmc->tomb, NULL);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(psf);
}
@@ -1990,7 +2026,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
int rv = 0;
psf_prev = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -1999,7 +2035,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* source filter not found, or count wrong => bug */
return -ESRCH;
}
- psf->sf_count[sfmode]--;
+ WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] - 1);
if (psf->sf_count[sfmode] == 0) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2011,19 +2047,28 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
/* no more filters for this source */
if (psf_prev)
- psf_prev->sf_next = psf->sf_next;
+ rcu_assign_pointer(psf_prev->sf_next,
+ pmc_dereference(psf->sf_next, pmc));
else
- pmc->sources = psf->sf_next;
+ rcu_assign_pointer(pmc->sources,
+ pmc_dereference(psf->sf_next, pmc));
#ifdef CONFIG_IP_MULTICAST
if (psf->sf_oldin &&
!IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
- psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
- psf->sf_next = pmc->tomb;
- pmc->tomb = psf;
- rv = 1;
- } else
+ struct ip_sf_list *dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
+
+ if (dpsf) {
+ *dpsf = *psf;
+ dpsf->sf_crcount = in_dev->mr_qrv ?:
+ READ_ONCE(net->ipv4.sysctl_igmp_qrv);
+ rcu_assign_pointer(dpsf->sf_next,
+ pmc_dereference(pmc->tomb, pmc));
+ rcu_assign_pointer(pmc->tomb, dpsf);
+ rv = 1;
+ }
+ }
#endif
- kfree(psf);
+ kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2060,7 +2105,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
err = -EINVAL;
if (!pmc->sfcount[sfmode])
goto out_unlock;
- pmc->sfcount[sfmode]--;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
}
err = 0;
for (i = 0; i < sfcount; i++) {
@@ -2083,7 +2128,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#ifdef CONFIG_IP_MULTICAST
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(pmc->interface);
} else if (sf_setstate(pmc) || changerec) {
@@ -2104,7 +2149,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
struct ip_sf_list *psf, *psf_prev;
psf_prev = NULL;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (psf->sf_inaddr == *psfsrc)
break;
psf_prev = psf;
@@ -2114,12 +2159,12 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
if (!psf)
return -ENOBUFS;
psf->sf_inaddr = *psfsrc;
- if (psf_prev) {
- psf_prev->sf_next = psf;
- } else
- pmc->sources = psf;
+ if (psf_prev)
+ rcu_assign_pointer(psf_prev->sf_next, psf);
+ else
+ rcu_assign_pointer(pmc->sources, psf);
}
- psf->sf_count[sfmode]++;
+ WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] + 1);
if (psf->sf_count[sfmode] == 1) {
ip_rt_multicast_event(pmc->interface);
}
@@ -2132,13 +2177,15 @@ static void sf_markstate(struct ip_mc_list *pmc)
struct ip_sf_list *psf;
int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
psf->sf_oldin = mca_xcount ==
psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
- } else
+ } else {
psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
+ }
+ }
}
static int sf_setstate(struct ip_mc_list *pmc)
@@ -2149,27 +2196,31 @@ static int sf_setstate(struct ip_mc_list *pmc)
int new_in, rv;
rv = 0;
- for (psf = pmc->sources; psf; psf = psf->sf_next) {
+ for_each_psf_mclock(pmc, psf) {
if (pmc->sfcount[MCAST_EXCLUDE]) {
new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
!psf->sf_count[MCAST_INCLUDE];
- } else
+ } else {
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
+ }
if (new_in) {
if (!psf->sf_oldin) {
struct ip_sf_list *prev = NULL;
- for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) {
+ for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
prev = dpsf;
}
if (dpsf) {
+ struct ip_sf_list *dpsf_next;
+
+ dpsf_next = pmc_dereference(dpsf->sf_next, pmc);
if (prev)
- prev->sf_next = dpsf->sf_next;
+ rcu_assign_pointer(prev->sf_next, dpsf_next);
else
- pmc->tomb = dpsf->sf_next;
- kfree(dpsf);
+ rcu_assign_pointer(pmc->tomb, dpsf_next);
+ kfree_rcu(dpsf, rcu);
}
psf->sf_crcount = qrv;
rv++;
@@ -2181,17 +2232,19 @@ static int sf_setstate(struct ip_mc_list *pmc)
* add or update "delete" records if an active filter
* is now inactive
*/
- for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next)
+ for_each_psf_tomb(pmc, dpsf) {
if (dpsf->sf_inaddr == psf->sf_inaddr)
break;
+ }
if (!dpsf) {
dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
if (!dpsf)
continue;
*dpsf = *psf;
/* pmc->lock held by callers */
- dpsf->sf_next = pmc->tomb;
- pmc->tomb = dpsf;
+ rcu_assign_pointer(dpsf->sf_next,
+ pmc_dereference(pmc->tomb, pmc));
+ rcu_assign_pointer(pmc->tomb, dpsf);
}
dpsf->sf_crcount = qrv;
rv++;
@@ -2231,7 +2284,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
#endif
isexclude = pmc->sfmode == MCAST_EXCLUDE;
if (!delta)
- pmc->sfcount[sfmode]++;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] + 1);
err = 0;
for (i = 0; i < sfcount; i++) {
err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]);
@@ -2242,7 +2295,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
int j;
if (!delta)
- pmc->sfcount[sfmode]--;
+ WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
for (j = 0; j < i; j++)
(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
@@ -2262,7 +2315,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
- for (psf = pmc->sources; psf; psf = psf->sf_next)
+ for_each_psf_mclock(pmc, psf)
psf->sf_crcount = 0;
igmp_ifc_event(in_dev);
} else if (sf_setstate(pmc)) {
@@ -2278,13 +2331,13 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
struct ip_sf_list *tomb, *sources;
spin_lock_bh(&pmc->lock);
- tomb = pmc->tomb;
- pmc->tomb = NULL;
- sources = pmc->sources;
- pmc->sources = NULL;
+ tomb = pmc_dereference(pmc->tomb, pmc);
+ RCU_INIT_POINTER(pmc->tomb, NULL);
+ sources = pmc_dereference(pmc->sources, pmc);
+ RCU_INIT_POINTER(pmc->sources, NULL);
pmc->sfmode = MCAST_EXCLUDE;
- pmc->sfcount[MCAST_INCLUDE] = 0;
- pmc->sfcount[MCAST_EXCLUDE] = 1;
+ WRITE_ONCE(pmc->sfcount[MCAST_INCLUDE], 0);
+ WRITE_ONCE(pmc->sfcount[MCAST_EXCLUDE], 1);
spin_unlock_bh(&pmc->lock);
ip_sf_list_clear_all(tomb);
@@ -2866,20 +2919,19 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
rv = 1;
} else if (im) {
if (src_addr) {
- spin_lock_bh(&im->lock);
- for (psf = im->sources; psf; psf = psf->sf_next) {
+ for_each_psf_rcu(im, psf) {
if (psf->sf_inaddr == src_addr)
break;
}
if (psf)
- rv = psf->sf_count[MCAST_INCLUDE] ||
- psf->sf_count[MCAST_EXCLUDE] !=
- im->sfcount[MCAST_EXCLUDE];
+ rv = READ_ONCE(psf->sf_count[MCAST_INCLUDE]) ||
+ READ_ONCE(psf->sf_count[MCAST_EXCLUDE]) !=
+ READ_ONCE(im->sfcount[MCAST_EXCLUDE]);
else
- rv = im->sfcount[MCAST_EXCLUDE] != 0;
- spin_unlock_bh(&im->lock);
- } else
+ rv = READ_ONCE(im->sfcount[MCAST_EXCLUDE]) != 0;
+ } else {
rv = 1; /* unspecified source; tentatively allow */
+ }
}
return rv;
}
@@ -3043,7 +3095,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
im = rcu_dereference(idev->mc_list);
if (likely(im)) {
spin_lock_bh(&im->lock);
- psf = im->sources;
+ psf = pmc_dereference(im->sources, im);
if (likely(psf)) {
state->im = im;
state->idev = idev;
@@ -3059,7 +3111,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
{
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
- psf = psf->sf_next;
+ psf = pmc_dereference(psf->sf_next, state->im);
while (!psf) {
spin_unlock_bh(&state->im->lock);
state->im = state->im->next;
@@ -3075,7 +3127,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
state->im = rcu_dereference(state->idev->mc_list);
}
spin_lock_bh(&state->im->lock);
- psf = state->im->sources;
+ psf = pmc_dereference(state->im->sources, state->im);
}
out:
return psf;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index b4237d0e994d..1c867a302444 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -3182,8 +3182,7 @@ void __tcp_close(struct sock *sk, long timeout)
/* Unread data was tossed, zap the connection. */
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, sk->sk_allocation,
- SK_RST_REASON_TCP_ABORT_ON_CLOSE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
sk->sk_prot->disconnect(sk, 0);
@@ -3257,7 +3256,7 @@ adjudge_to_death:
struct tcp_sock *tp = tcp_sk(sk);
if (READ_ONCE(tp->linger2) < 0) {
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_LINGER);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONLINGER);
@@ -3276,7 +3275,7 @@ adjudge_to_death:
if (sk->sk_state != TCP_CLOSE) {
if (tcp_check_oom(sk, 0)) {
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
__NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPABORTONMEMORY);
@@ -3377,14 +3376,14 @@ int tcp_disconnect(struct sock *sk, int flags)
} else if (unlikely(tp->repair)) {
WRITE_ONCE(sk->sk_err, ECONNABORTED);
} else if (tcp_need_reset(old_state)) {
- tcp_send_active_reset(sk, gfp_any(), SK_RST_REASON_TCP_STATE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (tp->snd_nxt != tp->write_seq &&
(1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) {
/* The last check adjusts for discrepancy of Linux wrt. RFC
* states
*/
- tcp_send_active_reset(sk, gfp_any(),
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_DISCONNECT_WITH_DATA);
WRITE_ONCE(sk->sk_err, ECONNRESET);
} else if (old_state == TCP_SYN_SENT)
@@ -4562,9 +4561,11 @@ int do_tcp_getsockopt(struct sock *sk, int level,
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
- ca_ops = icsk->icsk_ca_ops;
+ rcu_read_lock();
+ ca_ops = READ_ONCE(icsk->icsk_ca_ops);
if (ca_ops && ca_ops->get_info)
sz = ca_ops->get_info(sk, ~0U, &attr, &info);
+ rcu_read_unlock();
len = min_t(unsigned int, len, sz);
if (copy_to_sockptr(optlen, &len, sizeof(int)))
@@ -4577,16 +4578,24 @@ int do_tcp_getsockopt(struct sock *sk, int level,
val = !inet_csk_in_pingpong_mode(sk);
break;
- case TCP_CONGESTION:
+ case TCP_CONGESTION: {
+ char ca_name[TCP_CA_NAME_MAX] = {};
+
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
len = min_t(unsigned int, len, TCP_CA_NAME_MAX);
if (copy_to_sockptr(optlen, &len, sizeof(int)))
return -EFAULT;
- if (copy_to_sockptr(optval, icsk->icsk_ca_ops->name, len))
+
+ rcu_read_lock();
+ memcpy(ca_name, READ_ONCE(icsk->icsk_ca_ops)->name,
+ sizeof(ca_name));
+ rcu_read_unlock();
+
+ if (copy_to_sockptr(optval, ca_name, len))
return -EFAULT;
return 0;
-
+ }
case TCP_ULP:
if (copy_from_sockptr(&len, optlen, sizeof(int)))
return -EFAULT;
@@ -5147,8 +5156,7 @@ int tcp_abort(struct sock *sk, int err)
bh_lock_sock(sk);
if (tcp_need_reset(sk->sk_state))
- tcp_send_active_reset(sk, GFP_ATOMIC,
- SK_RST_REASON_TCP_STATE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
tcp_done_with_error(sk, err);
bh_unlock_sock(sk);
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index e9f6c77e0631..8e83ef81fc18 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -223,7 +223,7 @@ void tcp_assign_congestion_control(struct sock *sk)
ca = rcu_dereference(net->ipv4.tcp_congestion_control);
if (unlikely(!bpf_try_module_get(ca, ca->owner)))
ca = &tcp_reno;
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
rcu_read_unlock();
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
@@ -253,7 +253,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,
struct inet_connection_sock *icsk = inet_csk(sk);
tcp_cleanup_congestion_control(sk);
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
icsk->icsk_ca_setsockopt = 1;
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c
index 274e628e7cf8..5b457f68a581 100644
--- a/net/ipv4/tcp_dctcp.c
+++ b/net/ipv4/tcp_dctcp.c
@@ -111,7 +111,7 @@ __bpf_kfunc static void dctcp_init(struct sock *sk)
/* No ECN support? Fall back to Reno. Also need to clear
* ECT from sk since it is set during 3WHS for DCTCP.
*/
- inet_csk(sk)->icsk_ca_ops = &dctcp_reno;
+ WRITE_ONCE(inet_csk(sk)->icsk_ca_ops, &dctcp_reno);
INET_ECN_dontxmit(sk);
}
@@ -228,7 +228,7 @@ static size_t dctcp_get_info(struct sock *sk, u32 ext, int *attr,
if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) ||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
memset(&info->dctcp, 0, sizeof(info->dctcp));
- if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) {
+ if (READ_ONCE(inet_csk(sk)->icsk_ca_ops) != &dctcp_reno) {
info->dctcp.dctcp_enabled = 1;
info->dctcp.dctcp_ce_state = (u16) ca->ce_state;
info->dctcp.dctcp_alpha = ca->dctcp_alpha;
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index f3fa0b18eda0..0ddfd5af6e58 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -507,7 +507,7 @@ void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst)
ca = tcp_ca_find_key(ca_key);
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
ca_got_dst = true;
}
rcu_read_unlock();
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index 3b1fdcd3cb29..e74d99ca9fac 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -332,6 +332,7 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
flush |= skb->ip_summed != p->ip_summed;
flush |= skb->csum_level != p->csum_level;
flush |= NAPI_GRO_CB(p)->count >= 64;
+ flush |= NAPI_GRO_CB(p)->is_flist != NAPI_GRO_CB(skb)->is_flist;
skb_set_network_header(skb, skb_gro_receive_network_offset(skb));
if (flush || skb_gro_receive_list(p, skb))
@@ -395,12 +396,20 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
struct net *net;
int iif, sdif;
- if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
- return;
-
p = tcp_gro_lookup(head, th);
if (p) {
- NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ /* flist GRO applies to consecutive non-GSO skbs */
+ if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ /* Fall back to the regular GRO path */
+ if (NAPI_GRO_CB(p)->count == 1)
+ NAPI_GRO_CB(p)->is_flist = 0;
+
+ NAPI_GRO_CB(skb)->is_flist = 0;
+
return;
}
@@ -410,7 +419,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
sk = __inet_lookup_established(net, iph->saddr, th->source,
iph->daddr, ntohs(th->dest),
iif, sdif);
- NAPI_GRO_CB(skb)->is_flist = !sk;
+ NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
if (sk)
sock_gen_put(sk);
}
@@ -430,7 +439,8 @@ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
if (!th)
goto flush;
- tcp4_check_fraglist_gro(head, skb, th);
+ if (unlikely(skb->dev->features & NETIF_F_GRO_FRAGLIST))
+ tcp4_check_fraglist_gro(head, skb, th);
return tcp_gro_receive(head, skb, th);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 6f4dca4a4de9..d960e3de7d50 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3849,9 +3849,9 @@ void tcp_send_fin(struct sock *sk)
* was unread data in the receive queue. This behavior is recommended
* by RFC 2525, section 2.17. -DaveM
*/
-void tcp_send_active_reset(struct sock *sk, gfp_t priority,
- enum sk_rst_reason reason)
+void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason)
{
+ gfp_t priority = sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN);
struct sk_buff *skb;
TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
@@ -4092,7 +4092,7 @@ static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst)
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner);
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
- icsk->icsk_ca_ops = ca;
+ WRITE_ONCE(icsk->icsk_ca_ops, ca);
}
rcu_read_unlock();
}
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 1038e7ba9c2e..e56eae4bc341 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -126,7 +126,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
(!tp->snd_wnd && !tp->packets_out))
do_reset = true;
if (do_reset)
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
tcp_done(sk);
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);
@@ -809,7 +809,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
goto out;
}
}
- tcp_send_active_reset(sk, GFP_ATOMIC, SK_RST_REASON_TCP_STATE);
+ tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
goto death;
}
@@ -836,7 +836,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
icsk->icsk_probes_out > 0) ||
(user_timeout == 0 &&
icsk->icsk_probes_out >= keepalive_probes(tp))) {
- tcp_send_active_reset(sk, GFP_ATOMIC,
+ tcp_send_active_reset(sk,
SK_RST_REASON_TCP_KEEPALIVE_TIMEOUT);
tcp_write_err(sk);
goto out;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index af9603217444..bb8cfc62cb00 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -900,6 +900,15 @@ out:
return sk;
}
+static void udp_err_update_exception(struct net *net, struct sk_buff *skb,
+ int type, int code, u32 info)
+{
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+ ipv4_update_pmtu(skb, net, info, 0, IPPROTO_UDP);
+ else if (type == ICMP_REDIRECT)
+ ipv4_redirect(skb, net, 0, IPPROTO_UDP);
+}
+
/*
* This routine is called by the ICMP module when it gets some
* sort of error condition. If err < 0 then the socket should
@@ -923,6 +932,8 @@ int udp_err(struct sk_buff *skb, u32 info)
int harderr;
int err;
+ udp_err_update_exception(net, skb, type, code, info);
+
uh = (struct udphdr *)(skb->data + (iph->ihl << 2));
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
iph->saddr, uh->source, skb->dev->ifindex,
@@ -2166,10 +2177,10 @@ int __udp_disconnect(struct sock *sk, int flags)
*/
sk->sk_state = TCP_CLOSE;
- inet->inet_daddr = 0;
+ WRITE_ONCE(inet->inet_daddr, 0);
inet->inet_dport = 0;
sock_rps_reset_rxhash(sk);
- sk->sk_bound_dev_if = 0;
+ WRITE_ONCE(sk->sk_bound_dev_if, 0);
if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) {
inet_reset_saddr(sk);
if (sk->sk_prot->rehash &&
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 51941ad656a3..09a4552f7f08 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -445,7 +445,7 @@ looped_back:
hdr->segments_left--;
addr = hdr->segments + hdr->segments_left;
- skb_push(skb, sizeof(struct ipv6hdr));
+ skb_push(skb, -skb_network_offset(skb));
if (skb->ip_summed == CHECKSUM_COMPLETE)
seg6_update_csum(skb);
@@ -469,7 +469,7 @@ looped_back:
}
ipv6_hdr(skb)->hop_limit--;
- skb_pull(skb, sizeof(struct ipv6hdr));
+ skb_pull(skb, skb_transport_offset(skb));
goto looped_back;
}
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 69c51f1a5bf0..8ebda0b6a78b 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -878,6 +878,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ struct ip_tunnel_info *tun_info = NULL;
struct ip6_tnl *t = netdev_priv(dev);
__be16 payload_protocol;
int ret;
@@ -888,6 +889,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
goto tx_err;
+ if (t->parms.collect_md)
+ tun_info = skb_tunnel_info_txcheck(skb);
+
payload_protocol = skb_protocol(skb, true);
switch (payload_protocol) {
case htons(ETH_P_IP):
@@ -907,7 +911,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
tx_err:
- if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
+ if (!IS_ERR(tun_info))
DEV_STATS_INC(dev, tx_errors);
DEV_STATS_INC(dev, tx_dropped);
kfree_skb(skb);
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index aaba4c2aae23..ecef55f26189 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -240,7 +240,8 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
return err;
}
- mc_lst->next = np->ipv6_mc_list;
+ rcu_assign_pointer(mc_lst->next,
+ sock_dereference(np->ipv6_mc_list, sk));
rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
return 0;
@@ -300,7 +301,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
lnk = &mc_lst->next) {
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
ipv6_addr_equal(&mc_lst->addr, addr)) {
- *lnk = mc_lst->next;
+ rcu_assign_pointer(*lnk,
+ sock_dereference(mc_lst->next, sk));
__ipv6_sock_mc_drop(sk, mc_lst);
return 0;
}
@@ -333,7 +335,8 @@ void __ipv6_sock_mc_close(struct sock *sk)
struct ipv6_mc_socklist *mc_lst;
while ((mc_lst = sock_dereference(np->ipv6_mc_list, sk)) != NULL) {
- np->ipv6_mc_list = mc_lst->next;
+ rcu_assign_pointer(np->ipv6_mc_list,
+ sock_dereference(mc_lst->next, sk));
__ipv6_sock_mc_drop(sk, mc_lst);
}
}
@@ -355,12 +358,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
{
struct ipv6_pinfo *inet6 = inet6_sk(sk);
struct in6_addr *source, *group;
+ struct ip6_sf_socklist *newpsl, *psl;
struct net *net = sock_net(sk);
struct ipv6_mc_socklist *pmc;
- struct ip6_sf_socklist *psl;
struct inet6_dev *idev;
int leavegroup = 0;
- int i, j, rv;
+ int i, j;
int err;
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
@@ -409,13 +412,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
if (!add) {
if (!psl)
goto done; /* err = -EADDRNOTAVAIL */
- rv = !0;
for (i = 0; i < psl->sl_count; i++) {
- rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
- if (rv == 0)
+ if (ipv6_addr_equal(&psl->sl_addr[i], source))
break;
}
- if (rv) /* source not found */
+ if (i == psl->sl_count) /* source not found */
goto done; /* err = -EADDRNOTAVAIL */
/* special case - (INCLUDE, empty) == LEAVE_GROUP */
@@ -424,58 +425,74 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
goto done;
}
+ atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
+ &sk->sk_omem_alloc);
+
+ if (psl->sl_count == 1) {
+ newpsl = NULL;
+ } else {
+ newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr,
+ psl->sl_count - 1),
+ GFP_KERNEL);
+ if (!newpsl) {
+ atomic_add(struct_size(psl, sl_addr, psl->sl_max),
+ &sk->sk_omem_alloc);
+ err = -ENOBUFS;
+ goto done;
+ }
+ newpsl->sl_max = psl->sl_count - 1;
+ newpsl->sl_count = psl->sl_count - 1;
+ for (j = 0; j < i; j++)
+ newpsl->sl_addr[j] = psl->sl_addr[j];
+ for (j = i + 1; j < psl->sl_count; j++)
+ newpsl->sl_addr[j - 1] = psl->sl_addr[j];
+ }
+
/* update the interface filter */
ip6_mc_del_src(idev, group, omode, 1, source, 1);
- for (j = i+1; j < psl->sl_count; j++)
- psl->sl_addr[j-1] = psl->sl_addr[j];
- psl->sl_count--;
+ rcu_assign_pointer(pmc->sflist, newpsl);
+ kfree_rcu(psl, rcu);
err = 0;
goto done;
}
/* else, add a new source to the filter */
- if (psl && psl->sl_count >= sysctl_mld_max_msf) {
+ if (psl && psl->sl_count >= READ_ONCE(sysctl_mld_max_msf)) {
err = -ENOBUFS;
goto done;
}
- if (!psl || psl->sl_count == psl->sl_max) {
- struct ip6_sf_socklist *newpsl;
- int count = IP6_SFBLOCK;
-
- if (psl)
- count += psl->sl_max;
- newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count),
- GFP_KERNEL);
- if (!newpsl) {
- err = -ENOBUFS;
- goto done;
- }
- newpsl->sl_max = count;
- newpsl->sl_count = count - IP6_SFBLOCK;
- if (psl) {
- for (i = 0; i < psl->sl_count; i++)
- newpsl->sl_addr[i] = psl->sl_addr[i];
- atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
- &sk->sk_omem_alloc);
+ if (psl) {
+ for (i = 0; i < psl->sl_count; i++) {
+ if (ipv6_addr_equal(&psl->sl_addr[i], source))
+ goto done; /* err = -EADDRNOTAVAIL */
}
- rcu_assign_pointer(pmc->sflist, newpsl);
- kfree_rcu(psl, rcu);
- psl = newpsl;
}
- rv = 1; /* > 0 for insert logic below if sl_count is 0 */
- for (i = 0; i < psl->sl_count; i++) {
- rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
- if (rv == 0) /* There is an error in the address. */
- goto done;
+
+ i = psl ? psl->sl_count + 1 : 1;
+ newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, i),
+ GFP_KERNEL);
+ if (!newpsl) {
+ err = -ENOBUFS;
+ goto done;
}
- for (j = psl->sl_count-1; j >= i; j--)
- psl->sl_addr[j+1] = psl->sl_addr[j];
- psl->sl_addr[i] = *source;
- psl->sl_count++;
- err = 0;
+ newpsl->sl_max = i;
+ newpsl->sl_count = i;
+ if (psl) {
+ for (j = 0; j < psl->sl_count; j++)
+ newpsl->sl_addr[j] = psl->sl_addr[j];
+ }
+ newpsl->sl_addr[i - 1] = *source;
+
/* update the interface list */
ip6_mc_add_src(idev, group, omode, 1, source, 1);
+
+ if (psl)
+ atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
+ &sk->sk_omem_alloc);
+ rcu_assign_pointer(pmc->sflist, newpsl);
+ kfree_rcu(psl, rcu);
+ err = 0;
done:
mutex_unlock(&idev->mc_lock);
in6_dev_put(idev);
@@ -784,9 +801,11 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
if (!pmc)
return;
if (pmc_prev)
- rcu_assign_pointer(pmc_prev->next, pmc->next);
+ rcu_assign_pointer(pmc_prev->next,
+ mc_dereference(pmc->next, idev));
else
- rcu_assign_pointer(idev->mc_tomb, pmc->next);
+ rcu_assign_pointer(idev->mc_tomb,
+ mc_dereference(pmc->next, idev));
im->idev = pmc->idev;
if (im->mca_sfmode == MCAST_INCLUDE) {
@@ -966,7 +985,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
return -ENOMEM;
}
- rcu_assign_pointer(mc->next, idev->mc_list);
+ rcu_assign_pointer(mc->next, mc_dereference(idev->mc_list, idev));
rcu_assign_pointer(idev->mc_list, mc);
mld_del_delrec(idev, mc);
@@ -1000,7 +1019,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
WRITE_ONCE(ma->mca_users, new_users);
if (new_users == 0) {
- *map = ma->next;
+ rcu_assign_pointer(*map,
+ mc_dereference(ma->next, idev));
igmp6_group_dropped(ma);
inet6_ifmcaddr_notify(idev->dev, ma,
@@ -2351,14 +2371,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) &&
!mld_in_v1_mode(idev)) {
- psf->sf_crcount = idev->mc_qrv;
- rcu_assign_pointer(psf->sf_next,
- mc_dereference(pmc->mca_tomb, idev));
- rcu_assign_pointer(pmc->mca_tomb, psf);
- rv = 1;
- } else {
- kfree_rcu(psf, rcu);
+ struct ip6_sf_list *dpsf = kmalloc_obj(*dpsf);
+
+ if (dpsf) {
+ *dpsf = *psf;
+ dpsf->sf_crcount = idev->mc_qrv;
+ rcu_assign_pointer(dpsf->sf_next,
+ mc_dereference(pmc->mca_tomb, idev));
+ rcu_assign_pointer(pmc->mca_tomb, dpsf);
+ rv = 1;
+ }
}
+ kfree_rcu(psf, rcu);
}
return rv;
}
@@ -2621,7 +2645,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
static void igmp6_join_group(struct ifmcaddr6 *ma)
{
- unsigned long delay;
+ unsigned long delay, interval;
mc_assert_locked(ma->idev);
@@ -2630,13 +2654,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
- delay = get_random_u32_below(unsolicited_report_interval(ma->idev));
+ interval = unsolicited_report_interval(ma->idev);
+ delay = interval;
if (cancel_delayed_work(&ma->mca_work)) {
refcount_dec(&ma->mca_refcnt);
delay = ma->mca_work.timer.expires - jiffies;
}
+ if (delay >= interval)
+ delay = get_random_u32_below(interval);
+
if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
refcount_inc(&ma->mca_refcnt);
WRITE_ONCE(ma->mca_flags, ma->mca_flags |
@@ -3001,7 +3029,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
unsigned int mca_flags = READ_ONCE(im->mca_flags);
- unsigned long expires = READ_ONCE(im->mca_work.timer.expires);
+ long delta = READ_ONCE(im->mca_work.timer.expires) - jiffies;
seq_printf(seq,
"%-4d %-15s %pi6 %5d %08X %ld\n",
@@ -3009,7 +3037,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
&im->mca_addr,
READ_ONCE(im->mca_users), mca_flags,
(mca_flags & MAF_TIMER_RUNNING) ?
- jiffies_to_clock_t(expires - jiffies) : 0);
+ jiffies_delta_to_clock_t(delta) : 0);
return 0;
}
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 6a40c5074543..9658939511e0 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -3255,7 +3255,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
- ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if,
+ ip6_redirect(skb, sock_net(sk), skb->dev->ifindex,
READ_ONCE(sk->sk_mark), sk_uid(sk));
}
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index f2a659cd6183..eec3778855eb 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -26,7 +26,18 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
p = tcp_gro_lookup(head, th);
if (p) {
- NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ /* flist GRO applies to consecutive non-GSO skbs */
+ if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ /* Fall back to the regular GRO path */
+ if (NAPI_GRO_CB(p)->count == 1)
+ NAPI_GRO_CB(p)->is_flist = 0;
+
+ NAPI_GRO_CB(skb)->is_flist = 0;
+
return;
}
@@ -36,7 +47,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
sk = __inet6_lookup_established(net, &hdr->saddr, th->source,
&hdr->daddr, ntohs(th->dest),
iif, sdif);
- NAPI_GRO_CB(skb)->is_flist = !sk;
+ NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
if (sk)
sock_gen_put(sk);
#endif /* IS_ENABLED(CONFIG_IPV6) */
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index fd875908ac0c..93478d1ad576 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -690,6 +690,17 @@ out:
return sk;
}
+static void udpv6_err_update_exception(struct net *net, struct sk_buff *skb,
+ u8 type, __be32 info)
+{
+ if (type == ICMPV6_PKT_TOOBIG)
+ ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
+ else if (type == NDISC_REDIRECT)
+ ip6_redirect(skb, net, skb->dev->ifindex, 0,
+ sock_net_uid(net, NULL));
+}
+
static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
u8 type, u8 code, int offset, __be32 info)
{
@@ -703,6 +714,8 @@ static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
int harderr;
int err;
+ udpv6_err_update_exception(net, skb, type, info);
+
daddr = seg6_get_daddr(skb, opt) ? : &hdr->daddr;
saddr = &hdr->saddr;
sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index 4e5cc9da6e06..db261ecd19af 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -210,12 +210,6 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
phs_hdr->flags = flags;
if (flags == AF_IUCV_FLAG_SYN)
phs_hdr->window = iucv->msglimit;
- else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
- confirm_recv = atomic_read(&iucv->msg_recv);
- phs_hdr->window = confirm_recv;
- if (confirm_recv)
- phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
- }
memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
@@ -250,13 +244,22 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
}
skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
+ /* Claim the receive credit here, not while building the header: every
+ * way this frame can be dropped has now been ruled out, so the window
+ * is zeroed only for as long as the transmit itself takes.
+ */
+ if (flags == AF_IUCV_FLAG_WIN || !flags) {
+ confirm_recv = atomic_xchg(&iucv->msg_recv, 0);
+ phs_hdr->window = confirm_recv;
+ if (confirm_recv)
+ phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
+ }
+
atomic_inc(&iucv->skbs_in_xmit);
err = dev_queue_xmit(skb);
if (net_xmit_eval(err)) {
atomic_dec(&iucv->skbs_in_xmit);
- } else {
- atomic_sub(confirm_recv, &iucv->msg_recv);
- WARN_ON(atomic_read(&iucv->msg_recv) < 0);
+ atomic_add(confirm_recv, &iucv->msg_recv);
}
return net_xmit_eval(err);
@@ -1241,6 +1244,7 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
struct iucv_sock *iucv = iucv_sk(sk);
unsigned int copied, rlen;
struct sk_buff *skb, *rskb, *cskb;
+ bool send_win = false;
int err = 0;
u32 offset;
@@ -1331,16 +1335,20 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
if (skb_queue_empty(&iucv->backlog_skb_q)) {
if (!list_empty(&iucv->message_q.list))
iucv_process_message_q(sk);
- if (atomic_read(&iucv->msg_recv) >=
- iucv->msglimit / 2) {
- err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
- if (err) {
- sk->sk_state = IUCV_DISCONN;
- sk->sk_state_change(sk);
- }
- }
+ if (iucv->transport == AF_IUCV_TRANS_HIPER &&
+ atomic_read(&iucv->msg_recv) >=
+ iucv->msglimit / 2)
+ send_win = true;
}
spin_unlock_bh(&iucv->message_q.lock);
+
+ if (send_win) {
+ err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
+ if (err) {
+ sk->sk_state = IUCV_DISCONN;
+ sk->sk_state_change(sk);
+ }
+ }
}
done:
diff --git a/net/mac802154/ieee802154_i.h b/net/mac802154/ieee802154_i.h
index 8f2bff268392..c53aa293a222 100644
--- a/net/mac802154/ieee802154_i.h
+++ b/net/mac802154/ieee802154_i.h
@@ -76,7 +76,12 @@ struct ieee802154_local {
struct work_struct rx_mac_cmd_work;
/* Association */
- struct ieee802154_pan_device *assoc_dev;
+ /* assoc_lock protects assoc_dev_extended_addr, assoc_addr,
+ * assoc_status, the assoc_done reinit/complete pairing and the
+ * IEEE802154_IS_ASSOCIATING bit in @ongoing.
+ */
+ spinlock_t assoc_lock;
+ __le64 assoc_dev_extended_addr;
struct completion assoc_done;
__le16 assoc_addr;
u8 assoc_status;
diff --git a/net/mac802154/main.c b/net/mac802154/main.c
index ea1efef3572a..63e89bd586e3 100644
--- a/net/mac802154/main.c
+++ b/net/mac802154/main.c
@@ -104,6 +104,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
init_completion(&local->assoc_done);
+ spin_lock_init(&local->assoc_lock);
/* init supported flags with 802.15.4 default ranges */
phy->supported.max_minbe = 8;
diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c
index 005338f89b75..dd156c01ac49 100644
--- a/net/mac802154/scan.c
+++ b/net/mac802154/scan.c
@@ -536,7 +536,9 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
struct ieee802154_association_req_frame frame = {};
struct ieee802154_local *local = sdata->local;
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ __le16 resp_short_addr;
struct sk_buff *skb;
+ u8 resp_status;
int ret;
frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD;
@@ -578,9 +580,11 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
return ret;
}
- local->assoc_dev = coord;
+ spin_lock(&local->assoc_lock);
reinit_completion(&local->assoc_done);
+ local->assoc_dev_extended_addr = coord->extended_addr;
set_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
+ spin_unlock(&local->assoc_lock);
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb);
if (ret) {
@@ -599,25 +603,37 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
goto clear_assoc;
}
- if (local->assoc_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
- if (local->assoc_status == IEEE802154_PAN_AT_CAPACITY)
+ /* The association is complete: mac802154_process_association_resp()
+ * cleared the associating bit before waking us, so a second (e.g.
+ * malicious) ASSOC RESP can no longer pass the recheck and overwrite
+ * the result. Snapshot assoc_status/assoc_addr under the lock.
+ */
+ spin_lock(&local->assoc_lock);
+ resp_status = local->assoc_status;
+ resp_short_addr = local->assoc_addr;
+ spin_unlock(&local->assoc_lock);
+
+ if (resp_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
+ if (resp_status == IEEE802154_PAN_AT_CAPACITY)
ret = -ERANGE;
else
ret = -EPERM;
dev_warn(&sdata->dev->dev,
"Negative ASSOC RESP received from %8phC: %s\n", &ceaddr,
- local->assoc_status == IEEE802154_PAN_AT_CAPACITY ?
+ resp_status == IEEE802154_PAN_AT_CAPACITY ?
"PAN at capacity" : "access denied");
- goto clear_assoc;
+ return ret;
}
- ret = 0;
- *short_addr = local->assoc_addr;
+ *short_addr = resp_short_addr;
+
+ return 0;
clear_assoc:
+ spin_lock(&local->assoc_lock);
clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
- local->assoc_dev = NULL;
+ spin_unlock(&local->assoc_lock);
return ret;
}
@@ -639,19 +655,28 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata,
dest->mode != IEEE802154_EXTENDED_ADDRESSING))
return -EINVAL;
- if (unlikely(dest->extended_addr != wpan_dev->extended_addr ||
- src->extended_addr != local->assoc_dev->extended_addr))
+ spin_lock(&local->assoc_lock);
+ if (unlikely(!test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing) ||
+ dest->extended_addr != wpan_dev->extended_addr ||
+ src->extended_addr != local->assoc_dev_extended_addr)) {
+ spin_unlock(&local->assoc_lock);
return -ENODEV;
+ }
memcpy(&resp_pl, skb->data, sizeof(resp_pl));
local->assoc_addr = resp_pl.short_addr;
local->assoc_status = resp_pl.status;
+ /* Clear the associating bit before waking the waiter: once the result
+ * is saved, any subsequent (e.g. malicious) ASSOC RESP must fail the
+ * test_bit() recheck above and can no longer overwrite the result.
+ */
+ clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
+ complete(&local->assoc_done);
+ spin_unlock(&local->assoc_lock);
dev_dbg(&skb->dev->dev,
"ASSOC RESP 0x%x received from %8phC, getting short address %04x\n",
- local->assoc_status, &deaddr, local->assoc_addr);
-
- complete(&local->assoc_done);
+ resp_pl.status, &deaddr, resp_pl.short_addr);
return 0;
}
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index b474d03620a7..e1f08f71cdb1 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3109,8 +3109,7 @@ static void mptcp_do_fastclose(struct sock *sk)
*/
inet_csk(ssk)->icsk_ack.rcv_mss = TCP_MIN_MSS;
- tcp_send_active_reset(ssk, ssk->sk_allocation,
- SK_RST_REASON_TCP_ABORT_ON_CLOSE);
+ tcp_send_active_reset(ssk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
unlock:
release_sock(ssk);
}
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 06a107d4e839..87ccb84e9927 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -690,7 +690,7 @@ mptcp_send_active_reset_reason(struct sock *sk)
enum sk_rst_reason reason;
reason = sk_rst_convert_mptcp_reason(subflow->reset_reason);
- tcp_send_active_reset(sk, GFP_ATOMIC, reason);
+ tcp_send_active_reset(sk, reason);
}
/* Made the fwd mem carried by the given skb available to the msk,
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index b22cda322136..76bde7906d49 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2675,7 +2675,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
int size_max, void **data)
{
union tpacket_uhdr ph;
- int tp_len, off;
+ u32 tp_len;
+ int off;
ph.raw = frame;
@@ -2695,7 +2696,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
break;
}
if (unlikely(tp_len > size_max)) {
- pr_err("packet size is too long (%d > %d)\n", tp_len, size_max);
+ pr_err("packet size is too long (%u > %d)\n", tp_len, size_max);
return -EMSGSIZE;
}
diff --git a/net/qrtr/af_qrtr.c b/net/qrtr/af_qrtr.c
index a30fa56e6aa3..78347c937af7 100644
--- a/net/qrtr/af_qrtr.c
+++ b/net/qrtr/af_qrtr.c
@@ -9,6 +9,7 @@
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/spinlock.h>
#include <linux/wait.h>
+#include <linux/workqueue.h>
#include <net/sock.h>
@@ -120,8 +121,10 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports);
* @nid: node id
* @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port
* @qrtr_tx_lock: lock for qrtr_tx_flow inserts
+ * @hello_sent: hello packet send successful
* @rx_queue: receive queue
* @item: list item for broadcast list
+ * @say_hello: delayed work for sending hello packet
*/
struct qrtr_node {
struct mutex ep_lock;
@@ -132,8 +135,11 @@ struct qrtr_node {
struct xarray qrtr_tx_flow;
struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */
+ bool hello_sent;
+
struct sk_buff_head rx_queue;
struct list_head item;
+ struct delayed_work say_hello;
};
/**
@@ -187,6 +193,8 @@ static void __qrtr_node_release(struct kref *kref)
list_del(&node->item);
mutex_unlock(&qrtr_node_lock);
+ cancel_delayed_work_sync(&node->say_hello);
+
skb_queue_purge(&node->rx_queue);
/* Free tx flow counters */
@@ -341,6 +349,14 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
size_t len = skb->len;
int rc, confirm_rx;
+ mutex_lock(&node->ep_lock);
+ if (!node->hello_sent && type != QRTR_TYPE_HELLO) {
+ mutex_unlock(&node->ep_lock);
+ kfree_skb(skb);
+ return -EAGAIN;
+ }
+ mutex_unlock(&node->ep_lock);
+
confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type);
if (confirm_rx < 0) {
kfree_skb(skb);
@@ -353,7 +369,7 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
hdr->src_node_id = cpu_to_le32(from->sq_node);
hdr->src_port_id = cpu_to_le32(from->sq_port);
if (to->sq_port == QRTR_PORT_CTRL) {
- hdr->dst_node_id = cpu_to_le32(node->nid);
+ hdr->dst_node_id = cpu_to_le32(READ_ONCE(node->nid));
hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL);
} else {
hdr->dst_node_id = cpu_to_le32(to->sq_node);
@@ -372,6 +388,8 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
rc = node->ep->xmit(node->ep, skb);
else
kfree_skb(skb);
+ if (!rc && type == QRTR_TYPE_HELLO)
+ node->hello_sent = true;
mutex_unlock(&node->ep_lock);
}
/* Need to ensure that a subsequent message carries the otherwise lost
@@ -379,6 +397,9 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
if (rc && confirm_rx)
qrtr_tx_flow_failed(node, to->sq_node, to->sq_port);
+ if (rc == -EAGAIN && type == QRTR_TYPE_HELLO)
+ schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
+
return rc;
}
@@ -416,7 +437,7 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid)
spin_lock_irqsave(&qrtr_nodes_lock, flags);
radix_tree_insert(&qrtr_nodes, nid, node);
if (node->nid == QRTR_EP_NID_AUTO)
- node->nid = nid;
+ WRITE_ONCE(node->nid, nid);
spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
}
@@ -570,6 +591,38 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt,
return skb;
}
+static void qrtr_hello_work(struct work_struct *work)
+{
+ struct sockaddr_qrtr from = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
+ struct sockaddr_qrtr to = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
+ struct qrtr_ctrl_pkt *pkt;
+ struct qrtr_node *node;
+ struct qrtr_sock *ctrl;
+ struct sk_buff *skb;
+
+ node = container_of(to_delayed_work(work), struct qrtr_node, say_hello);
+
+ /* NS must be bound before we can send; retry with backoff if not ready */
+ ctrl = qrtr_port_lookup(QRTR_PORT_CTRL);
+ if (!ctrl) {
+ schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
+ return;
+ }
+
+ skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL);
+ if (!skb) {
+ qrtr_port_put(ctrl);
+ schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
+ return;
+ }
+
+ pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO);
+ from.sq_node = qrtr_local_nid;
+ to.sq_node = node->nid;
+ qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to);
+ qrtr_port_put(ctrl);
+}
+
/**
* qrtr_endpoint_register() - register a new endpoint
* @ep: endpoint to register
@@ -595,6 +648,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
node->nid = QRTR_EP_NID_AUTO;
node->ep = ep;
+ node->hello_sent = false;
+ INIT_DELAYED_WORK(&node->say_hello, qrtr_hello_work);
+
xa_init(&node->qrtr_tx_flow);
mutex_init(&node->qrtr_tx_lock);
@@ -605,6 +661,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
mutex_unlock(&qrtr_node_lock);
ep->node = node;
+ /* Initiate HELLO handshake from the core layer */
+ schedule_delayed_work(&node->say_hello, 0);
+
return 0;
}
EXPORT_SYMBOL_GPL(qrtr_endpoint_register);
@@ -879,6 +938,9 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
mutex_lock(&qrtr_node_lock);
list_for_each_entry(node, &qrtr_all_nodes, item) {
+ /* Skip nodes with no assigned node ID yet. */
+ if (READ_ONCE(node->nid) == QRTR_EP_NID_AUTO)
+ continue;
skbn = pskb_copy(skb, GFP_KERNEL);
if (!skbn)
break;
diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
index c5e7e01db249..bcb090ee79d4 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -212,6 +212,7 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
pr_err("failed to send lookup notification\n");
}
+/* Announce the list of servers registered on the local node */
static int announce_servers(struct sockaddr_qrtr *sq)
{
struct qrtr_server *srv;
@@ -326,38 +327,8 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
return 0;
}
-static int say_hello(struct sockaddr_qrtr *dest)
-{
- struct qrtr_ctrl_pkt pkt;
- struct msghdr msg = { };
- struct kvec iv;
- int ret;
-
- iv.iov_base = &pkt;
- iv.iov_len = sizeof(pkt);
-
- memset(&pkt, 0, sizeof(pkt));
- pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO);
-
- msg.msg_name = (struct sockaddr *)dest;
- msg.msg_namelen = sizeof(*dest);
-
- ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
- if (ret < 0)
- pr_err("failed to send hello msg\n");
-
- return ret;
-}
-
-/* Announce the list of servers registered on the local node */
static int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
{
- int ret;
-
- ret = say_hello(sq);
- if (ret < 0)
- return ret;
-
return announce_servers(sq);
}
@@ -774,10 +745,6 @@ int qrtr_ns_init(void)
qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
- ret = say_hello(&qrtr_ns.bcast_sq);
- if (ret < 0)
- goto err_wq;
-
/* As the qrtr ns socket owner and creator is the same module, we have
* to decrease the qrtr module reference count to guarantee that it
* remains zero after the ns socket is created, otherwise, executing
diff --git a/net/rds/connection.c b/net/rds/connection.c
index 7c8ab8e973e1..b6c4beb50eaf 100644
--- a/net/rds/connection.c
+++ b/net/rds/connection.c
@@ -106,10 +106,12 @@ static struct rds_connection *rds_conn_lookup(struct net *net,
}
/*
- * This is called by transports as they're bringing down a connection.
- * It clears partial message state so that the transport can start sending
- * and receiving over this connection again in the future. It is up to
- * the transport to have serialized this call with its send and recv.
+ * This is called by rds_conn_shutdown() once the transport has brought
+ * a path down. It clears partial message state so that the transport
+ * can start sending and receiving over this path again in the future.
+ * The caller owns RDS_IN_XMIT and RDS_RECV_REFILL across this call,
+ * which is what serializes it against the send and receive-refill
+ * paths.
*/
static void rds_conn_path_reset(struct rds_conn_path *cp)
{
@@ -120,7 +122,16 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)
rds_stats_inc(s_conn_reset);
rds_send_path_reset(cp);
- cp->cp_flags = 0;
+
+ /* Clear the bits the reset is responsible for individually: a
+ * blanket cp_flags = 0 is a plain store that can clobber a
+ * concurrent atomic read-modify-write on the same word.
+ * RDS_IN_XMIT and RDS_RECV_REFILL are held as locks by the
+ * caller, rds_conn_shutdown(), which releases them once the
+ * teardown is complete.
+ */
+ clear_bit(RDS_LL_SEND_FULL, &cp->cp_flags);
+ clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
/* Do not clear next_rx_seq here, else we cannot distinguish
* retransmitted packets from new packets, and will hand all
@@ -406,28 +417,70 @@ void rds_conn_shutdown(struct rds_conn_path *cp)
}
mutex_unlock(&cp->cp_cm_lock);
+ /* Quiesce the transmit and receive-refill paths by
+ * acquiring their bit locks, not merely waiting for
+ * them to be released: with a plain wait, either path
+ * can re-take its lock the instant after we sample it
+ * clear and then run concurrently with the transport
+ * shutdown and the path reset below. Holding both
+ * locks across the teardown makes that structurally
+ * impossible.
+ */
wait_event(cp->cp_waitq,
- !test_bit(RDS_IN_XMIT, &cp->cp_flags));
+ !test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
wait_event(cp->cp_waitq,
- !test_bit(RDS_RECV_REFILL, &cp->cp_flags));
+ !test_and_set_bit(RDS_RECV_REFILL, &cp->cp_flags));
conn->c_trans->conn_path_shutdown(cp);
rds_conn_path_reset(cp);
+ /* Release the two locks and wake any waiter (e.g.
+ * rds_tcp_reset_callbacks()) that blocked on them while
+ * we held them. The unlock orders the transport's ring
+ * re-initialization and the path reset above before
+ * either bit is seen clear. rds_conn_path_reset() leaves
+ * both bits alone: ownership ends here, not inside the
+ * reset.
+ */
+ clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
+ clear_bit_unlock(RDS_RECV_REFILL, &cp->cp_flags);
+ wake_up_all(&cp->cp_waitq);
+
if (!rds_conn_path_transition(cp, RDS_CONN_DISCONNECTING,
- RDS_CONN_DOWN) &&
- !rds_conn_path_transition(cp, RDS_CONN_ERROR,
RDS_CONN_DOWN)) {
- /* This can happen - eg when we're in the middle of tearing
- * down the connection, and someone unloads the rds module.
- * Quite reproducible with loopback connections.
- * Mostly harmless.
+ /* The path was dropped again while we tore it
+ * down: by a socket state-change callback in
+ * irq context on receipt of a FIN, or by an
+ * accept that claimed the path just before a
+ * drop put it back to RDS_CONN_ERROR and then
+ * installed a fresh socket on it. Unless a
+ * pending destroy suppressed it, the drop also
+ * queued another shutdown pass, and that pass
+ * must run, because it is what tears down
+ * whatever attached to the path after the
+ * transport shutdown above sampled its state.
+ * Consuming the RDS_CONN_ERROR here would turn
+ * that pass into a no-op: leave the state
+ * alone, and let the pass finish the job.
*
- * Note that this also happens with rds-tcp because
- * we could have triggered rds_conn_path_drop in irq
- * mode from rds_tcp_state change on the receipt of
- * a FIN, thus we need to recheck for RDS_CONN_ERROR
- * here.
+ * Quiesce the reconnect timer before bailing
+ * out, though. When a pending destroy did
+ * suppress the queue, no later pass runs, and
+ * rds_conn_path_destroy() is about to flush
+ * cp_down_w and free the path: it must not
+ * find cp_conn_w still armed. A successor
+ * pass, when there is one, re-arms the
+ * reconnect from its own tail.
+ */
+ cancel_delayed_work_sync(&cp->cp_conn_w);
+ clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
+
+ if (rds_conn_path_state(cp) == RDS_CONN_ERROR)
+ return;
+ /* No current cp_state writer leaves a
+ * DISCONNECTING path in any state but
+ * RDS_CONN_ERROR; report loudly if one ever
+ * does.
*/
rds_conn_path_error(cp, "%s: failed to transition "
"to state DOWN, current state "
diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c
index 357128d34a54..bd6cb3ffaa57 100644
--- a/net/rds/ib_recv.c
+++ b/net/rds/ib_recv.c
@@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn)
static void release_refill(struct rds_connection *conn)
{
- clear_bit(RDS_RECV_REFILL, &conn->c_flags);
- smp_mb__after_atomic();
+ clear_bit_unlock(RDS_RECV_REFILL, &conn->c_flags);
/* We don't use wait_on_bit()/wake_up_bit() because our waking is in a
* hot path and finding waiters is very rare. We don't want to walk
* the system-wide hashed waitqueue buckets in the fast path only to
* almost never find waiters.
*/
- if (waitqueue_active(&conn->c_waitq))
+ if (wq_has_sleeper(&conn->c_waitq))
wake_up_all(&conn->c_waitq);
}
@@ -392,7 +391,9 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)
/* the goal here is to just make sure that someone, somewhere
* is posting buffers. If we can't get the refill lock,
- * let them do their thing
+ * let them do their thing. The holder may also be
+ * rds_conn_shutdown() tearing the path down, in which case
+ * there is nothing to post.
*/
if (!acquire_refill(conn))
return;
diff --git a/net/rds/send.c b/net/rds/send.c
index 15a1b97f13e7..1afa981e5c06 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp)
* hot path and finding waiters is very rare. We don't want to walk
* the system-wide hashed waitqueue buckets in the fast path only to
* almost never find waiters.
+ *
+ * wq_has_sleeper() supplies the full barrier that orders the wait
+ * queue read after the bit clear; clear_bit_unlock() alone is only
+ * a release and would let this check read a stale empty queue,
+ * losing the wake-up.
*/
- if (waitqueue_active(&cp->cp_waitq))
+ if (wq_has_sleeper(&cp->cp_waitq))
wake_up_all(&cp->cp_waitq);
}
@@ -239,8 +244,11 @@ restart:
WRITE_ONCE(cp->cp_send_gen, send_gen);
/*
- * rds_conn_shutdown() sets the conn state and then tests RDS_IN_XMIT,
- * we do the opposite to avoid races.
+ * rds_conn_shutdown() sets the conn state and then acquires
+ * RDS_IN_XMIT; we take the lock first and then check the state.
+ * Ownership is decided by the atomic RMW on the cp_flags word:
+ * if the teardown won the bit we back off here, and if we won
+ * it the teardown waits until we release it.
*/
if (!rds_conn_path_up(cp)) {
release_in_xmit(cp);
diff --git a/net/rds/tcp.c b/net/rds/tcp.c
index b263634ac750..774a71f88d37 100644
--- a/net/rds/tcp.c
+++ b/net/rds/tcp.c
@@ -115,46 +115,90 @@ void rds_tcp_restore_callbacks(struct socket *sock,
}
/*
- * rds_tcp_reset_callbacks() switches the to the new sock and
- * returns the existing tc->t_sock.
+ * rds_tcp_reset_callbacks() switches a path to a new socket and
+ * releases the old one it finds in tc->t_sock, resolving a duelling
+ * SYN.
*
- * The only functions that set tc->t_sock are rds_tcp_set_callbacks
- * and rds_tcp_reset_callbacks. Send and receive trust that
- * it is set. The absence of RDS_CONN_UP bit protects those paths
- * from being called while it isn't set.
+ * tc->t_sock is set by rds_tcp_set_callbacks() and cleared by
+ * rds_tcp_restore_callbacks(). Four paths write it: the active
+ * connect in rds_tcp_conn_path_connect(), which sets it and clears it
+ * again on failure; the accept path in rds_tcp_accept_one(), which
+ * sets it for a path with no socket yet; the teardown in
+ * rds_tcp_conn_path_shutdown(), which clears it; and the swap done
+ * here, which does both. The connect and accept paths are serialized
+ * against each other by t_conn_path_lock. Send and receive trust
+ * that it is set: the absence of RDS_CONN_UP protects those paths
+ * from being called while it isn't, and the swap done here runs under
+ * RDS_IN_XMIT so that it cannot interleave with a sender already
+ * inside rds_send_xmit().
*/
void rds_tcp_reset_callbacks(struct socket *sock,
struct rds_conn_path *cp)
{
struct rds_tcp_connection *tc = cp->cp_transport_data;
- struct socket *osock = tc->t_sock;
-
- if (!osock)
- goto newsock;
+ struct socket *osock;
/* Need to resolve a duelling SYN between peers.
* We have an outstanding SYN to this peer, which may
* potentially have transitioned to the RDS_CONN_UP state,
* so we must quiesce any send threads before resetting
- * cp_transport_data. We quiesce these threads by setting
- * cp_state to something other than RDS_CONN_UP, and then
- * waiting for any existing threads in rds_send_xmit to
- * complete release_in_xmit(). (Subsequent threads entering
- * rds_send_xmit() will bail on !rds_conn_up().
+ * cp_transport_data. Setting cp_state to something other
+ * than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT
+ * excludes any thread already inside rds_send_xmit() - or a
+ * teardown in rds_conn_shutdown(), which holds the same lock
+ * for the duration of the transport shutdown - for the whole
+ * socket swap and the rds_send_path_reset() below.
*
- * However an incoming syn-ack at this point would end up
- * marking the conn as RDS_CONN_UP, and would again permit
- * rds_send_xmi() threads through, so ideally we would
- * synchronize on RDS_CONN_UP after lock_sock(), but cannot
- * do that: waiting on !RDS_IN_XMIT after lock_sock() may
- * end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT
- * would not get set. As a result, we set c_state to
- * RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change
- * cannot mark rds_conn_path_up() in the window before lock_sock()
+ * An incoming syn-ack at this point would end up marking the
+ * conn as RDS_CONN_UP, and would again permit rds_send_xmit()
+ * threads through, so ideally we would synchronize on
+ * RDS_CONN_UP after lock_sock(), but cannot do that: acquiring
+ * RDS_IN_XMIT after lock_sock() may end up deadlocking with
+ * tcp_sendmsg(), which takes the socket lock while holding
+ * RDS_IN_XMIT. As a result, we set c_state to
+ * RDS_CONN_RESETTING, to ensure that rds_tcp_state_change
+ * cannot mark rds_conn_path_up() in the window before
+ * lock_sock().
+ *
+ * Only make that transition if the path is still connecting
+ * (or already resetting from an earlier duel). A path in any
+ * other state - typically RDS_CONN_DISCONNECTING or
+ * RDS_CONN_ERROR with a shutdown in flight - is dropped
+ * instead. That still replaces its state, with RDS_CONN_ERROR,
+ * and, unless a pending destroy is about to reap the whole
+ * connection anyway, queues one more shutdown pass. A shutdown
+ * already in flight leaves that RDS_CONN_ERROR alone when it
+ * finishes; the queued pass then completes the transition to
+ * RDS_CONN_DOWN and tears down anything that attached to the
+ * path in the meantime.
+ */
+ if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
+ RDS_CONN_RESETTING) &&
+ !rds_conn_path_transition(cp, RDS_CONN_RESETTING,
+ RDS_CONN_RESETTING))
+ rds_conn_path_drop(cp, 0);
+ wait_event(cp->cp_waitq,
+ !test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
+
+ /* Read t_sock only while owning RDS_IN_XMIT, never before the
+ * wait: the teardown in rds_conn_shutdown() releases the old
+ * socket and clears t_sock, so a pointer sampled earlier can
+ * be stale by the time we wake up. The teardown holds the
+ * same lock while it does so, so what we read here cannot
+ * change under us until we release it.
+ */
+ osock = tc->t_sock;
+ if (!osock)
+ goto newsock;
+
+ /* reset receive side state for rds_tcp_data_recv() for osock.
+ *
+ * The sync cancels while owning RDS_IN_XMIT rely on cp_wq
+ * being ordered: a teardown blocked on the bit occupies
+ * cp_wq's only execution slot, so cp_send_w and cp_recv_w are
+ * pending at most and the cancels never flush. Nothing here
+ * may flush or wait on cp_wq itself.
*/
- atomic_set(&cp->cp_state, RDS_CONN_RESETTING);
- wait_event(cp->cp_waitq, !test_bit(RDS_IN_XMIT, &cp->cp_flags));
- /* reset receive side state for rds_tcp_data_recv() for osock */
cancel_delayed_work_sync(&cp->cp_send_w);
cancel_delayed_work_sync(&cp->cp_recv_w);
lock_sock(osock->sk);
@@ -172,6 +216,9 @@ newsock:
lock_sock(sock->sk);
rds_tcp_set_callbacks(sock, cp);
release_sock(sock->sk);
+
+ clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
+ wake_up_all(&cp->cp_waitq);
}
/* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments
diff --git a/net/rds/tcp_listen.c b/net/rds/tcp_listen.c
index a3db9b057084..13fa60c1985b 100644
--- a/net/rds/tcp_listen.c
+++ b/net/rds/tcp_listen.c
@@ -295,7 +295,11 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
if (rs_tcp->t_sock) {
/* Duelling SYN has been handled in rds_tcp_accept_one() */
rds_tcp_reset_callbacks(new_sock, cp);
- /* rds_connect_path_complete() marks RDS_CONN_UP */
+ /* rds_connect_path_complete() marks RDS_CONN_UP, or,
+ * if a concurrent shutdown won the duel, drops the
+ * path again and the pass that drop queues reaps the
+ * socket installed above.
+ */
rds_connect_path_complete(cp, RDS_CONN_RESETTING);
} else {
rds_tcp_set_callbacks(new_sock, cp);
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index b4415d358c91..37eced84dfa5 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
+ nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
+ cookie_len /* TCA_ACT_COOKIE */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */
+ /* TCA_ACT_USED_HW_STATS */
+ + nla_total_size(sizeof(struct nla_bitfield32))
+ + nla_total_size(sizeof(u32)) /* TCA_ACT_IN_HW_COUNT */
+ nla_total_size(0) /* TCA_ACT_STATS nested */
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */
/* TCA_STATS_BASIC */
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
- /* TCA_STATS_PKT64 */
- + nla_total_size_64bit(sizeof(u64))
+ /* TCA_STATS_BASIC_HW */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_basic))
+ /* TCA_STATS_PKT64, emitted by both of the basic copies above */
+ + 2 * nla_total_size_64bit(sizeof(u64))
+ /* TCA_STATS_RATE_EST */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est))
+ /* TCA_STATS_RATE_EST64 */
+ + nla_total_size_64bit(sizeof(struct gnet_stats_rate_est64))
/* TCA_STATS_QUEUE */
+ nla_total_size_64bit(sizeof(struct gnet_stats_queue))
+ nla_total_size(0) /* TCA_ACT_OPTIONS nested */
@@ -1688,12 +1697,12 @@ out_nlmsg_trim:
static int
tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
- struct tc_action *actions[], int event,
+ struct tc_action *actions[], size_t attr_size, int event,
struct netlink_ext_ack *extack)
{
struct sk_buff *skb;
- skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
return -ENOBUFS;
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
@@ -1858,11 +1867,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])
static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
struct tc_action *action)
{
- size_t attr_size = tcf_action_fill_size(action);
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
[0] = action,
};
struct sk_buff *skb;
+ size_t attr_size;
+
+ attr_size = tcf_action_full_attrs_size(tcf_action_fill_size(action));
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
if (!skb)
@@ -1879,15 +1890,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
{
const struct tc_action_ops *ops = action->ops;
- struct sk_buff *skb;
+ struct sk_buff *skb = NULL;
int ret;
- if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
- skb = NULL;
- } else {
+ if (rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
skb = tcf_reoffload_del_notify_msg(net, action);
+ /* The action has already lost its hardware instance and is
+ * skip_sw, so it must be released whether or not the
+ * notification can be built. Drop the notification rather
+ * than leave an action behind that processes no packets.
+ */
if (IS_ERR(skb))
- return PTR_ERR(skb);
+ skb = NULL;
}
ret = tcf_idr_release_unsafe(action);
@@ -2044,7 +2058,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
attr_size = tcf_action_full_attrs_size(attr_size);
if (event == RTM_GETACTION)
- ret = tcf_get_notify(net, portid, n, actions, event, extack);
+ ret = tcf_get_notify(net, portid, n, actions, attr_size, event,
+ extack);
else { /* delete */
ret = tcf_del_notify(net, n, actions, portid, attr_size, extack);
if (ret)
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 0e275b58151c..1cefea571efd 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -1703,6 +1703,11 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
return -EINVAL;
}
nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
+
+ if (msk_depth && !nla_ok(nla_opt_msk, msk_depth)) {
+ NL_SET_ERR_MSG(extack, "A mask attribute is invalid");
+ return -EINVAL;
+ }
}
return 0;
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index ac6d0fa5a40e..a3e65c8cf29e 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)
refcount_set(&root_ht->refcnt, 1);
root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0);
+ if (root_ht->handle == 0) {
+ kfree(root_ht);
+ return -ENOMEM;
+ }
root_ht->prio = tp->prio;
root_ht->is_root = true;
idr_init(&root_ht->handle_idr);
@@ -695,21 +699,33 @@ out:
return ret;
}
-static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
+static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid, int *err)
{
u32 index = htid | 0x800;
u32 max = htid | 0xFFF;
+ *err = 0;
+
if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
index = htid + 1;
- if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
- GFP_KERNEL))
- index = max;
+ *err = idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
+ GFP_KERNEL);
+ if (*err)
+ return 0;
}
return index;
}
+static int u32_kid_extack(int err, struct netlink_ext_ack *extack)
+{
+ if (err == -ENOSPC)
+ NL_SET_ERR_MSG_MOD(extack, "Hash table node ID pool exhausted");
+ else
+ NL_SET_ERR_MSG_MOD(extack, "Failed to allocate node ID");
+ return err;
+}
+
static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
[TCA_U32_CLASSID] = { .type = NLA_U32 },
[TCA_U32_HASH] = { .type = NLA_U32 },
@@ -1079,7 +1095,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
* handle which is used to uniquely identify the match entry.
*/
if (!TC_U32_NODE(handle)) {
- handle = gen_new_kid(ht, htid);
+ handle = gen_new_kid(ht, htid, &err);
+ if (err)
+ return u32_kid_extack(err, extack);
} else {
handle = htid | TC_U32_NODE(handle);
err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
@@ -1091,7 +1109,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
/* The user did not give us a handle; lets just generate one
* from the table's pool of nodeids.
*/
- handle = gen_new_kid(ht, htid);
+ handle = gen_new_kid(ht, htid, &err);
+ if (err)
+ return u32_kid_extack(err, extack);
}
if (tb[TCA_U32_SEL] == NULL) {
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 5f988b3a8814..d666cec6b194 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -212,8 +212,10 @@ new_skb:
chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
skb_pull(chunk->skb, sizeof(*ch));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
-
- if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) {
+ if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
+ chunk->pdiscard = 1;
+ } else if (chunk->chunk_end + sizeof(*ch) <=
+ skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */
chunk->singleton = 0;
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 236e25abc7a4..84a4c97d0f75 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3215,6 +3215,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc,
*errp = param.p;
switch (param.p->type) {
case SCTP_PARAM_ERR_CAUSE:
+ if (length < sizeof(struct sctp_addip_param) +
+ sizeof(struct sctp_errhdr))
+ return false;
break;
case SCTP_PARAM_IPV4_ADDRESS:
if (length != sizeof(struct sctp_ipv4addr_param))
@@ -3448,20 +3451,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
case SCTP_PARAM_ERR_CAUSE:
length = sizeof(*asconf_ack_param);
err_param = (void *)asconf_ack_param + length;
- asconf_ack_len -= length;
- if (asconf_ack_len > 0)
- return err_param->cause;
- else
- return SCTP_ERROR_INV_PARAM;
- break;
+ return err_param->cause;
default:
return SCTP_ERROR_INV_PARAM;
}
}
length = ntohs(asconf_ack_param->param_hdr.length);
- asconf_ack_param = (void *)asconf_ack_param + length;
- asconf_ack_len -= length;
+ asconf_ack_param = (void *)asconf_ack_param + SCTP_PAD4(length);
+ asconf_ack_len -= SCTP_PAD4(length);
}
return err_code;
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 94716406d602..0d99b7e8c082 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1545,17 +1545,8 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
timeout = asoc->timeouts[cmd->obj.to];
BUG_ON(!timeout);
- /*
- * SCTP has a hard time with timer starts. Because we process
- * timer starts as side effects, it can be hard to tell if we
- * have already started a timer or not, which leads to BUG
- * halts when we call add_timer. So here, instead of just starting
- * a timer, if the timer is already started, and just mod
- * the timer with the shorter of the two expiration times
- */
- if (!timer_pending(timer))
+ if (!timer_reduce(timer, jiffies + timeout))
sctp_association_hold(asoc);
- timer_reduce(timer, jiffies + timeout);
break;
case SCTP_CMD_TIMER_RESTART:
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 49dfc098d89b..6427c69f8929 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
self_str, if_name, peer_str);
- strcpy(l->if_name, if_name);
+ strscpy(l->if_name, if_name);
l->addr = peer;
l->peer_caps = peer_caps;
l->net = net;
@@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,
snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name,
peer_str);
} else {
- strcpy(l->name, tipc_bclink_name);
+ strscpy(l->name, tipc_bclink_name);
}
trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
tipc_link_reset(l);
@@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
msg_set_dest_session(hdr, l->peer_session);
}
msg_set_max_pkt(hdr, l->advertised_mtu);
- strcpy(data, l->if_name);
+ memcpy(data, l->if_name, TIPC_MAX_IF_NAME);
msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
}
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 253c72d1366e..6fda36ab1766 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua,
struct tipc_socket_addr *sk, u32 key)
{
struct name_table *nt = tipc_name_table(net);
+ u32 max_user_pub = TIPC_MAX_PUBL - 1;
struct tipc_net *tn = tipc_net(net);
struct publication *p = NULL;
struct sk_buff *skb = NULL;
+ bool protocol_type = false;
u32 rc_dests;
+ if (ua->sr.type == TIPC_NODE_STATE || ua->sr.type == TIPC_LINK_STATE ||
+ ua->sr.type == TIPC_TOP_SRV)
+ protocol_type = true;
+
spin_lock_bh(&tn->nametbl_lock);
+ if (protocol_type)
+ goto insert;
- if (nt->local_publ_count >= TIPC_MAX_PUBL) {
- pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL);
+ /* Reserve one entry for node state service type because it has cluster
+ * scope and it is distributed in bulk. So, the maximum number of user's
+ * publications is (TIPC_MAX_PUBL - 1).
+ */
+ if (nt->local_publ_count >= max_user_pub) {
+ pr_warn("Bind failed, max limit %u reached\n", max_user_pub);
goto exit;
}
+insert:
p = tipc_nametbl_insert_publ(net, ua, sk, key);
if (p) {
- nt->local_publ_count++;
+ /* Not count node state, link state and topology server types
+ * so that maximum nt->local_publ_count does not prevent
+ * protocol service types from being inserted into the name
+ * table.
+ */
+ if (!protocol_type)
+ nt->local_publ_count++;
skb = tipc_named_publish(net, p);
}
rc_dests = nt->rc_dests;
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua,
p = tipc_nametbl_remove_publ(net, ua, sk, key);
if (p) {
- nt->local_publ_count--;
+ if (p->sr.type != TIPC_NODE_STATE &&
+ p->sr.type != TIPC_LINK_STATE &&
+ p->sr.type != TIPC_TOP_SRV)
+ nt->local_publ_count--;
skb = tipc_named_withdraw(net, p);
list_del_init(&p->binding_sock);
kfree_rcu(p, rcu);
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 683a136e53ef..bd91378b7540 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1333,7 +1333,9 @@ static void tipc_node_reset_links(struct tipc_node *n)
pr_warn("Resetting all links to %x\n", n->addr);
+ tipc_node_write_lock(n);
trace_tipc_node_reset_links(n, true, " ");
+ tipc_node_write_unlock_fast(n);
for (i = 0; i < MAX_BEARERS; i++) {
tipc_node_link_down(n, i, false);
}
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index a33b2a2d381d..f840498b58af 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -438,6 +438,38 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
}
EXPORT_SYMBOL_GPL(vsock_find_connected_socket);
+/**
+ * vsock_check_source - validate a packet source against a socket peer
+ * @vsk: socket receiving the packet
+ * @transport: transport receiving the packet
+ * @src: source address from the packet
+ *
+ * Return: true if the packet arrived on the socket's assigned transport and
+ * its source matches the stored peer. Loopback packets are generated
+ * internally and always use the local CID as their source, including
+ * connections using a valid CID alias.
+ *
+ * The caller must hold the socket lock and must not call this for listening
+ * sockets, which accept packets from any source and have no assigned
+ * transport.
+ */
+bool vsock_check_source(const struct vsock_sock *vsk,
+ const struct vsock_transport *transport,
+ const struct sockaddr_vm *src)
+{
+ if (vsk->transport != transport)
+ return false;
+
+ if (src->svm_port != vsk->remote_addr.svm_port)
+ return false;
+
+ if (src->svm_cid == vsk->remote_addr.svm_cid)
+ return true;
+
+ return transport->get_local_cid() == VMADDR_CID_LOCAL;
+}
+EXPORT_SYMBOL_GPL(vsock_check_source);
+
void vsock_remove_sock(struct vsock_sock *vsk)
{
/* Transport reassignment must not remove the binding. */
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index 88df82364f77..f225f53ed4ba 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1836,7 +1836,8 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
* lock_sock (note: listener sockets are not assigned to any transport)
*/
if (sock_flag(sk, SOCK_DONE) ||
- (sk->sk_state != TCP_LISTEN && vsk->transport != &t->transport)) {
+ (sk->sk_state != TCP_LISTEN &&
+ !vsock_check_source(vsk, &t->transport, &src))) {
(void)virtio_transport_reset_no_sock(t, skb, net);
release_sock(sk);
sock_put(sk);
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 1c4ee039c166..1f186e8f8364 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -680,11 +680,13 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
struct vmci_transport_packet *pkt;
struct vsock_sock *vsk;
bool bh_process_pkt;
+ bool drop_pkt;
int err;
sk = NULL;
err = VMCI_SUCCESS;
bh_process_pkt = false;
+ drop_pkt = false;
/* Ignore incoming packets from resources that aren't vsock
* implementations.
@@ -765,17 +767,29 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
bh_lock_sock(sk);
if (!sock_owned_by_user(sk)) {
- /* The local context ID may be out of date, update it. */
- vsk->local_addr.svm_cid = dst.svm_cid;
+ if (sk->sk_state != TCP_LISTEN &&
+ !vsock_check_source(vsk, &vmci_transport, &src)) {
+ drop_pkt = true;
+ err = VMCI_ERROR_NO_ACCESS;
+ } else {
+ /* The local context ID may be out of date, update it. */
+ vsk->local_addr.svm_cid = dst.svm_cid;
- if (sk->sk_state == TCP_ESTABLISHED)
- vmci_trans(vsk)->notify_ops->handle_notify_pkt(
- sk, pkt, true, &dst, &src,
- &bh_process_pkt);
+ if (sk->sk_state == TCP_ESTABLISHED)
+ vmci_trans(vsk)->notify_ops->handle_notify_pkt(sk, pkt, true,
+ &dst, &src,
+ &bh_process_pkt);
+ }
}
bh_unlock_sock(sk);
+ if (drop_pkt) {
+ if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
+ pr_err("unable to send reset\n");
+ goto out;
+ }
+
if (!bh_process_pkt) {
struct vmci_transport_recv_pkt_info *recv_pkt_info;
@@ -900,6 +914,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
{
struct vmci_transport_recv_pkt_info *recv_pkt_info;
struct vmci_transport_packet *pkt;
+ struct sockaddr_vm src;
struct sock *sk;
recv_pkt_info =
@@ -908,6 +923,12 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
pkt = &recv_pkt_info->pkt;
lock_sock(sk);
+ vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
+ if (sk->sk_state != TCP_LISTEN &&
+ !vsock_check_source(vsock_sk(sk), &vmci_transport, &src)) {
+ vmci_transport_reply_reset(pkt);
+ goto out;
+ }
/* The local context ID may be out of date. */
vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context;
@@ -937,6 +958,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
break;
}
+out:
release_sock(sk);
kfree(recv_pkt_info);
/* Release reference obtained in the stream callback when we fetched
diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening
index 6923036e1a2f..81c81ad983ad 100644
--- a/security/Kconfig.hardening
+++ b/security/Kconfig.hardening
@@ -278,7 +278,7 @@ config CC_HAS_RANDSTRUCT
choice
prompt "Randomize layout of sensitive kernel structures"
- default RANDSTRUCT_FULL if COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT)
+ default RANDSTRUCT_FULL if !(RUST_IS_AVAILABLE && HAVE_RUST) && COMPILE_TEST && (GCC_PLUGINS || CC_HAS_RANDSTRUCT)
default RANDSTRUCT_NONE
help
If you enable this, the layouts of structures that are entirely
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index b3a9f86809b0..72654cf797cd 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -46,12 +46,16 @@ config IMA_KEXEC
config IMA_MEASURE_PCR_IDX
int
+ prompt "PCR Index for Aggregate" if EXPERT
range 8 14
default 10
help
IMA_MEASURE_PCR_IDX determines the TPM PCR register index
that IMA uses to maintain the integrity aggregate of the
- measurement list. If unsure, use the default 10.
+ measurement list. Most attestation tooling expects PCR 10.
+
+ The default is almost always what you want. Only change this
+ if you know what you are doing.
config IMA_LSM_RULES
bool
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index ced2e131b061..b280488e15fc 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -748,6 +748,8 @@ static int validate_hash_algo(struct dentry *dentry,
return -EACCES;
path = dentry_path(dentry, pathbuf, PATH_MAX);
+ if (IS_ERR(path))
+ path = NULL;
integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
"set_data", errmsg, -EACCES, 0);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index f79d07bb63c6..68d9a5e6c232 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -165,7 +165,10 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = {
{.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC,
.flags = IMA_FSMAGIC},
{.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
- {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC}
+ {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC,
+ .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE, .fsmagic = CONFIGFS_MAGIC,
+ .flags = IMA_FSMAGIC}
};
static struct ima_rule_entry original_measurement_rules[] __ro_after_init = {
@@ -211,6 +214,8 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = {
{.action = DONT_APPRAISE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
{.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC},
+ {.action = DONT_APPRAISE, .fsmagic = CONFIGFS_MAGIC,
+ .flags = IMA_FSMAGIC},
#ifdef CONFIG_IMA_WRITE_POLICY
{.action = APPRAISE, .func = POLICY_CHECK,
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 035aaf113d1d..e5e17f100aae 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7267,24 +7267,6 @@ static int selinux_bpf_prog(struct bpf_prog *prog)
BPF__PROG_RUN, NULL);
}
-static u32 selinux_bpffs_creator_sid(u32 fd)
-{
- struct path path;
- struct super_block *sb;
- struct superblock_security_struct *sbsec;
-
- CLASS(fd, f)(fd);
-
- if (fd_empty(f))
- return SECSID_NULL;
-
- path = fd_file(f)->f_path;
- sb = path.dentry->d_sb;
- sbsec = selinux_superblock(sb);
-
- return sbsec->creator_sid;
-}
-
static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
{
@@ -7297,7 +7279,7 @@ static int selinux_bpf_map_create(struct bpf_map *map, union bpf_attr *attr,
if (!token)
ssid = bpfsec->sid;
else
- ssid = selinux_bpffs_creator_sid(attr->map_token_fd);
+ ssid = selinux_bpf_token_security(token)->grantor_sid;
return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__MAP_CREATE,
NULL);
@@ -7315,7 +7297,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
if (!token)
ssid = bpfsec->sid;
else
- ssid = selinux_bpffs_creator_sid(attr->prog_token_fd);
+ ssid = selinux_bpf_token_security(token)->grantor_sid;
return avc_has_perm(ssid, bpfsec->sid, SECCLASS_BPF, BPF__PROG_LOAD,
NULL);
@@ -7329,12 +7311,14 @@ static int selinux_bpf_token_create(struct bpf_token *token,
const struct path *path)
{
struct bpf_security_struct *bpfsec;
- u32 sid = selinux_bpffs_creator_sid(attr->token_create.bpffs_fd);
+ struct superblock_security_struct *sbsec;
int err;
+ sbsec = selinux_superblock(path->dentry->d_sb);
+
bpfsec = selinux_bpf_token_security(token);
bpfsec->sid = current_sid();
- bpfsec->grantor_sid = sid;
+ bpfsec->grantor_sid = sbsec->creator_sid;
bpfsec->perms = 0;
/**
@@ -7343,15 +7327,15 @@ static int selinux_bpf_token_create(struct bpf_token *token,
* in the allowed_cmds bitmap.
*/
if (bpf_token_cmd(token, BPF_MAP_CREATE)) {
- err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF,
- BPF__MAP_CREATE_AS, NULL);
+ err = avc_has_perm(bpfsec->sid, bpfsec->grantor_sid,
+ SECCLASS_BPF, BPF__MAP_CREATE_AS, NULL);
if (err)
return err;
bpfsec->perms |= BPF__MAP_CREATE;
}
if (bpf_token_cmd(token, BPF_PROG_LOAD)) {
- err = avc_has_perm(bpfsec->sid, sid, SECCLASS_BPF,
- BPF__PROG_LOAD_AS, NULL);
+ err = avc_has_perm(bpfsec->sid, bpfsec->grantor_sid,
+ SECCLASS_BPF, BPF__PROG_LOAD_AS, NULL);
if (err)
return err;
bpfsec->perms |= BPF__PROG_LOAD;
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 4a5057e7629d..62324282fcae 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1468,6 +1468,8 @@ static int snd_pcm_pre_start(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
if (runtime->state != SNDRV_PCM_STATE_PREPARED)
return -EBADFD;
+ if (atomic_read(&runtime->buffer_accessing) < 0)
+ return -EBADFD; /* during hw_params, hw_free or prepare */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
!snd_pcm_playback_data(substream))
return -EPIPE;
@@ -4021,20 +4023,33 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
return -EINVAL;
}
runtime = substream->runtime;
- if (runtime->state == SNDRV_PCM_STATE_OPEN)
- return -EBADFD;
- if (!(runtime->info & SNDRV_PCM_INFO_MMAP))
- return -ENXIO;
+ /* don't race with buffer reallocation in hw_params/hw_free */
+ if (!atomic_inc_unless_negative(&runtime->buffer_accessing))
+ return -EBUSY;
+ if (runtime->state == SNDRV_PCM_STATE_OPEN) {
+ err = -EBADFD;
+ goto out;
+ }
+ if (!(runtime->info & SNDRV_PCM_INFO_MMAP)) {
+ err = -ENXIO;
+ goto out;
+ }
if (runtime->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED ||
- runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
- return -EINVAL;
+ runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) {
+ err = -EINVAL;
+ goto out;
+ }
size = area->vm_end - area->vm_start;
offset = area->vm_pgoff << PAGE_SHIFT;
dma_bytes = PAGE_ALIGN(runtime->dma_bytes);
- if ((size_t)size > dma_bytes)
- return -EINVAL;
- if (offset > dma_bytes - size)
- return -EINVAL;
+ if ((size_t)size > dma_bytes) {
+ err = -EINVAL;
+ goto out;
+ }
+ if (offset > dma_bytes - size) {
+ err = -EINVAL;
+ goto out;
+ }
area->vm_ops = &snd_pcm_vm_ops_data;
area->vm_private_data = substream;
@@ -4044,6 +4059,8 @@ int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file,
err = snd_pcm_lib_default_mmap(substream, area);
if (!err)
atomic_inc(&substream->mmap_count);
+out:
+ atomic_dec(&runtime->buffer_accessing);
return err;
}
EXPORT_SYMBOL(snd_pcm_mmap_data);
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 34b4c7d6dbe6..2617bb5b4faf 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -785,7 +785,7 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream,
substream->framing = framing;
substream->clock_type = clock_type;
}
- return 0;
+ return err;
}
EXPORT_SYMBOL(snd_rawmidi_input_params);
diff --git a/sound/core/ump.c b/sound/core/ump.c
index d183c8a000bd..3d1a2ed3b476 100644
--- a/sound/core/ump.c
+++ b/sound/core/ump.c
@@ -1335,6 +1335,8 @@ static void update_legacy_names(struct snd_ump_endpoint *ump)
{
struct snd_rawmidi *rmidi = ump->legacy_rmidi;
+ if (!rmidi)
+ return;
update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
}
@@ -1343,6 +1345,8 @@ static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
{
struct snd_rawmidi *rmidi = ump->legacy_rmidi;
+ if (!rmidi)
+ return;
snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
ump->core.name);
}
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index b908d2564aee..3f6bfee29986 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -808,7 +808,7 @@ static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_el
left = ucontrol->value.integer.value[0] & 1;
right = ucontrol->value.integer.value[1] & 1;
guard(spinlock_irq)(&dummy->mixer_lock);
- change = dummy->capture_source[addr][0] != left &&
+ change = dummy->capture_source[addr][0] != left ||
dummy->capture_source[addr][1] != right;
dummy->capture_source[addr][0] = left;
dummy->capture_source[addr][1] = right;
diff --git a/sound/hda/codecs/cirrus/cs420x.c b/sound/hda/codecs/cirrus/cs420x.c
index 85c2ecf46d38..6cba01228c27 100644
--- a/sound/hda/codecs/cirrus/cs420x.c
+++ b/sound/hda/codecs/cirrus/cs420x.c
@@ -571,6 +571,7 @@ static const struct hda_model_fixup cs4208_models[] = {
static const struct hda_quirk cs4208_fixup_tbl[] = {
SND_PCI_QUIRK_VENDOR(0x106b, "Apple", CS4208_MAC_AUTO),
+ SND_PCI_QUIRK(0x8086, 0x7270, "MacBookAir 7,2", CS4208_MAC_AUTO),
{} /* terminator */
};
@@ -583,6 +584,7 @@ static const struct hda_quirk cs4208_mac_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x7800, "MacPro 6,1", CS4208_MACMINI),
SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
SND_PCI_QUIRK(0x106b, 0x7f00, "iMac 16,1", CS4208_MBP11),
+ SND_PCI_QUIRK(0x8086, 0x7270, "MacBookAir 7,2", CS4208_MBA6),
{} /* terminator */
};
diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c
index 7357dc91ac49..9bfdbf5c9032 100644
--- a/sound/hda/codecs/conexant.c
+++ b/sound/hda/codecs/conexant.c
@@ -249,6 +249,25 @@ static void cx_update_headset_mic_vref(struct hda_codec *codec, struct hda_jack_
}
}
+#define SN6140_S3_AFG_D0_DELAY_MS 1000
+
+static void cx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
+ unsigned int power_state)
+{
+ snd_hda_codec_write_sync(codec, fg, 0, AC_VERB_SET_POWER_STATE, power_state);
+
+ /*
+ * SN6140 may not respond to AFG D0 immediately after S3.
+ * Wait before the D0 verb so the power-state command itself succeeds.
+ */
+ if (codec->core.vendor_id == 0x14f11f87 &&
+ power_state == AC_PWRST_D0 &&
+ codec->core.dev.power.power_state.event == PM_EVENT_RESUME)
+ msleep(SN6140_S3_AFG_D0_DELAY_MS);
+
+ snd_hda_codec_set_power_to_all(codec, fg, power_state);
+}
+
static int cx_suspend(struct hda_codec *codec)
{
cx_auto_shutdown(codec);
@@ -1308,6 +1327,7 @@ static const struct hda_codec_ops cx_codec_ops = {
.init = cx_init,
.unsol_event = snd_hda_jack_unsol_event,
.suspend = cx_suspend,
+ .set_power_state = cx_set_power_state,
.check_power_status = snd_hda_gen_check_power_status,
.stream_pm = snd_hda_gen_stream_pm,
};
diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c
index e4349743a251..95b40a177d2b 100644
--- a/sound/hda/codecs/realtek/alc269.c
+++ b/sound/hda/codecs/realtek/alc269.c
@@ -2379,6 +2379,33 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
}
}
+/*
+ * On the Acer Aspire A515-57G (and possibly other models sharing this
+ * board), if headphones are already inserted into the combo jack before
+ * the codec powers up (cold boot), the impedance-based headset-type
+ * sensing races and misclassifies the jack, driving the wrong output
+ * configuration (audible as missing center-panned/vocal content). A
+ * genuine physical unplug/replug after boot fixes it by forcing a fresh
+ * sense transient. Mirror that here on cold boot only: give the sense
+ * hardware time to settle, then force a fresh classification.
+ */
+static void alc_fixup_headset_mode_acer_coldboot(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ alc_fixup_headset_mode(codec, fix, action);
+
+ if (action == HDA_FIXUP_ACT_INIT &&
+ !is_s3_resume(codec) && !is_s4_resume(codec) &&
+ spec->current_headset_mode != ALC_HEADSET_MODE_UNPLUGGED) {
+ msleep(500);
+ spec->current_headset_mode = ALC_HEADSET_MODE_UNKNOWN;
+ spec->current_headset_type = ALC_HEADSET_TYPE_UNKNOWN;
+ alc_fixup_headset_mode(codec, fix, action);
+ }
+}
+
static void alc288_update_headset_jack_cb(struct hda_codec *codec,
struct hda_jack_callback *jack)
{
@@ -4248,6 +4275,7 @@ enum {
ALC282_FIXUP_ACER_DISABLE_LINEOUT,
ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
ALC256_FIXUP_ACER_HEADSET_MIC,
+ ALC256_FIXUP_ACER_COLDBOOT,
ALC285_FIXUP_IDEAPAD_S740_COEF,
ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST,
ALC295_FIXUP_ASUS_DACS,
@@ -6311,6 +6339,12 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC
},
+ [ALC256_FIXUP_ACER_COLDBOOT] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_acer_coldboot,
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ACER_SFG16_MICMUTE_LED,
+ },
[ALC285_FIXUP_IDEAPAD_S740_COEF] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc285_fixup_ideapad_s740_coef,
@@ -7148,13 +7182,14 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
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, 0x1616, "Acer Aspire A515-57", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
+ SND_PCI_QUIRK(0x1025, 0x1616, "Acer Aspire A515-57", ALC256_FIXUP_ACER_COLDBOOT),
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),
SND_PCI_QUIRK(0x1025, 0x166c, "Acer Predator PH16-71", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED),
+ SND_PCI_QUIRK(0x1025, 0x1731, "Acer Predator PHN16-72", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED),
SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2),
@@ -8100,6 +8135,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x3801, "Lenovo Yoga9 14IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
HDA_CODEC_QUIRK(0x17aa, 0x3802, "DuetITL 2021", ALC287_FIXUP_YOGA7_14ITL_SPEAKERS),
SND_PCI_QUIRK(0x17aa, 0x3802, "Lenovo Yoga Pro 9 14IRP8", ALC287_FIXUP_TAS2781_I2C),
+ SND_PCI_QUIRK(0x17aa, 0x380b, "Lenovo Yoga Slim 9 14ILL10", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
/* Yoga Pro 9 16IMH9 and Legion 7 16ITHG6 share PCI SSID 17aa:3811
* with Legion S7 15IMH05; use codec SSID to distinguish them
*/
@@ -8287,6 +8323,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d05, 0x30ba, "TongFang XxAF5xxx", ALC2XX_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS),
+ SND_PCI_QUIRK(0x1d19, 0x0006, "VAIO VJS131", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC),
SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1d72, 0x1901, "RedmiBook 14", ALC256_FIXUP_ASUS_HEADSET_MIC),
diff --git a/sound/hda/core/device.c b/sound/hda/core/device.c
index 776d629ba252..a45f61e12c1a 100644
--- a/sound/hda/core/device.c
+++ b/sound/hda/core/device.c
@@ -404,6 +404,7 @@ static void setup_fg_nodes(struct hdac_device *codec)
*/
int snd_hdac_refresh_widgets(struct hdac_device *codec)
{
+ hda_nid_t fg = codec->afg ? codec->afg : codec->mfg;
hda_nid_t start_nid;
int nums, err = 0;
@@ -412,10 +413,10 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec)
* widgets array.
*/
guard(mutex)(&codec->widget_lock);
- nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid);
+ nums = snd_hdac_get_sub_nodes(codec, fg, &start_nid);
if (!start_nid || nums <= 0 || nums >= 0xff) {
dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n",
- codec->afg);
+ fg);
return -EINVAL;
}
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index fb40476c6c91..a9625aedf2a4 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -868,6 +868,9 @@ snd_harmony_create(struct snd_card *card,
goto free_and_ret;
}
+ spin_lock_init(&h->mixer_lock);
+ spin_lock_init(&h->lock);
+
err = request_irq(padev->irq, snd_harmony_interrupt, 0,
"harmony", h);
if (err) {
@@ -877,9 +880,6 @@ snd_harmony_create(struct snd_card *card,
}
h->irq = padev->irq;
- spin_lock_init(&h->mixer_lock);
- spin_lock_init(&h->lock);
-
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops);
if (err < 0)
goto free_and_ret;
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index ba3f73455ebe..bb6280aa3533 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -828,16 +828,13 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *cdev)
cdev->data_urbs_in = alloc_urbs(cdev, SNDRV_PCM_STREAM_CAPTURE, &ret);
if (ret < 0) {
- kfree(cdev->data_cb_info);
- free_urbs(cdev->data_urbs_in);
+ snd_usb_caiaq_audio_free(cdev);
return ret;
}
cdev->data_urbs_out = alloc_urbs(cdev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
if (ret < 0) {
- kfree(cdev->data_cb_info);
- free_urbs(cdev->data_urbs_in);
- free_urbs(cdev->data_urbs_out);
+ snd_usb_caiaq_audio_free(cdev);
return ret;
}
@@ -858,6 +855,9 @@ void snd_usb_caiaq_audio_free(struct snd_usb_caiaqdev *cdev)
dev_dbg(dev, "%s(%p)\n", __func__, cdev);
free_urbs(cdev->data_urbs_in);
+ cdev->data_urbs_in = NULL;
free_urbs(cdev->data_urbs_out);
+ cdev->data_urbs_out = NULL;
kfree(cdev->data_cb_info);
+ cdev->data_cb_info = NULL;
}
diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c
index 5fc2131b4561..68bb7eabf107 100644
--- a/sound/usb/fcp.c
+++ b/sound/usb/fcp.c
@@ -191,6 +191,10 @@ static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
const int max_retries = 5;
int err;
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err < 0)
+ return -EIO;
+
if (!private->urb)
return -ENODEV;
@@ -1026,6 +1030,10 @@ static int fcp_init(struct usb_mixer_interface *mixer,
struct usb_device *dev = mixer->chip->dev;
int err;
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err < 0)
+ return -EIO;
+
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
FCP_USB_REQ_STEP0,
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 8a9bc37f0b6e..7e5b1b13360f 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -971,6 +971,8 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
default:
count = 2;
}
+ if (ep->max_transfer < count)
+ return;
count = snd_rawmidi_transmit(ep->ports[0].substream,
urb->transfer_buffer,
count);
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index ace4ccad8f51..69093c666282 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -519,6 +519,19 @@ static const struct usbmix_name_map audient_id14_map[] = {
};
/*
+ * Audient iD24: feature unit 12 ("Speaker Playback Volume") sits in the
+ * monitor-mixer branch and does not apply volume to all of its channels;
+ * when userspace adopts it as the master playback volume, the left main
+ * output stays at 0 dB while the right one is attenuated, producing a
+ * stereo imbalance. Rename it so that it is not picked up as the
+ * stream's master volume control.
+ */
+static const struct usbmix_name_map audient_id24_map[] = {
+ { 12, "Monitor Mix Playback" }, /* FU, partial channel coverage */
+ {}
+};
+
+/*
* Control map entries
*/
@@ -612,6 +625,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = {
.map = audient_id14_map,
},
{
+ /* Audient iD24 */
+ .id = USB_ID(0x2708, 0x000d),
+ .map = audient_id24_map,
+ },
+ {
/* KEF X300A */
.id = USB_ID(0x27ac, 0x1000),
.map = scms_usb3318_map,
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index a1f5592cc5d5..fc622eb95dc5 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -3480,6 +3480,10 @@ static int snd_rme_digiface_write_reg(struct snd_kcontrol *kcontrol, int item, u
struct usb_device *dev = chip->dev;
int err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return -EIO;
+
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
item,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -3499,6 +3503,10 @@ static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 statu
__le32 buf[4] = {};
int err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return -EIO;
+
err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
RME_DIGIFACE_READ_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
diff --git a/sound/usb/mixer_s1810c.c b/sound/usb/mixer_s1810c.c
index 2e5a8d37ec57..bdb5e3aaff3b 100644
--- a/sound/usb/mixer_s1810c.c
+++ b/sound/usb/mixer_s1810c.c
@@ -474,6 +474,10 @@ snd_s1810c_switch_get(struct snd_kcontrol *kctl,
u32 state = 0;
int ret;
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err < 0)
+ return -EIO;
+
guard(mutex)(&private->data_mutex);
ret = snd_s1810c_get_switch_state(mixer, kctl, &state);
if (ret < 0)
@@ -504,6 +508,10 @@ snd_s1810c_switch_set(struct snd_kcontrol *kctl,
u32 newval = 0;
int ret = 0;
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err < 0)
+ return -EIO;
+
guard(mutex)(&private->data_mutex);
ret = snd_s1810c_get_switch_state(mixer, kctl, &curval);
if (ret < 0)
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index 673eb8d8724d..369968565c19 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -707,6 +707,10 @@ static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl,
int idx = snd_usb_ctrl_intf(elem->head.mixer->hostif) | (elem->head.id << 8);
int err;
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return -EIO;
+
err = snd_usb_ctl_msg(chip->dev,
usb_rcvctrlpipe(chip->dev, 0),
UAC2_CS_MEM,
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 502854cc9f9f..ed5fe746d438 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -2603,9 +2603,9 @@ static int scarlett2_usb_rx(struct usb_device *dev, int interface,
}
/* Send a proprietary format request to the Scarlett interface */
-static int scarlett2_usb(
- struct usb_mixer_interface *mixer, u32 cmd,
- void *req_data, u16 req_size, void *resp_data, u16 resp_size)
+static int scarlett2_usb_nopm(struct usb_mixer_interface *mixer, u32 cmd,
+ void *req_data, u16 req_size,
+ void *resp_data, u16 resp_size)
{
struct scarlett2_data *private = mixer->private_data;
struct usb_device *dev = mixer->chip->dev;
@@ -2713,6 +2713,18 @@ retry:
return err;
}
+static int scarlett2_usb(struct usb_mixer_interface *mixer, u32 cmd,
+ void *req_data, u16 req_size,
+ void *resp_data, u16 resp_size)
+{
+ CLASS(snd_usb_lock, pm)(mixer->chip);
+ if (pm.err < 0)
+ return -EIO;
+
+ return scarlett2_usb_nopm(mixer, cmd, req_data, req_size,
+ resp_data, resp_size);
+}
+
/* Send a USB message to get data; result placed in *buf */
static int scarlett2_usb_get(
struct usb_mixer_interface *mixer,
@@ -3020,9 +3032,21 @@ static int scarlett2_usb_set_config_buf(
/* Send SCARLETT2_USB_DATA_CMD SCARLETT2_USB_CONFIG_SAVE */
static void scarlett2_config_save(struct usb_mixer_interface *mixer)
{
- int err;
+ __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE);
+ int err = scarlett2_usb(mixer, SCARLETT2_USB_DATA_CMD,
+ &req, sizeof(req), NULL, 0);
+
+ if (err < 0)
+ usb_audio_err(mixer->chip, "config save failed: %d\n", err);
+}
+
+/* The USB suspend callback must not acquire another PM reference. */
+static void scarlett2_config_save_nopm(struct usb_mixer_interface *mixer)
+{
+ __le32 req = cpu_to_le32(SCARLETT2_USB_CONFIG_SAVE);
+ int err = scarlett2_usb_nopm(mixer, SCARLETT2_USB_DATA_CMD,
+ &req, sizeof(req), NULL, 0);
- err = scarlett2_usb_activate_config(mixer, SCARLETT2_USB_CONFIG_SAVE);
if (err < 0)
usb_audio_err(mixer->chip, "config save failed: %d\n", err);
}
@@ -8639,7 +8663,7 @@ static void scarlett2_private_suspend(struct usb_mixer_interface *mixer)
struct scarlett2_data *private = mixer->private_data;
if (cancel_delayed_work_sync(&private->work))
- scarlett2_config_save(private->mixer);
+ scarlett2_config_save_nopm(private->mixer);
scarlett2_cleanup_urb(mixer);
}
diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c
index ebff185cbd2c..14fb1ad764a7 100644
--- a/sound/usb/mixer_us16x08.c
+++ b/sound/usb/mixer_us16x08.c
@@ -151,6 +151,9 @@ static const char *const route_names[] = {
static int snd_us16x08_recv_urb(struct snd_usb_audio *chip,
unsigned char *buf, int size)
{
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return -EIO;
guard(mutex)(&chip->mutex);
snd_usb_ctl_msg(chip->dev,
@@ -165,6 +168,10 @@ static int snd_us16x08_recv_urb(struct snd_usb_audio *chip,
*/
static int snd_us16x08_send_urb(struct snd_usb_audio *chip, char *buf, int size)
{
+ CLASS(snd_usb_lock, pm)(chip);
+ if (pm.err < 0)
+ return -EIO;
+
return snd_usb_ctl_msg(chip->dev, usb_sndctrlpipe(chip->dev, 0),
SND_US16X08_URB_REQUEST, SND_US16X08_URB_REQUESTTYPE,
0, 0, buf, size);
diff --git a/tools/sched_ext/include/scx/common.bpf.h b/tools/sched_ext/include/scx/common.bpf.h
index 979d4cabfaf9..76f5e025e107 100644
--- a/tools/sched_ext/include/scx/common.bpf.h
+++ b/tools/sched_ext/include/scx/common.bpf.h
@@ -48,6 +48,7 @@
extern int LINUX_KERNEL_VERSION __kconfig;
extern const char CONFIG_CC_VERSION_TEXT[64] __kconfig __weak;
extern const char CONFIG_LOCALVERSION[64] __kconfig __weak;
+extern bool CONFIG_PREEMPT_RCU __kconfig __weak;
/*
* Earlier versions of clang/pahole lost upper 32bits in 64bit enums which can
@@ -97,6 +98,7 @@ s32 scx_bpf_pick_any_cpu_node(const cpumask_t *cpus_allowed, int node, u64 flags
s32 scx_bpf_pick_any_cpu(const cpumask_t *cpus_allowed, u64 flags) __ksym;
bool scx_bpf_task_running(const struct task_struct *p) __ksym;
s32 scx_bpf_task_cpu(const struct task_struct *p) __ksym;
+struct rq *scx_bpf_cpu_rq(s32 cpu) __ksym __weak;
struct rq *scx_bpf_locked_rq(void) __ksym;
struct task_struct *scx_bpf_cpu_curr(s32 cpu) __ksym __weak;
struct task_struct *scx_bpf_tid_to_task(u64 tid) __ksym __weak;
@@ -528,31 +530,102 @@ static __always_inline const struct cpumask *cast_mask(struct bpf_cpumask *mask)
}
/*
+ * True if the non-sleepable BPF trampoline prolog (__bpf_prog_enter) calls
+ * migrate_disable() for the current task. Recorded once by
+ * scx_lib_init_probe, an fentry program on bpf_scx_reg() that fires during
+ * the natural scheduler-attach call chain (auto-attached by scx_ops_attach!).
+ *
+ * Defaults to true (conservative). Over-reporting in is_migration_disabled()
+ * causes local-only dispatch, which is safe. Under-reporting can crash the
+ * scheduler, so we err high if the probe somehow fails to run.
+ */
+bool __scx_prolog_disables_migration __weak = true;
+
+/*
+ * scx_lib_init_probe - non-sleepable prolog probe.
+ *
+ * Attached to bpf_scx_reg(), the .reg callback in bpf_sched_ext_ops
+ * (kernel/sched/ext.c). The kernel's struct_ops machinery invokes
+ * bpf_scx_reg when userspace creates the scheduler link, before
+ * ops.init() fires. Its address is taken in the vtable, so the symbol
+ * is non-inlinable and has been stable since introduction.
+ *
+ * Entering via fentry runs us through __bpf_prog_enter -- the
+ * non-sleepable prolog that consumers of is_migration_disabled() live
+ * under.
+ *
+ * Loud warning: the prolog adds at most 1 to migration_disabled.
+ * Reading > 1 means something upstream in the
+ * bpf_struct_ops_link_create -> bpf_scx_reg path disabled migration
+ * before the prolog ran, invalidating the probe; audit and adjust.
+ */
+SEC("fentry/bpf_scx_reg") __weak
+int scx_lib_init_probe(void *ctx)
+{
+ if (bpf_core_field_exists(((struct task_struct *)0)->migration_disabled)) {
+ const struct task_struct *p = bpf_get_current_task_btf();
+ unsigned int md = p->migration_disabled;
+
+ if (md > 1)
+ bpf_printk("scx_lib_init_probe: unexpected migration_disabled=%u "
+ "upstream of BPF prolog; probe result unreliable",
+ md);
+
+ __scx_prolog_disables_migration = md > 0;
+ }
+ return 0;
+}
+
+/*
* Return true if task @p cannot migrate to a different CPU, false
* otherwise.
+ *
+ * IMPORTANT: designed for NON-SLEEPABLE BPF contexts only. Sleepable
+ * contexts (BPF_STRUCT_OPS_SLEEPABLE, SEC("syscall"),
+ * SEC("fentry.s/...")) enter via __bpf_prog_enter_sleepable() or
+ * __bpf_prog_enter_sleepable_recur(), both of which unconditionally
+ * call migrate_disable(); this helper can yield a false negative for
+ * p == current there, which can crash the scheduler.
*/
static inline bool is_migration_disabled(const struct task_struct *p)
{
/*
- * Testing p->migration_disabled in a BPF code is tricky because the
- * migration is _always_ disabled while running the BPF code.
- * The prolog (__bpf_prog_enter) and epilog (__bpf_prog_exit) for BPF
- * code execution disable and re-enable the migration of the current
- * task, respectively. So, the _current_ task of the sched_ext ops is
- * always migration-disabled. Moreover, p->migration_disabled could be
- * two or greater when a sched_ext ops BPF code (e.g., ops.tick) is
- * executed in the middle of the other BPF code execution.
+ * Testing p->migration_disabled in BPF is tricky because the BPF prolog
+ * (__bpf_prog_enter) may call migrate_disable() for the current task,
+ * making migration_disabled == 1 even for tasks that are not truly
+ * migration-disabled.
+ *
+ * Since commit 8e4f0b1ebcf2 ("bpf: use rcu_read_lock_dont_migrate() for
+ * trampoline.c"), the BPF prolog calls migrate_disable() only when
+ * CONFIG_PREEMPT_RCU is enabled. Two fast paths cover the common cases:
+ *
+ * 1) CONFIG_PREEMPT_RCU: prolog always calls migrate_disable(), so
+ * migration_disabled == 1 for the current task is ambiguous.
+ * Disambiguate by checking p == current.
+ *
+ * 2) v6.18+ without CONFIG_PREEMPT_RCU: prolog never calls
+ * migrate_disable(), so migration_disabled == 1 is unambiguously
+ * a real migrate_disable() call.
*
- * Therefore, we should decide that the _current_ task is
- * migration-disabled only when its migration_disabled count is greater
- * than one. In other words, when p->migration_disabled == 1, there is
- * an ambiguity, so we should check if @p is the current task or not.
+ * A slow path handles pre-v6.18 kernels without CONFIG_PREEMPT_RCU,
+ * where the prolog historically called migrate_disable() unconditionally
+ * but a cherry-picked downstream kernel may not. The runtime-probed flag
+ * __scx_prolog_disables_migration (set by scx_lib_init_probe) distinguishes
+ * the two cases without relying on the kernel version alone.
*/
if (bpf_core_field_exists(p->migration_disabled)) {
- if (p->migration_disabled == 1)
- return bpf_get_current_task_btf() != p;
- else
- return p->migration_disabled;
+ if (p->migration_disabled == 1) {
+ /* Fast path: prolog always disables migration */
+ if (CONFIG_PREEMPT_RCU)
+ return bpf_get_current_task_btf() != p;
+ /* Fast path: prolog never disables migration */
+ if (LINUX_KERNEL_VERSION >= KERNEL_VERSION(6, 18, 0))
+ return true;
+ /* Slow path: pre-v6.18, !PREEMPT_RCU - use runtime flag */
+ return __scx_prolog_disables_migration ?
+ bpf_get_current_task_btf() != p : true;
+ }
+ return p->migration_disabled;
}
return false;
}
@@ -1021,7 +1094,20 @@ static inline u64 scx_clock_task(u32 cpu)
{
struct rq___local *rq = get_current_rq(cpu);
- /* Equivalent to the kernel's rq_clock_task(). */
+ /*
+ * Equivalent to the kernel's rq_clock_task(): wall-clock time minus
+ * cumulative IRQ time (CONFIG_IRQ_TIME_ACCOUNTING) and hypervisor
+ * steal time (CONFIG_PARAVIRT_TIME_ACCOUNTING). Without those configs,
+ * it equals rq->clock.
+ *
+ * Conceptually this clock advances during idle (the idle task counts
+ * as a running task), but rq->clock_task is only updated on scheduling
+ * events. With NO_HZ_IDLE (the default), the periodic tick is stopped
+ * on idle CPUs, so rq->clock_task is not refreshed while a CPU is
+ * idle. Reading this clock for a remote idle CPU from a BPF timer
+ * callback returns the value from when the CPU last went idle, making
+ * the delta over an idle interval effectively zero.
+ */
return rq ? rq->clock_task : 0;
}
@@ -1032,9 +1118,23 @@ static inline u64 scx_clock_pelt(u32 cpu)
/*
* Equivalent to the kernel's rq_clock_pelt(): subtracts
* lost_idle_time from clock_pelt to absorb the jump that occurs
- * when clock_pelt resyncs with clock_task at idle exit. The result
- * is a continuous, capacity-invariant clock safe for both task
- * execution time stamping and cross-idle measurements.
+ * when clock_pelt resyncs with clock_task at idle exit. The intent
+ * is a continuous, capacity- and frequency-invariant clock that is
+ * frozen during idle, IRQ, and hypervisor steal.
+ *
+ * However, like scx_clock_task(), this clock has a stale-read issue
+ * for remote idle CPUs with NO_HZ_IDLE (the default). clock_pelt
+ * itself advances at wall-clock rate (hardware-clock based), but
+ * lost_idle_time is only updated via update_rq_clock_pelt(), which
+ * requires update_rq_clock() to be called. With NO_HZ_IDLE, the
+ * periodic tick is stopped on idle CPUs, so lost_idle_time is not
+ * refreshed during idle. Reading this clock for a remote idle CPU
+ * from a BPF timer callback therefore returns a value that drifts
+ * at wall-clock rate -- the same stale behaviour as scx_clock_task().
+ *
+ * Without NO_HZ_IDLE, periodic ticks keep lost_idle_time nearly in
+ * sync (stale by at most one tick period, ~1 ms), so the result is
+ * accurate.
*/
return rq ? (rq->clock_pelt - rq->lost_idle_time) : 0;
}
diff --git a/tools/sched_ext/include/scx/compat.bpf.h b/tools/sched_ext/include/scx/compat.bpf.h
index 3ab642f92c8a..6944221f96cc 100644
--- a/tools/sched_ext/include/scx/compat.bpf.h
+++ b/tools/sched_ext/include/scx/compat.bpf.h
@@ -92,15 +92,20 @@ int bpf_cpumask_populate(struct bpf_cpumask *dst, void *src, size_t src__sz) __k
/*
* v6.19: Introduce lockless peek API for user DSQs.
+ * v7.1: Fix scx_bpf_dsq_peek() spuriously returning NULL on non-empty
+ * FIFO DSQs (2f2ea7709266).
*
- * Preserve the following macro until v6.21.
+ * The kfunc exists from v6.19 but can return NULL for a non-empty FIFO DSQ
+ * before the v7.1 fix. Require kernel version >= 7.1.0 before calling it;
+ * otherwise fall through to the bpf_iter_scx_dsq fallback below.
*/
static inline struct task_struct *__COMPAT_scx_bpf_dsq_peek(u64 dsq_id)
{
struct task_struct *p = NULL;
struct bpf_iter_scx_dsq it;
- if (bpf_ksym_exists(scx_bpf_dsq_peek))
+ if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
+ LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
return scx_bpf_dsq_peek(dsq_id);
if (!bpf_iter_scx_dsq_new(&it, dsq_id, 0))
p = bpf_iter_scx_dsq_next(&it);
@@ -239,6 +244,26 @@ static inline bool __COMPAT_is_enq_cpu_selected(u64 enq_flags)
scx_bpf_pick_any_cpu(cpus_allowed, flags))
/*
+ * v6.18: Add a helper to retrieve the current task running on a CPU.
+ *
+ * The kernel tree dropped this helper and scx_bpf_cpu_rq(), but schedulers in
+ * this tree still support pre-v6.18 kernels where scx_bpf_cpu_curr() doesn't
+ * resolve and the scx_bpf_cpu_rq() fallback still exists. Keep it until
+ * pre-v6.18 kernels fall out of the support window.
+ */
+static inline struct task_struct *__COMPAT_scx_bpf_cpu_curr(int cpu)
+{
+ struct rq *rq;
+
+ if (bpf_ksym_exists(scx_bpf_cpu_curr))
+ return scx_bpf_cpu_curr(cpu);
+
+ rq = scx_bpf_cpu_rq(cpu);
+
+ return rq ? rq->curr : NULL;
+}
+
+/*
* v6.19: To work around BPF maximum parameter limit, the following kfuncs are
* replaced with variants that pack scalar arguments in a struct. Wrappers are
* provided to maintain source compatibility.
@@ -379,6 +404,17 @@ static inline void scx_bpf_task_set_dsq_vtime(struct task_struct *p, u64 vtime)
}
/*
+ * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
+ * will eventually deprecate scx_bpf_reenqueue_local().
+ */
+void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
+
+static inline bool __COMPAT_has_generic_reenq(void)
+{
+ return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
+}
+
+/*
* v6.19: The new void variant can be called from anywhere while the older v1
* variant can only be called from ops.cpu_release(). The double ___ prefixes on
* the v2 variant need to be removed once libbpf is updated to ignore ___ prefix
@@ -395,21 +431,31 @@ static inline bool __COMPAT_scx_bpf_reenqueue_local_from_anywhere(void)
static inline void scx_bpf_reenqueue_local(void)
{
- if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
+ if (__COMPAT_has_generic_reenq())
+ scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
+ else if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere())
scx_bpf_reenqueue_local___v2___compat();
else
scx_bpf_reenqueue_local___v1();
}
-/*
- * v7.1: New scx_bpf_dsq_reenq() that allows re-enqueues on more DSQs. This
- * will eventually deprecate scx_bpf_reenqueue_local().
- */
-void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
-
-static inline bool __COMPAT_has_generic_reenq(void)
+static inline int scx_bpf_reenqueue_local_from_anywhere(void)
{
- return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
+ /*
+ * The generic reenq kfunc and the v2 reenqueue-local variant can both be
+ * called from anywhere; v1 cannot. Test each ksym in its own branch with a
+ * distinct call: combining them with || would fold into a bitwise OR of the
+ * two ksym addresses, which the verifier rejects.
+ */
+ if (__COMPAT_has_generic_reenq()) {
+ scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
+ return 0;
+ }
+ if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) {
+ scx_bpf_reenqueue_local___v2___compat();
+ return 0;
+ }
+ return -EOPNOTSUPP;
}
static inline void scx_bpf_dsq_reenq(u64 dsq_id, u64 reenq_flags)
diff --git a/tools/sched_ext/include/scx/compat.h b/tools/sched_ext/include/scx/compat.h
index d2e4384df5af..7c12df45fdba 100644
--- a/tools/sched_ext/include/scx/compat.h
+++ b/tools/sched_ext/include/scx/compat.h
@@ -10,9 +10,14 @@
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
+#include "enums_abi.autogen.h"
+
struct btf *__COMPAT_vmlinux_btf __attribute__((weak));
static inline void __COMPAT_load_vmlinux_btf(void)
@@ -23,6 +28,85 @@ static inline void __COMPAT_load_vmlinux_btf(void)
}
}
+/*
+ * Recover the true value of a 64-bit enum enumerator whose kernel BTF entry
+ * was truncated to its low 32 bits.
+ *
+ * Kernels whose BTF was generated without BTF_KIND_ENUM64 support encode
+ * 64-bit enums as 8-byte BTF_KIND_ENUM entries whose enumerator values only
+ * carry the low 32 bits. This happens with pahole < 1.24, which predates
+ * ENUM64, and with pahole passing --skip_encoding_btf_enum64 (e.g. Google's
+ * Container-Optimized OS / GKE kernels deliberately pass it for backward
+ * compatibility with older BTF consumers). The high bits
+ * can't be recovered from kernel BTF, so substitute the value from the
+ * vmlinux.h this tree was built against, cross-checked against the low 32
+ * bits the kernel did provide.
+ *
+ * Note that this is a best-effort recovery, not a ground truth. The
+ * substitution assumes the running kernel agrees with this tree's vmlinux.h
+ * on the high 32 bits, but only the low 32 bits can actually be verified.
+ * The cross-check is vacuous for enumerators whose value has no low bits
+ * set (e.g. SCX_DSQ_FLAG_BUILTIN, __SCX_ENQ_INTERNAL_MASK,
+ * SCX_ENQ_CLEAR_OPSS, SCX_ECODE_*): their lo32 is 0 and matches anything,
+ * so those substitutions rest entirely on the high bits never moving. An
+ * enumerator missing from the table (a kernel newer than this tree's
+ * vmlinux.h, or a stale autogen table) can't be recovered at all. If a
+ * substitution is ever wrong, the scheduler operates on bogus values (e.g.
+ * dispatching to nonexistent DSQ ids or silently dropping flags) and can
+ * wildly malfunction, which is why the mismatch and table-miss paths refuse
+ * instead of guessing.
+ */
+static inline bool __COMPAT_recover_truncated_enum64(const char *type,
+ const char *name,
+ u32 lo32, u64 *v)
+{
+ static bool warned;
+ size_t i;
+
+ for (i = 0; i < sizeof(__scx_enum_abi_vals) / sizeof(__scx_enum_abi_vals[0]); i++) {
+ const struct __scx_enum_abi_val *e = &__scx_enum_abi_vals[i];
+
+ if (strcmp(e->type, type) || strcmp(e->name, name))
+ continue;
+
+ if (e->val <= (u64)UINT32_MAX) {
+ *v = lo32;
+ return true;
+ }
+
+ if ((u32)e->val != lo32) {
+ fprintf(stderr, "ERROR: kernel BTF value of %s::%s (0x%x) doesn't match the low 32 bits of the vmlinux.h value (0x%llx); refusing to substitute\n",
+ type, name, lo32, (unsigned long long)e->val);
+ return false;
+ }
+
+ if (!warned) {
+ fprintf(stderr,
+ "WARNING: kernel BTF lacks BTF_KIND_ENUM64 encoding (generated by\n"
+ "WARNING: pahole < 1.24 or with --skip_encoding_btf_enum64), so 64-bit\n"
+ "WARNING: scx enum values are truncated to their low 32 bits in kernel\n"
+ "WARNING: BTF. Substituting the full 64-bit values from the vmlinux.h\n"
+ "WARNING: this binary was built against, cross-checked against the low\n"
+ "WARNING: 32 bits the kernel does provide. The high 32 bits cannot be\n"
+ "WARNING: verified: if the running kernel's actual values differ from\n"
+ "WARNING: the build-time vmlinux.h (e.g. an enum that moved in a newer\n"
+ "WARNING: kernel), the scheduler will operate on bogus values, such as\n"
+ "WARNING: dispatching to nonexistent DSQ ids, and can wildly malfunction.\n");
+ warned = true;
+ }
+ *v = e->val;
+ return true;
+ }
+
+ /*
+ * Unknown enumerator (likely a stale autogen table). Fail
+ * pessimistically to avoid returning an invalid value.
+ */
+ fprintf(stderr, "ERROR: kernel BTF truncates 64-bit enum %s::%s to 0x%x; 64-bit variant not found in vmlinux.h\n",
+ type, name, lo32);
+ return false;
+}
+
static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v)
{
const struct btf_type *t;
@@ -46,6 +130,19 @@ static inline bool __COMPAT_read_enum(const char *type, const char *name, u64 *v
n = btf__name_by_offset(__COMPAT_vmlinux_btf, e[i].name_off);
SCX_BUG_ON(!n, "btf__name_by_offset()");
if (!strcmp(n, name)) {
+ /*
+ * Try to recover a 64-bit enum from an 8-byte
+ * BTF_KIND_ENUM that was encoded without ENUM64
+ * support (old pahole or
+ * --skip_encoding_btf_enum64). Only scx_*
+ * types are covered by the substitution table;
+ * non-scx types fall through to the raw value
+ * so this generic utility keeps working for
+ * them.
+ */
+ if (t->size == 8 && !strncmp(type, "scx_", 4))
+ return __COMPAT_recover_truncated_enum64(type, name,
+ (u32)e[i].val, v);
*v = e[i].val;
return true;
}
diff --git a/tools/sched_ext/include/scx/enum_defs.autogen.h b/tools/sched_ext/include/scx/enum_defs.autogen.h
index 19aa1de3e700..63b6b14b19bd 100644
--- a/tools/sched_ext/include/scx/enum_defs.autogen.h
+++ b/tools/sched_ext/include/scx/enum_defs.autogen.h
@@ -56,6 +56,10 @@
#define HAVE_SCX_DEQ_SLEEP
#define HAVE_SCX_DEQ_CORE_SCHED_EXEC
#define HAVE_SCX_DEQ_SCHED_CHANGE
+#define HAVE_SCX_DSP_NONE
+#define HAVE_SCX_DSP_LOCAL
+#define HAVE_SCX_DSP_PREV
+#define HAVE_SCX_DSP_RETRY
#define HAVE_SCX_DSQ_FLAG_BUILTIN
#define HAVE_SCX_DSQ_FLAG_LOCAL_ON
#define HAVE_SCX_DSQ_INVALID
@@ -188,7 +192,6 @@
#define HAVE_SCX_RQ_SUB_IDLE_RENOTIFY
#define HAVE_SCX_RQ_ROOT_IDLE_RENOTIFY
#define HAVE_SCX_RQ_IN_WAKEUP
-#define HAVE_SCX_RQ_IN_BALANCE
#define HAVE_SCX_RQ_IN_DISPATCH
#define HAVE_SCX_SCHED_PCPU_BYPASSING
#define HAVE_SCX_SLICE_OOB_DUR_BITS
diff --git a/tools/sched_ext/include/scx/enums_abi.autogen.h b/tools/sched_ext/include/scx/enums_abi.autogen.h
new file mode 100644
index 000000000000..d53899764f5a
--- /dev/null
+++ b/tools/sched_ext/include/scx/enums_abi.autogen.h
@@ -0,0 +1,223 @@
+/*
+ * WARNING: This file is autogenerated from gen_enum_defs.py [1].
+ *
+ * scx enumerator values from the vmlinux.h this tree is built against.
+ * Used as the substitution source when the running kernel's BTF lacks
+ * BTF_KIND_ENUM64 encoding and 64-bit enum values are truncated.
+ *
+ * [1] https://github.com/sched-ext/scx/blob/main/scripts/gen_enum_defs.py
+ */
+
+#ifndef __ENUMS_ABI_AUTOGEN_H__
+#define __ENUMS_ABI_AUTOGEN_H__
+
+struct __scx_enum_abi_val {
+ const char *type;
+ const char *name;
+ u64 val;
+};
+
+static const struct __scx_enum_abi_val __scx_enum_abi_vals[]
+ __attribute__((unused)) = {
+ { "scx_arena_consts", "SCX_ARENA_MIN_ORDER", 0x3LLU },
+ { "scx_arena_consts", "SCX_ARENA_GROW_PAGES", 0x4LLU },
+ { "scx_cap_flags", "__SCX_CAP_ENQ_IMMED", 0x0LLU },
+ { "scx_cap_flags", "__SCX_CAP_ENQ", 0x1LLU },
+ { "scx_cap_flags", "__SCX_CAP_PREEMPT", 0x2LLU },
+ { "scx_cap_flags", "__SCX_CAP_PERF", 0x3LLU },
+ { "scx_cap_flags", "__SCX_NR_CAPS", 0x4LLU },
+ { "scx_cap_flags", "__SCX_CAP_ALL", 0xfLLU },
+ { "scx_cap_flags", "SCX_CAP_ENQ_IMMED", 0x1LLU },
+ { "scx_cap_flags", "SCX_CAP_ENQ", 0x2LLU },
+ { "scx_cap_flags", "SCX_CAP_PREEMPT", 0x4LLU },
+ { "scx_cap_flags", "SCX_CAP_PERF", 0x8LLU },
+ { "scx_cap_flags", "SCX_CAP_BASE", 0x1LLU },
+ { "scx_cap_flags", "SCX_CAPS_REENQ_ON_LOSS", 0x3LLU },
+ { "scx_cid_consts", "SCX_CID_SHARD_SIZE_DFL", 0x18LLU },
+ { "scx_cid_consts", "SCX_CID_SHARD_MAX_CPUS", 0x200LLU },
+ { "scx_consts", "SCX_DSP_DFL_MAX_BATCH", 0x20LLU },
+ { "scx_consts", "SCX_DSP_MAX_LOOPS", 0x20LLU },
+ { "scx_consts", "SCX_WATCHDOG_MAX_TIMEOUT", 0x7530LLU },
+ { "scx_consts", "SCX_RESCUE_DFL_BW_PPT", 0x14LLU },
+ { "scx_consts", "SCX_RESCUE_MAX_BW_PPT", 0xfaLLU },
+ { "scx_consts", "SCX_RESCUE_DISABLE", 0xffffffffLLU },
+ { "scx_consts", "SCX_RESCUE_DFL_QUANTUM_US", 0x1388LLU },
+ { "scx_consts", "SCX_RESCUE_MIN_QUANTUM_US", 0x3e8LLU },
+ { "scx_consts", "SCX_RESCUE_MAX_QUANTUM_US", 0x186a0LLU },
+ { "scx_consts", "SCX_RESCUE_MIN_SLICE_US", 0x3e8LLU },
+ { "scx_consts", "SCX_RESCUE_OVERLOAD_MULT", 0x10LLU },
+ { "scx_consts", "SCX_RESCUE_MIN_OVERLOAD_MS", 0x3e8LLU },
+ { "scx_consts", "SCX_RESCUE_MAX_OVERLOAD_MS", 0x3a98LLU },
+ { "scx_consts", "SCX_TID_CHUNK", 0x400LLU },
+ { "scx_consts", "SCX_EXIT_BT_LEN", 0x40LLU },
+ { "scx_consts", "SCX_EXIT_MSG_LEN", 0x400LLU },
+ { "scx_consts", "SCX_EXIT_DUMP_DFL_LEN", 0x8000LLU },
+ { "scx_consts", "SCX_CPUPERF_ONE", 0x400LLU },
+ { "scx_consts", "SCX_TASK_ITER_BATCH", 0x20LLU },
+ { "scx_consts", "SCX_BYPASS_HOST_NTH", 0x2LLU },
+ { "scx_consts", "SCX_BYPASS_LB_DFL_INTV_US", 0x7a120LLU },
+ { "scx_consts", "SCX_BYPASS_LB_DONOR_PCT", 0x7dLLU },
+ { "scx_consts", "SCX_BYPASS_LB_MIN_DELTA_DIV", 0x4LLU },
+ { "scx_consts", "SCX_BYPASS_LB_BATCH", 0x100LLU },
+ { "scx_consts", "SCX_REENQ_MAX_REPEAT", 0x100LLU },
+ { "scx_consts", "SCX_SUB_MAX_DEPTH", 0x4LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_RT", 0x0LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_DL", 0x1LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_STOP", 0x2LLU },
+ { "scx_cpu_preempt_reason", "SCX_CPU_PREEMPT_UNKNOWN", 0x3LLU },
+ { "scx_deq_flags", "SCX_DEQ_SLEEP", 0x1LLU },
+ { "scx_deq_flags", "SCX_DEQ_CORE_SCHED_EXEC", 0x100000000LLU },
+ { "scx_deq_flags", "SCX_DEQ_SCHED_CHANGE", 0x200000000LLU },
+ { "scx_dsp_verdict", "SCX_DSP_NONE", 0x0LLU },
+ { "scx_dsp_verdict", "SCX_DSP_LOCAL", 0x1LLU },
+ { "scx_dsp_verdict", "SCX_DSP_PREV", 0x2LLU },
+ { "scx_dsp_verdict", "SCX_DSP_RETRY", 0x3LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_FLAG_BUILTIN", 0x8000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_FLAG_LOCAL_ON", 0x4000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_INVALID", 0x8000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_GLOBAL", 0x8000000000000001LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_LOCAL", 0x8000000000000002LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_BYPASS", 0x8000000000000003LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_REJECT", 0x8000000000000004LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_RESCUE", 0x8000000000000005LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_LOCAL_ON", 0xc000000000000000LLU },
+ { "scx_dsq_id_flags", "SCX_DSQ_LOCAL_CPU_MASK", 0xffffffffLLU },
+ { "scx_dsq_iter_flags", "SCX_DSQ_ITER_REV", 0x10000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_SLICE", 0x40000000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_HAS_VTIME", 0x80000000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_USER_FLAGS", 0x10000LLU },
+ { "scx_dsq_iter_flags", "__SCX_DSQ_ITER_ALL_FLAGS", 0xc0010000LLU },
+ { "scx_dsq_lnode_flags", "SCX_DSQ_LNODE_ITER_CURSOR", 0x1LLU },
+ { "scx_dsq_lnode_flags", "__SCX_DSQ_LNODE_PRIV_SHIFT", 0x10LLU },
+ { "scx_enable_state", "SCX_ENABLING", 0x0LLU },
+ { "scx_enable_state", "SCX_ENABLED", 0x1LLU },
+ { "scx_enable_state", "SCX_DISABLING", 0x2LLU },
+ { "scx_enable_state", "SCX_DISABLED", 0x3LLU },
+ { "scx_enq_flags", "SCX_ENQ_WAKEUP", 0x1LLU },
+ { "scx_enq_flags", "SCX_ENQ_HEAD", 0x10000LLU },
+ { "scx_enq_flags", "SCX_ENQ_CPU_SELECTED", 0x100000LLU },
+ { "scx_enq_flags", "SCX_ENQ_PREEMPT", 0x100000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_IMMED", 0x200000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_RESCUE", 0x400000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_REENQ", 0x10000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_LAST", 0x20000000000LLU },
+ { "scx_enq_flags", "__SCX_ENQ_INTERNAL_MASK", 0xff00000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_CLEAR_OPSS", 0x100000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_DSQ_PRIQ", 0x200000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_NESTED", 0x400000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_GDSQ_FALLBACK", 0x800000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_IGNORE_CAPS", 0x1000000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_APPLY_SLICE", 0x2000000000000000LLU },
+ { "scx_enq_flags", "SCX_ENQ_SLICE_DFL", 0x4000000000000000LLU },
+ { "scx_ent_dsq_flags", "SCX_TASK_DSQ_ON_PRIQ", 0x1LLU },
+ { "scx_ent_flags", "SCX_TASK_QUEUED", 0x1LLU },
+ { "scx_ent_flags", "SCX_TASK_IN_CUSTODY", 0x2LLU },
+ { "scx_ent_flags", "SCX_TASK_RESET_RUNNABLE_AT", 0x4LLU },
+ { "scx_ent_flags", "SCX_TASK_DEQD_FOR_SLEEP", 0x8LLU },
+ { "scx_ent_flags", "SCX_TASK_SUB_INIT", 0x10LLU },
+ { "scx_ent_flags", "SCX_TASK_IMMED", 0x20LLU },
+ { "scx_ent_flags", "SCX_TASK_PROTECTED", 0x40LLU },
+ { "scx_ent_flags", "SCX_TASK_STATE_SHIFT", 0x8LLU },
+ { "scx_ent_flags", "SCX_TASK_STATE_BITS", 0x3LLU },
+ { "scx_ent_flags", "SCX_TASK_STATE_MASK", 0x700LLU },
+ { "scx_ent_flags", "SCX_TASK_NONE", 0x0LLU },
+ { "scx_ent_flags", "SCX_TASK_INIT_BEGIN", 0x100LLU },
+ { "scx_ent_flags", "SCX_TASK_INIT", 0x200LLU },
+ { "scx_ent_flags", "SCX_TASK_READY", 0x300LLU },
+ { "scx_ent_flags", "SCX_TASK_ENABLED", 0x400LLU },
+ { "scx_ent_flags", "SCX_TASK_DEAD", 0x500LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_REASON_SHIFT", 0xcLLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_REASON_BITS", 0x3LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_REASON_MASK", 0x7000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_NONE", 0x0LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_KFUNC", 0x1000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_IMMED", 0x2000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_PREEMPTED", 0x3000LLU },
+ { "scx_ent_flags", "SCX_TASK_REENQ_CAP", 0x4000LLU },
+ { "scx_ent_flags", "SCX_TASK_CURSOR", 0xffffffff80000000LLU },
+ { "scx_exit_code", "SCX_ECODE_RSN_HOTPLUG", 0x100000000LLU },
+ { "scx_exit_code", "SCX_ECODE_RSN_CGROUP_OFFLINE", 0x200000000LLU },
+ { "scx_exit_code", "SCX_ECODE_ACT_RESTART", 0x1000000000000LLU },
+ { "scx_exit_flags", "SCX_EFLAG_INITIALIZED", 0x1LLU },
+ { "scx_exit_kind", "SCX_EXIT_NONE", 0x0LLU },
+ { "scx_exit_kind", "SCX_EXIT_DONE", 0x1LLU },
+ { "scx_exit_kind", "SCX_EXIT_UNREG", 0x40LLU },
+ { "scx_exit_kind", "SCX_EXIT_UNREG_BPF", 0x41LLU },
+ { "scx_exit_kind", "SCX_EXIT_UNREG_KERN", 0x42LLU },
+ { "scx_exit_kind", "SCX_EXIT_SYSRQ", 0x43LLU },
+ { "scx_exit_kind", "SCX_EXIT_PARENT", 0x44LLU },
+ { "scx_exit_kind", "SCX_EXIT_PARENT_KILL", 0x45LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR", 0x400LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_BPF", 0x401LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_STALL", 0x402LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_REENQ", 0x403LLU },
+ { "scx_exit_kind", "SCX_EXIT_ERROR_RESCUE", 0x404LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_UNLOCKED", 0x1LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_INIT_CIDS", 0x2LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_CPU_RELEASE", 0x4LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_DISPATCH", 0x8LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_ENQUEUE", 0x10LLU },
+ { "scx_kf_allow_flags", "SCX_KF_ALLOW_SELECT_CPU", 0x20LLU },
+ { "scx_kick_flags", "SCX_KICK_IDLE", 0x1LLU },
+ { "scx_kick_flags", "SCX_KICK_PREEMPT", 0x2LLU },
+ { "scx_kick_flags", "SCX_KICK_WAIT", 0x4LLU },
+ { "scx_opi", "SCX_OPI_BEGIN", 0x0LLU },
+ { "scx_opi", "SCX_OPI_NORMAL_BEGIN", 0x0LLU },
+ { "scx_opi", "SCX_OPI_NORMAL_END", 0x21LLU },
+ { "scx_opi", "SCX_OPI_CPU_HOTPLUG_BEGIN", 0x21LLU },
+ { "scx_opi", "SCX_OPI_CPU_HOTPLUG_END", 0x23LLU },
+ { "scx_opi", "SCX_OPI_END", 0x23LLU },
+ { "scx_ops_flags", "SCX_OPS_KEEP_BUILTIN_IDLE", 0x1LLU },
+ { "scx_ops_flags", "SCX_OPS_ENQ_LAST", 0x2LLU },
+ { "scx_ops_flags", "SCX_OPS_ENQ_EXITING", 0x4LLU },
+ { "scx_ops_flags", "SCX_OPS_SWITCH_PARTIAL", 0x8LLU },
+ { "scx_ops_flags", "SCX_OPS_ENQ_MIGRATION_DISABLED", 0x10LLU },
+ { "scx_ops_flags", "SCX_OPS_ALLOW_QUEUED_WAKEUP", 0x20LLU },
+ { "scx_ops_flags", "SCX_OPS_BUILTIN_IDLE_PER_NODE", 0x40LLU },
+ { "scx_ops_flags", "SCX_OPS_ALWAYS_ENQ_IMMED", 0x80LLU },
+ { "scx_ops_flags", "SCX_OPS_TID_TO_TASK", 0x100LLU },
+ { "scx_ops_flags", "SCX_OPS_ALL_FLAGS", 0x1ffLLU },
+ { "scx_ops_flags", "__SCX_OPS_INTERNAL_MASK", 0xff00000000000000LLU },
+ { "scx_ops_flags", "SCX_OPS_HAS_CPU_PREEMPT", 0x100000000000000LLU },
+ { "scx_ops_state", "SCX_OPSS_NONE", 0x0LLU },
+ { "scx_ops_state", "SCX_OPSS_QUEUEING", 0x1LLU },
+ { "scx_ops_state", "SCX_OPSS_QUEUED", 0x2LLU },
+ { "scx_ops_state", "SCX_OPSS_DISPATCHING", 0x3LLU },
+ { "scx_ops_state", "SCX_OPSS_QSEQ_SHIFT", 0x2LLU },
+ { "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_CORE", 0x1LLU },
+ { "scx_pick_idle_cpu_flags", "SCX_PICK_IDLE_IN_NODE", 0x2LLU },
+ { "scx_public_consts", "SCX_OPS_NAME_LEN", 0x80LLU },
+ { "scx_public_consts", "SCX_SLICE_DFL", 0x1312d00LLU },
+ { "scx_public_consts", "SCX_SLICE_BYPASS", 0x4c4b40LLU },
+ { "scx_public_consts", "SCX_SLICE_INF", 0xffffffffffffffffLLU },
+ { "scx_reenq_flags", "SCX_REENQ_ANY", 0x1LLU },
+ { "scx_reenq_flags", "SCX_REENQ_CAP_REVOKE", 0x2LLU },
+ { "scx_reenq_flags", "__SCX_REENQ_FILTER_MASK", 0xffffLLU },
+ { "scx_reenq_flags", "__SCX_REENQ_USER_MASK", 0x1LLU },
+ { "scx_reenq_flags", "SCX_REENQ_TSR_RQ_OPEN", 0x100000000LLU },
+ { "scx_reenq_flags", "SCX_REENQ_TSR_NOT_FIRST", 0x200000000LLU },
+ { "scx_reenq_flags", "__SCX_REENQ_TSR_MASK", 0xf00000000LLU },
+ { "scx_rq_flags", "SCX_RQ_ONLINE", 0x1LLU },
+ { "scx_rq_flags", "SCX_RQ_CAN_STOP_TICK", 0x2LLU },
+ { "scx_rq_flags", "SCX_RQ_CLK_VALID", 0x20LLU },
+ { "scx_rq_flags", "SCX_RQ_BAL_CB_PENDING", 0x40LLU },
+ { "scx_rq_flags", "SCX_RQ_SUB_IDLE_RENOTIFY", 0x80LLU },
+ { "scx_rq_flags", "SCX_RQ_ROOT_IDLE_RENOTIFY", 0x100LLU },
+ { "scx_rq_flags", "SCX_RQ_IN_WAKEUP", 0x10000LLU },
+ { "scx_rq_flags", "SCX_RQ_IN_DISPATCH", 0x20000LLU },
+ { "scx_sched_pcpu_flags", "SCX_SCHED_PCPU_BYPASSING", 0x1LLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_BITS", 0x2bLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_BITS", 0x14LLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_DUR_MASK", 0x7ffffffffffLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_SHIFT", 0x2bLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_ID_MASK", 0xfffffLLU },
+ { "scx_slice_oob_consts", "SCX_SLICE_OOB_PENDING", 0x8000000000000000LLU },
+ { "scx_tg_flags", "SCX_TG_ONLINE", 0x1LLU },
+ { "scx_tg_flags", "SCX_TG_INITED", 0x2LLU },
+ { "scx_tg_flags", "SCX_TG_SUB_INIT", 0x4LLU },
+ { "scx_wake_flags", "SCX_WAKE_FORK", 0x4LLU },
+ { "scx_wake_flags", "SCX_WAKE_TTWU", 0x8LLU },
+ { "scx_wake_flags", "SCX_WAKE_SYNC", 0x10LLU },
+};
+
+#endif /* __ENUMS_ABI_AUTOGEN_H__ */
diff --git a/tools/sched_ext/scx_central.bpf.c b/tools/sched_ext/scx_central.bpf.c
index 64dd60b3e922..65dae9e45400 100644
--- a/tools/sched_ext/scx_central.bpf.c
+++ b/tools/sched_ext/scx_central.bpf.c
@@ -299,6 +299,7 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
u64 now = scx_bpf_now();
u64 nr_to_kick = nr_queued;
s32 i, curr_cpu;
+ int ret;
curr_cpu = bpf_get_smp_processor_id();
if (timer_pinned && (curr_cpu != central_cpu)) {
@@ -332,7 +333,10 @@ static int central_timerfn(void *map, int *key, struct bpf_timer *timer)
scx_bpf_kick_cpu(cpu, SCX_KICK_PREEMPT);
}
- bpf_timer_start(timer, TIMER_INTERVAL_NS, BPF_F_TIMER_CPU_PIN);
+ ret = bpf_timer_start(timer, TIMER_INTERVAL_NS,
+ timer_pinned ? BPF_F_TIMER_CPU_PIN : 0);
+ if (ret)
+ scx_bpf_error("bpf_timer_start failed (%d)", ret);
__sync_fetch_and_add(&nr_timers, 1);
return 0;
}
diff --git a/tools/sched_ext/scx_flatcg.bpf.c b/tools/sched_ext/scx_flatcg.bpf.c
index 64cf4dd964d6..454ebb820c5e 100644
--- a/tools/sched_ext/scx_flatcg.bpf.c
+++ b/tools/sched_ext/scx_flatcg.bpf.c
@@ -937,7 +937,7 @@ void BPF_STRUCT_OPS(fcg_cgroup_move, struct task_struct *p,
if (!(from_cgc = find_cgrp_ctx(from)) || !(to_cgc = find_cgrp_ctx(to)))
return;
- delta = time_delta(p->scx.dsq_vtime, from_cgc->tvtime_now);
+ delta = (s64)(p->scx.dsq_vtime - from_cgc->tvtime_now);
scx_bpf_task_set_dsq_vtime(p, to_cgc->tvtime_now + delta);
}
diff --git a/tools/sched_ext/scx_qmap.bpf.c b/tools/sched_ext/scx_qmap.bpf.c
index 5bb8b90a275a..9f6e61d7ca07 100644
--- a/tools/sched_ext/scx_qmap.bpf.c
+++ b/tools/sched_ext/scx_qmap.bpf.c
@@ -1246,7 +1246,8 @@ static int monitor_timerfn(void *map, int *key, struct bpf_timer *timer)
scx_read_event(&events, SCX_EV_BYPASS_ACTIVATE));
}
- bpf_timer_start(timer, ONE_SEC_IN_NS, 0);
+ if (bpf_timer_start(timer, ONE_SEC_IN_NS, 0))
+ scx_bpf_error("failed to re-arm stats timer");
return 0;
}
@@ -1268,7 +1269,8 @@ struct {
static int lowpri_timerfn(void *map, int *key, struct bpf_timer *timer)
{
scx_bpf_dsq_reenq(LOWPRI_DSQ, 0);
- bpf_timer_start(timer, LOWPRI_INTV_NS, 0);
+ if (bpf_timer_start(timer, LOWPRI_INTV_NS, 0))
+ scx_bpf_error("failed to re-arm lowpri timer");
return 0;
}
@@ -1747,7 +1749,8 @@ static void rr_advance(void)
static int round_robin_timerfn(void *map, int *key, struct bpf_timer *timer)
{
rr_advance();
- bpf_timer_start(timer, round_robin_ns, 0);
+ if (bpf_timer_start(timer, round_robin_ns, 0))
+ scx_bpf_error("failed to re-arm round-robin timer");
return 0;
}
diff --git a/tools/testing/selftests/alsa/mixer-test.c b/tools/testing/selftests/alsa/mixer-test.c
index a329f901c5ed..0857d64c322a 100644
--- a/tools/testing/selftests/alsa/mixer-test.c
+++ b/tools/testing/selftests/alsa/mixer-test.c
@@ -319,8 +319,8 @@ static bool ctl_value_index_valid(struct ctl_data *ctl,
/* Only check step size if there is one and we're in bounds */
if (snd_ctl_elem_info_get_step(ctl->info) &&
- (int_val - snd_ctl_elem_info_get_min(ctl->info) %
- snd_ctl_elem_info_get_step(ctl->info))) {
+ (int_val - snd_ctl_elem_info_get_min(ctl->info)) %
+ snd_ctl_elem_info_get_step(ctl->info)) {
ksft_print_msg("%s.%d value %ld invalid for step %ld minimum %ld\n",
ctl->name, index, int_val,
snd_ctl_elem_info_get_step(ctl->info),
diff --git a/tools/testing/selftests/cgroup/test_core.c b/tools/testing/selftests/cgroup/test_core.c
index e9bee164bb70..20d2b63774c3 100644
--- a/tools/testing/selftests/cgroup/test_core.c
+++ b/tools/testing/selftests/cgroup/test_core.c
@@ -919,7 +919,6 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), &nsdelegate)) {
if (setup_named_v1_root(root, sizeof(root), CG_NAMED_NAME))
ksft_exit_skip("cgroup v2 isn't mounted and could not setup named v1 hierarchy\n");
@@ -932,6 +931,7 @@ int main(int argc, char *argv[])
ksft_exit_skip("Failed to set memory controller\n");
post_v2_setup:
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_cpu.c b/tools/testing/selftests/cgroup/test_cpu.c
index f9f7017d9299..735a53bb222b 100644
--- a/tools/testing/selftests/cgroup/test_cpu.c
+++ b/tools/testing/selftests/cgroup/test_cpu.c
@@ -832,7 +832,6 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -840,6 +839,7 @@ int main(int argc, char *argv[])
if (cg_write(root, "cgroup.subtree_control", "+cpu"))
ksft_exit_skip("Failed to set cpu controller\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_cpuset.c b/tools/testing/selftests/cgroup/test_cpuset.c
index 8c2d4d4ef1fc..3dfadd280c1c 100644
--- a/tools/testing/selftests/cgroup/test_cpuset.c
+++ b/tools/testing/selftests/cgroup/test_cpuset.c
@@ -497,7 +497,6 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -505,6 +504,7 @@ int main(int argc, char *argv[])
if (cg_write(root, "cgroup.subtree_control", "+cpuset"))
ksft_exit_skip("Failed to set cpuset controller\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index da8f7b920178..131d8b4551ef 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -797,7 +797,6 @@ check_isolcpus()
EXPECTED_ISOLCPUS=$1
ISCPUS=${CGROUP2}/cpuset.cpus.isolated
ISOLCPUS=$(cat $ISCPUS)
- HKICPUS=$(cat /sys/devices/system/cpu/isolated)
LASTISOLCPU=
SCHED_DOMAINS=/sys/kernel/debug/sched/domains
if [[ $EXPECTED_ISOLCPUS = . ]]
@@ -836,11 +835,6 @@ check_isolcpus()
EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN
#
- # The inverse of HK_TYPE_DOMAIN cpumask in $HKICPUS should match $ISOLCPUS
- #
- [[ "$ISOLCPUS" != "$HKICPUS" ]] && return 1
-
- #
# Use the sched domain in debugfs to check isolated CPUs, if available
#
[[ -d $SCHED_DOMAINS ]] || return 0
@@ -1162,6 +1156,63 @@ test_isolated()
}
#
+# Select an online CPU isolated from scheduler domains at boot.
+# $1: test name used in the skip message
+#
+get_boot_isolated_cpu()
+{
+ TEST_NAME=$1
+ BOOT_ISOLATED_FILE=/sys/devices/system/cpu/isolated
+
+ [[ -r $BOOT_ISOLATED_FILE ]] || {
+ echo "$TEST_NAME test SKIPPED: boot isolation state unavailable"
+ return 1
+ }
+ BOOT_CPUS=$(cat $BOOT_ISOLATED_FILE)
+ [[ -n "$BOOT_CPUS" ]] || {
+ echo "$TEST_NAME test SKIPPED: no boot-isolated CPU"
+ return 1
+ }
+
+ BOOT_CPU=$(echo "$BOOT_CPUS" | sed -e 's/[,-].*//')
+ CPU_ONLINE=/sys/devices/system/cpu/cpu${BOOT_CPU}/online
+ [[ ! -e $CPU_ONLINE || $(cat $CPU_ONLINE) -eq 1 ]] || {
+ echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is offline"
+ return 1
+ }
+}
+
+#
+# A CPU isolated at boot must stay isolated after it is released by a dynamic
+# isolated partition.
+#
+test_boot_isolated()
+{
+ TEST_NAME="Boot-isolated CPU partition release"
+ get_boot_isolated_cpu "$TEST_NAME" || return 0
+ echo "Running $TEST_NAME test ..."
+
+ cd $CGROUP2/test
+ echo member > cpuset.cpus.partition
+ echo $BOOT_CPU > cpuset.cpus
+ [[ $(cat cpuset.cpus.effective) = "$BOOT_CPU" ]] || {
+ echo "$TEST_NAME test SKIPPED: CPU $BOOT_CPU is unavailable"
+ echo "" > cpuset.cpus
+ cd $CGROUP2
+ return 0
+ }
+ test_partition isolated
+ test_partition member
+ check_isolcpus "." || {
+ echo "Boot-isolated CPU $BOOT_CPU was lost after partition release"
+ exit 1
+ }
+ echo "" > cpuset.cpus
+ cd $CGROUP2
+ echo "$TEST_NAME test PASSED."
+}
+
+#
# Wait for inotify event for the given file and read it
# $1: cgroup file to wait for
# $2: file to store the read result
@@ -1232,5 +1283,6 @@ trap cleanup 0 2 3 6
run_state_test TEST_MATRIX
run_remote_state_test REMOTE_TEST_MATRIX
test_isolated
+test_boot_isolated
test_inotify
echo "All tests PASSED."
diff --git a/tools/testing/selftests/cgroup/test_freezer.c b/tools/testing/selftests/cgroup/test_freezer.c
index 0569e93fa6b0..f28bb02e9783 100644
--- a/tools/testing/selftests/cgroup/test_freezer.c
+++ b/tools/testing/selftests/cgroup/test_freezer.c
@@ -1491,9 +1491,9 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_kill.c b/tools/testing/selftests/cgroup/test_kill.c
index f6cd23a8ecc7..bac1ddd8cb94 100644
--- a/tools/testing/selftests/cgroup/test_kill.c
+++ b/tools/testing/selftests/cgroup/test_kill.c
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <unistd.h>
#include "kselftest.h"
@@ -261,6 +262,59 @@ cleanup:
return ret;
}
+/*
+ * Test that a cgroup that was killed in the past can still be the target
+ * of clone3(CLONE_INTO_CGROUP): writing cgroup.kill must only kill the
+ * tasks in the cgroup at the time of the write, not tasks cloned into
+ * it afterwards.
+ */
+static int test_cgkill_clone_into_killed(const char *root)
+{
+ pid_t pid;
+ int cgroup_fd = -EBADF;
+ int ret = KSFT_FAIL;
+ char *cgroup = NULL;
+
+ cgroup = cg_name(root, "cg_test_clone_into_killed");
+ if (!cgroup)
+ goto cleanup;
+
+ if (cg_create(cgroup))
+ goto cleanup;
+
+ /* Kill the cgroup while it is still empty. */
+ if (cg_write(cgroup, "cgroup.kill", "1"))
+ goto cleanup;
+
+ cgroup_fd = dirfd_open_opath(cgroup);
+ if (cgroup_fd < 0)
+ goto cleanup;
+
+ pid = clone_into_cgroup(cgroup_fd);
+ if (pid < 0) {
+ if (errno == ENOSYS)
+ ret = KSFT_SKIP;
+ goto cleanup;
+ }
+
+ if (pid == 0)
+ exit(EXIT_SUCCESS);
+
+ /* The child must not be SIGKILLed; it has to exit cleanly. */
+ if (clone_reap(pid, WEXITED) != EXIT_SUCCESS)
+ goto cleanup;
+
+ ret = KSFT_PASS;
+
+cleanup:
+ if (cgroup_fd >= 0)
+ close(cgroup_fd);
+ if (cgroup)
+ cg_destroy(cgroup);
+ free(cgroup);
+ return ret;
+}
+
#define T(x) { x, #x }
struct cgkill_test {
int (*fn)(const char *root);
@@ -269,6 +323,7 @@ struct cgkill_test {
T(test_cgkill_simple),
T(test_cgkill_tree),
T(test_cgkill_forkbomb),
+ T(test_cgkill_clone_into_killed),
};
#undef T
@@ -278,9 +333,9 @@ int main(int argc, char *argv[])
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_kmem.c b/tools/testing/selftests/cgroup/test_kmem.c
index 1db0ba1226b9..437f2d35f205 100644
--- a/tools/testing/selftests/cgroup/test_kmem.c
+++ b/tools/testing/selftests/cgroup/test_kmem.c
@@ -145,7 +145,7 @@ static int cg_run_in_subcgroups(const char *parent,
return -1;
}
- if (cg_run(child, fn, NULL)) {
+ if (cg_run(child, fn, arg)) {
cg_destroy(child);
free(child);
return -1;
@@ -426,7 +426,6 @@ int main(int argc, char **argv)
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -441,6 +440,7 @@ int main(int argc, char **argv)
if (cg_write(root, "cgroup.subtree_control", "+memory"))
ksft_exit_skip("Failed to set memory controller\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c
index 0ebf796f3cff..3a84d068fbf3 100644
--- a/tools/testing/selftests/cgroup/test_memcontrol.c
+++ b/tools/testing/selftests/cgroup/test_memcontrol.c
@@ -1798,7 +1798,6 @@ int main(int argc, char **argv)
page_size = BUF_SIZE;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -1823,6 +1822,7 @@ int main(int argc, char **argv)
ksft_exit_skip("Failed to query cgroup mount option\n");
has_localevents = proc_status;
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_pids.c b/tools/testing/selftests/cgroup/test_pids.c
index 9a387c815d2c..710109b53dfe 100644
--- a/tools/testing/selftests/cgroup/test_pids.c
+++ b/tools/testing/selftests/cgroup/test_pids.c
@@ -148,7 +148,6 @@ int main(int argc, char **argv)
char root[PATH_MAX];
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -163,6 +162,7 @@ int main(int argc, char **argv)
if (cg_write(root, "cgroup.subtree_control", "+pids"))
ksft_exit_skip("Failed to set pids controller\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (int i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/cgroup/test_zswap.c b/tools/testing/selftests/cgroup/test_zswap.c
index f7b4c4370db6..609c48f38524 100644
--- a/tools/testing/selftests/cgroup/test_zswap.c
+++ b/tools/testing/selftests/cgroup/test_zswap.c
@@ -819,7 +819,6 @@ int main(int argc, char **argv)
page_size = BUF_SIZE;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests));
if (cg_find_unified_root(root, sizeof(root), NULL))
ksft_exit_skip("cgroup v2 isn't mounted\n");
@@ -836,6 +835,7 @@ int main(int argc, char **argv)
if (cg_write(root, "cgroup.subtree_control", "+memory"))
ksft_exit_skip("Failed to set memory controller\n");
+ ksft_set_plan(ARRAY_SIZE(tests));
for (i = 0; i < ARRAY_SIZE(tests); i++) {
switch (tests[i].fn(root)) {
case KSFT_PASS:
diff --git a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
index fc9694fc4e89..1d49df671919 100644
--- a/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
+++ b/tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c
@@ -390,6 +390,116 @@ static void test_alloc_errors(char *heap_name)
close(heap_fd);
}
+/*
+ * count_open_fds - return the number of open file descriptors.
+ *
+ * The fd opened by opendir() itself is counted, but since it is opened
+ * and closed within each call, it cancels out when comparing two counts.
+ * Returns -1 on error.
+ */
+static int count_open_fds(void)
+{
+ DIR *d = opendir("/proc/self/fd");
+ struct dirent *de;
+ int count = 0;
+
+ if (!d)
+ return -1;
+
+ while ((de = readdir(d)))
+ if (de->d_name[0] != '.')
+ count++;
+ closedir(d);
+ return count;
+}
+
+/*
+ * test_alloc_no_fd_leak_on_efault - verify no fd is leaked when
+ * copy_to_user() fails during DMA_HEAP_IOCTL_ALLOC.
+ *
+ * The bug: dma_buf_fd() called fd_install() before copy_to_user().
+ * If copy_to_user() then failed (e.g. via mprotect), the fd was
+ * silently installed in the fd table but never returned to userspace.
+ *
+ * The fix: reserve the fd with get_unused_fd_flags() first, attempt
+ * copy_to_user(), and only call fd_install() on success.
+ *
+ * We trigger the failure by placing the ioctl argument in a private
+ * anonymous page and flipping it to PROT_READ before the ioctl.
+ * Inside the kernel, copy_from_user() reads from the page (reads are
+ * allowed under PROT_READ, so it succeeds), but copy_to_user() that
+ * writes the fd number back faults, returning -EFAULT. We then
+ * count open file descriptors before and after; with the bug an extra
+ * fd is left in the table.
+ */
+static void test_alloc_no_fd_leak_on_efault(char *heap_name)
+{
+ int heap_fd = -1;
+ int fd_before, fd_after;
+ int ret;
+ long page_size;
+ struct dma_heap_allocation_data *req;
+
+ ksft_print_msg("Testing fd leak when copy_to_user() fails:\n");
+
+ heap_fd = dmabuf_heap_open(heap_name);
+
+ page_size = sysconf(_SC_PAGESIZE);
+
+ /*
+ * Place the ioctl argument in its own private anonymous page so
+ * we can flip its protection independently.
+ */
+ req = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (req == MAP_FAILED) {
+ ksft_test_result_fail("mmap failed: %s\n", strerror(errno));
+ goto out;
+ }
+
+ memset(req, 0, sizeof(*req));
+ req->len = page_size;
+ req->fd_flags = O_RDWR | O_CLOEXEC;
+
+ fd_before = count_open_fds();
+ if (fd_before < 0) {
+ ksft_test_result_fail("count_open_fds: %s\n", strerror(errno));
+ munmap(req, page_size);
+ goto out;
+ }
+
+ /*
+ * Make the page read-only so copy_to_user() will fault. The
+ * ioctl must fail with -1; if it returns success the test setup
+ * is broken (mprotect is synchronous, so there is no race).
+ */
+ mprotect(req, page_size, PROT_READ);
+
+ ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, req);
+
+ /* Re-allow writes so munmap can clean up */
+ mprotect(req, page_size, PROT_READ | PROT_WRITE);
+ munmap(req, page_size);
+
+ if (ret != -1) {
+ ksft_test_result_fail("ioctl returned %d, expected -1 EFAULT\n",
+ ret);
+ goto out;
+ }
+
+ fd_after = count_open_fds();
+ if (fd_after < 0) {
+ ksft_test_result_fail("count_open_fds: %s\n", strerror(errno));
+ goto out;
+ }
+
+ ksft_test_result(fd_before == fd_after,
+ "fd leak on EFAULT: before=%d after=%d\n",
+ fd_before, fd_after);
+out:
+ close(heap_fd);
+}
+
static int numer_of_heaps(void)
{
DIR *d = opendir(DEVPATH);
@@ -420,7 +530,7 @@ int main(void)
return KSFT_SKIP;
}
- ksft_set_plan(11 * numer_of_heaps());
+ ksft_set_plan(12 * numer_of_heaps());
while ((dir = readdir(d))) {
if (!strncmp(dir->d_name, ".", 2))
@@ -435,6 +545,7 @@ int main(void)
test_alloc_zeroed(dir->d_name, ONE_MEG);
test_alloc_compat(dir->d_name);
test_alloc_errors(dir->d_name);
+ test_alloc_no_fd_leak_on_efault(dir->d_name);
}
closedir(d);
diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index b851339308c2..7ab86296ff23 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -67,14 +67,17 @@ struct test_program {
int insert_head;
};
#define LOAD_PROGRAMS(progs) \
- load_programs(progs, ARRAY_SIZE(progs), _metadata, self, variant)
+ load_programs(progs, ARRAY_SIZE(progs), false, _metadata, self, variant)
+#define LOAD_PROGRAMS_MAY_FAIL(progs) \
+ load_programs(progs, ARRAY_SIZE(progs), true, _metadata, self, variant)
#define LOAD_BPF \
- load_programs(NULL, 0, _metadata, self, variant)
-static void load_programs(const struct test_program programs[],
- const size_t progs_count,
- struct __test_metadata *_metadata,
- FIXTURE_DATA(hid_bpf) * self,
- const FIXTURE_VARIANT(hid_bpf) * variant)
+ load_programs(NULL, 0, false, _metadata, self, variant)
+static int load_programs(const struct test_program programs[],
+ const size_t progs_count,
+ bool load_may_fail,
+ struct __test_metadata *_metadata,
+ FIXTURE_DATA(hid_bpf) * self,
+ const FIXTURE_VARIANT(hid_bpf) * variant)
{
struct bpf_map *iter_map;
int err = -EINVAL;
@@ -128,6 +131,9 @@ static void load_programs(const struct test_program programs[],
}
err = hid__load(self->skel);
+ if (err && load_may_fail)
+ return err;
+
ASSERT_OK(err) TH_LOG("hid_skel_load failed: %d", err);
for (int i = 0; i < progs_count; i++) {
@@ -147,6 +153,7 @@ static void load_programs(const struct test_program programs[],
self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
+ return 0;
}
/*
@@ -904,11 +911,39 @@ TEST_F(hid_bpf, test_rdesc_fixup_get_data_overflow)
{ .name = "hid_rdesc_fixup_get_data_overflow" },
};
- LOAD_PROGRAMS(progs);
+ /* newer verifier can detect the overflow at load time */
+ if (LOAD_PROGRAMS_MAY_FAIL(progs))
+ return;
ASSERT_EQ(self->skel->bss->get_data_overflow_check, 1);
}
+TEST_F(hid_bpf, test_rdesc_fixup_change_uniq_name_phys)
+{
+ const struct test_program progs[] = {
+ { .name = "hid_rdesc_fixup_change_uniq_name_phys" },
+ };
+ char expected[256], buf[256] = {};
+ int err;
+
+ LOAD_PROGRAMS(progs);
+
+ err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(buf)), buf);
+ ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWNAME");
+ ASSERT_STREQ("name coming from bpf", buf);
+
+ snprintf(expected, sizeof(expected), "%d phys:coming:from:bpf", self->hid.dev_id);
+
+ err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(buf)), buf);
+ ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWPHYS");
+ ASSERT_STREQ(expected, buf);
+
+ err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(buf)), buf);
+ ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ");
+ ASSERT_STREQ("uniq:coming:from:bpf", buf);
+
+}
+
static int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c
index b21fbb13c926..361dc7eaad22 100644
--- a/tools/testing/selftests/hid/progs/hid.c
+++ b/tools/testing/selftests/hid/progs/hid.c
@@ -255,6 +255,32 @@ struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
.hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
};
+SEC("?struct_ops.s/hid_rdesc_fixup")
+int BPF_PROG(hid_rdesc_fixup_change_uniq_name_phys, struct hid_bpf_ctx *hid_ctx)
+{
+#define HID_BPF_MEMCPY(target, str) \
+ __builtin_memcpy(target, str, sizeof(str))
+
+ HID_BPF_MEMCPY(hid_ctx->hid->name, "name coming from bpf");
+ HID_BPF_MEMCPY(hid_ctx->hid->uniq, "uniq:coming:from:bpf");
+ /* hid_bpf relies on a phys being a rand % 1024 */
+ for (int i = 0; i < 5; i++) {
+ if (!hid_ctx->hid->phys[i]) {
+ HID_BPF_MEMCPY(hid_ctx->hid->phys + i, " phys:coming:from:bpf");
+ break;
+ }
+ }
+
+#undef HID_BPF_MEMCPY
+
+ return 0;
+}
+
+SEC(".struct_ops.link")
+struct hid_bpf_ops rdesc_fixup_change_uniq_name_phys = {
+ .hid_rdesc_fixup = (void *)hid_rdesc_fixup_change_uniq_name_phys,
+};
+
SEC("?struct_ops/hid_device_event")
int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
{
diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
index cdca912f3afd..05698793762a 100644
--- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
+++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
@@ -61,6 +61,9 @@ enum hid_report_type {
struct hid_device {
unsigned int id;
+ char name[128];
+ char phys[64];
+ char uniq[64];
} __attribute__((preserve_access_index));
struct bpf_wq {
diff --git a/tools/testing/selftests/mm/memfd_secret.c b/tools/testing/selftests/mm/memfd_secret.c
index aac4f795c327..c55d84c5e613 100644
--- a/tools/testing/selftests/mm/memfd_secret.c
+++ b/tools/testing/selftests/mm/memfd_secret.c
@@ -57,33 +57,6 @@ static void test_file_apis(int fd)
pass("file IO is blocked as expected\n");
}
-static void test_mlock_limit(int fd)
-{
- size_t len;
- char *mem;
-
- len = mlock_limit_cur;
- if (len % page_size != 0)
- len = (len/page_size) * page_size;
-
- mem = mmap(NULL, len, prot, mode, fd, 0);
- if (mem == MAP_FAILED) {
- fail("unable to mmap secret memory\n");
- return;
- }
- munmap(mem, len);
-
- len = mlock_limit_max * 2;
- mem = mmap(NULL, len, prot, mode, fd, 0);
- if (mem != MAP_FAILED) {
- fail("unexpected mlock limit violation\n");
- munmap(mem, len);
- return;
- }
-
- pass("mlock limit is respected\n");
-}
-
static void test_vmsplice(int fd, const char *desc)
{
ssize_t transferred;
@@ -297,7 +270,7 @@ static void prepare(void)
strerror(errno));
}
-#define NUM_TESTS 6
+#define NUM_TESTS 5
int main(int argc, char *argv[])
{
@@ -319,7 +292,6 @@ int main(int argc, char *argv[])
if (ftruncate(fd, page_size))
ksft_exit_fail_msg("ftruncate failed: %s\n", strerror(errno));
- test_mlock_limit(fd);
test_file_apis(fd);
/*
* We have to run the first vmsplice test before any secretmem page was
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 0f5c178bc224..517c09d60bef 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -28,6 +28,7 @@ TEST_PROGS := \
double_udp_encap.sh \
drop_monitor_tests.sh \
ecmp_rehash.sh \
+ exception_cache.sh \
fcnal-ipv4.sh \
fcnal-ipv6.sh \
fcnal-other.sh \
diff --git a/tools/testing/selftests/net/exception_cache.sh b/tools/testing/selftests/net/exception_cache.sh
new file mode 100755
index 000000000000..8d3eed5c532a
--- /dev/null
+++ b/tools/testing/selftests/net/exception_cache.sh
@@ -0,0 +1,521 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Test that the state of the route exception cache after an ICMP error is
+# processed does not depend on whether the quoted packet was matched to a
+# socket. Otherwise, an off-path attacker can probe the cache to discover the
+# ephemeral port used by a connected UDP socket.
+#
+# When the quoted packet is not matched to a socket, the same exception is
+# created as when it is matched, so that neither its presence nor its contents
+# reveal the result of socket matching.
+#
+# +----+
+# +---------| r1 |
+# | +----+
+# +----+ +--------+ | .1
+# | h1 |---| bridge | | 198.51.100.0/30
+# +----+ +--------+ | 2001:db8:2::/64
+# .1 | | .2
+# | +----+ +----+
+# +---------| r2 |-----------| h2 |
+# .2 .3 +----+ .1 .2 +----+
+# 203.0.113.0/24
+# 2001:db8:3::/64
+# 192.0.2.0/24
+# 2001:db8:1::/64
+#
+# Traffic from h1 to h2 is routed via r1, which reaches h2's network via r2
+# over the point-to-point link. The MTU of the r2 - h2 link is lowered so that
+# r2 emits ICMP errors towards h1.
+#
+# For the redirect tests r1's route to h2's network is replaced with one via r2
+# on the shared segment, so that r1 forwards the packet back to the segment it
+# arrived from and emits a redirect towards h1.
+#
+# The packets that provoke the ICMP errors are injected with a packet socket so
+# that no socket is ever associated with them. A socket is created separately,
+# with socat, when a test needs the ICMP error to be matched.
+
+# shellcheck disable=SC1091,SC2034,SC2154,SC2329
+source lib.sh
+
+require_command jq
+require_command mausezahn
+require_command nstat
+require_command socat
+
+ALL_TESTS="
+ pmtu_no_socket_ipv4
+ pmtu_no_socket_ipv6
+ pmtu_socket_ipv4
+ pmtu_socket_ipv6
+ pmtu_omit_ipv4
+ pmtu_omit_ipv6
+ redirect_no_socket_ipv4
+ redirect_no_socket_ipv6
+ redirect_socket_ipv4
+ redirect_socket_ipv6
+"
+
+# Shared segment.
+H1_ADDR4=192.0.2.1
+R1_ADDR4=192.0.2.2
+R2_ADDR4=192.0.2.3
+H1_ADDR6=2001:db8:1::1
+R1_ADDR6=2001:db8:1::2
+R2_ADDR6=2001:db8:1::3
+
+# r1 - r2 link.
+R2_R1_ADDR4=198.51.100.2
+R2_R1_ADDR6=2001:db8:2::2
+
+# r2 - h2 link.
+H2_ADDR4=203.0.113.2
+H2_NET4=203.0.113.0/24
+H2_ADDR6=2001:db8:3::2
+H2_NET6=2001:db8:3::/64
+
+SPORT=12345
+DPORT=54321
+
+# The MTU of the shared segment and of the r1 - r2 link. Large enough for the
+# injected packets to reach r2 intact.
+SEGMENT_MTU=2000
+# Size of the injected packets. The PMTU tests need a size that exceeds every
+# MTU used for the r2 - h2 link, so that r2 responds with an ICMP error. The
+# redirect tests need a size that does not, otherwise r2 would respond with an
+# ICMP error in addition to the redirect emitted by r1.
+PMTU_PACKET_SIZE=1800
+REDIRECT_PACKET_SIZE=100
+
+# The MTUs used for the r2 - h2 link. All of them must be at least
+# IPV6_MIN_MTU, otherwise IPv6 silently ignores the error instead of creating
+# an exception.
+MTU_MID=1400
+MTU_LOW=1300
+
+# Values for the IP{,V6}_MTU_DISCOVER socket option.
+PMTUDISC_DONT=0
+PMTUDISC_OMIT=5
+
+SOCAT_PID=
+
+linklocal_get()
+{
+ local ns=$1; shift
+ local dev=$1; shift
+
+ ip -n "$ns" -j -6 addr show dev "$dev" | \
+ jq -r '.[]["addr_info"][] | select(.scope == "link") | .local'
+}
+
+linklocal_exists()
+{
+ local ns=$1; shift
+ local dev=$1; shift
+
+ [ -n "$(linklocal_get "$ns" "$dev")" ]
+}
+
+family_vars_set()
+{
+ local family=$1; shift
+
+ FAMILY=$family
+
+ if [ "$family" -eq 4 ]; then
+ H1_ADDR=$H1_ADDR4
+ H2_ADDR=$H2_ADDR4
+ MZ_FAMILY_OPT=()
+ # Without the Don't Fragment bit set r2 fragments the packet
+ # instead of reporting the MTU of the next hop.
+ MZ_IP_OPTS="df,"
+ SOCAT_DST="UDP4-CONNECT:$H2_ADDR4:$DPORT"
+ SOCAT_BIND="bind=$H1_ADDR4:$SPORT"
+ SOCAT_PMTUDISC="ip-mtu-discover"
+ else
+ H1_ADDR=$H1_ADDR6
+ H2_ADDR=$H2_ADDR6
+ MZ_FAMILY_OPT=(-6)
+ MZ_IP_OPTS=
+ SOCAT_DST="UDP6-CONNECT:[$H2_ADDR6]:$DPORT"
+ SOCAT_BIND="bind=[$H1_ADDR6]:$SPORT"
+ SOCAT_PMTUDISC="ipv6-mtu-discover"
+ fi
+}
+
+topology_setup()
+{
+ local ns
+
+ setup_ns h1 r1 r2 h2 sw
+ defer cleanup_all_ns
+
+ # Link-local addresses are generated from the MAC address and read
+ # back during setup, so request that generation mode explicitly and
+ # make the addresses available as soon as the devices are brought up.
+ for ns in "$h1" "$r1" "$r2" "$h2" "$sw"; do
+ ip netns exec "$ns" sysctl -qw \
+ net.ipv6.conf.default.addr_gen_mode=0 \
+ net.ipv6.conf.default.accept_dad=0 \
+ net.ipv6.conf.all.accept_dad=0
+ done
+
+ ip -n "$sw" link add name br0 type bridge
+ ip -n "$sw" link set dev br0 mtu "$SEGMENT_MTU" up
+
+ ip -n "$h1" link add name eth0 mtu "$SEGMENT_MTU" type veth \
+ peer name swp1 mtu "$SEGMENT_MTU" netns "$sw"
+ ip -n "$r1" link add name eth0 mtu "$SEGMENT_MTU" type veth \
+ peer name swp2 mtu "$SEGMENT_MTU" netns "$sw"
+ ip -n "$r2" link add name eth0 mtu "$SEGMENT_MTU" type veth \
+ peer name swp3 mtu "$SEGMENT_MTU" netns "$sw"
+ ip -n "$r1" link add name eth1 mtu "$SEGMENT_MTU" type veth \
+ peer name eth1 mtu "$SEGMENT_MTU" netns "$r2"
+ ip -n "$r2" link add name eth2 type veth peer name eth0 netns "$h2"
+
+ ip -n "$sw" link set dev swp1 master br0 up
+ ip -n "$sw" link set dev swp2 master br0 up
+ ip -n "$sw" link set dev swp3 master br0 up
+
+ ip -n "$h1" link set dev eth0 up
+ ip -n "$r1" link set dev eth0 up
+ ip -n "$r1" link set dev eth1 up
+ ip -n "$r2" link set dev eth0 up
+ ip -n "$r2" link set dev eth1 up
+ ip -n "$r2" link set dev eth2 up
+ ip -n "$h2" link set dev eth0 up
+
+ ip -n "$h1" address add "$H1_ADDR4/24" dev eth0
+ ip -n "$r1" address add "$R1_ADDR4/24" dev eth0
+ ip -n "$r2" address add "$R2_ADDR4/24" dev eth0
+ ip -n "$r1" address add 198.51.100.1/30 dev eth1
+ ip -n "$r2" address add "$R2_R1_ADDR4/30" dev eth1
+ ip -n "$r2" address add 203.0.113.1/24 dev eth2
+ ip -n "$h2" address add "$H2_ADDR4/24" dev eth0
+
+ ip -n "$h1" -6 address add "$H1_ADDR6/64" dev eth0 nodad
+ ip -n "$r1" -6 address add "$R1_ADDR6/64" dev eth0 nodad
+ ip -n "$r2" -6 address add "$R2_ADDR6/64" dev eth0 nodad
+ ip -n "$r1" -6 address add 2001:db8:2::1/64 dev eth1 nodad
+ ip -n "$r2" -6 address add "$R2_R1_ADDR6/64" dev eth1 nodad
+ ip -n "$r2" -6 address add 2001:db8:3::1/64 dev eth2 nodad
+ ip -n "$h2" -6 address add "$H2_ADDR6/64" dev eth0 nodad
+
+ ip netns exec "$r1" sysctl -qw net.ipv4.ip_forward=1
+ ip netns exec "$r1" sysctl -qw net.ipv4.conf.all.send_redirects=1
+ ip netns exec "$r1" sysctl -qw net.ipv6.conf.all.forwarding=1
+ ip netns exec "$r2" sysctl -qw net.ipv4.ip_forward=1
+ ip netns exec "$r2" sysctl -qw net.ipv6.conf.all.forwarding=1
+
+ ip netns exec "$h1" sysctl -qw net.ipv4.conf.all.accept_redirects=1
+ ip netns exec "$h1" sysctl -qw net.ipv4.conf.eth0.accept_redirects=1
+ ip netns exec "$h1" sysctl -qw net.ipv6.conf.all.accept_redirects=1
+ ip netns exec "$h1" sysctl -qw net.ipv6.conf.eth0.accept_redirects=1
+
+ slowwait 5 linklocal_exists "$r1" eth0
+ check_err $? "r1: link-local address was not generated"
+ slowwait 5 linklocal_exists "$r2" eth0
+ check_err $? "r2: link-local address was not generated"
+
+ R1_LLADDR=$(linklocal_get "$r1" eth0)
+ R2_LLADDR=$(linklocal_get "$r2" eth0)
+ R1_MAC=$(ip -n "$r1" -j link show dev eth0 | jq -r '.[]["address"]')
+ R2_MAC=$(ip -n "$r2" -j link show dev eth0 | jq -r '.[]["address"]')
+
+ ip -n "$h1" route add "$H2_NET4" via "$R1_ADDR4" dev eth0
+ ip -n "$h1" -6 route add "$H2_NET6" via "$R1_LLADDR" dev eth0
+ ip -n "$r1" route add "$H2_NET4" via "$R2_R1_ADDR4" dev eth1
+ ip -n "$r1" -6 route add "$H2_NET6" via "$R2_R1_ADDR6" dev eth1
+ ip -n "$h2" route add default via 203.0.113.1 dev eth0
+ ip -n "$h2" -6 route add default via 2001:db8:3::1 dev eth0
+
+ far_mtu_set "$MTU_MID"
+}
+
+# Make r1 forward towards h2's network over the segment it receives the packet
+# from, so that it emits a redirect towards h1.
+redirect_route_set()
+{
+ ip -n "$r1" route replace "$H2_NET4" via "$R2_ADDR4" dev eth0
+ ip -n "$r1" -6 route replace "$H2_NET6" via "$R2_LLADDR" dev eth0
+
+ # __ip_do_redirect() only creates an exception if the new gateway is
+ # already a valid neighbour. Otherwise it merely triggers address
+ # resolution. IPv6 resolves the target itself, in rt6_do_redirect().
+ ip -n "$h1" neigh replace "$R2_ADDR4" lladdr "$R2_MAC" dev eth0 \
+ nud permanent
+}
+
+far_mtu_set()
+{
+ local mtu=$1; shift
+
+ ip -n "$r2" link set dev eth2 mtu "$mtu"
+ ip -n "$h2" link set dev eth0 mtu "$mtu"
+}
+
+socket_is_open()
+{
+ ip netns exec "$h1" ss -uHn "sport = :$SPORT" | grep -q .
+}
+
+socket_start()
+{
+ # Disable PMTU discovery by default so that ICMP errors are not
+ # reported to the socket. Otherwise socat would exit when the first one
+ # arrives and later packets in the same test would not be matched to a
+ # socket. The exception is still created, as ip{,6}_sk_accept_pmtu()
+ # only rejects IP{,V6}_PMTUDISC_{INTERFACE,OMIT}.
+ local pmtudisc=${1:-$PMTUDISC_DONT}
+
+ # Send socat's diagnostics to /dev/null. It reports the ICMP errors
+ # that reach the socket, which is exactly what the tests provoke.
+ ip netns exec "$h1" socat -u -lf/dev/null \
+ "$SOCAT_DST,$SOCAT_BIND,$SOCAT_PMTUDISC=$pmtudisc" \
+ OPEN:/dev/null,wronly=1 &
+ SOCAT_PID=$!
+ defer socket_stop
+
+ slowwait 5 socket_is_open
+ check_err $? "socket did not open"
+}
+
+socket_stop()
+{
+ [ -z "$SOCAT_PID" ] && return 0
+
+ kill "$SOCAT_PID" &> /dev/null
+ wait "$SOCAT_PID" 2> /dev/null
+ SOCAT_PID=
+}
+
+# Inject a packet towards h2 with a packet socket. No socket is associated with
+# it, so an ICMP error quoting it is matched to a socket only if one was
+# created separately with the same source port.
+packet_send()
+{
+ local size=$1; shift
+
+ ip netns exec "$h1" mausezahn "${MZ_FAMILY_OPT[@]}" eth0 \
+ -a own -b "$R1_MAC" -A "$H1_ADDR" -B "$H2_ADDR" \
+ -t udp "${MZ_IP_OPTS}sp=$SPORT,dp=$DPORT" \
+ -p "$size" -c 1 -q
+}
+
+exception_show()
+{
+ if [ "$FAMILY" -eq 4 ]; then
+ # IPv4 exceptions without a bound route are not dumped, but
+ # "route get" reports the exception and binds a route to it.
+ ip -n "$h1" route get "$H2_ADDR"
+ else
+ # IPv6 does not report a cache indication in "route get"
+ # output, so dump the exceptions instead.
+ ip -n "$h1" -6 route show cache | grep -F "$H2_ADDR" || true
+ fi
+}
+
+exception_mtu_get()
+{
+ exception_show | grep -o "mtu [0-9]*" | cut -d ' ' -f 2
+}
+
+exception_gw_get()
+{
+ exception_show | grep -o "via [0-9a-f.:]*" | cut -d ' ' -f 2
+}
+
+exception_mtu_check()
+{
+ local expected=$1; shift
+
+ [ "$(exception_mtu_get)" = "$expected" ]
+}
+
+icmp_errors_get()
+{
+ local ctr=IcmpInDestUnreachs
+
+ [ "$FAMILY" -eq 6 ] && ctr=Icmp6InPktTooBigs
+
+ ip netns exec "$h1" nstat -asz "$ctr" | \
+ awk -v ctr="$ctr" '$1 == ctr { print $2 }'
+}
+
+exception_pmtu_check()
+{
+ local mtu=$1; shift
+ local desc=$1; shift
+
+ busywait "$BUSYWAIT_TIMEOUT" exception_mtu_check "$mtu"
+ check_err $? "$desc: exception does not carry an MTU of $mtu"
+}
+
+pmtu_no_socket()
+{
+ local family=$1; shift
+
+ RET=0
+ family_vars_set "$family"
+ topology_setup
+
+ packet_send "$PMTU_PACKET_SIZE"
+ exception_pmtu_check "$MTU_MID" "No socket"
+
+ log_test "IPv$family: PMTU: exception without a matching socket"
+}
+
+pmtu_no_socket_ipv4()
+{
+ pmtu_no_socket 4
+}
+
+pmtu_no_socket_ipv6()
+{
+ pmtu_no_socket 6
+}
+
+pmtu_socket()
+{
+ local family=$1; shift
+ local t0
+
+ RET=0
+ family_vars_set "$family"
+ topology_setup
+ socket_start
+
+ packet_send "$PMTU_PACKET_SIZE"
+ exception_pmtu_check "$MTU_MID" "Matching socket"
+
+ # A lower PMTU replaces the one currently stored in the exception.
+ far_mtu_set "$MTU_LOW"
+ packet_send "$PMTU_PACKET_SIZE"
+ exception_pmtu_check "$MTU_LOW" "Lower PMTU"
+
+ # A higher PMTU is ignored, so the exception is left as it is. Wait
+ # for the error to be received, as otherwise the check below would
+ # pass even if it never was.
+ far_mtu_set "$MTU_MID"
+ t0=$(icmp_errors_get)
+ packet_send "$PMTU_PACKET_SIZE"
+ busywait "$BUSYWAIT_TIMEOUT" until_counter_is ">= $((t0 + 1))" \
+ icmp_errors_get > /dev/null
+ check_err $? "Higher PMTU: ICMP error was not received"
+
+ exception_mtu_check "$MTU_LOW"
+ check_err $? "Higher PMTU: exception does not carry an MTU of $MTU_LOW"
+
+ log_test "IPv$family: PMTU: exception with a matching socket"
+}
+
+pmtu_socket_ipv4()
+{
+ pmtu_socket 4
+}
+
+pmtu_socket_ipv6()
+{
+ pmtu_socket 6
+}
+
+pmtu_omit()
+{
+ local family=$1; shift
+
+ RET=0
+ family_vars_set "$family"
+ topology_setup
+ socket_start "$PMTUDISC_OMIT"
+
+ packet_send "$PMTU_PACKET_SIZE"
+ exception_pmtu_check "$MTU_MID" "PMTU discovery disabled"
+
+ log_test "IPv$family: PMTU: exception with a socket ignoring it"
+}
+
+pmtu_omit_ipv4()
+{
+ pmtu_omit 4
+}
+
+pmtu_omit_ipv6()
+{
+ pmtu_omit 6
+}
+
+exception_gw_check()
+{
+ local expected=$1; shift
+
+ [ -n "$expected" ] && [ "$(exception_gw_get)" = "$expected" ]
+}
+
+redirect_gw_new()
+{
+ if [ "$FAMILY" -eq 4 ]; then
+ echo "$R2_ADDR4"
+ else
+ echo "$R2_LLADDR"
+ fi
+}
+
+redirect_no_socket()
+{
+ local family=$1; shift
+
+ RET=0
+ family_vars_set "$family"
+ topology_setup
+ redirect_route_set
+
+ packet_send "$REDIRECT_PACKET_SIZE"
+ busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)"
+ check_err $? "No socket: exception does not carry the new gateway"
+
+ log_test "IPv$family: Redirect: exception without a matching socket"
+}
+
+redirect_no_socket_ipv4()
+{
+ redirect_no_socket 4
+}
+
+redirect_no_socket_ipv6()
+{
+ redirect_no_socket 6
+}
+
+redirect_socket()
+{
+ local family=$1; shift
+
+ RET=0
+ family_vars_set "$family"
+ topology_setup
+ redirect_route_set
+ socket_start
+
+ packet_send "$REDIRECT_PACKET_SIZE"
+ busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)"
+ check_err $? "Matching socket: exception does not carry the new gateway"
+
+ log_test "IPv$family: Redirect: exception with a matching socket"
+}
+
+redirect_socket_ipv4()
+{
+ redirect_socket 4
+}
+
+redirect_socket_ipv6()
+{
+ redirect_socket 6
+}
+
+trap defer_scopes_cleanup EXIT
+tests_run
+
+exit "$EXIT_STATUS"
diff --git a/tools/testing/selftests/net/test_vxlan_mdb.sh b/tools/testing/selftests/net/test_vxlan_mdb.sh
index 58da5de99ac4..f9600aabd4a2 100755
--- a/tools/testing/selftests/net/test_vxlan_mdb.sh
+++ b/tools/testing/selftests/net/test_vxlan_mdb.sh
@@ -685,6 +685,9 @@ star_g_common()
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $grp dst $vtep_ip src_vni 10010"
log_test $? 255 "Invalid source in source list"
+ run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $all_zeros_grp dst $vtep_ip src_vni 10010"
+ log_test $? 255 "All-zeros source in source list"
+
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent source_list $src1 dst $vtep_ip src_vni 10010"
log_test $? 255 "Source list without filter mode"
}
@@ -784,6 +787,9 @@ sg_common()
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $grp permanent dst $vtep_ip src_vni 10010"
log_test $? 255 "(S, G) with an invalid source list"
+ run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $all_zeros_grp permanent dst $vtep_ip src_vni 10010"
+ log_test $? 255 "(S, G) with an all-zeros source"
+
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $all_zeros_grp src $src permanent dst $vtep_ip src_vni 10010"
log_test $? 255 "All-zeros group with source"
}
diff --git a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json
index b2ca9d4e991b..e2b03f2b5e89 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/filters/u32.json
@@ -353,5 +353,28 @@
"teardown": [
"$TC qdisc del dev $DEV1 parent root drr"
]
+ },
+ {
+ "id": "70fd",
+ "name": "Add u32 filter when node ID pool is exhausted (4096th filter rejected)",
+ "category": [
+ "filter",
+ "u32"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY clsact",
+ "bash -c 'for i in {1..4095}; do echo filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0; done | $TC -b -'"
+ ],
+ "cmdUnderTest": "$TC filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0",
+ "expExitCode": "2",
+ "verifyCmd": "$TC -d filter show dev $DUMMY ingress",
+ "matchPattern": "fh 800::",
+ "matchCount": "4095",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY clsact"
+ ]
}
]
diff --git a/tools/workqueue/wq_dump.py b/tools/workqueue/wq_dump.py
index 31afc24ef17b..9313ebe0c525 100644
--- a/tools/workqueue/wq_dump.py
+++ b/tools/workqueue/wq_dump.py
@@ -78,6 +78,12 @@ def cpumask_str(cpumask):
wq_type_len = 9
+def wq_attrs(wq):
+ try:
+ return wq.attrs
+ except AttributeError:
+ return wq.unbound_attrs
+
def wq_type_str(wq):
if wq.flags & WQ_BH:
return f'{"bh":{wq_type_len}}'
@@ -85,7 +91,7 @@ def wq_type_str(wq):
if wq.flags & WQ_ORDERED:
return f'{"ordered":{wq_type_len}}'
else:
- if wq.attrs.affn_strict:
+ if wq_attrs(wq).affn_strict:
return f'{"unbound,S":{wq_type_len}}'
else:
return f'{"unbound":{wq_type_len}}'
@@ -206,7 +212,7 @@ for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(
print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
if wq.flags & WQ_UNBOUND:
- print(f' {cpumask_str(wq.attrs.cpumask):{ucpus_len}}', end='')
+ print(f' {cpumask_str(wq_attrs(wq).cpumask):{ucpus_len}}', end='')
else:
print(f' {"":{ucpus_len}}', end='')