Merge branch 'mhi-net-immutable' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorJakub Kicinski <kuba@kernel.org>
Sat, 30 Jan 2021 03:40:25 +0000 (19:40 -0800)
committerJakub Kicinski <kuba@kernel.org>
Sat, 30 Jan 2021 03:40:52 +0000 (19:40 -0800)
Needed by mhi-net patches.

Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1912 files changed:
.mailmap
CREDITS
Documentation/ABI/testing/sysfs-class-devlink
Documentation/ABI/testing/sysfs-class-net-qmi
Documentation/ABI/testing/sysfs-devices-consumer
Documentation/ABI/testing/sysfs-devices-supplier
Documentation/ABI/testing/sysfs-driver-ufs
Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst
Documentation/RCU/Design/Requirements/Requirements.rst
Documentation/admin-guide/binfmt-misc.rst
Documentation/admin-guide/bootconfig.rst
Documentation/admin-guide/device-mapper/dm-integrity.rst
Documentation/admin-guide/kernel-parameters.rst
Documentation/admin-guide/kernel-parameters.txt
Documentation/admin-guide/mm/concepts.rst
Documentation/core-api/index.rst
Documentation/dev-tools/kasan.rst
Documentation/dev-tools/kunit/usage.rst
Documentation/devicetree/bindings/dma/ti/k3-bcdma.yaml
Documentation/devicetree/bindings/dma/ti/k3-pktdma.yaml
Documentation/devicetree/bindings/dma/ti/k3-udma.yaml
Documentation/devicetree/bindings/iio/accel/bosch,bma255.yaml
Documentation/devicetree/bindings/net/amlogic,meson-dwmac.yaml
Documentation/devicetree/bindings/net/brcm,bcm7445-switch-v4.0.txt
Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml
Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/net/dsa/brcm,sf2.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/net/dsa/mt7530.txt
Documentation/devicetree/bindings/net/qca,ar803x.yaml
Documentation/devicetree/bindings/net/qcom,ipa.yaml
Documentation/devicetree/bindings/net/renesas,etheravb.yaml
Documentation/devicetree/bindings/net/snps,dwmac.yaml
Documentation/devicetree/bindings/net/ti,k3-am654-cpsw-nuss.yaml
Documentation/devicetree/bindings/net/ti,k3-am654-cpts.yaml
Documentation/devicetree/bindings/regulator/nxp,pf8x00-regulator.yaml
Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.txt
Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
Documentation/devicetree/bindings/sound/ti,j721e-cpb-audio.yaml
Documentation/devicetree/bindings/sound/ti,j721e-cpb-ivi-audio.yaml
Documentation/devicetree/bindings/usb/ti,j721e-usb.yaml
Documentation/doc-guide/sphinx.rst
Documentation/driver-api/auxiliary_bus.rst
Documentation/firmware-guide/acpi/apei/einj.rst
Documentation/hwmon/sbtsi_temp.rst
Documentation/kbuild/makefiles.rst
Documentation/kernel-hacking/locking.rst
Documentation/networking/bonding.rst
Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst
Documentation/networking/device_drivers/ethernet/mellanox/mlx5.rst
Documentation/networking/devlink/devlink-port.rst [new file with mode: 0644]
Documentation/networking/devlink/devlink-resource.rst
Documentation/networking/devlink/devlink-trap.rst
Documentation/networking/devlink/index.rst
Documentation/networking/filter.rst
Documentation/networking/ip-sysctl.rst
Documentation/networking/netdev-FAQ.rst
Documentation/networking/netdevices.rst
Documentation/networking/packet_mmap.rst
Documentation/networking/phy.rst
Documentation/networking/snmp_counter.rst
Documentation/networking/timestamping.rst
Documentation/networking/tls-offload.rst
Documentation/process/4.Coding.rst
Documentation/sound/alsa-configuration.rst
Documentation/sound/kernel-api/writing-an-alsa-driver.rst
Documentation/virt/kvm/api.rst
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/include/asm/local64.h [deleted file]
arch/arc/Makefile
arch/arc/boot/Makefile
arch/arc/include/asm/Kbuild
arch/arc/include/asm/page.h
arch/arc/kernel/entry.S
arch/arc/plat-hsdk/Kconfig
arch/arm/boot/dts/imx6q-tbs2910.dts
arch/arm/boot/dts/imx6qdl-gw52xx.dtsi
arch/arm/boot/dts/imx6qdl-kontron-samx6i.dtsi
arch/arm/boot/dts/imx6qdl-sr-som.dtsi
arch/arm/boot/dts/imx7d-flex-concentrator.dts
arch/arm/boot/dts/omap3-n950-n9.dtsi
arch/arm/boot/dts/picoxcell-pc3x2.dtsi
arch/arm/boot/dts/ste-db8500.dtsi
arch/arm/boot/dts/ste-db8520.dtsi
arch/arm/boot/dts/ste-db9500.dtsi [new file with mode: 0644]
arch/arm/boot/dts/ste-snowball.dts
arch/arm/boot/dts/ste-ux500-samsung-golden.dts
arch/arm/configs/omap2plus_defconfig
arch/arm/crypto/chacha-glue.c
arch/arm/include/asm/Kbuild
arch/arm/mach-imx/suspend-imx6.S
arch/arm/mach-omap2/omap_device.c
arch/arm/mach-omap2/pmic-cpcap.c
arch/arm/net/bpf_jit_32.c
arch/arm/xen/enlighten.c
arch/arm64/Kconfig
arch/arm64/Makefile
arch/arm64/boot/dts/bitmain/bm1880.dtsi
arch/arm64/boot/dts/broadcom/stingray/stingray-usb.dtsi
arch/arm64/boot/dts/freescale/fsl-ls1028a.dtsi
arch/arm64/boot/dts/freescale/imx8mn.dtsi
arch/arm64/boot/dts/freescale/imx8mp.dtsi
arch/arm64/boot/dts/qcom/sc7180.dtsi
arch/arm64/boot/dts/qcom/sdm845.dtsi
arch/arm64/configs/defconfig
arch/arm64/include/asm/Kbuild
arch/arm64/include/asm/atomic.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/include/asm/uaccess.h
arch/arm64/kernel/asm-offsets.c
arch/arm64/kernel/cpufeature.c
arch/arm64/kernel/entry-ftrace.S
arch/arm64/kernel/entry.S
arch/arm64/kernel/perf_event.c
arch/arm64/kernel/probes/kprobes.c
arch/arm64/kernel/probes/kprobes_trampoline.S
arch/arm64/kernel/signal.c
arch/arm64/kernel/smp.c
arch/arm64/kernel/syscall.c
arch/arm64/kernel/traps.c
arch/arm64/kernel/vdso/Makefile
arch/arm64/kernel/vdso/vdso.lds.S
arch/arm64/kvm/Kconfig
arch/arm64/kvm/Makefile
arch/arm64/kvm/arch_timer.c
arch/arm64/kvm/arm.c
arch/arm64/kvm/hyp/include/hyp/adjust_pc.h
arch/arm64/kvm/hyp/nvhe/hyp-main.c
arch/arm64/kvm/hyp/nvhe/hyp-smp.c
arch/arm64/kvm/hyp/nvhe/psci-relay.c
arch/arm64/kvm/pmu-emul.c
arch/arm64/kvm/sys_regs.c
arch/arm64/kvm/va_layout.c
arch/arm64/kvm/vgic/vgic-init.c
arch/arm64/kvm/vgic/vgic-v2.c
arch/arm64/kvm/vgic/vgic-v3.c
arch/arm64/mm/fault.c
arch/arm64/mm/init.c
arch/arm64/mm/proc.S
arch/arm64/net/bpf_jit_comp.c
arch/csky/include/asm/Kbuild
arch/h8300/include/asm/Kbuild
arch/hexagon/include/asm/Kbuild
arch/ia64/include/asm/local64.h [deleted file]
arch/ia64/include/asm/sparsemem.h
arch/ia64/include/uapi/asm/cmpxchg.h
arch/ia64/kernel/time.c
arch/ia64/mm/init.c
arch/m68k/include/asm/Kbuild
arch/microblaze/include/asm/Kbuild
arch/mips/boot/compressed/decompress.c
arch/mips/cavium-octeon/octeon-irq.c
arch/mips/include/asm/Kbuild
arch/mips/include/asm/highmem.h
arch/mips/kernel/binfmt_elfn32.c
arch/mips/kernel/binfmt_elfo32.c
arch/mips/kernel/relocate.c
arch/mips/net/ebpf_jit.c
arch/nds32/include/asm/Kbuild
arch/openrisc/include/asm/Kbuild
arch/openrisc/include/asm/io.h
arch/openrisc/mm/ioremap.c
arch/parisc/Kconfig
arch/parisc/include/asm/Kbuild
arch/parisc/include/asm/irq.h
arch/parisc/kernel/entry.S
arch/powerpc/include/asm/Kbuild
arch/powerpc/include/asm/exception-64s.h
arch/powerpc/include/asm/feature-fixups.h
arch/powerpc/include/asm/highmem.h
arch/powerpc/include/asm/vdso/gettimeofday.h
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/head_book3s_32.S
arch/powerpc/kernel/vmlinux.lds.S
arch/powerpc/lib/feature-fixups.c
arch/powerpc/net/bpf_jit_comp64.c
arch/riscv/Kconfig
arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
arch/riscv/configs/defconfig
arch/riscv/include/asm/Kbuild
arch/riscv/include/asm/pgtable.h
arch/riscv/include/asm/vdso.h
arch/riscv/kernel/cacheinfo.c
arch/riscv/kernel/entry.S
arch/riscv/kernel/setup.c
arch/riscv/kernel/stacktrace.c
arch/riscv/kernel/time.c
arch/riscv/kernel/vdso.c
arch/riscv/mm/init.c
arch/riscv/mm/kasan_init.c
arch/riscv/net/bpf_jit_comp32.c
arch/riscv/net/bpf_jit_comp64.c
arch/s390/Kconfig
arch/s390/configs/debug_defconfig
arch/s390/configs/defconfig
arch/s390/configs/zfcpdump_defconfig
arch/s390/include/asm/Kbuild
arch/s390/net/bpf_jit_comp.c
arch/sh/Kconfig
arch/sh/boards/mach-sh03/rtc.c
arch/sh/configs/landisk_defconfig
arch/sh/configs/microdev_defconfig
arch/sh/configs/sdk7780_defconfig
arch/sh/configs/sdk7786_defconfig
arch/sh/configs/se7750_defconfig
arch/sh/configs/sh03_defconfig
arch/sh/drivers/dma/Kconfig
arch/sh/include/asm/Kbuild
arch/sh/include/asm/gpio.h
arch/sh/kernel/cpu/sh3/entry.S
arch/sh/mm/Kconfig
arch/sh/mm/asids-debugfs.c
arch/sh/mm/cache-debugfs.c
arch/sh/mm/pmb.c
arch/sparc/include/asm/Kbuild
arch/sparc/include/asm/highmem.h
arch/sparc/net/bpf_jit_comp_64.c
arch/x86/Kconfig
arch/x86/entry/common.c
arch/x86/hyperv/hv_init.c
arch/x86/hyperv/mmu.c
arch/x86/include/asm/fpu/api.h
arch/x86/include/asm/idtentry.h
arch/x86/include/asm/intel-family.h
arch/x86/include/asm/kvm_host.h
arch/x86/include/asm/local64.h [deleted file]
arch/x86/include/asm/mshyperv.h
arch/x86/include/asm/msr.h
arch/x86/include/asm/topology.h
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/cpu/mce/core.c
arch/x86/kernel/cpu/mshyperv.c
arch/x86/kernel/cpu/mtrr/generic.c
arch/x86/kernel/cpu/resctrl/rdtgroup.c
arch/x86/kernel/cpu/topology.c
arch/x86/kernel/fpu/core.c
arch/x86/kernel/setup.c
arch/x86/kernel/sev-es-shared.c
arch/x86/kernel/sev-es.c
arch/x86/kernel/smpboot.c
arch/x86/kvm/kvm_cache_regs.h
arch/x86/kvm/lapic.c
arch/x86/kvm/mmu.h
arch/x86/kvm/mmu/mmu.c
arch/x86/kvm/mmu/tdp_mmu.c
arch/x86/kvm/mmu/tdp_mmu.h
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/sev.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/svm/svm.h
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/pmu_intel.c
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/x86.c
arch/x86/lib/mmx_32.c
arch/x86/mm/pgtable.c
arch/x86/net/bpf_jit_comp.c
arch/x86/net/bpf_jit_comp32.c
arch/x86/xen/enlighten_hvm.c
arch/x86/xen/enlighten_pv.c
arch/x86/xen/smp_hvm.c
arch/x86/xen/xen-asm.S
arch/xtensa/include/asm/Kbuild
block/bfq-iosched.c
block/blk-core.c
block/blk-iocost.c
block/blk-mq-debugfs.c
block/blk-mq.c
block/blk-pm.c
block/blk-pm.h
block/genhd.c
crypto/asymmetric_keys/asym_tpm.c
crypto/asymmetric_keys/public_key.c
crypto/ecdh.c
crypto/xor.c
drivers/acpi/Kconfig
drivers/acpi/internal.h
drivers/acpi/scan.c
drivers/acpi/x86/s2idle.c
drivers/atm/idt77252.c
drivers/base/core.c
drivers/base/dd.c
drivers/base/platform.c
drivers/base/regmap/regmap-debugfs.c
drivers/block/Kconfig
drivers/block/rnbd/Kconfig
drivers/block/rnbd/README
drivers/block/rnbd/rnbd-clt.c
drivers/block/rnbd/rnbd-srv.c
drivers/block/xen-blkfront.c
drivers/bus/arm-integrator-lm.c
drivers/bus/fsl-mc/fsl-mc-bus.c
drivers/clk/imx/Kconfig
drivers/clk/mmp/clk-audio.c
drivers/clk/qcom/gcc-sc7180.c
drivers/clk/qcom/gcc-sm8250.c
drivers/clk/tegra/clk-tegra30.c
drivers/counter/ti-eqep.c
drivers/cpufreq/intel_pstate.c
drivers/cpufreq/powernow-k8.c
drivers/crypto/Kconfig
drivers/crypto/marvell/cesa/cesa.h
drivers/dma-buf/dma-buf.c
drivers/dma-buf/heaps/cma_heap.c
drivers/dma/dw-edma/dw-edma-core.c
drivers/dma/idxd/sysfs.c
drivers/dma/mediatek/mtk-hsdma.c
drivers/dma/milbeaut-xdmac.c
drivers/dma/qcom/bam_dma.c
drivers/dma/qcom/gpi.c
drivers/dma/stm32-mdma.c
drivers/dma/ti/k3-udma.c
drivers/dma/xilinx/xilinx_dma.c
drivers/firmware/imx/Kconfig
drivers/gpio/Kconfig
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpiolib-cdev.c
drivers/gpio/gpiolib.c
drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
drivers/gpu/drm/amd/amdgpu/mmhub_v2_3.c
drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h
drivers/gpu/drm/amd/amdgpu/soc15.c
drivers/gpu/drm/amd/amdkfd/kfd_crat.c
drivers/gpu/drm/amd/display/Kconfig
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h
drivers/gpu/drm/amd/display/dc/calcs/Makefile
drivers/gpu/drm/amd/display/dc/clk_mgr/Makefile
drivers/gpu/drm/amd/display/dc/clk_mgr/dcn30/dcn30_clk_mgr.c
drivers/gpu/drm/amd/display/dc/core/dc_link.c
drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
drivers/gpu/drm/amd/display/dc/dcn10/Makefile
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_mpc.c
drivers/gpu/drm/amd/display/dc/dcn10/dcn10_resource.c
drivers/gpu/drm/amd/display/dc/dcn20/Makefile
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
drivers/gpu/drm/amd/display/dc/dcn20/dcn20_resource.c
drivers/gpu/drm/amd/display/dc/dcn21/Makefile
drivers/gpu/drm/amd/display/dc/dcn21/dcn21_resource.c
drivers/gpu/drm/amd/display/dc/dcn30/Makefile
drivers/gpu/drm/amd/display/dc/dcn301/Makefile
drivers/gpu/drm/amd/display/dc/dcn301/dcn301_resource.c
drivers/gpu/drm/amd/display/dc/dcn302/Makefile
drivers/gpu/drm/amd/display/dc/dml/Makefile
drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20v2.c
drivers/gpu/drm/amd/display/dc/dsc/Makefile
drivers/gpu/drm/amd/display/dc/os_types.h
drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c
drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.h
drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_gem_vram_helper.c
drivers/gpu/drm/drm_plane.c
drivers/gpu/drm/drm_syncobj.c
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/display/icl_dsi.c
drivers/gpu/drm/i915/display/intel_ddi.c
drivers/gpu/drm/i915/display/intel_display_types.h
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_dp.h
drivers/gpu/drm/i915/display/intel_hdcp.c
drivers/gpu/drm/i915/display/intel_panel.c
drivers/gpu/drm/i915/display/vlv_dsi.c
drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
drivers/gpu/drm/i915/gt/gen7_renderclear.c
drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
drivers/gpu/drm/i915/gt/intel_lrc.c
drivers/gpu/drm/i915/gt/intel_ring_submission.c
drivers/gpu/drm/i915/gt/intel_timeline.c
drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
drivers/gpu/drm/i915/gvt/display.c
drivers/gpu/drm/i915/gvt/vgpu.c
drivers/gpu/drm/i915/i915_cmd_parser.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_mitigations.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_mitigations.h [new file with mode: 0644]
drivers/gpu/drm/i915/i915_pmu.c
drivers/gpu/drm/i915/i915_request.h
drivers/gpu/drm/msm/adreno/a2xx_gpu.c
drivers/gpu/drm/msm/adreno/a3xx_gpu.c
drivers/gpu/drm/msm/adreno/a4xx_gpu.c
drivers/gpu/drm/msm/adreno/adreno_device.c
drivers/gpu/drm/msm/adreno/adreno_gpu.c
drivers/gpu/drm/msm/adreno/adreno_gpu.h
drivers/gpu/drm/msm/dp/dp_display.c
drivers/gpu/drm/msm/dp/dp_panel.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_gem.c
drivers/gpu/drm/nouveau/dispnv50/Kbuild
drivers/gpu/drm/nouveau/dispnv50/core.c
drivers/gpu/drm/nouveau/dispnv50/curs.c
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/dispnv50/disp.h
drivers/gpu/drm/nouveau/dispnv50/wimm.c
drivers/gpu/drm/nouveau/dispnv50/wimmc37b.c
drivers/gpu/drm/nouveau/dispnv50/wndw.c
drivers/gpu/drm/nouveau/dispnv50/wndw.h
drivers/gpu/drm/nouveau/dispnv50/wndwc57e.c
drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/include/nvif/cl0080.h
drivers/gpu/drm/nouveau/include/nvif/class.h
drivers/gpu/drm/nouveau/include/nvkm/core/device.h
drivers/gpu/drm/nouveau/include/nvkm/engine/disp.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/devinit.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/gpio.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/i2c.h
drivers/gpu/drm/nouveau/include/nvkm/subdev/mc.h
drivers/gpu/drm/nouveau/nouveau_backlight.c
drivers/gpu/drm/nouveau/nvif/disp.c
drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
drivers/gpu/drm/nouveau/nvkm/engine/device/user.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/Kbuild
drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/rootga102.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.h
drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/engine/disp/sortu102.c
drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadow.c
drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
drivers/gpu/drm/nouveau/nvkm/subdev/devinit/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/devinit/priv.h
drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c
drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ram.h
drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/gpio/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gm200.c
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/pad.h
drivers/gpu/drm/nouveau/nvkm/subdev/i2c/priv.h
drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gf100.c
drivers/gpu/drm/nouveau/nvkm/subdev/ibus/gk104.c
drivers/gpu/drm/nouveau/nvkm/subdev/mc/Kbuild
drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c [new file with mode: 0644]
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/ttm/ttm_pool.c
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/hid/Kconfig
drivers/hid/amd-sfh-hid/amd_sfh_client.c
drivers/hid/amd-sfh-hid/amd_sfh_hid.h
drivers/hid/amd-sfh-hid/amd_sfh_pcie.c
drivers/hid/amd-sfh-hid/amd_sfh_pcie.h
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-logitech-dj.c
drivers/hid/hid-logitech-hidpp.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-uclogic-params.c
drivers/hid/hid-wiimote-core.c
drivers/hid/wacom_sys.c
drivers/hid/wacom_wac.h
drivers/hv/vmbus_drv.c
drivers/hwmon/amd_energy.c
drivers/hwmon/pwm-fan.c
drivers/hwtracing/intel_th/pci.c
drivers/hwtracing/stm/heartbeat.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/i2c-i801.c
drivers/i2c/busses/i2c-imx.c
drivers/i2c/busses/i2c-mt65xx.c
drivers/i2c/busses/i2c-octeon-core.c
drivers/i2c/busses/i2c-sprd.c
drivers/i2c/busses/i2c-tegra-bpmp.c
drivers/i2c/busses/i2c-tegra.c
drivers/ide/ide-atapi.c
drivers/ide/ide-io.c
drivers/ide/ide-pm.c
drivers/idle/intel_idle.c
drivers/iio/adc/ti_am335x_adc.c
drivers/iio/common/st_sensors/st_sensors_trigger.c
drivers/iio/dac/ad5504.c
drivers/iio/proximity/sx9310.c
drivers/iio/temperature/mlx90632.c
drivers/infiniband/core/cma_configfs.c
drivers/infiniband/core/restrack.c
drivers/infiniband/core/ucma.c
drivers/infiniband/core/umem.c
drivers/infiniband/hw/cxgb4/qp.c
drivers/infiniband/hw/hns/hns_roce_device.h
drivers/infiniband/hw/hns/hns_roce_qp.c
drivers/infiniband/hw/mlx5/main.c
drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
drivers/infiniband/hw/usnic/usnic_ib_sysfs.c
drivers/infiniband/hw/usnic/usnic_ib_verbs.c
drivers/infiniband/hw/vmw_pvrdma/pvrdma.h
drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c
drivers/infiniband/sw/rxe/rxe_net.c
drivers/infiniband/sw/rxe/rxe_resp.c
drivers/interconnect/imx/imx.c
drivers/interconnect/imx/imx8mq.c
drivers/interconnect/qcom/Kconfig
drivers/iommu/amd/init.c
drivers/iommu/amd/iommu.c
drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
drivers/iommu/dma-iommu.c
drivers/iommu/intel/dmar.c
drivers/iommu/intel/iommu.c
drivers/iommu/intel/irq_remapping.c
drivers/iommu/intel/svm.c
drivers/iommu/iova.c
drivers/irqchip/Kconfig
drivers/irqchip/irq-bcm2836.c
drivers/irqchip/irq-loongson-liointc.c
drivers/irqchip/irq-mips-cpu.c
drivers/irqchip/irq-sl28cpld.c
drivers/isdn/mISDN/Kconfig
drivers/lightnvm/Kconfig
drivers/lightnvm/core.c
drivers/md/Kconfig
drivers/md/bcache/features.c
drivers/md/bcache/features.h
drivers/md/bcache/super.c
drivers/md/dm-bufio.c
drivers/md/dm-crypt.c
drivers/md/dm-integrity.c
drivers/md/dm-raid.c
drivers/md/dm-snap.c
drivers/md/dm-table.c
drivers/md/dm.c
drivers/md/md.c
drivers/media/cec/platform/Makefile
drivers/media/common/videobuf2/videobuf2-v4l2.c
drivers/media/i2c/ccs-pll.c
drivers/media/i2c/ccs/ccs-data.c
drivers/media/pci/intel/ipu3/ipu3-cio2.c
drivers/media/platform/qcom/venus/core.c
drivers/media/platform/rcar-vin/rcar-core.c
drivers/media/rc/ir-mce_kbd-decoder.c
drivers/media/rc/ite-cir.c
drivers/media/rc/rc-main.c
drivers/media/rc/serial_ir.c
drivers/media/v4l2-core/v4l2-common.c
drivers/misc/cardreader/rtsx_pcr.c
drivers/misc/habanalabs/common/command_submission.c
drivers/misc/habanalabs/common/device.c
drivers/misc/habanalabs/common/firmware_if.c
drivers/misc/habanalabs/common/habanalabs.h
drivers/misc/habanalabs/common/habanalabs_drv.c
drivers/misc/habanalabs/common/habanalabs_ioctl.c
drivers/misc/habanalabs/common/hw_queue.c
drivers/misc/habanalabs/common/memory.c
drivers/misc/habanalabs/common/mmu.c
drivers/misc/habanalabs/common/mmu_v1.c
drivers/misc/habanalabs/common/pci.c
drivers/misc/habanalabs/gaudi/gaudi.c
drivers/misc/habanalabs/gaudi/gaudiP.h
drivers/misc/habanalabs/gaudi/gaudi_coresight.c
drivers/misc/habanalabs/goya/goya.c
drivers/misc/habanalabs/include/common/hl_boot_if.h
drivers/misc/pvpanic.c
drivers/mmc/core/queue.c
drivers/mmc/host/sdhci-brcmstb.c
drivers/mmc/host/sdhci-of-dwcmshc.c
drivers/mmc/host/sdhci-xenon.c
drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/raw/intel-nand-controller.c
drivers/mtd/nand/raw/nandsim.c
drivers/mtd/nand/raw/omap2.c
drivers/mtd/nand/spi/core.c
drivers/net/Kconfig
drivers/net/arcnet/com20020_cs.c
drivers/net/bareudp.c
drivers/net/bonding/bond_main.c
drivers/net/bonding/bond_options.c
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/at91_can.c
drivers/net/can/c_can/c_can.c
drivers/net/can/cc770/cc770.c
drivers/net/can/dev.c [deleted file]
drivers/net/can/dev/Makefile [new file with mode: 0644]
drivers/net/can/dev/bittiming.c [new file with mode: 0644]
drivers/net/can/dev/dev.c [new file with mode: 0644]
drivers/net/can/dev/length.c [new file with mode: 0644]
drivers/net/can/dev/netlink.c [new file with mode: 0644]
drivers/net/can/dev/rx-offload.c [new file with mode: 0644]
drivers/net/can/dev/skb.c [new file with mode: 0644]
drivers/net/can/flexcan.c
drivers/net/can/grcan.c
drivers/net/can/ifi_canfd/ifi_canfd.c
drivers/net/can/kvaser_pciefd.c
drivers/net/can/m_can/Makefile
drivers/net/can/m_can/m_can.c
drivers/net/can/m_can/tcan4x5x-core.c [new file with mode: 0644]
drivers/net/can/m_can/tcan4x5x-regmap.c [new file with mode: 0644]
drivers/net/can/m_can/tcan4x5x.c [deleted file]
drivers/net/can/m_can/tcan4x5x.h [new file with mode: 0644]
drivers/net/can/mscan/mscan.c
drivers/net/can/pch_can.c
drivers/net/can/peak_canfd/peak_canfd.c
drivers/net/can/rcar/Kconfig
drivers/net/can/rcar/rcar_can.c
drivers/net/can/rcar/rcar_canfd.c
drivers/net/can/rx-offload.c [deleted file]
drivers/net/can/sja1000/sja1000.c
drivers/net/can/softing/softing_main.c
drivers/net/can/spi/hi311x.c
drivers/net/can/spi/mcp251x.c
drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
drivers/net/can/sun4i_can.c
drivers/net/can/ti_hecc.c
drivers/net/can/usb/ems_usb.c
drivers/net/can/usb/esd_usb2.c
drivers/net/can/usb/gs_usb.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
drivers/net/can/usb/mcba_usb.c
drivers/net/can/usb/peak_usb/pcan_usb_core.c
drivers/net/can/usb/peak_usb/pcan_usb_fd.c
drivers/net/can/usb/ucan.c
drivers/net/can/usb/usb_8dev.c
drivers/net/can/vxcan.c
drivers/net/can/xilinx_can.c
drivers/net/dsa/Kconfig
drivers/net/dsa/Makefile
drivers/net/dsa/b53/b53_common.c
drivers/net/dsa/b53/b53_priv.h
drivers/net/dsa/bcm_sf2.c
drivers/net/dsa/bcm_sf2_cfp.c
drivers/net/dsa/bcm_sf2_regs.h
drivers/net/dsa/dsa_loop.c
drivers/net/dsa/hirschmann/Kconfig
drivers/net/dsa/hirschmann/hellcreek.c
drivers/net/dsa/hirschmann/hellcreek.h
drivers/net/dsa/lan9303-core.c
drivers/net/dsa/lantiq_gswip.c
drivers/net/dsa/microchip/ksz8795.c
drivers/net/dsa/microchip/ksz9477.c
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h
drivers/net/dsa/mt7530.c
drivers/net/dsa/mt7530.h
drivers/net/dsa/mv88e6xxx/Kconfig
drivers/net/dsa/mv88e6xxx/Makefile
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/chip.h
drivers/net/dsa/mv88e6xxx/global1.h
drivers/net/dsa/mv88e6xxx/global1_vtu.c
drivers/net/dsa/mv88e6xxx/global2.c
drivers/net/dsa/mv88e6xxx/global2.h
drivers/net/dsa/mv88e6xxx/port.c
drivers/net/dsa/mv88e6xxx/port.h
drivers/net/dsa/ocelot/felix.c
drivers/net/dsa/ocelot/felix.h
drivers/net/dsa/ocelot/felix_vsc9959.c
drivers/net/dsa/ocelot/seville_vsc9953.c
drivers/net/dsa/qca/ar9331.c
drivers/net/dsa/qca8k.c
drivers/net/dsa/realtek-smi-core.h
drivers/net/dsa/rtl8366.c
drivers/net/dsa/rtl8366rb.c
drivers/net/dsa/sja1105/sja1105.h
drivers/net/dsa/sja1105/sja1105_devlink.c
drivers/net/dsa/sja1105/sja1105_main.c
drivers/net/dsa/xrs700x/Kconfig [new file with mode: 0644]
drivers/net/dsa/xrs700x/Makefile [new file with mode: 0644]
drivers/net/dsa/xrs700x/xrs700x.c [new file with mode: 0644]
drivers/net/dsa/xrs700x/xrs700x.h [new file with mode: 0644]
drivers/net/dsa/xrs700x/xrs700x_i2c.c [new file with mode: 0644]
drivers/net/dsa/xrs700x/xrs700x_mdio.c [new file with mode: 0644]
drivers/net/dsa/xrs700x/xrs700x_reg.h [new file with mode: 0644]
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/amazon/ena/ena_netdev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/aquantia/Kconfig
drivers/net/ethernet/aurora/Kconfig [deleted file]
drivers/net/ethernet/aurora/Makefile [deleted file]
drivers/net/ethernet/aurora/nb8800.c [deleted file]
drivers/net/ethernet/aurora/nb8800.h [deleted file]
drivers/net/ethernet/broadcom/Kconfig
drivers/net/ethernet/broadcom/bcm63xx_enet.c
drivers/net/ethernet/broadcom/bcm63xx_enet.h
drivers/net/ethernet/broadcom/bcmsysport.c
drivers/net/ethernet/broadcom/bcmsysport.h
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bgmac.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt.h
drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
drivers/net/ethernet/broadcom/genet/bcmgenet.h
drivers/net/ethernet/broadcom/genet/bcmmii.c
drivers/net/ethernet/broadcom/tg3.c
drivers/net/ethernet/broadcom/tg3.h
drivers/net/ethernet/broadcom/unimac.h [new file with mode: 0644]
drivers/net/ethernet/cadence/macb_main.c
drivers/net/ethernet/cavium/liquidio/lio_core.c
drivers/net/ethernet/cavium/liquidio/lio_main.c
drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
drivers/net/ethernet/cavium/liquidio/octeon_device.c
drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
drivers/net/ethernet/cavium/thunder/nicvf_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/sge.c
drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
drivers/net/ethernet/chelsio/cxgb4/t4_tcb.h
drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c
drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls.h
drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c
drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_hw.c
drivers/net/ethernet/cisco/enic/enic_main.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/ethoc.c
drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h
drivers/net/ethernet/freescale/dpaa2/dpni.c
drivers/net/ethernet/freescale/dpaa2/dpni.h
drivers/net/ethernet/freescale/enetc/enetc_mdio.c
drivers/net/ethernet/freescale/fec.h
drivers/net/ethernet/freescale/fec_main.c
drivers/net/ethernet/freescale/fman/fman_memac.c
drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
drivers/net/ethernet/freescale/fs_enet/mii-fec.c
drivers/net/ethernet/freescale/gianfar.c
drivers/net/ethernet/freescale/ucc_geth.c
drivers/net/ethernet/freescale/ucc_geth.h
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
drivers/net/ethernet/hisilicon/hns3/hclge_mbx.h
drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h
drivers/net/ethernet/ibm/ibmvnic.c
drivers/net/ethernet/intel/e100.c
drivers/net/ethernet/intel/e1000e/e1000.h
drivers/net/ethernet/intel/e1000e/ethtool.c
drivers/net/ethernet/intel/e1000e/ich8lan.c
drivers/net/ethernet/intel/e1000e/netdev.c
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
drivers/net/ethernet/intel/i40e/i40e_xsk.c
drivers/net/ethernet/intel/iavf/iavf_main.c
drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_ethtool.c
drivers/net/ethernet/intel/ice/ice_ethtool_fdir.c
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_txrx.c
drivers/net/ethernet/intel/ice/ice_txrx_lib.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/igbvf/netdev.c
drivers/net/ethernet/intel/igc/igc_ethtool.c
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
drivers/net/ethernet/marvell/Kconfig
drivers/net/ethernet/marvell/mvneta.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.h
drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.c
drivers/net/ethernet/marvell/mvpp2/mvpp2_prs.h
drivers/net/ethernet/marvell/octeontx2/af/cgx.c
drivers/net/ethernet/marvell/octeontx2/af/mbox.h
drivers/net/ethernet/marvell/octeontx2/af/npc.h
drivers/net/ethernet/marvell/octeontx2/af/rvu.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.h
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h
drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c
drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_eth_soc.h
drivers/net/ethernet/mellanox/mlx4/en_netdev.c
drivers/net/ethernet/mellanox/mlx4/en_rx.c
drivers/net/ethernet/mellanox/mlx5/core/Kconfig
drivers/net/ethernet/mellanox/mlx5/core/Makefile
drivers/net/ethernet/mellanox/mlx5/core/cmd.c
drivers/net/ethernet/mellanox/mlx5/core/devlink.c
drivers/net/ethernet/mellanox/mlx5/core/devlink.h
drivers/net/ethernet/mellanox/mlx5/core/en.h
drivers/net/ethernet/mellanox/mlx5/core/en/fs.h
drivers/net/ethernet/mellanox/mlx5/core/en/health.c
drivers/net/ethernet/mellanox/mlx5/core/en/params.h
drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
drivers/net/ethernet/mellanox/mlx5/core/en/qos.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/qos.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_mplsoudp.c
drivers/net/ethernet/mellanox/mlx5/core/en/trap.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/trap.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/en_accel.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_rxtx.h
drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
drivers/net/ethernet/mellanox/mlx5/core/en_main.c
drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
drivers/net/ethernet/mellanox/mlx5/core/en_stats.h
drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c
drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_ofld.c
drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
drivers/net/ethernet/mellanox/mlx5/core/events.c
drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
drivers/net/ethernet/mellanox/mlx5/core/lag.c
drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c
drivers/net/ethernet/mellanox/mlx5/core/lib/mlx5.h
drivers/net/ethernet/mellanox/mlx5/core/main.c
drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
drivers/net/ethernet/mellanox/mlx5/core/qos.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/qos.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/rdma.c
drivers/net/ethernet/mellanox/mlx5/core/sf/cmd.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/priv.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_action.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_domain.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_matcher.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.c
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c [new file with mode: 0644]
drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h
drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5_ifc_dr.h
drivers/net/ethernet/mellanox/mlx5/core/vport.c
drivers/net/ethernet/mellanox/mlxfw/mlxfw_mfa2.c
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/core_thermal.c
drivers/net/ethernet/mellanox/mlxsw/pci.c
drivers/net/ethernet/mellanox/mlxsw/spectrum.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_span.h
drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
drivers/net/ethernet/micrel/Kconfig
drivers/net/ethernet/micrel/ks8851.h
drivers/net/ethernet/micrel/ks8851_common.c
drivers/net/ethernet/micrel/ks8851_par.c
drivers/net/ethernet/micrel/ks8851_spi.c
drivers/net/ethernet/microchip/lan743x_main.c
drivers/net/ethernet/microchip/lan743x_main.h
drivers/net/ethernet/mscc/Makefile
drivers/net/ethernet/mscc/ocelot.c
drivers/net/ethernet/mscc/ocelot.h
drivers/net/ethernet/mscc/ocelot_devlink.c [new file with mode: 0644]
drivers/net/ethernet/mscc/ocelot_net.c
drivers/net/ethernet/mscc/ocelot_vsc7514.c
drivers/net/ethernet/natsemi/macsonic.c
drivers/net/ethernet/natsemi/xtsonic.c
drivers/net/ethernet/netronome/nfp/bpf/jit.c
drivers/net/ethernet/netronome/nfp/bpf/main.h
drivers/net/ethernet/netronome/nfp/bpf/verifier.c
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/pensando/ionic/ionic_txrx.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/netxen/netxen_nic_ctx.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c
drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
drivers/net/ethernet/qlogic/qede/qede_fp.c
drivers/net/ethernet/qlogic/qede/qede_main.c
drivers/net/ethernet/qlogic/qla3xxx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
drivers/net/ethernet/realtek/r8169_main.c
drivers/net/ethernet/renesas/ravb.h
drivers/net/ethernet/renesas/ravb_main.c
drivers/net/ethernet/renesas/sh_eth.c
drivers/net/ethernet/rocker/rocker.h
drivers/net/ethernet/rocker/rocker_main.c
drivers/net/ethernet/rocker/rocker_ofdpa.c
drivers/net/ethernet/sfc/efx.c
drivers/net/ethernet/sfc/efx_channels.c
drivers/net/ethernet/sfc/rx.c
drivers/net/ethernet/smsc/smc91x.c
drivers/net/ethernet/smsc/smsc911x.c
drivers/net/ethernet/socionext/netsec.c
drivers/net/ethernet/stmicro/stmmac/dwmac-intel-plat.c
drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
drivers/net/ethernet/stmicro/stmmac/dwmac5.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h
drivers/net/ethernet/ti/am65-cpsw-qos.c
drivers/net/ethernet/ti/am65-cpts.c
drivers/net/ethernet/ti/cpsw.c
drivers/net/ethernet/ti/cpsw_ale.c
drivers/net/ethernet/ti/cpsw_new.c
drivers/net/ethernet/ti/cpsw_switchdev.c
drivers/net/ethernet/ti/cpts.c
drivers/net/ethernet/toshiba/ps3_gelic_net.c
drivers/net/ethernet/toshiba/spider_net.c
drivers/net/ethernet/xscale/ixp4xx_eth.c
drivers/net/geneve.c
drivers/net/gtp.c
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc.c
drivers/net/hyperv/netvsc_bpf.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/hyperv/rndis_filter.c
drivers/net/ipa/Kconfig
drivers/net/ipa/gsi.c
drivers/net/ipa/gsi_trans.h
drivers/net/ipa/ipa.h
drivers/net/ipa/ipa_clock.c
drivers/net/ipa/ipa_cmd.c
drivers/net/ipa/ipa_cmd.h
drivers/net/ipa/ipa_data-sc7180.c
drivers/net/ipa/ipa_data-sdm845.c
drivers/net/ipa/ipa_data.h
drivers/net/ipa/ipa_endpoint.c
drivers/net/ipa/ipa_main.c
drivers/net/ipa/ipa_modem.c
drivers/net/ipvlan/ipvlan_core.c
drivers/net/macvlan.c
drivers/net/mdio/mdio-bitbang.c
drivers/net/mhi_net.c
drivers/net/netdevsim/fib.c
drivers/net/netdevsim/netdev.c
drivers/net/phy/at803x.c
drivers/net/phy/bcm7xxx.c
drivers/net/phy/mdio_bus.c
drivers/net/phy/micrel.c
drivers/net/phy/national.c
drivers/net/phy/phy.c
drivers/net/phy/phy_device.c
drivers/net/phy/realtek.c
drivers/net/phy/sfp-bus.c
drivers/net/phy/sfp.c
drivers/net/phy/smsc.c
drivers/net/ppp/ppp_generic.c
drivers/net/ppp/pptp.c
drivers/net/tap.c
drivers/net/team/team.c
drivers/net/tun.c
drivers/net/usb/Kconfig
drivers/net/usb/cdc_ether.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/hso.c
drivers/net/usb/qmi_wwan.c
drivers/net/usb/r8152.c
drivers/net/usb/r8153_ecm.c
drivers/net/usb/rndis_host.c
drivers/net/usb/usbnet.c
drivers/net/veth.c
drivers/net/virtio_net.c
drivers/net/vxlan.c
drivers/net/wan/Kconfig
drivers/net/wan/hdlc_ppp.c
drivers/net/wan/ixp4xx_hss.c
drivers/net/wan/sbni.c
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/dp_rx.c
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/pci.c
drivers/net/wireless/ath/ath11k/pci.h
drivers/net/wireless/ath/ath11k/peer.c
drivers/net/wireless/ath/ath11k/peer.h
drivers/net/wireless/ath/ath11k/qmi.c
drivers/net/wireless/ath/ath11k/qmi.h
drivers/net/wireless/ath/ath11k/reg.c
drivers/net/wireless/ath/ath11k/wmi.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.c
drivers/net/wireless/ath/ath6kl/init.c
drivers/net/wireless/ath/wil6210/Kconfig
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/netdev.c
drivers/net/wireless/ath/wil6210/pcie_bus.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/intel/iwlwifi/cfg/22000.c
drivers/net/wireless/intel/iwlwifi/fw/acpi.c
drivers/net/wireless/intel/iwlwifi/fw/acpi.h
drivers/net/wireless/intel/iwlwifi/fw/pnvm.c
drivers/net/wireless/intel/iwlwifi/iwl-config.h
drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c
drivers/net/wireless/intel/iwlwifi/iwl-io.c
drivers/net/wireless/intel/iwlwifi/iwl-io.h
drivers/net/wireless/intel/iwlwifi/iwl-prph.h
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/sta.c
drivers/net/wireless/intel/iwlwifi/mvm/tx.c
drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c
drivers/net/wireless/intel/iwlwifi/pcie/drv.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c
drivers/net/wireless/intel/iwlwifi/pcie/tx.c
drivers/net/wireless/intel/iwlwifi/queue/tx.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/marvell/mwifiex/cfg80211.c
drivers/net/wireless/marvell/mwifiex/main.c
drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
drivers/net/wireless/mediatek/mt76/mt7615/sdio_txrx.c
drivers/net/wireless/mediatek/mt76/mt7915/init.c
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
drivers/net/wireless/mediatek/mt76/sdio.c
drivers/net/wireless/mediatek/mt76/usb.c
drivers/net/wireless/mediatek/mt7601u/dma.c
drivers/net/wireless/microchip/wilc1000/cfg80211.c
drivers/net/wireless/microchip/wilc1000/mon.c
drivers/net/wireless/microchip/wilc1000/netdev.c
drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
drivers/net/wireless/quantenna/qtnfmac/core.c
drivers/net/wireless/realtek/rtlwifi/core.c
drivers/net/xen-netback/common.h
drivers/net/xen-netback/interface.c
drivers/net/xen-netback/netback.c
drivers/net/xen-netback/xenbus.c
drivers/net/xen-netfront.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/fdp/i2c.c
drivers/nfc/trf7970a.c
drivers/nfc/virtual_ncidev.c [new file with mode: 0644]
drivers/nvme/host/core.c
drivers/nvme/host/fc.c
drivers/nvme/host/nvme.h
drivers/nvme/host/pci.c
drivers/nvme/host/rdma.c
drivers/nvme/host/tcp.c
drivers/nvme/target/admin-cmd.c
drivers/nvme/target/fcloop.c
drivers/nvme/target/rdma.c
drivers/opp/core.c
drivers/perf/arm_pmu.c
drivers/phy/ingenic/Makefile
drivers/phy/mediatek/Kconfig
drivers/phy/motorola/phy-cpcap-usb.c
drivers/pinctrl/aspeed/pinctrl-aspeed-g6.c
drivers/pinctrl/mediatek/pinctrl-mtk-common-v2.c
drivers/pinctrl/nomadik/pinctrl-nomadik.c
drivers/pinctrl/pinctrl-ingenic.c
drivers/pinctrl/qcom/pinctrl-msm.c
drivers/pinctrl/qcom/pinctrl-msm.h
drivers/platform/surface/Kconfig
drivers/platform/surface/surface_gpe.c
drivers/platform/x86/amd-pmc.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/i2c-multi-instantiate.c
drivers/platform/x86/ideapad-laptop.c
drivers/platform/x86/intel-vbtn.c
drivers/platform/x86/thinkpad_acpi.c
drivers/platform/x86/touchscreen_dmi.c
drivers/ptp/Kconfig
drivers/regulator/Kconfig
drivers/regulator/bd718x7-regulator.c
drivers/regulator/core.c
drivers/regulator/pf8x00-regulator.c
drivers/regulator/qcom-rpmh-regulator.c
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_core_sys.c
drivers/s390/net/qeth_l2_main.c
drivers/s390/net/qeth_l3_main.c
drivers/scsi/cxgbi/cxgb4i/Kconfig
drivers/scsi/fnic/vnic_dev.c
drivers/scsi/hisi_sas/hisi_sas.h
drivers/scsi/hisi_sas/hisi_sas_main.c
drivers/scsi/hisi_sas/hisi_sas_v2_hw.c
drivers/scsi/ibmvscsi/ibmvfc.c
drivers/scsi/libfc/fc_exch.c
drivers/scsi/megaraid/megaraid_sas_base.c
drivers/scsi/megaraid/megaraid_sas_fusion.c
drivers/scsi/mpt3sas/Kconfig
drivers/scsi/mpt3sas/mpt3sas_base.c
drivers/scsi/qedi/qedi_main.c
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_transport_spi.c
drivers/scsi/scsi_transport_srp.c
drivers/scsi/sd.c
drivers/scsi/ufs/Kconfig
drivers/scsi/ufs/ufs-mediatek-trace.h
drivers/scsi/ufs/ufs-mediatek.c
drivers/scsi/ufs/ufs-mediatek.h
drivers/scsi/ufs/ufs.h
drivers/scsi/ufs/ufshcd-pci.c
drivers/scsi/ufs/ufshcd.c
drivers/scsi/ufs/ufshcd.h
drivers/sh/intc/core.c
drivers/sh/intc/virq-debugfs.c
drivers/soc/atmel/soc.c
drivers/soc/fsl/qe/qe_common.c
drivers/soc/imx/Kconfig
drivers/soc/litex/litex_soc_ctrl.c
drivers/spi/spi-altera.c
drivers/spi/spi-cadence.c
drivers/spi/spi-fsl-spi.c
drivers/spi/spi-geni-qcom.c
drivers/spi/spi-stm32.c
drivers/spi/spi.c
drivers/spi/spidev.c
drivers/staging/comedi/comedi_fops.c
drivers/staging/fsl-dpaa2/ethsw/ethsw.c
drivers/staging/hikey9xx/hisi-spmi-controller.c
drivers/staging/media/atomisp/pci/atomisp_subdev.c
drivers/staging/media/hantro/hantro_v4l2.c
drivers/staging/media/sunxi/cedrus/cedrus_h264.c
drivers/staging/mt7621-dma/mtk-hsdma.c
drivers/staging/rtl8723bs/include/rtw_wifi_regd.h
drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c
drivers/staging/rtl8723bs/os_dep/wifi_regd.c
drivers/target/target_core_user.c
drivers/target/target_core_xcopy.c
drivers/target/target_core_xcopy.h
drivers/tee/optee/call.c
drivers/thunderbolt/icm.c
drivers/tty/Kconfig
drivers/tty/Makefile
drivers/tty/n_tty.c
drivers/tty/serial/mvebu-uart.c
drivers/tty/serial/sifive.c
drivers/tty/tty_io.c
drivers/tty/ttynull.c
drivers/usb/cdns3/cdns3-imx.c
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/class/cdc-acm.c
drivers/usb/class/cdc-wdm.c
drivers/usb/class/usblp.c
drivers/usb/core/hcd.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/dwc3-meson-g12a.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/ulpi.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/composite.c
drivers/usb/gadget/configfs.c
drivers/usb/gadget/function/f_printer.c
drivers/usb/gadget/function/f_uac2.c
drivers/usb/gadget/function/u_ether.c
drivers/usb/gadget/legacy/acm_ms.c
drivers/usb/gadget/udc/Kconfig
drivers/usb/gadget/udc/Makefile
drivers/usb/gadget/udc/aspeed-vhub/epn.c
drivers/usb/gadget/udc/bdc/Kconfig
drivers/usb/gadget/udc/core.c
drivers/usb/gadget/udc/dummy_hcd.c
drivers/usb/gadget/udc/fsl_mxc_udc.c [deleted file]
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci-tegra.c
drivers/usb/host/xhci.c
drivers/usb/misc/yurex.c
drivers/usb/serial/iuu_phoenix.c
drivers/usb/serial/option.c
drivers/usb/storage/unusual_uas.h
drivers/usb/typec/altmodes/Kconfig
drivers/usb/typec/class.c
drivers/usb/typec/mux/intel_pmc_mux.c
drivers/usb/usbip/vhci_hcd.c
drivers/vhost/net.c
drivers/vhost/vsock.c
drivers/xen/events/events_base.c
drivers/xen/platform-pci.c
drivers/xen/privcmd.c
drivers/xen/xenbus/xenbus.h
drivers/xen/xenbus/xenbus_comms.c
drivers/xen/xenbus/xenbus_probe.c
fs/afs/dir.c
fs/afs/dir_edit.c
fs/afs/xdr_fs.h
fs/block_dev.c
fs/btrfs/backref.c
fs/btrfs/block-group.c
fs/btrfs/btrfs_inode.h
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/dev-replace.c
fs/btrfs/discard.c
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/extent_io.c
fs/btrfs/file-item.c
fs/btrfs/inode.c
fs/btrfs/ioctl.c
fs/btrfs/print-tree.c
fs/btrfs/print-tree.h
fs/btrfs/qgroup.c
fs/btrfs/reflink.c
fs/btrfs/relocation.c
fs/btrfs/send.c
fs/btrfs/space-info.c
fs/btrfs/super.c
fs/btrfs/tests/btrfs-tests.c
fs/btrfs/tests/inode-tests.c
fs/btrfs/transaction.c
fs/btrfs/tree-checker.c
fs/btrfs/volumes.c
fs/cachefiles/rdwr.c
fs/ceph/mds_client.c
fs/cifs/connect.c
fs/cifs/dfs_cache.c
fs/cifs/fs_context.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/transport.c
fs/ext4/ext4_jbd2.c
fs/ext4/ext4_jbd2.h
fs/ext4/fast_commit.c
fs/ext4/file.c
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/namei.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/xattr.c
fs/file.c
fs/fs-writeback.c
fs/io_uring.c
fs/kernfs/file.c
fs/namespace.c
fs/nfs/delegation.c
fs/nfs/internal.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4super.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/pnfs_nfs.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfssvc.c
fs/nfsd/xdr4.h
fs/notify/fanotify/fanotify_user.c
fs/pipe.c
fs/proc/proc_sysctl.c
fs/proc/task_mmu.c
fs/select.c
fs/udf/super.c
fs/zonefs/Kconfig
include/asm-generic/Kbuild
include/asm-generic/bitops/atomic.h
include/dt-bindings/sound/apq8016-lpass.h
include/dt-bindings/sound/qcom,lpass.h [new file with mode: 0644]
include/dt-bindings/sound/sc7180-lpass.h
include/kvm/arm_pmu.h
include/linux/acpi.h
include/linux/blk-mq.h
include/linux/blkdev.h
include/linux/bpf.h
include/linux/bpf_verifier.h
include/linux/brcmphy.h
include/linux/btf.h
include/linux/build_bug.h
include/linux/buildid.h [new file with mode: 0644]
include/linux/can/bittiming.h [new file with mode: 0644]
include/linux/can/dev.h
include/linux/can/length.h [new file with mode: 0644]
include/linux/can/rx-offload.h
include/linux/can/skb.h
include/linux/ceph/msgr.h
include/linux/compiler-gcc.h
include/linux/compiler_attributes.h
include/linux/compiler_types.h
include/linux/console.h
include/linux/device.h
include/linux/dm-bufio.h
include/linux/dsa/brcm.h [new file with mode: 0644]
include/linux/filter.h
include/linux/intel-iommu.h
include/linux/ipv6.h
include/linux/kasan.h
include/linux/kcov.h
include/linux/kdev_t.h
include/linux/kthread.h
include/linux/ktime.h
include/linux/mdio-bitbang.h
include/linux/mdio.h
include/linux/memcontrol.h
include/linux/mlx5/device.h
include/linux/mlx5/driver.h
include/linux/mlx5/mlx5_ifc.h
include/linux/mm.h
include/linux/netdev_features.h
include/linux/netdevice.h
include/linux/nvme.h
include/linux/perf/arm_pmu.h
include/linux/phy.h
include/linux/qed/qed_chain.h
include/linux/rcupdate.h
include/linux/regulator/consumer.h
include/linux/remoteproc/qcom_rproc.h
include/linux/sizes.h
include/linux/skbuff.h
include/linux/soc/qcom/mdt_loader.h
include/linux/syscalls.h
include/linux/tcp.h
include/linux/timekeeping32.h [deleted file]
include/linux/tty.h
include/linux/usb/usbnet.h
include/media/v4l2-common.h
include/net/act_api.h
include/net/bonding.h
include/net/cfg80211.h
include/net/devlink.h
include/net/dsa.h
include/net/flow_offload.h
include/net/fq.h
include/net/fq_impl.h
include/net/inet_connection_sock.h
include/net/ip6_route.h
include/net/iucv/af_iucv.h
include/net/lapb.h
include/net/mac80211.h
include/net/netfilter/nf_tables.h
include/net/nexthop.h
include/net/pkt_cls.h
include/net/red.h
include/net/sch_generic.h
include/net/sock.h
include/net/switchdev.h
include/net/tcp.h
include/net/udp_tunnel.h
include/net/xdp.h
include/net/xdp_sock.h
include/net/xsk_buff_pool.h
include/soc/fsl/qe/qe.h
include/soc/fsl/qe/ucc_fast.h
include/soc/mscc/ocelot.h
include/soc/mscc/ocelot_qsys.h
include/soc/nps/common.h [deleted file]
include/soc/nps/mtm.h [deleted file]
include/sound/pcm.h
include/trace/events/afs.h
include/trace/events/sched.h
include/trace/events/sunrpc.h
include/uapi/linux/bcache.h
include/uapi/linux/bpf.h
include/uapi/linux/devlink.h
include/uapi/linux/gtp.h
include/uapi/linux/if_bonding.h
include/uapi/linux/if_link.h
include/uapi/linux/if_tunnel.h
include/uapi/linux/ipv6.h
include/uapi/linux/kvm.h
include/uapi/linux/mptcp.h
include/uapi/linux/mrp_bridge.h
include/uapi/linux/netfilter/nf_tables.h
include/uapi/linux/perf_event.h
include/uapi/linux/pkt_cls.h
include/uapi/linux/pkt_sched.h
include/uapi/linux/ppp-ioctl.h
include/uapi/linux/rpl.h
include/uapi/linux/sysctl.h
include/uapi/linux/tcp.h
include/uapi/linux/v4l2-subdev.h
include/uapi/misc/habanalabs.h
include/uapi/rdma/vmw_pvrdma-abi.h
include/xen/xenbus.h
init/main.c
kernel/bpf/bpf_inode_storage.c
kernel/bpf/bpf_task_storage.c
kernel/bpf/btf.c
kernel/bpf/cgroup.c
kernel/bpf/core.c
kernel/bpf/disasm.c
kernel/bpf/hashtab.c
kernel/bpf/helpers.c
kernel/bpf/preload/iterators/iterators.c
kernel/bpf/stackmap.c
kernel/bpf/syscall.c
kernel/bpf/task_iter.c
kernel/bpf/verifier.c
kernel/cgroup/cgroup-v1.c
kernel/cgroup/cgroup.c
kernel/configs/android-recommended.config
kernel/events/core.c
kernel/exit.c
kernel/fork.c
kernel/futex.c
kernel/irq/manage.c
kernel/irq/msi.c
kernel/kthread.c
kernel/locking/lockdep.c
kernel/locking/rtmutex.c
kernel/locking/rtmutex_common.h
kernel/printk/printk.c
kernel/printk/printk_ringbuffer.c
kernel/rcu/tasks.h
kernel/sched/core.c
kernel/sched/sched.h
kernel/signal.c
kernel/smpboot.c
kernel/time/ntp.c
kernel/time/timekeeping.c
kernel/trace/Kconfig
kernel/trace/trace_kprobe.c
kernel/workqueue.c
lib/Kconfig.debug
lib/Kconfig.ubsan
lib/Makefile
lib/buildid.c [new file with mode: 0644]
lib/fonts/font_ter16x32.c
lib/genalloc.c
lib/iov_iter.c
lib/raid6/Makefile
lib/test_bpf.c
lib/zlib_dfltcc/Makefile
lib/zlib_dfltcc/dfltcc.c
lib/zlib_dfltcc/dfltcc_deflate.c
lib/zlib_dfltcc/dfltcc_inflate.c
lib/zlib_dfltcc/dfltcc_syms.c [deleted file]
mm/highmem.c
mm/hugetlb.c
mm/kasan/generic.c
mm/kasan/hw_tags.c
mm/kasan/init.c
mm/memblock.c
mm/memcontrol.c
mm/memory-failure.c
mm/memory.c
mm/memory_hotplug.c
mm/mempolicy.c
mm/migrate.c
mm/mremap.c
mm/page-writeback.c
mm/page_alloc.c
mm/process_vm_access.c
mm/slub.c
mm/vmalloc.c
mm/vmscan.c
net/8021q/vlan.c
net/8021q/vlan_dev.c
net/9p/Kconfig
net/Makefile
net/atm/pppoatm.c
net/batman-adv/Kconfig
net/bluetooth/Kconfig
net/bpf/test_run.c
net/bpfilter/Kconfig
net/bridge/Makefile
net/bridge/br.c
net/bridge/br_fdb.c
net/bridge/br_forward.c
net/bridge/br_input.c
net/bridge/br_mrp.c
net/bridge/br_multicast.c
net/bridge/br_multicast_eht.c [new file with mode: 0644]
net/bridge/br_netlink.c
net/bridge/br_private.h
net/bridge/br_private_mcast_eht.h [new file with mode: 0644]
net/bridge/br_private_mrp.h
net/bridge/br_stp.c
net/bridge/br_switchdev.c
net/bridge/br_sysfs_if.c
net/bridge/br_vlan.c
net/can/Kconfig
net/can/gw.c
net/can/isotp.c
net/can/raw.c
net/ceph/auth_x.c
net/ceph/crypto.c
net/ceph/messenger_v1.c
net/ceph/messenger_v2.c
net/ceph/mon_client.c
net/ceph/osd_client.c
net/core/dev.c
net/core/devlink.c
net/core/filter.c
net/core/flow_dissector.c
net/core/gen_estimator.c
net/core/neighbour.c
net/core/net-sysfs.c
net/core/pktgen.c
net/core/rtnetlink.c
net/core/skbuff.c
net/core/sock.c
net/core/sock_map.c
net/core/sock_reuseport.c
net/core/sysctl_net_core.c
net/dcb/Makefile
net/dcb/dcbnl.c
net/decnet/dn_route.c
net/dns_resolver/Kconfig
net/dsa/Kconfig
net/dsa/Makefile
net/dsa/dsa.c
net/dsa/dsa2.c
net/dsa/dsa_priv.h
net/dsa/master.c
net/dsa/port.c
net/dsa/slave.c
net/dsa/switch.c
net/dsa/tag_brcm.c
net/dsa/tag_dsa.c
net/dsa/tag_xrs700x.c [new file with mode: 0644]
net/ethtool/common.c
net/ife/Kconfig
net/ipv4/esp4.c
net/ipv4/fib_frontend.c
net/ipv4/gre_demux.c
net/ipv4/gre_offload.c
net/ipv4/inet_connection_sock.c
net/ipv4/ip_output.c
net/ipv4/ip_tunnel.c
net/ipv4/ip_tunnel_core.c
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv4/netfilter/ipt_rpfilter.c
net/ipv4/nexthop.c
net/ipv4/tcp.c
net/ipv4/tcp_cubic.c
net/ipv4/tcp_input.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv4/tcp_recovery.c
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/ipv4/udp_offload.c
net/ipv4/udp_tunnel_core.c
net/ipv6/addrconf.c
net/ipv6/esp6.c
net/ipv6/ip6_fib.c
net/ipv6/ip6_output.c
net/ipv6/ndisc.c
net/ipv6/netfilter/ip6_tables.c
net/ipv6/route.c
net/ipv6/sit.c
net/iucv/af_iucv.c
net/kcm/kcmsock.c
net/key/af_key.c
net/l3mdev/Makefile
net/lapb/lapb_iface.c
net/lapb/lapb_timer.c
net/llc/Kconfig
net/mac80211/Makefile
net/mac80211/debugfs.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/he.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/pm.c
net/mac80211/rc80211_minstrel.c [deleted file]
net/mac80211/rc80211_minstrel.h [deleted file]
net/mac80211/rc80211_minstrel_debugfs.c [deleted file]
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rc80211_minstrel_ht.h
net/mac80211/rc80211_minstrel_ht_debugfs.c
net/mac80211/rx.c
net/mac80211/spectmgmt.c
net/mac80211/sta_info.h
net/mac80211/tdls.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mptcp/mib.c
net/mptcp/mib.h
net/mptcp/options.c
net/mptcp/pm.c
net/mptcp/pm_netlink.c
net/mptcp/protocol.c
net/mptcp/protocol.h
net/mptcp/subflow.c
net/ncsi/ncsi-rsp.c
net/netfilter/Kconfig
net/netfilter/ipset/ip_set_hash_gen.h
net/netfilter/ipvs/Kconfig
net/netfilter/nf_conntrack_standalone.c
net/netfilter/nf_nat_core.c
net/netfilter/nf_tables_api.c
net/netfilter/nft_dynset.c
net/netfilter/xt_RATEEST.c
net/nfc/Kconfig
net/nfc/hci/llc_shdlc.c
net/nfc/nci/core.c
net/nfc/netlink.c
net/nfc/rawsock.c
net/openvswitch/actions.c
net/openvswitch/flow_netlink.c
net/packet/af_packet.c
net/packet/internal.h
net/psample/Kconfig
net/qrtr/ns.c
net/qrtr/qrtr.c
net/qrtr/qrtr.h
net/rxrpc/call_accept.c
net/rxrpc/input.c
net/rxrpc/key.c
net/sched/act_api.c
net/sched/act_ct.c
net/sched/cls_api.c
net/sched/cls_flower.c
net/sched/cls_tcindex.c
net/sched/sch_api.c
net/sched/sch_atm.c
net/sched/sch_cbq.c
net/sched/sch_choke.c
net/sched/sch_drr.c
net/sched/sch_dsmark.c
net/sched/sch_gred.c
net/sched/sch_hfsc.c
net/sched/sch_htb.c
net/sched/sch_qfq.c
net/sched/sch_red.c
net/sched/sch_sfb.c
net/sched/sch_sfq.c
net/sched/sch_taprio.c
net/sctp/offload.c
net/smc/smc_core.c
net/smc/smc_ib.c
net/smc/smc_ism.c
net/sunrpc/addr.c
net/sunrpc/svc_xprt.c
net/sunrpc/svcsock.c
net/switchdev/Makefile
net/switchdev/switchdev.c
net/tipc/link.c
net/tipc/monitor.c
net/tipc/msg.c
net/tipc/node.c
net/tls/tls_device.c
net/tls/tls_device_fallback.c
net/wireless/Kconfig
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/debugfs.c
net/wireless/ibss.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/reg.c
net/wireless/reg.h
net/wireless/scan.c
net/wireless/sme.c
net/wireless/sysfs.c
net/wireless/util.c
net/wireless/wext-compat.c
net/wireless/wext-core.c
net/wireless/wext-sme.c
net/xdp/xsk.c
net/xdp/xsk_buff_pool.c
net/xdp/xsk_queue.h
net/xfrm/xfrm_input.c
net/xfrm/xfrm_policy.c
samples/bpf/bpf_insn.h
samples/bpf/cookie_uid_helper_example.c
samples/bpf/sock_example.c
samples/bpf/test_cgrp2_attach.c
scripts/checkpatch.pl
scripts/config
scripts/depmod.sh
scripts/gcc-plugins/Makefile
scripts/kconfig/Makefile
scripts/kconfig/mconf-cfg.sh
security/lsm_audit.c
sound/core/pcm_native.c
sound/core/seq/oss/seq_oss_synth.c
sound/firewire/fireface/ff-transaction.c
sound/firewire/tascam/tascam-transaction.c
sound/hda/intel-dsp-config.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_tegra.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_via.c
sound/soc/amd/raven/pci-acp3x.c
sound/soc/amd/renoir/rn-pci-acp3x.c
sound/soc/atmel/Kconfig
sound/soc/codecs/Kconfig
sound/soc/codecs/ak4458.c
sound/soc/codecs/hdmi-codec.c
sound/soc/codecs/max98373-i2c.c
sound/soc/codecs/max98373-sdw.c
sound/soc/codecs/max98373.c
sound/soc/codecs/max98373.h
sound/soc/codecs/rt711.c
sound/soc/codecs/wm_adsp.c
sound/soc/fsl/imx-hdmi.c
sound/soc/intel/boards/haswell.c
sound/soc/intel/boards/sof_sdw.c
sound/soc/intel/skylake/cnl-sst.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c
sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c
sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
sound/soc/meson/axg-tdm-interface.c
sound/soc/meson/axg-tdmin.c
sound/soc/qcom/lpass-cpu.c
sound/soc/qcom/lpass-ipq806x.c
sound/soc/qcom/lpass-lpaif-reg.h
sound/soc/qcom/lpass-platform.c
sound/soc/qcom/lpass-sc7180.c
sound/soc/qcom/lpass.h
sound/soc/sh/rcar/adg.c
sound/soc/soc-dapm.c
sound/soc/soc-topology.c
sound/soc/sof/Kconfig
sound/soc/sof/intel/Kconfig
sound/soc/sof/intel/hda-codec.c
sound/soc/sof/intel/hda-dsp.c
sound/soc/sof/intel/hda.h
sound/soc/sof/sof-acpi-dev.c
sound/soc/sof/sof-pci-dev.c
sound/usb/card.c
sound/usb/card.h
sound/usb/clock.c
sound/usb/endpoint.c
sound/usb/endpoint.h
sound/usb/format.c
sound/usb/implicit.c
sound/usb/midi.c
sound/usb/pcm.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/usbaudio.h
tools/bootconfig/scripts/bconf2ftrace.sh
tools/bootconfig/scripts/ftrace2bconf.sh
tools/bpf/bpftool/Makefile
tools/bpf/bpftool/net.c
tools/bpf/resolve_btfids/main.c
tools/gpio/gpio-event-mon.c
tools/gpio/gpio-watch.c
tools/include/linux/build_bug.h
tools/include/linux/filter.h
tools/include/uapi/linux/bpf.h
tools/include/uapi/linux/if_link.h
tools/include/uapi/linux/kvm.h
tools/include/uapi/linux/pkt_sched.h
tools/lib/bpf/bpf_core_read.h
tools/lib/bpf/bpf_helpers.h
tools/lib/bpf/btf.c
tools/lib/bpf/libbpf.c
tools/lib/perf/evlist.c
tools/lib/perf/tests/test-cpumap.c
tools/lib/perf/tests/test-evlist.c
tools/lib/perf/tests/test-evsel.c
tools/lib/perf/tests/test-threadmap.c
tools/objtool/check.c
tools/objtool/elf.c
tools/perf/builtin-script.c
tools/perf/examples/bpf/5sec.c
tools/perf/tests/shell/stat+shadow_stat.sh
tools/perf/util/header.c
tools/perf/util/machine.c
tools/perf/util/metricgroup.c
tools/perf/util/session.c
tools/perf/util/stat-shadow.c
tools/power/x86/intel-speed-select/isst-config.c
tools/testing/kunit/kunit.py
tools/testing/kunit/kunit_config.py
tools/testing/kunit/kunit_json.py
tools/testing/kunit/kunit_kernel.py
tools/testing/kunit/kunit_parser.py
tools/testing/selftests/Makefile
tools/testing/selftests/arm64/fp/fpsimd-test.S
tools/testing/selftests/arm64/fp/sve-test.S
tools/testing/selftests/bpf/Makefile
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
tools/testing/selftests/bpf/prog_tests/atomics.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/btf_map_in_map.c
tools/testing/selftests/bpf/prog_tests/cgroup_attach_multi.c
tools/testing/selftests/bpf/prog_tests/core_read_macros.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/ksyms_module.c [new file with mode: 0644]
tools/testing/selftests/bpf/prog_tests/test_local_storage.c
tools/testing/selftests/bpf/prog_tests/test_lsm.c
tools/testing/selftests/bpf/progs/atomics.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/bind4_prog.c
tools/testing/selftests/bpf/progs/bind6_prog.c
tools/testing/selftests/bpf/progs/bprm_opts.c
tools/testing/selftests/bpf/progs/local_storage.c
tools/testing/selftests/bpf/progs/test_core_read_macros.c [new file with mode: 0644]
tools/testing/selftests/bpf/progs/test_ksyms_module.c [new file with mode: 0644]
tools/testing/selftests/bpf/test_cgroup_storage.c
tools/testing/selftests/bpf/test_maps.c
tools/testing/selftests/bpf/test_progs.c
tools/testing/selftests/bpf/test_progs.h
tools/testing/selftests/bpf/test_verifier.c
tools/testing/selftests/bpf/verifier/atomic_and.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/atomic_cmpxchg.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/atomic_fetch_add.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/atomic_or.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/atomic_xchg.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/atomic_xor.c [new file with mode: 0644]
tools/testing/selftests/bpf/verifier/ctx.c
tools/testing/selftests/bpf/verifier/direct_packet_access.c
tools/testing/selftests/bpf/verifier/leak_ptr.c
tools/testing/selftests/bpf/verifier/meta_access.c
tools/testing/selftests/bpf/verifier/spill_fill.c
tools/testing/selftests/bpf/verifier/unpriv.c
tools/testing/selftests/bpf/verifier/value_illegal_alu.c
tools/testing/selftests/bpf/verifier/xadd.c
tools/testing/selftests/bpf/xdpxceiver.c
tools/testing/selftests/drivers/net/mlxsw/port_scale.sh [new file with mode: 0644]
tools/testing/selftests/drivers/net/mlxsw/qos_pfc.sh
tools/testing/selftests/drivers/net/mlxsw/spectrum-2/port_scale.sh [new file with mode: 0644]
tools/testing/selftests/drivers/net/mlxsw/spectrum-2/resource_scale.sh
tools/testing/selftests/drivers/net/mlxsw/spectrum/port_scale.sh [new file with mode: 0644]
tools/testing/selftests/drivers/net/mlxsw/spectrum/resource_scale.sh
tools/testing/selftests/kvm/Makefile
tools/testing/selftests/kvm/demand_paging_test.c
tools/testing/selftests/kvm/dirty_log_perf_test.c
tools/testing/selftests/kvm/dirty_log_test.c
tools/testing/selftests/kvm/include/guest_modes.h [new file with mode: 0644]
tools/testing/selftests/kvm/include/kvm_util.h
tools/testing/selftests/kvm/include/perf_test_util.h
tools/testing/selftests/kvm/lib/guest_modes.c [new file with mode: 0644]
tools/testing/selftests/kvm/lib/kvm_util.c
tools/testing/selftests/kvm/lib/perf_test_util.c [new file with mode: 0644]
tools/testing/selftests/nci/Makefile [new file with mode: 0644]
tools/testing/selftests/nci/config [new file with mode: 0644]
tools/testing/selftests/nci/nci_dev.c [new file with mode: 0644]
tools/testing/selftests/net/Makefile
tools/testing/selftests/net/fcnal-test.sh
tools/testing/selftests/net/fib_nexthops.sh
tools/testing/selftests/net/fib_tests.sh
tools/testing/selftests/net/forwarding/router_mpath_nh.sh
tools/testing/selftests/net/forwarding/router_multipath.sh
tools/testing/selftests/net/forwarding/tc_chains.sh
tools/testing/selftests/net/mptcp/mptcp_join.sh
tools/testing/selftests/net/mptcp/pm_netlink.sh
tools/testing/selftests/net/mptcp/pm_nl_ctl.c
tools/testing/selftests/net/mptcp/settings
tools/testing/selftests/net/nettest.c
tools/testing/selftests/net/pmtu.sh
tools/testing/selftests/net/tls.c
tools/testing/selftests/net/udpgro.sh
tools/testing/selftests/net/unicast_extensions.sh [new file with mode: 0755]
tools/testing/selftests/net/xfrm_policy.sh
tools/testing/selftests/netfilter/Makefile
tools/testing/selftests/netfilter/ipip-conntrack-mtu.sh [new file with mode: 0755]
tools/testing/selftests/netfilter/nft_conntrack_helper.sh
tools/testing/selftests/powerpc/alignment/alignment_handler.c
tools/testing/selftests/powerpc/mm/pkey_exec_prot.c
tools/testing/selftests/powerpc/mm/pkey_siginfo.c
tools/testing/selftests/vDSO/.gitignore
tools/testing/selftests/vDSO/vdso_test_correctness.c
tools/testing/selftests/vm/Makefile
tools/testing/selftests/wireguard/qemu/debug.config
virt/kvm/kvm_main.c

index 632700c..cc4e91d 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -9,9 +9,6 @@
 #
 # Please keep this list dictionary sorted.
 #
-# This comment is parsed by git-shortlog:
-# repo-abbrev: /pub/scm/linux/kernel/git/
-#
 Aaron Durbin <adurbin@google.com>
 Adam Oldham <oldhamca@gmail.com>
 Adam Radford <aradford@gmail.com>
@@ -55,6 +52,8 @@ Bart Van Assche <bvanassche@acm.org> <bart.vanassche@wdc.com>
 Ben Gardner <bgardner@wabtec.com>
 Ben M Cahill <ben.m.cahill@intel.com>
 Björn Steinbrink <B.Steinbrink@gmx.de>
+Björn Töpel <bjorn@kernel.org> <bjorn.topel@gmail.com>
+Björn Töpel <bjorn@kernel.org> <bjorn.topel@intel.com>
 Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com>
 Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com>
 Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com>
diff --git a/CREDITS b/CREDITS
index 090ed4b..9add7e6 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -710,6 +710,10 @@ S: Las Cuevas 2385 - Bo Guemes
 S: Las Heras, Mendoza CP 5539
 S: Argentina
 
+N: Jay Cliburn
+E: jcliburn@gmail.com
+D: ATLX Ethernet drivers
+
 N: Steven P. Cole
 E: scole@lanl.gov
 E: elenstev@mesatop.com
@@ -1284,6 +1288,10 @@ D: Major kbuild rework during the 2.5 cycle
 D: ISDN Maintainer
 S: USA
 
+N: Gerrit Renker
+E: gerrit@erg.abdn.ac.uk
+D: DCCP protocol support.
+
 N: Philip Gladstone
 E: philip@gladstonefamily.net
 D: Kernel / timekeeping stuff
@@ -2138,6 +2146,10 @@ E: seasons@falcon.sch.bme.hu
 E: seasons@makosteszta.sote.hu
 D: Original author of software suspend
 
+N: Alexey Kuznetsov
+E: kuznet@ms2.inr.ac.ru
+D: Author and maintainer of large parts of the networking stack
+
 N: Jaroslav Kysela
 E: perex@perex.cz
 W: https://www.perex.cz
@@ -2696,6 +2708,10 @@ N: Wolfgang Muees
 E: wolfgang@iksw-muees.de
 D: Auerswald USB driver
 
+N: Shrijeet Mukherjee
+E: shrijeet@gmail.com
+D: Network routing domains (VRF).
+
 N: Paul Mundt
 E: paul.mundt@gmail.com
 D: SuperH maintainer
@@ -4110,6 +4126,10 @@ S: B-1206 Jingmao Guojigongyu
 S: 16 Baliqiao Nanjie, Beijing 101100
 S: People's Repulic of China
 
+N: Aviad Yehezkel
+E: aviadye@nvidia.com
+D: Kernel TLS implementation and offload support.
+
 N: Victor Yodaiken
 E: yodaiken@fsmlabs.com
 D: RTLinux (RealTime Linux)
@@ -4167,6 +4187,10 @@ S: 1507 145th Place SE #B5
 S: Bellevue, Washington 98007
 S: USA
 
+N: Wensong Zhang
+E: wensong@linux-vs.org
+D: IP virtual server (IPVS).
+
 N: Haojian Zhuang
 E: haojian.zhuang@gmail.com
 D: MMP support
index b662f74..8a21ce5 100644 (file)
@@ -5,8 +5,8 @@ Description:
                Provide a place in sysfs for the device link objects in the
                kernel at any given time.  The name of a device link directory,
                denoted as ... above, is of the form <supplier>--<consumer>
-               where <supplier> is the supplier device name and <consumer> is
-               the consumer device name.
+               where <supplier> is the supplier bus:device name and <consumer>
+               is the consumer bus:device name.
 
 What:          /sys/class/devlink/.../auto_remove_on
 Date:          May 2020
index c310db4..ed79f58 100644 (file)
@@ -48,3 +48,13 @@ Description:
 
                Write a number ranging from 1 to 254 to delete a previously
                created qmap mux based network device.
+
+What:          /sys/class/net/<qmimux iface>/qmap/mux_id
+Date:          January 2021
+KernelVersion: 5.12
+Contact:       Daniele Palmas <dnlplm@gmail.com>
+Description:
+               Unsigned integer
+
+               Indicates the mux id associated to the qmimux network interface
+               during its creation.
index 1f06d74..0809fda 100644 (file)
@@ -4,5 +4,6 @@ Contact:        Saravana Kannan <saravanak@google.com>
 Description:
                The /sys/devices/.../consumer:<consumer> are symlinks to device
                links where this device is the supplier. <consumer> denotes the
-               name of the consumer in that device link. There can be zero or
-               more of these symlinks for a given device.
+               name of the consumer in that device link and is of the form
+               bus:device name. There can be zero or more of these symlinks
+               for a given device.
index a919e0d..207f597 100644 (file)
@@ -4,5 +4,6 @@ Contact:        Saravana Kannan <saravanak@google.com>
 Description:
                The /sys/devices/.../supplier:<supplier> are symlinks to device
                links where this device is the consumer. <supplier> denotes the
-               name of the supplier in that device link. There can be zero or
-               more of these symlinks for a given device.
+               name of the supplier in that device link and is of the form
+               bus:device name. There can be zero or more of these symlinks
+               for a given device.
index adc0d0e..75ccc5c 100644 (file)
@@ -916,21 +916,25 @@ Date:             September 2014
 Contact:       Subhash Jadavani <subhashj@codeaurora.org>
 Description:   This entry could be used to set or show the UFS device
                runtime power management level. The current driver
-               implementation supports 6 levels with next target states:
+               implementation supports 7 levels with next target states:
 
                ==  ====================================================
-               0   an UFS device will stay active, an UIC link will
+               0   UFS device will stay active, UIC link will
                    stay active
-               1   an UFS device will stay active, an UIC link will
+               1   UFS device will stay active, UIC link will
                    hibernate
-               2   an UFS device will moved to sleep, an UIC link will
+               2   UFS device will be moved to sleep, UIC link will
                    stay active
-               3   an UFS device will moved to sleep, an UIC link will
+               3   UFS device will be moved to sleep, UIC link will
                    hibernate
-               4   an UFS device will be powered off, an UIC link will
+               4   UFS device will be powered off, UIC link will
                    hibernate
-               5   an UFS device will be powered off, an UIC link will
+               5   UFS device will be powered off, UIC link will
                    be powered off
+               6   UFS device will be moved to deep sleep, UIC link
+                   will be powered off. Note, deep sleep might not be
+                   supported in which case this value will not be
+                   accepted
                ==  ====================================================
 
 What:          /sys/bus/platform/drivers/ufshcd/*/rpm_target_dev_state
@@ -954,21 +958,25 @@ Date:             September 2014
 Contact:       Subhash Jadavani <subhashj@codeaurora.org>
 Description:   This entry could be used to set or show the UFS device
                system power management level. The current driver
-               implementation supports 6 levels with next target states:
+               implementation supports 7 levels with next target states:
 
                ==  ====================================================
-               0   an UFS device will stay active, an UIC link will
+               0   UFS device will stay active, UIC link will
                    stay active
-               1   an UFS device will stay active, an UIC link will
+               1   UFS device will stay active, UIC link will
                    hibernate
-               2   an UFS device will moved to sleep, an UIC link will
+               2   UFS device will be moved to sleep, UIC link will
                    stay active
-               3   an UFS device will moved to sleep, an UIC link will
+               3   UFS device will be moved to sleep, UIC link will
                    hibernate
-               4   an UFS device will be powered off, an UIC link will
+               4   UFS device will be powered off, UIC link will
                    hibernate
-               5   an UFS device will be powered off, an UIC link will
+               5   UFS device will be powered off, UIC link will
                    be powered off
+               6   UFS device will be moved to deep sleep, UIC link
+                   will be powered off. Note, deep sleep might not be
+                   supported in which case this value will not be
+                   accepted
                ==  ====================================================
 
 What:          /sys/bus/platform/drivers/ufshcd/*/spm_target_dev_state
index 83ae3b7..a648b42 100644 (file)
@@ -473,7 +473,7 @@ read-side critical sections that follow the idle period (the oval near
 the bottom of the diagram above).
 
 Plumbing this into the full grace-period execution is described
-`below <#Forcing%20Quiescent%20States>`__.
+`below <Forcing Quiescent States_>`__.
 
 CPU-Hotplug Interface
 ^^^^^^^^^^^^^^^^^^^^^
@@ -494,7 +494,7 @@ mask to detect CPUs having gone offline since the beginning of this
 grace period.
 
 Plumbing this into the full grace-period execution is described
-`below <#Forcing%20Quiescent%20States>`__.
+`below <Forcing Quiescent States_>`__.
 
 Forcing Quiescent States
 ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -532,7 +532,7 @@ from other CPUs.
 | RCU. But this diagram is complex enough as it is, so simplicity       |
 | overrode accuracy. You can think of it as poetic license, or you can  |
 | think of it as misdirection that is resolved in the                   |
-| `stitched-together diagram <#Putting%20It%20All%20Together>`__.       |
+| `stitched-together diagram <Putting It All Together_>`__.             |
 +-----------------------------------------------------------------------+
 
 Grace-Period Cleanup
@@ -596,7 +596,7 @@ maintain ordering. For example, if the callback function wakes up a task
 that runs on some other CPU, proper ordering must in place in both the
 callback function and the task being awakened. To see why this is
 important, consider the top half of the `grace-period
-cleanup <#Grace-Period%20Cleanup>`__ diagram. The callback might be
+cleanup`_ diagram. The callback might be
 running on a CPU corresponding to the leftmost leaf ``rcu_node``
 structure, and awaken a task that is to run on a CPU corresponding to
 the rightmost leaf ``rcu_node`` structure, and the grace-period kernel
index e8c84fc..d4c9a01 100644 (file)
@@ -45,7 +45,7 @@ requirements:
 #. `Other RCU Flavors`_
 #. `Possible Future Changes`_
 
-This is followed by a `summary <#Summary>`__, however, the answers to
+This is followed by a summary_, however, the answers to
 each quick quiz immediately follows the quiz. Select the big white space
 with your mouse to see the answer.
 
@@ -1096,7 +1096,7 @@ memory barriers.
 | case, voluntary context switch) within an RCU read-side critical      |
 | section. However, sleeping locks may be used within userspace RCU     |
 | read-side critical sections, and also within Linux-kernel sleepable   |
-| RCU `(SRCU) <#Sleepable%20RCU>`__ read-side critical sections. In     |
+| RCU `(SRCU) <Sleepable RCU_>`__ read-side critical sections. In       |
 | addition, the -rt patchset turns spinlocks into a sleeping locks so   |
 | that the corresponding critical sections can be preempted, which also |
 | means that these sleeplockified spinlocks (but not other sleeping     |
@@ -1186,7 +1186,7 @@ non-preemptible (``CONFIG_PREEMPT=n``) kernels, and thus `tiny
 RCU <https://lkml.kernel.org/g/20090113221724.GA15307@linux.vnet.ibm.com>`__
 was born. Josh Triplett has since taken over the small-memory banner
 with his `Linux kernel tinification <https://tiny.wiki.kernel.org/>`__
-project, which resulted in `SRCU <#Sleepable%20RCU>`__ becoming optional
+project, which resulted in `SRCU <Sleepable RCU_>`__ becoming optional
 for those kernels not needing it.
 
 The remaining performance requirements are, for the most part,
@@ -1457,8 +1457,8 @@ will vary as the value of ``HZ`` varies, and can also be changed using
 the relevant Kconfig options and kernel boot parameters. RCU currently
 does not do much sanity checking of these parameters, so please use
 caution when changing them. Note that these forward-progress measures
-are provided only for RCU, not for `SRCU <#Sleepable%20RCU>`__ or `Tasks
-RCU <#Tasks%20RCU>`__.
+are provided only for RCU, not for `SRCU <Sleepable RCU_>`__ or `Tasks
+RCU`_.
 
 RCU takes the following steps in ``call_rcu()`` to encourage timely
 invocation of callbacks when any given non-\ ``rcu_nocbs`` CPU has
@@ -1477,8 +1477,8 @@ encouragement was provided:
 
 Again, these are default values when running at ``HZ=1000``, and can be
 overridden. Again, these forward-progress measures are provided only for
-RCU, not for `SRCU <#Sleepable%20RCU>`__ or `Tasks
-RCU <#Tasks%20RCU>`__. Even for RCU, callback-invocation forward
+RCU, not for `SRCU <Sleepable RCU_>`__ or `Tasks
+RCU`_. Even for RCU, callback-invocation forward
 progress for ``rcu_nocbs`` CPUs is much less well-developed, in part
 because workloads benefiting from ``rcu_nocbs`` CPUs tend to invoke
 ``call_rcu()`` relatively infrequently. If workloads emerge that need
@@ -1920,7 +1920,7 @@ Hotplug CPU
 
 The Linux kernel supports CPU hotplug, which means that CPUs can come
 and go. It is of course illegal to use any RCU API member from an
-offline CPU, with the exception of `SRCU <#Sleepable%20RCU>`__ read-side
+offline CPU, with the exception of `SRCU <Sleepable RCU_>`__ read-side
 critical sections. This requirement was present from day one in
 DYNIX/ptx, but on the other hand, the Linux kernel's CPU-hotplug
 implementation is “interesting.”
@@ -2177,7 +2177,7 @@ handles these states differently:
 However, RCU must be reliably informed as to whether any given CPU is
 currently in the idle loop, and, for ``NO_HZ_FULL``, also whether that
 CPU is executing in usermode, as discussed
-`earlier <#Energy%20Efficiency>`__. It also requires that the
+`earlier <Energy Efficiency_>`__. It also requires that the
 scheduling-clock interrupt be enabled when RCU needs it to be:
 
 #. If a CPU is either idle or executing in usermode, and RCU believes it
@@ -2294,7 +2294,7 @@ Performance, Scalability, Response Time, and Reliability
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Expanding on the `earlier
-discussion <#Performance%20and%20Scalability>`__, RCU is used heavily by
+discussion <Performance and Scalability_>`__, RCU is used heavily by
 hot code paths in performance-critical portions of the Linux kernel's
 networking, security, virtualization, and scheduling code paths. RCU
 must therefore use efficient implementations, especially in its
index 7a86413..59cd902 100644 (file)
@@ -23,7 +23,7 @@ Here is what the fields mean:
 
 - ``name``
    is an identifier string. A new /proc file will be created with this
-   ``name below /proc/sys/fs/binfmt_misc``; cannot contain slashes ``/`` for
+   name below ``/proc/sys/fs/binfmt_misc``; cannot contain slashes ``/`` for
    obvious reasons.
 - ``type``
    is the type of recognition. Give ``M`` for magic and ``E`` for extension.
@@ -83,7 +83,7 @@ Here is what the fields mean:
       ``F`` - fix binary
             The usual behaviour of binfmt_misc is to spawn the
            binary lazily when the misc format file is invoked.  However,
-           this doesn``t work very well in the face of mount namespaces and
+           this doesn't work very well in the face of mount namespaces and
            changeroots, so the ``F`` mode opens the binary as soon as the
            emulation is installed and uses the opened image to spawn the
            emulator, meaning it is always available once installed,
index 9b90efc..452b7dc 100644 (file)
@@ -154,7 +154,7 @@ get the boot configuration data.
 Because of this "piggyback" method, there is no need to change or
 update the boot loader and the kernel image itself as long as the boot
 loader passes the correct initrd file size. If by any chance, the boot
-loader passes a longer size, the kernel feils to find the bootconfig data.
+loader passes a longer size, the kernel fails to find the bootconfig data.
 
 To do this operation, Linux kernel provides "bootconfig" command under
 tools/bootconfig, which allows admin to apply or delete the config file
index 4e6f504..2cc5488 100644 (file)
@@ -177,14 +177,20 @@ bitmap_flush_interval:number
        The bitmap flush interval in milliseconds. The metadata buffers
        are synchronized when this interval expires.
 
+allow_discards
+       Allow block discard requests (a.k.a. TRIM) for the integrity device.
+       Discards are only allowed to devices using internal hash.
+
 fix_padding
        Use a smaller padding of the tag area that is more
        space-efficient. If this option is not present, large padding is
        used - that is for compatibility with older kernels.
 
-allow_discards
-       Allow block discard requests (a.k.a. TRIM) for the integrity device.
-       Discards are only allowed to devices using internal hash.
+legacy_recalculate
+       Allow recalculating of volumes with HMAC keys. This is disabled by
+       default for security reasons - an attacker could modify the volume,
+       set recalc_sector to zero, and the kernel would not detect the
+       modification.
 
 The journal mode (D/J), buffer_sectors, journal_watermark, commit_time and
 allow_discards can be changed when reloading the target (load an inactive
index 06fb1b4..682ab28 100644 (file)
@@ -3,8 +3,8 @@
 The kernel's command-line parameters
 ====================================
 
-The following is a consolidated list of the kernel parameters as
-implemented by the __setup(), core_param() and module_param() macros
+The following is a consolidated list of the kernel parameters as implemented
+by the __setup(), early_param(), core_param() and module_param() macros
 and sorted into English Dictionary order (defined as ignoring all
 punctuation and sorting digits before letters in a case insensitive
 manner), and with descriptions where known.
index c722ec1..a10b545 100644 (file)
 
        ftrace_filter=[function-list]
                        [FTRACE] Limit the functions traced by the function
-                       tracer at boot up. function-list is a comma separated
+                       tracer at boot up. function-list is a comma-separated
                        list of functions. This list can be changed at run
                        time by the set_ftrace_filter file in the debugfs
                        tracing directory.
        ftrace_graph_filter=[function-list]
                        [FTRACE] Limit the top level callers functions traced
                        by the function graph tracer at boot up.
-                       function-list is a comma separated list of functions
+                       function-list is a comma-separated list of functions
                        that can be changed at run time by the
                        set_graph_function file in the debugfs tracing directory.
 
        ftrace_graph_notrace=[function-list]
                        [FTRACE] Do not trace from the functions specified in
-                       function-list.  This list is a comma separated list of
+                       function-list.  This list is a comma-separated list of
                        functions that can be changed at run time by the
                        set_graph_notrace file in the debugfs tracing directory.
 
                        when set.
                        Format: <int>
 
-       libata.force=   [LIBATA] Force configurations.  The format is comma
+       libata.force=   [LIBATA] Force configurations.  The format is comma-
                        separated list of "[ID:]VAL" where ID is
                        PORT[.DEVICE].  PORT and DEVICE are decimal numbers
                        matching port, link or device.  Basically, it matches
 
        stacktrace_filter=[function-list]
                        [FTRACE] Limit the functions that the stack tracer
-                       will trace at boot up. function-list is a comma separated
+                       will trace at boot up. function-list is a comma-separated
                        list of functions. This list can be changed at run
                        time by the stack_trace_filter file in the debugfs
                        tracing directory. Note, this enables stack tracing
        trace_event=[event-list]
                        [FTRACE] Set and start specified trace events in order
                        to facilitate early boot debugging. The event-list is a
-                       comma separated list of trace events to enable. See
+                       comma-separated list of trace events to enable. See
                        also Documentation/trace/events.rst
 
        trace_options=[option-list]
                        This option is obsoleted by the "nopv" option, which
                        has equivalent effect for XEN platform.
 
+       xen_no_vector_callback
+                       [KNL,X86,XEN] Disable the vector callback for Xen
+                       event channel interrupts.
+
        xen_scrub_pages=        [XEN]
                        Boolean option to control scrubbing pages before giving them back
                        to Xen, for use by other domains. Can be also changed at runtime
index fa0974f..b966fcf 100644 (file)
@@ -184,7 +184,7 @@ pages either asynchronously or synchronously, depending on the state
 of the system. When the system is not loaded, most of the memory is free
 and allocation requests will be satisfied immediately from the free
 pages supply. As the load increases, the amount of the free pages goes
-down and when it reaches a certain threshold (high watermark), an
+down and when it reaches a certain threshold (low watermark), an
 allocation request will awaken the ``kswapd`` daemon. It will
 asynchronously scan memory pages and either just free them if the data
 they contain is available elsewhere, or evict to the backing storage
index 69171b1..f1c9d20 100644 (file)
@@ -53,7 +53,6 @@ How Linux keeps everything from happening at the same time.  See
 .. toctree::
    :maxdepth: 1
 
-   atomic_ops
    refcount-vs-atomic
    irq/index
    local_ops
index 0fc3fb1..1651d96 100644 (file)
@@ -160,29 +160,14 @@ intended for use in production as a security mitigation. Therefore it supports
 boot parameters that allow to disable KASAN competely or otherwise control
 particular KASAN features.
 
-The things that can be controlled are:
+- ``kasan=off`` or ``=on`` controls whether KASAN is enabled (default: ``on``).
 
-1. Whether KASAN is enabled at all.
-2. Whether KASAN collects and saves alloc/free stacks.
-3. Whether KASAN panics on a detected bug or not.
+- ``kasan.stacktrace=off`` or ``=on`` disables or enables alloc and free stack
+  traces collection (default: ``on`` for ``CONFIG_DEBUG_KERNEL=y``, otherwise
+  ``off``).
 
-The ``kasan.mode`` boot parameter allows to choose one of three main modes:
-
-- ``kasan.mode=off`` - KASAN is disabled, no tag checks are performed
-- ``kasan.mode=prod`` - only essential production features are enabled
-- ``kasan.mode=full`` - all KASAN features are enabled
-
-The chosen mode provides default control values for the features mentioned
-above. However it's also possible to override the default values by providing:
-
-- ``kasan.stacktrace=off`` or ``=on`` - enable alloc/free stack collection
-                                       (default: ``on`` for ``mode=full``,
-                                        otherwise ``off``)
-- ``kasan.fault=report`` or ``=panic`` - only print KASAN report or also panic
-                                        (default: ``report``)
-
-If ``kasan.mode`` parameter is not provided, it defaults to ``full`` when
-``CONFIG_DEBUG_KERNEL`` is enabled, and to ``prod`` otherwise.
+- ``kasan.fault=report`` or ``=panic`` controls whether to only print a KASAN
+  report or also panic the kernel (default: ``report``).
 
 For developers
 ~~~~~~~~~~~~~~
index d9fdc14..650f995 100644 (file)
@@ -522,6 +522,63 @@ There's more boilerplate involved, but it can:
   * E.g. if we wanted to also test ``sha256sum``, we could add a ``sha256``
     field and reuse ``cases``.
 
+* be converted to a "parameterized test", see below.
+
+Parameterized Testing
+~~~~~~~~~~~~~~~~~~~~~
+
+The table-driven testing pattern is common enough that KUnit has special
+support for it.
+
+Reusing the same ``cases`` array from above, we can write the test as a
+"parameterized test" with the following.
+
+.. code-block:: c
+
+       // This is copy-pasted from above.
+       struct sha1_test_case {
+               const char *str;
+               const char *sha1;
+       };
+       struct sha1_test_case cases[] = {
+               {
+                       .str = "hello world",
+                       .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
+               },
+               {
+                       .str = "hello world!",
+                       .sha1 = "430ce34d020724ed75a196dfc2ad67c77772d169",
+               },
+       };
+
+       // Need a helper function to generate a name for each test case.
+       static void case_to_desc(const struct sha1_test_case *t, char *desc)
+       {
+               strcpy(desc, t->str);
+       }
+       // Creates `sha1_gen_params()` to iterate over `cases`.
+       KUNIT_ARRAY_PARAM(sha1, cases, case_to_desc);
+
+       // Looks no different from a normal test.
+       static void sha1_test(struct kunit *test)
+       {
+               // This function can just contain the body of the for-loop.
+               // The former `cases[i]` is accessible under test->param_value.
+               char out[40];
+               struct sha1_test_case *test_param = (struct sha1_test_case *)(test->param_value);
+
+               sha1sum(test_param->str, out);
+               KUNIT_EXPECT_STREQ_MSG(test, (char *)out, test_param->sha1,
+                                     "sha1sum(%s)", test_param->str);
+       }
+
+       // Instead of KUNIT_CASE, we use KUNIT_CASE_PARAM and pass in the
+       // function declared by KUNIT_ARRAY_PARAM.
+       static struct kunit_case sha1_test_cases[] = {
+               KUNIT_CASE_PARAM(sha1_test, sha1_gen_params),
+               {}
+       };
+
 .. _kunit-on-non-uml:
 
 KUnit on non-UML architectures
index b15f68c..df29d59 100644 (file)
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
 %YAML 1.2
 ---
 $id: http://devicetree.org/schemas/dma/ti/k3-bcdma.yaml#
@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Texas Instruments K3 DMSS BCDMA Device Tree Bindings
 
 maintainers:
-  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
 
 description: |
   The Block Copy DMA (BCDMA) is intended to perform similar functions as the TR
index b13ab60..ea19d12 100644 (file)
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
 %YAML 1.2
 ---
 $id: http://devicetree.org/schemas/dma/ti/k3-pktdma.yaml#
@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Texas Instruments K3 DMSS PKTDMA Device Tree Bindings
 
 maintainers:
-  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
 
 description: |
   The Packet DMA (PKTDMA) is intended to perform similar functions as the packet
index 9a87fd9..6a09bbf 100644 (file)
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2019 Texas Instruments Incorporated
+# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
 %YAML 1.2
 ---
 $id: http://devicetree.org/schemas/dma/ti/k3-udma.yaml#
@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Texas Instruments K3 NAVSS Unified DMA Device Tree Bindings
 
 maintainers:
-  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
 
 description: |
   The UDMA-P is intended to perform similar (but significantly upgraded)
index 6eef348..c2efbb8 100644 (file)
@@ -16,8 +16,8 @@ description:
 properties:
   compatible:
     enum:
-      - bosch,bmc150
-      - bosch,bmi055
+      - bosch,bmc150_accel
+      - bosch,bmi055_accel
       - bosch,bma255
       - bosch,bma250e
       - bosch,bma222
index 1f133f4..0467441 100644 (file)
@@ -74,17 +74,60 @@ allOf:
             Any configuration is ignored when the phy-mode is set to "rmii".
 
         amlogic,rx-delay-ns:
+          deprecated: true
           enum:
             - 0
             - 2
           default: 0
           description:
-            The internal RGMII RX clock delay (provided by this IP block) in
-            nanoseconds. When phy-mode is set to "rgmii" then the RX delay
-            should be explicitly configured. When the phy-mode is set to
-            either "rgmii-id" or "rgmii-rxid" the RX clock delay is already
-            provided by the PHY. Any configuration is ignored when the
-            phy-mode is set to "rmii".
+            The internal RGMII RX clock delay in nanoseconds. Deprecated, use
+            rx-internal-delay-ps instead.
+
+        rx-internal-delay-ps:
+          default: 0
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - amlogic,meson8b-dwmac
+              - amlogic,meson8m2-dwmac
+              - amlogic,meson-gxbb-dwmac
+              - amlogic,meson-axg-dwmac
+    then:
+      properties:
+        rx-internal-delay-ps:
+          enum:
+            - 0
+            - 2000
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - amlogic,meson-g12a-dwmac
+    then:
+      properties:
+        rx-internal-delay-ps:
+          enum:
+            - 0
+            - 200
+            - 400
+            - 600
+            - 800
+            - 1000
+            - 1200
+            - 1400
+            - 1600
+            - 1800
+            - 2000
+            - 2200
+            - 2400
+            - 2600
+            - 2800
+            - 3000
 
 properties:
   compatible:
index 97ca62b..d0935d2 100644 (file)
 * Broadcom Starfighter 2 integrated swich
 
-Required properties:
+See dsa/brcm,bcm7445-switch-v4.0.yaml for the documentation.
 
-- compatible: should be one of
-       "brcm,bcm7445-switch-v4.0"
-       "brcm,bcm7278-switch-v4.0"
-       "brcm,bcm7278-switch-v4.8"
-- reg: addresses and length of the register sets for the device, must be 6
-  pairs of register addresses and lengths
-- interrupts: interrupts for the devices, must be two interrupts
-- #address-cells: must be 1, see dsa/dsa.txt
-- #size-cells: must be 0, see dsa/dsa.txt
-
-Deprecated binding required properties:
+*Deprecated* binding required properties:
 
 - dsa,mii-bus: phandle to the MDIO bus controller, see dsa/dsa.txt
 - dsa,ethernet: phandle to the CPU network interface controller, see dsa/dsa.txt
 - #address-cells: must be 2, see dsa/dsa.txt
 
-Subnodes:
-
-The integrated switch subnode should be specified according to the binding
-described in dsa/dsa.txt.
-
-Optional properties:
-
-- reg-names: litteral names for the device base register addresses, when present
-  must be: "core", "reg", "intrl2_0", "intrl2_1", "fcb", "acb"
-
-- interrupt-names: litternal names for the device interrupt lines, when present
-  must be: "switch_0" and "switch_1"
-
-- brcm,num-gphy: specify the maximum number of integrated gigabit PHYs in the
-  switch
-
-- brcm,num-rgmii-ports: specify the maximum number of RGMII interfaces supported
-  by the switch
-
-- brcm,fcb-pause-override: boolean property, if present indicates that the switch
-  supports Failover Control Block pause override capability
-
-- brcm,acb-packets-inflight: boolean property, if present indicates that the switch
-  Admission Control Block supports reporting the number of packets in-flight in a
-  switch queue
-
-- resets: a single phandle and reset identifier pair. See
-  Documentation/devicetree/bindings/reset/reset.txt for details.
-
-- reset-names: If the "reset" property is specified, this property should have
-  the value "switch" to denote the switch reset line.
-
-- clocks: when provided, the first phandle is to the switch's main clock and
-  is valid for both BCM7445 and BCM7278. The second phandle is only applicable
-  to BCM7445 and is to support dividing the switch core clock.
-
-- clock-names: when provided, the first phandle must be "sw_switch", and the
-  second must be named "sw_switch_mdiv".
-
-Port subnodes:
-
-Optional properties:
-
-- brcm,use-bcm-hdr: boolean property, if present, indicates that the switch
-  port has Broadcom tags enabled (per-packet metadata)
-
-Example:
-
-switch_top@f0b00000 {
-       compatible = "simple-bus";
-       #size-cells = <1>;
-       #address-cells = <1>;
-       ranges = <0 0xf0b00000 0x40804>;
-
-       ethernet_switch@0 {
-               compatible = "brcm,bcm7445-switch-v4.0";
-               #size-cells = <0>;
-               #address-cells = <1>;
-               reg = <0x0 0x40000
-                       0x40000 0x110
-                       0x40340 0x30
-                       0x40380 0x30
-                       0x40400 0x34
-                       0x40600 0x208>;
-               reg-names = "core", "reg", intrl2_0", "intrl2_1",
-                           "fcb, "acb";
-               interrupts = <0 0x18 0
-                               0 0x19 0>;
-               brcm,num-gphy = <1>;
-               brcm,num-rgmii-ports = <2>;
-               brcm,fcb-pause-override;
-               brcm,acb-packets-inflight;
-
-               ports {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
-
-                       port@0 {
-                               label = "gphy";
-                               reg = <0>;
-                       };
-               };
-       };
-};
-
 Example using the old DSA DeviceTree binding:
 
 switch_top@f0b00000 {
@@ -132,7 +37,7 @@ switch_top@f0b00000 {
                switch@0 {
                        reg = <0 0>;
                        #size-cells = <0>;
-                       #address-cells <1>;
+                       #address-cells <1>;
 
                        port@0 {
                                label = "gphy";
index 0d2df30..fe6a949 100644 (file)
@@ -110,6 +110,16 @@ properties:
     description:
       Enable CAN remote wakeup.
 
+  fsl,scu-index:
+    description: |
+      The scu index of CAN instance.
+      For SoCs with SCU support, need setup stop mode via SCU firmware, so this
+      property can help indicate a resource. It supports up to 3 CAN instances
+      now.
+    $ref: /schemas/types.yaml#/definitions/uint8
+    minimum: 0
+    maximum: 2
+
 required:
   - compatible
   - reg
@@ -137,4 +147,5 @@ examples:
         clocks = <&clks 1>, <&clks 2>;
         clock-names = "ipg", "per";
         fsl,stop-mode = <&gpr 0x34 28>;
+        fsl,scu-index = /bits/ 8 <1>;
     };
diff --git a/Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml b/Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml
new file mode 100644 (file)
index 0000000..3f01b65
--- /dev/null
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/arrow,xrs700x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Arrow SpeedChips XRS7000 Series Switch Device Tree Bindings
+
+allOf:
+  - $ref: dsa.yaml#
+
+maintainers:
+  - George McCollister <george.mccollister@gmail.com>
+
+description:
+  The Arrow SpeedChips XRS7000 Series of single chip gigabit Ethernet switches
+  are designed for critical networking applications. They have up to three
+  RGMII ports and one RMII port and are managed via i2c or mdio.
+
+properties:
+  compatible:
+    oneOf:
+      - enum:
+          - arrow,xrs7003e
+          - arrow,xrs7003f
+          - arrow,xrs7004e
+          - arrow,xrs7004f
+
+  reg:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        switch@8 {
+            compatible = "arrow,xrs7004e";
+            reg = <0x8>;
+
+            ethernet-ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                ethernet-port@1 {
+                    reg = <1>;
+                    label = "lan0";
+                    phy-handle = <&swphy0>;
+                    phy-mode = "rgmii-id";
+                };
+                ethernet-port@2 {
+                    reg = <2>;
+                    label = "lan1";
+                    phy-handle = <&swphy1>;
+                    phy-mode = "rgmii-id";
+                };
+                ethernet-port@3 {
+                    reg = <3>;
+                    label = "cpu";
+                    ethernet = <&fec1>;
+                    fixed-link {
+                        speed = <1000>;
+                        full-duplex;
+                    };
+                };
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/net/dsa/brcm,sf2.yaml b/Documentation/devicetree/bindings/net/dsa/brcm,sf2.yaml
new file mode 100644 (file)
index 0000000..d730fe5
--- /dev/null
@@ -0,0 +1,173 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/brcm,sf2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom Starfighter 2 integrated swich
+
+maintainers:
+  - Florian Fainelli <f.fainelli@gmail.com>
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - brcm,bcm4908-switch
+          - brcm,bcm7278-switch-v4.0
+          - brcm,bcm7278-switch-v4.8
+          - brcm,bcm7445-switch-v4.0
+
+  reg:
+    minItems: 6
+    maxItems: 6
+
+  reg-names:
+    items:
+      - const: core
+      - const: reg
+      - const: intrl2_0
+      - const: intrl2_1
+      - const: fcb
+      - const: acb
+
+  interrupts:
+    minItems: 2
+    maxItems: 2
+
+  interrupt-names:
+    items:
+      - const: switch_0
+      - const: switch_1
+
+  resets:
+    maxItems: 1
+
+  reset-names:
+    const: switch
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+    items:
+      - description: switch's main clock
+      - description: dividing of the switch core clock
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      - const: sw_switch
+      - const: sw_switch_mdiv
+
+  brcm,num-gphy:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: maximum number of integrated gigabit PHYs in the switch
+
+  brcm,num-rgmii-ports:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: maximum number of RGMII interfaces supported by the switch
+
+  brcm,fcb-pause-override:
+    description: if present indicates that the switch supports Failover Control
+      Block pause override capability
+    type: boolean
+
+  brcm,acb-packets-inflight:
+    description: if present indicates that the switch Admission Control Block
+      supports reporting the number of packets in-flight in a switch queue
+    type: boolean
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  ports:
+    type: object
+
+    properties:
+      brcm,use-bcm-hdr:
+        description: if present, indicates that the switch port has Broadcom
+          tags enabled (per-packet metadata)
+        type: boolean
+
+required:
+  - reg
+  - interrupts
+  - "#address-cells"
+  - "#size-cells"
+
+allOf:
+  - $ref: "dsa.yaml#"
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - brcm,bcm7278-switch-v4.0
+              - brcm,bcm7278-switch-v4.8
+    then:
+      properties:
+        clocks:
+          minItems: 1
+          maxItems: 1
+        clock-names:
+          minItems: 1
+          maxItems: 1
+      required:
+        - clocks
+        - clock-names
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: brcm,bcm7445-switch-v4.0
+    then:
+      properties:
+        clocks:
+          minItems: 2
+          maxItems: 2
+        clock-names:
+          minItems: 2
+          maxItems: 2
+      required:
+        - clocks
+        - clock-names
+
+additionalProperties: false
+
+examples:
+  - |
+    switch@f0b00000 {
+            compatible = "brcm,bcm7445-switch-v4.0";
+            #address-cells = <1>;
+            #size-cells = <0>;
+            reg = <0xf0b00000 0x40000>,
+                  <0xf0b40000 0x110>,
+                  <0xf0b40340 0x30>,
+                  <0xf0b40380 0x30>,
+                  <0xf0b40400 0x34>,
+                  <0xf0b40600 0x208>;
+            reg-names = "core", "reg", "intrl2_0", "intrl2_1",
+                        "fcb", "acb";
+            interrupts = <0 0x18 0>,
+                         <0 0x19 0>;
+            clocks = <&sw_switch>, <&sw_switch_mdiv>;
+            clock-names = "sw_switch", "sw_switch_mdiv";
+            brcm,num-gphy = <1>;
+            brcm,num-rgmii-ports = <2>;
+            brcm,fcb-pause-override;
+            brcm,acb-packets-inflight;
+
+            ports {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+
+                    port@0 {
+                            label = "gphy";
+                            reg = <0>;
+                    };
+            };
+    };
index 560369e..de04626 100644 (file)
@@ -76,6 +76,12 @@ phy-mode must be set, see also example 2 below!
  * mt7621: phy-mode = "rgmii-txid";
  * mt7623: phy-mode = "rgmii";
 
+Optional properties:
+
+- gpio-controller: Boolean; if defined, MT7530's LED controller will run on
+       GPIO mode.
+- #gpio-cells: Must be 2 if gpio-controller is defined.
+
 See Documentation/devicetree/bindings/net/dsa/dsa.txt for a list of additional
 required, optional properties and how the integrated switch subnodes must
 be specified.
index 64b3357..b3d4013 100644 (file)
@@ -28,6 +28,10 @@ properties:
     $ref: /schemas/types.yaml#/definitions/uint32
     enum: [0, 1, 2]
 
+  qca,disable-smarteee:
+    description: Disable Atheros SmartEEE feature.
+    type: boolean
+
   qca,keep-pll-enabled:
     description: |
       If set, keep the PLL enabled even if there is no link. Useful if you
@@ -36,6 +40,18 @@ properties:
       Only supported on the AR8031.
     type: boolean
 
+  qca,smarteee-tw-us-100m:
+    description: EEE Tw parameter for 100M links.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 255
+
+  qca,smarteee-tw-us-1g:
+    description: EEE Tw parameter for gigabit links.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 1
+    maximum: 255
+
   vddio-supply:
     description: |
       RGMII I/O voltage regulator (see regulator/regulator.yaml).
index 8a2d126..8f86084 100644 (file)
@@ -113,13 +113,6 @@ properties:
       performing early IPA initialization, including loading and
       validating firwmare used by the GSI.
 
-  modem-remoteproc:
-    $ref: /schemas/types.yaml#/definitions/phandle
-    description:
-      This defines the phandle to the remoteproc node representing
-      the modem subsystem.  This is requied so the IPA driver can
-      receive and act on notifications of modem up/down events.
-
   memory-region:
     maxItems: 1
     description:
@@ -135,7 +128,6 @@ required:
   - interrupts
   - interconnects
   - qcom,smem-states
-  - modem-remoteproc
 
 oneOf:
   - required:
@@ -147,7 +139,7 @@ additionalProperties: false
 
 examples:
   - |
-        #include <dt-bindings/interrupt-controller/irq.h>
+        #include <dt-bindings/interrupt-controller/arm-gic.h>
         #include <dt-bindings/clock/qcom,rpmh.h>
         #include <dt-bindings/interconnect/qcom,sdm845.h>
 
@@ -168,7 +160,6 @@ examples:
                 compatible = "qcom,sdm845-ipa";
 
                 modem-init;
-                modem-remoteproc = <&mss_pil>;
 
                 iommus = <&apps_smmu 0x720 0x3>;
                 reg = <0x1e40000 0x7000>,
@@ -178,8 +169,8 @@ examples:
                             "ipa-shared",
                             "gsi";
 
-                interrupts-extended = <&intc 0 311 IRQ_TYPE_EDGE_RISING>,
-                                      <&intc 0 432 IRQ_TYPE_LEVEL_HIGH>,
+                interrupts-extended = <&intc GIC_SPI 311 IRQ_TYPE_EDGE_RISING>,
+                                      <&intc GIC_SPI 432 IRQ_TYPE_LEVEL_HIGH>,
                                       <&ipa_smp2p_in 0 IRQ_TYPE_EDGE_RISING>,
                                       <&ipa_smp2p_in 1 IRQ_TYPE_EDGE_RISING>;
                 interrupt-names = "ipa",
index 244befb..91ba96d 100644 (file)
@@ -40,6 +40,7 @@ properties:
               - renesas,etheravb-r8a77980     # R-Car V3H
               - renesas,etheravb-r8a77990     # R-Car E3
               - renesas,etheravb-r8a77995     # R-Car D3
+              - renesas,etheravb-r8a779a0     # R-Car V3U
           - const: renesas,etheravb-rcar-gen3 # R-Car Gen3 and RZ/G2
 
   reg: true
@@ -163,12 +164,14 @@ allOf:
             enum:
               - renesas,etheravb-r8a774a1
               - renesas,etheravb-r8a774b1
+              - renesas,etheravb-r8a774e1
               - renesas,etheravb-r8a7795
               - renesas,etheravb-r8a7796
               - renesas,etheravb-r8a77961
               - renesas,etheravb-r8a77965
               - renesas,etheravb-r8a77970
               - renesas,etheravb-r8a77980
+              - renesas,etheravb-r8a779a0
     then:
       required:
         - tx-internal-delay-ps
index b2f6083..dfbf5fe 100644 (file)
@@ -161,7 +161,8 @@ properties:
             * snps,route-dcbcp, DCB Control Packets
             * snps,route-up, Untagged Packets
             * snps,route-multi-broad, Multicast & Broadcast Packets
-          * snps,priority, RX queue priority (Range 0x0 to 0xF)
+          * snps,priority, bitmask of the tagged frames priorities assigned to
+            the queue
 
   snps,mtl-tx-config:
     $ref: /schemas/types.yaml#/definitions/phandle
@@ -188,7 +189,10 @@ properties:
             * snps,idle_slope, unlock on WoL
             * snps,high_credit, max write outstanding req. limit
             * snps,low_credit, max read outstanding req. limit
-          * snps,priority, TX queue priority (Range 0x0 to 0xF)
+          * snps,priority, bitmask of the priorities assigned to the queue.
+            When a PFC frame is received with priorities matching the bitmask,
+            the queue is blocked from transmitting for the pause time specified
+            in the PFC frame.
 
   snps,reset-gpio:
     deprecated: true
index c47b58f..3fae9a5 100644 (file)
@@ -4,7 +4,7 @@
 $id: http://devicetree.org/schemas/net/ti,k3-am654-cpsw-nuss.yaml#
 $schema: http://devicetree.org/meta-schemas/core.yaml#
 
-title: The TI AM654x/J721E SoC Gigabit Ethernet MAC (Media Access Controller) Device Tree Bindings
+title: The TI AM654x/J721E/AM642x SoC Gigabit Ethernet MAC (Media Access Controller) Device Tree Bindings
 
 maintainers:
   - Grygorii Strashko <grygorii.strashko@ti.com>
@@ -13,19 +13,16 @@ maintainers:
 description:
   The TI AM654x/J721E SoC Gigabit Ethernet MAC (CPSW2G NUSS) has two ports
   (one external) and provides Ethernet packet communication for the device.
-  CPSW2G NUSS features - the Reduced Gigabit Media Independent Interface (RGMII),
-  Reduced Media Independent Interface (RMII), the Management Data
-  Input/Output (MDIO) interface for physical layer device (PHY) management,
-  new version of Common Platform Time Sync (CPTS), updated Address Lookup
-  Engine (ALE).
-  One external Ethernet port (port 1) with selectable RGMII/RMII interfaces and
-  an internal Communications Port Programming Interface (CPPI5) (Host port 0).
+  The TI AM642x SoC Gigabit Ethernet MAC (CPSW3G NUSS) has three ports
+  (two external) and provides Ethernet packet communication and switching.
+
+  The internal Communications Port Programming Interface (CPPI5) (Host port 0).
   Host Port 0 CPPI Packet Streaming Interface interface supports 8 TX channels
-  and one RX channels and operating by TI AM654x/J721E NAVSS Unified DMA
-  Peripheral Root Complex (UDMA-P) controller.
-  The CPSW2G NUSS is integrated into device MCU domain named MCU_CPSW0.
+  and one RX channels and operating by NAVSS Unified DMA  Peripheral Root
+  Complex (UDMA-P) controller.
 
-  Additional features
+  CPSWxG features
+  updated Address Lookup Engine (ALE).
   priority level Quality Of Service (QOS) support (802.1p)
   Support for Audio/Video Bridging (P802.1Qav/D6.0)
   Support for IEEE 1588 Clock Synchronization (2008 Annex D, Annex E and Annex F)
@@ -38,10 +35,18 @@ description:
   VLAN support, 802.1Q compliant, Auto add port VLAN for untagged frames on
   ingress, Auto VLAN removal on egress and auto pad to minimum frame size.
   RX/TX csum offload
+  Management Data Input/Output (MDIO) interface for PHYs management
+  RMII/RGMII Interfaces support
+  new version of Common Platform Time Sync (CPTS)
+
+  The CPSWxG NUSS is integrated into
+    device MCU domain named MCU_CPSW0 on AM654x/J721E SoC.
+    device MAIN domain named CPSW0 on AM642x SoC.
 
   Specifications can be found at
-    http://www.ti.com/lit/ug/spruid7e/spruid7e.pdf
-    http://www.ti.com/lit/ug/spruil1a/spruil1a.pdf
+    https://www.ti.com/lit/pdf/spruid7
+    https://www.ti.com/lit/zip/spruil1
+    https://www.ti.com/lit/pdf/spruim2
 
 properties:
   "#address-cells": true
@@ -51,11 +56,12 @@ properties:
     oneOf:
       - const: ti,am654-cpsw-nuss
       - const: ti,j721e-cpsw-nuss
+      - const: ti,am642-cpsw-nuss
 
   reg:
     maxItems: 1
     description:
-      The physical base address and size of full the CPSW2G NUSS IO range
+      The physical base address and size of full the CPSWxG NUSS IO range
 
   reg-names:
     items:
@@ -66,12 +72,16 @@ properties:
   dma-coherent: true
 
   clocks:
-    description: CPSW2G NUSS functional clock
+    description: CPSWxG NUSS functional clock
 
   clock-names:
     items:
       - const: fck
 
+  assigned-clock-parents: true
+
+  assigned-clocks: true
+
   power-domains:
     maxItems: 1
 
@@ -99,16 +109,16 @@ properties:
         const: 0
 
     patternProperties:
-      port@1:
+      port@[1-2]:
         type: object
-        description: CPSW2G NUSS external ports
+        description: CPSWxG NUSS external ports
 
         $ref: ethernet-controller.yaml#
 
         properties:
           reg:
-            items:
-              - const: 1
+            minimum: 1
+            maximum: 2
             description: CPSW port number
 
           phys:
index 9b71179..ce43a1c 100644 (file)
@@ -73,6 +73,13 @@ properties:
     items:
       - const: cpts
 
+  assigned-clock-parents: true
+
+  assigned-clocks: true
+
+  power-domains:
+    maxItems: 1
+
   ti,cpts-ext-ts-inputs:
     $ref: /schemas/types.yaml#/definitions/uint32
     maximum: 8
index a6c259c..956156f 100644 (file)
@@ -19,7 +19,9 @@ description: |
 properties:
   compatible:
     enum:
-      - nxp,pf8x00
+      - nxp,pf8100
+      - nxp,pf8121a
+      - nxp,pf8200
 
   reg:
     maxItems: 1
@@ -118,7 +120,7 @@ examples:
         #size-cells = <0>;
 
         pmic@8 {
-            compatible = "nxp,pf8x00";
+            compatible = "nxp,pf8100";
             reg = <0x08>;
 
             regulators {
index b8f0b78..7d462b8 100644 (file)
@@ -44,6 +44,7 @@ First Level Nodes - PMIC
        Definition: Must be one of below:
                    "qcom,pm8005-rpmh-regulators"
                    "qcom,pm8009-rpmh-regulators"
+                   "qcom,pm8009-1-rpmh-regulators"
                    "qcom,pm8150-rpmh-regulators"
                    "qcom,pm8150l-rpmh-regulators"
                    "qcom,pm8350-rpmh-regulators"
index bf8c8ba..5465082 100644 (file)
@@ -7,8 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Mediatek MT8192 with MT6359, RT1015 and RT5682 ASoC sound card driver
 
 maintainers:
-   - Jiaxin Yu <jiaxin.yu@mediatek.com>
-   - Shane Chien <shane.chien@mediatek.com>
+  - Jiaxin Yu <jiaxin.yu@mediatek.com>
+  - Shane Chien <shane.chien@mediatek.com>
 
 description:
   This binding describes the MT8192 sound card.
index 805da4d..ec06789 100644 (file)
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
 %YAML 1.2
 ---
 $id: http://devicetree.org/schemas/sound/ti,j721e-cpb-audio.yaml#
@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Texas Instruments J721e Common Processor Board Audio Support
 
 maintainers:
-  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
 
 description: |
   The audio support on the board is using pcm3168a codec connected to McASP10
index bb780f6..ee9f960 100644 (file)
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020 Texas Instruments Incorporated
+# Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
 %YAML 1.2
 ---
 $id: http://devicetree.org/schemas/sound/ti,j721e-cpb-ivi-audio.yaml#
@@ -7,7 +9,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
 title: Texas Instruments J721e Common Processor Board Audio Support
 
 maintainers:
-  - Peter Ujfalusi <peter.ujfalusi@ti.com>
+  - Peter Ujfalusi <peter.ujfalusi@gmail.com>
 
 description: |
   The Infotainment board plugs into the Common Processor Board, the support of the
index 388245b..148b3fb 100644 (file)
@@ -11,8 +11,12 @@ maintainers:
 
 properties:
   compatible:
-    items:
+    oneOf:
       - const: ti,j721e-usb
+      - const: ti,am64-usb
+      - items:
+          - const: ti,j721e-usb
+          - const: ti,am64-usb
 
   reg:
     description: module registers
index 2fb2ff2..36ac216 100644 (file)
@@ -48,12 +48,12 @@ or ``virtualenv``, depending on how your distribution packaged Python 3.
       those versions, you should run ``pip install 'docutils==0.12'``.
 
    #) It is recommended to use the RTD theme for html output. Depending
-      on the Sphinx version, it should be installed  in separate,
+      on the Sphinx version, it should be installed separately,
       with ``pip install sphinx_rtd_theme``.
 
-   #) Some ReST pages contain math expressions. Due to the way Sphinx work,
+   #) Some ReST pages contain math expressions. Due to the way Sphinx works,
       those expressions are written using LaTeX notation. It needs texlive
-      installed with amdfonts and amsmath in order to evaluate them.
+      installed with amsfonts and amsmath in order to evaluate them.
 
 In summary, if you want to install Sphinx version 1.7.9, you should do::
 
@@ -128,7 +128,7 @@ Sphinx Build
 ============
 
 The usual way to generate the documentation is to run ``make htmldocs`` or
-``make pdfdocs``. There are also other formats available, see the documentation
+``make pdfdocs``. There are also other formats available: see the documentation
 section of ``make help``. The generated documentation is placed in
 format-specific subdirectories under ``Documentation/output``.
 
@@ -303,17 +303,17 @@ and *targets* (e.g. a ref to ``:ref:`last row <last row>``` / :ref:`last row
         - head col 3
         - head col 4
 
-      * - column 1
+      * - row 1
         - field 1.1
         - field 1.2 with autospan
 
-      * - column 2
+      * - row 2
         - field 2.1
         - :rspan:`1` :cspan:`1` field 2.2 - 3.3
 
       * .. _`last row`:
 
-        - column 3
+        - row 3
 
 Rendered as:
 
@@ -325,17 +325,17 @@ Rendered as:
         - head col 3
         - head col 4
 
-      * - column 1
+      * - row 1
         - field 1.1
         - field 1.2 with autospan
 
-      * - column 2
+      * - row 2
         - field 2.1
         - :rspan:`1` :cspan:`1` field 2.2 - 3.3
 
       * .. _`last row`:
 
-        - column 3
+        - row 3
 
 Cross-referencing
 -----------------
@@ -361,7 +361,7 @@ Figures & Images
 
 If you want to add an image, you should use the ``kernel-figure`` and
 ``kernel-image`` directives. E.g. to insert a figure with a scalable
-image format use SVG (:ref:`svg_image_example`)::
+image format, use SVG (:ref:`svg_image_example`)::
 
     .. kernel-figure::  svg_image.svg
        :alt:    simple SVG image
@@ -375,7 +375,7 @@ image format use SVG (:ref:`svg_image_example`)::
 
    SVG image example
 
-The kernel figure (and image) directive support **DOT** formatted files, see
+The kernel figure (and image) directive supports **DOT** formatted files, see
 
 * DOT: http://graphviz.org/pdf/dotguide.pdf
 * Graphviz: http://www.graphviz.org/content/dot-language
@@ -394,7 +394,7 @@ A simple example (:ref:`hello_dot_file`)::
 
    DOT's hello world example
 
-Embed *render* markups (or languages) like Graphviz's **DOT** is provided by the
+Embedded *render* markups (or languages) like Graphviz's **DOT** are provided by the
 ``kernel-render`` directives.::
 
   .. kernel-render:: DOT
@@ -406,7 +406,7 @@ Embed *render* markups (or languages) like Graphviz's **DOT** is provided by the
      }
 
 How this will be rendered depends on the installed tools. If Graphviz is
-installed, you will see an vector image. If not the raw markup is inserted as
+installed, you will see a vector image. If not, the raw markup is inserted as
 *literal-block* (:ref:`hello_dot_render`).
 
 .. _hello_dot_render:
@@ -421,8 +421,8 @@ installed, you will see an vector image. If not the raw markup is inserted as
 
 The *render* directive has all the options known from the *figure* directive,
 plus option ``caption``.  If ``caption`` has a value, a *figure* node is
-inserted. If not, a *image* node is inserted. A ``caption`` is also needed, if
-you want to refer it (:ref:`hello_svg_render`).
+inserted. If not, an *image* node is inserted. A ``caption`` is also needed, if
+you want to refer to it (:ref:`hello_svg_render`).
 
 Embedded **SVG**::
 
index 2312506..fff96c7 100644 (file)
@@ -1,5 +1,7 @@
 .. SPDX-License-Identifier: GPL-2.0-only
 
+.. _auxiliary_bus:
+
 =============
 Auxiliary Bus
 =============
index e588bcc..c042176 100644 (file)
@@ -50,8 +50,8 @@ The following files belong to it:
   0x00000010        Memory Uncorrectable non-fatal
   0x00000020        Memory Uncorrectable fatal
   0x00000040        PCI Express Correctable
-  0x00000080        PCI Express Uncorrectable fatal
-  0x00000100        PCI Express Uncorrectable non-fatal
+  0x00000080        PCI Express Uncorrectable non-fatal
+  0x00000100        PCI Express Uncorrectable fatal
   0x00000200        Platform Correctable
   0x00000400        Platform Uncorrectable non-fatal
   0x00000800        Platform Uncorrectable fatal
index 922b3c8..749f518 100644 (file)
@@ -1,7 +1,7 @@
 .. SPDX-License-Identifier: GPL-2.0-or-later
 
 Kernel driver sbtsi_temp
-==================
+========================
 
 Supported hardware:
 
index d36768c..9f6a118 100644 (file)
@@ -598,7 +598,7 @@ more details, with real examples.
        explicitly added to $(targets).
 
        Assignments to $(targets) are without $(obj)/ prefix. if_changed may be
-       used in conjunction with custom rules as defined in "3.9 Custom Rules".
+       used in conjunction with custom rules as defined in "3.11 Custom Rules".
 
        Note: It is a typical mistake to forget the FORCE prerequisite.
        Another common pitfall is that whitespace is sometimes significant; for
index 6ed806e..c344892 100644 (file)
@@ -118,11 +118,11 @@ spinlock, but you may block holding a mutex. If you can't lock a mutex,
 your task will suspend itself, and be woken up when the mutex is
 released. This means the CPU can do something else while you are
 waiting. There are many cases when you simply can't sleep (see
-`What Functions Are Safe To Call From Interrupts? <#sleeping-things>`__),
+`What Functions Are Safe To Call From Interrupts?`_),
 and so have to use a spinlock instead.
 
 Neither type of lock is recursive: see
-`Deadlock: Simple and Advanced <#deadlock>`__.
+`Deadlock: Simple and Advanced`_.
 
 Locks and Uniprocessor Kernels
 ------------------------------
@@ -179,7 +179,7 @@ perfect world).
 
 Note that you can also use spin_lock_irq() or
 spin_lock_irqsave() here, which stop hardware interrupts
-as well: see `Hard IRQ Context <#hard-irq-context>`__.
+as well: see `Hard IRQ Context`_.
 
 This works perfectly for UP as well: the spin lock vanishes, and this
 macro simply becomes local_bh_disable()
@@ -230,7 +230,7 @@ The Same Softirq
 ~~~~~~~~~~~~~~~~
 
 The same softirq can run on the other CPUs: you can use a per-CPU array
-(see `Per-CPU Data <#per-cpu-data>`__) for better performance. If you're
+(see `Per-CPU Data`_) for better performance. If you're
 going so far as to use a softirq, you probably care about scalable
 performance enough to justify the extra complexity.
 
index adc3146..5f690f0 100644 (file)
@@ -951,6 +951,19 @@ xmit_hash_policy
                packets will be distributed according to the encapsulated
                flows.
 
+       vlan+srcmac
+
+               This policy uses a very rudimentary vlan ID and source mac
+               hash to load-balance traffic per-vlan, with failover
+               should one leg fail. The intended use case is for a bond
+               shared by multiple virtual machines, all configured to
+               use their own vlan, to give lacp-like functionality
+               without requiring lacp-capable switching hardware.
+
+               The formula for the hash is simply
+
+               hash = (vlan ID) XOR (source MAC vendor) XOR (source MAC dev)
+
        The default value is layer2.  This option was added in bonding
        version 2.6.3.  In earlier versions of bonding, this parameter
        does not exist, and the layer2 policy is the only policy.  The
index d3fcf53..dd5cd69 100644 (file)
@@ -164,46 +164,126 @@ Devlink health reporters
 
 NPA Reporters
 -------------
-The NPA reporters are responsible for reporting and recovering the following group of errors
+The NPA reporters are responsible for reporting and recovering the following group of errors:
+
 1. GENERAL events
+
    - Error due to operation of unmapped PF.
    - Error due to disabled alloc/free for other HW blocks (NIX, SSO, TIM, DPI and AURA).
+
 2. ERROR events
+
    - Fault due to NPA_AQ_INST_S read or NPA_AQ_RES_S write.
    - AQ Doorbell Error.
+
 3. RAS events
+
    - RAS Error Reporting for NPA_AQ_INST_S/NPA_AQ_RES_S.
+
 4. RVU events
+
    - Error due to unmapped slot.
 
-Sample Output
+Sample Output::
+
+       ~# devlink health
+       pci/0002:01:00.0:
+         reporter hw_npa_intr
+             state healthy error 2872 recover 2872 last_dump_date 2020-12-10 last_dump_time 09:39:09 grace_period 0 auto_recover true auto_dump true
+         reporter hw_npa_gen
+             state healthy error 2872 recover 2872 last_dump_date 2020-12-11 last_dump_time 04:43:04 grace_period 0 auto_recover true auto_dump true
+         reporter hw_npa_err
+             state healthy error 2871 recover 2871 last_dump_date 2020-12-10 last_dump_time 09:39:17 grace_period 0 auto_recover true auto_dump true
+          reporter hw_npa_ras
+             state healthy error 0 recover 0 last_dump_date 2020-12-10 last_dump_time 09:32:40 grace_period 0 auto_recover true auto_dump true
+
+Each reporter dumps the
+
+ - Error Type
+ - Error Register value
+ - Reason in words
+
+For example::
+
+       ~# devlink health dump show  pci/0002:01:00.0 reporter hw_npa_gen
+        NPA_AF_GENERAL:
+                NPA General Interrupt Reg : 1
+                NIX0: free disabled RX
+       ~# devlink health dump show  pci/0002:01:00.0 reporter hw_npa_intr
+        NPA_AF_RVU:
+                NPA RVU Interrupt Reg : 1
+                Unmap Slot Error
+       ~# devlink health dump show  pci/0002:01:00.0 reporter hw_npa_err
+        NPA_AF_ERR:
+               NPA Error Interrupt Reg : 4096
+               AQ Doorbell Error
+
+
+NIX Reporters
 -------------
-~# devlink health
-pci/0002:01:00.0:
-  reporter hw_npa_intr
-      state healthy error 2872 recover 2872 last_dump_date 2020-12-10 last_dump_time 09:39:09 grace_period 0 auto_recover true auto_dump true
-  reporter hw_npa_gen
-      state healthy error 2872 recover 2872 last_dump_date 2020-12-11 last_dump_time 04:43:04 grace_period 0 auto_recover true auto_dump true
-  reporter hw_npa_err
-      state healthy error 2871 recover 2871 last_dump_date 2020-12-10 last_dump_time 09:39:17 grace_period 0 auto_recover true auto_dump true
-   reporter hw_npa_ras
-      state healthy error 0 recover 0 last_dump_date 2020-12-10 last_dump_time 09:32:40 grace_period 0 auto_recover true auto_dump true
+The NIX reporters are responsible for reporting and recovering the following group of errors:
+
+1. GENERAL events
+
+   - Receive mirror/multicast packet drop due to insufficient buffer.
+   - SMQ Flush operation.
+
+2. ERROR events
+
+   - Memory Fault due to WQE read/write from multicast/mirror buffer.
+   - Receive multicast/mirror replication list error.
+   - Receive packet on an unmapped PF.
+   - Fault due to NIX_AQ_INST_S read or NIX_AQ_RES_S write.
+   - AQ Doorbell Error.
+
+3. RAS events
+
+   - RAS Error Reporting for NIX Receive Multicast/Mirror Entry Structure.
+   - RAS Error Reporting for WQE/Packet Data read from Multicast/Mirror Buffer..
+   - RAS Error Reporting for NIX_AQ_INST_S/NIX_AQ_RES_S.
+
+4. RVU events
+
+   - Error due to unmapped slot.
+
+Sample Output::
+
+       ~# ./devlink health
+       pci/0002:01:00.0:
+         reporter hw_npa_intr
+           state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
+         reporter hw_npa_gen
+           state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
+         reporter hw_npa_err
+           state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
+         reporter hw_npa_ras
+           state healthy error 0 recover 0 grace_period 0 auto_recover true auto_dump true
+         reporter hw_nix_intr
+           state healthy error 1121 recover 1121 last_dump_date 2021-01-19 last_dump_time 05:42:26 grace_period 0 auto_recover true auto_dump true
+         reporter hw_nix_gen
+           state healthy error 949 recover 949 last_dump_date 2021-01-19 last_dump_time 05:42:43 grace_period 0 auto_recover true auto_dump true
+         reporter hw_nix_err
+           state healthy error 1147 recover 1147 last_dump_date 2021-01-19 last_dump_time 05:42:59 grace_period 0 auto_recover true auto_dump true
+         reporter hw_nix_ras
+           state healthy error 409 recover 409 last_dump_date 2021-01-19 last_dump_time 05:43:16 grace_period 0 auto_recover true auto_dump true
 
 Each reporter dumps the
+
  - Error Type
  - Error Register value
  - Reason in words
 
-For eg:
-~# devlink health dump show  pci/0002:01:00.0 reporter hw_npa_gen
- NPA_AF_GENERAL:
-         NPA General Interrupt Reg : 1
-         NIX0: free disabled RX
-~# devlink health dump show  pci/0002:01:00.0 reporter hw_npa_intr
- NPA_AF_RVU:
-         NPA RVU Interrupt Reg : 1
-         Unmap Slot Error
-~# devlink health dump show  pci/0002:01:00.0 reporter hw_npa_err
- NPA_AF_ERR:
-        NPA Error Interrupt Reg : 4096
-        AQ Doorbell Error
+For example::
+
+       ~# devlink health dump show pci/0002:01:00.0 reporter hw_nix_intr
+        NIX_AF_RVU:
+               NIX RVU Interrupt Reg : 1
+               Unmap Slot Error
+       ~# devlink health dump show pci/0002:01:00.0 reporter hw_nix_gen
+        NIX_AF_GENERAL:
+               NIX General Interrupt Reg : 1
+               Rx multicast pkt drop
+       ~# devlink health dump show pci/0002:01:00.0 reporter hw_nix_err
+        NIX_AF_ERR:
+               NIX Error Interrupt Reg : 64
+               Rx on unmapped PF_FUNC
index e9b6503..a1b32fc 100644 (file)
@@ -12,6 +12,8 @@ Contents
 - `Enabling the driver and kconfig options`_
 - `Devlink info`_
 - `Devlink parameters`_
+- `mlx5 subfunction`_
+- `mlx5 port function`_
 - `Devlink health reporters`_
 - `mlx5 tracepoints`_
 
@@ -97,6 +99,11 @@ Enabling the driver and kconfig options
 
 |   Provides low-level InfiniBand/RDMA and `RoCE <https://community.mellanox.com/s/article/recommended-network-configuration-examples-for-roce-deployment>`_ support.
 
+**CONFIG_MLX5_SF=(y/n)**
+
+|   Build support for subfunction.
+|   Subfunctons are more light weight than PCI SRIOV VFs. Choosing this option
+|   will enable support for creating subfunction devices.
 
 **External options** ( Choose if the corresponding mlx5 feature is required )
 
@@ -176,6 +183,214 @@ User command examples:
       values:
          cmode driverinit value true
 
+mlx5 subfunction
+================
+mlx5 supports subfunction management using devlink port (see :ref:`Documentation/networking/devlink/devlink-port.rst <devlink_port>`) interface.
+
+A Subfunction has its own function capabilities and its own resources. This
+means a subfunction has its own dedicated queues (txq, rxq, cq, eq). These
+queues are neither shared nor stolen from the parent PCI function.
+
+When a subfunction is RDMA capable, it has its own QP1, GID table and rdma
+resources neither shared nor stolen from the parent PCI function.
+
+A subfunction has a dedicated window in PCI BAR space that is not shared
+with ther other subfunctions or the parent PCI function. This ensures that all
+devices (netdev, rdma, vdpa etc.) of the subfunction accesses only assigned
+PCI BAR space.
+
+A Subfunction supports eswitch representation through which it supports tc
+offloads. The user configures eswitch to send/receive packets from/to
+the subfunction port.
+
+Subfunctions share PCI level resources such as PCI MSI-X IRQs with
+other subfunctions and/or with its parent PCI function.
+
+Example mlx5 software, system and device view::
+
+       _______
+      | admin |
+      | user  |----------
+      |_______|         |
+          |             |
+      ____|____       __|______            _________________
+     |         |     |         |          |                 |
+     | devlink |     | tc tool |          |    user         |
+     | tool    |     |_________|          | applications    |
+     |_________|         |                |_________________|
+           |             |                   |          |
+           |             |                   |          |         Userspace
+ +---------|-------------|-------------------|----------|--------------------+
+           |             |           +----------+   +----------+   Kernel
+           |             |           |  netdev  |   | rdma dev |
+           |             |           +----------+   +----------+
+   (devlink port add/del |              ^               ^
+    port function set)   |              |               |
+           |             |              +---------------|
+      _____|___          |              |        _______|_______
+     |         |         |              |       | mlx5 class    |
+     | devlink |   +------------+       |       |   drivers     |
+     | kernel  |   | rep netdev |       |       |(mlx5_core,ib) |
+     |_________|   +------------+       |       |_______________|
+           |             |              |               ^
+   (devlink ops)         |              |          (probe/remove)
+  _________|________     |              |           ____|________
+ | subfunction      |    |     +---------------+   | subfunction |
+ | management driver|-----     | subfunction   |---|  driver     |
+ | (mlx5_core)      |          | auxiliary dev |   | (mlx5_core) |
+ |__________________|          +---------------+   |_____________|
+           |                                            ^
+  (sf add/del, vhca events)                             |
+           |                                      (device add/del)
+      _____|____                                    ____|________
+     |          |                                  | subfunction |
+     |  PCI NIC |---- activate/deactive events---->| host driver |
+     |__________|                                  | (mlx5_core) |
+                                                   |_____________|
+
+Subfunction is created using devlink port interface.
+
+- Change device to switchdev mode::
+
+    $ devlink dev eswitch set pci/0000:06:00.0 mode switchdev
+
+- Add a devlink port of subfunction flaovur::
+
+    $ devlink port add pci/0000:06:00.0 flavour pcisf pfnum 0 sfnum 88
+    pci/0000:06:00.0/32768: type eth netdev eth6 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
+      function:
+        hw_addr 00:00:00:00:00:00 state inactive opstate detached
+
+- Show a devlink port of the subfunction::
+
+    $ devlink port show pci/0000:06:00.0/32768
+    pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88
+      function:
+        hw_addr 00:00:00:00:00:00 state inactive opstate detached
+
+- Delete a devlink port of subfunction after use::
+
+    $ devlink port del pci/0000:06:00.0/32768
+
+mlx5 function attributes
+========================
+The mlx5 driver provides a mechanism to setup PCI VF/SF function attributes in
+a unified way for SmartNIC and non-SmartNIC.
+
+This is supported only when the eswitch mode is set to switchdev. Port function
+configuration of the PCI VF/SF is supported through devlink eswitch port.
+
+Port function attributes should be set before PCI VF/SF is enumerated by the
+driver.
+
+MAC address setup
+-----------------
+mlx5 driver provides mechanism to setup the MAC address of the PCI VF/SF.
+
+The configured MAC address of the PCI VF/SF will be used by netdevice and rdma
+device created for the PCI VF/SF.
+
+- Get the MAC address of the VF identified by its unique devlink port index::
+
+    $ devlink port show pci/0000:06:00.0/2
+    pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
+      function:
+        hw_addr 00:00:00:00:00:00
+
+- Set the MAC address of the VF identified by its unique devlink port index::
+
+    $ devlink port function set pci/0000:06:00.0/2 hw_addr 00:11:22:33:44:55
+
+    $ devlink port show pci/0000:06:00.0/2
+    pci/0000:06:00.0/2: type eth netdev enp6s0pf0vf1 flavour pcivf pfnum 0 vfnum 1
+      function:
+        hw_addr 00:11:22:33:44:55
+
+- Get the MAC address of the SF identified by its unique devlink port index::
+
+    $ devlink port show pci/0000:06:00.0/32768
+    pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcisf pfnum 0 sfnum 88
+      function:
+        hw_addr 00:00:00:00:00:00
+
+- Set the MAC address of the VF identified by its unique devlink port index::
+
+    $ devlink port function set pci/0000:06:00.0/32768 hw_addr 00:00:00:00:88:88
+
+    $ devlink port show pci/0000:06:00.0/32768
+    pci/0000:06:00.0/32768: type eth netdev enp6s0pf0sf88 flavour pcivf pfnum 0 sfnum 88
+      function:
+        hw_addr 00:00:00:00:88:88
+
+SF state setup
+--------------
+To use the SF, the user must active the SF using the SF function state
+attribute.
+
+- Get the state of the SF identified by its unique devlink port index::
+
+   $ devlink port show ens2f0npf0sf88
+   pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
+     function:
+       hw_addr 00:00:00:00:88:88 state inactive opstate detached
+
+- Activate the function and verify its state is active::
+
+   $ devlink port function set ens2f0npf0sf88 state active
+
+   $ devlink port show ens2f0npf0sf88
+   pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
+     function:
+       hw_addr 00:00:00:00:88:88 state active opstate detached
+
+Upon function activation, the PF driver instance gets the event from the device
+that a particular SF was activated. It's the cue to put the device on bus, probe
+it and instantiate the devlink instance and class specific auxiliary devices
+for it.
+
+- Show the auxiliary device and port of the subfunction::
+
+    $ devlink dev show
+    devlink dev show auxiliary/mlx5_core.sf.4
+
+    $ devlink port show auxiliary/mlx5_core.sf.4/1
+    auxiliary/mlx5_core.sf.4/1: type eth netdev p0sf88 flavour virtual port 0 splittable false
+
+    $ rdma link show mlx5_0/1
+    link mlx5_0/1 state ACTIVE physical_state LINK_UP netdev p0sf88
+
+    $ rdma dev show
+    8: rocep6s0f1: node_type ca fw 16.29.0550 node_guid 248a:0703:00b3:d113 sys_image_guid 248a:0703:00b3:d112
+    13: mlx5_0: node_type ca fw 16.29.0550 node_guid 0000:00ff:fe00:8888 sys_image_guid 248a:0703:00b3:d112
+
+- Subfunction auxiliary device and class device hierarchy::
+
+                 mlx5_core.sf.4
+          (subfunction auxiliary device)
+                       /\
+                      /  \
+                     /    \
+                    /      \
+                   /        \
+      mlx5_core.eth.4     mlx5_core.rdma.4
+     (sf eth aux dev)     (sf rdma aux dev)
+         |                      |
+         |                      |
+      p0sf88                  mlx5_0
+     (sf netdev)          (sf rdma device)
+
+Additionally, the SF port also gets the event when the driver attaches to the
+auxiliary device of the subfunction. This results in changing the operational
+state of the function. This provides visiblity to the user to decide when is it
+safe to delete the SF port for graceful termination of the subfunction.
+
+- Show the SF port operational state::
+
+    $ devlink port show ens2f0npf0sf88
+    pci/0000:06:00.0/32768: type eth netdev ens2f0npf0sf88 flavour pcisf controller 0 pfnum 0 sfnum 88 external false splittable false
+      function:
+        hw_addr 00:00:00:00:88:88 state active opstate attached
+
 Devlink health reporters
 ========================
 
diff --git a/Documentation/networking/devlink/devlink-port.rst b/Documentation/networking/devlink/devlink-port.rst
new file mode 100644 (file)
index 0000000..e99b415
--- /dev/null
@@ -0,0 +1,199 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _devlink_port:
+
+============
+Devlink Port
+============
+
+``devlink-port`` is a port that exists on the device. It has a logically
+separate ingress/egress point of the device. A devlink port can be any one
+of many flavours. A devlink port flavour along with port attributes
+describe what a port represents.
+
+A device driver that intends to publish a devlink port sets the
+devlink port attributes and registers the devlink port.
+
+Devlink port flavours are described below.
+
+.. list-table:: List of devlink port flavours
+   :widths: 33 90
+
+   * - Flavour
+     - Description
+   * - ``DEVLINK_PORT_FLAVOUR_PHYSICAL``
+     - Any kind of physical port. This can be an eswitch physical port or any
+       other physical port on the device.
+   * - ``DEVLINK_PORT_FLAVOUR_DSA``
+     - This indicates a DSA interconnect port.
+   * - ``DEVLINK_PORT_FLAVOUR_CPU``
+     - This indicates a CPU port applicable only to DSA.
+   * - ``DEVLINK_PORT_FLAVOUR_PCI_PF``
+     - This indicates an eswitch port representing a port of PCI
+       physical function (PF).
+   * - ``DEVLINK_PORT_FLAVOUR_PCI_VF``
+     - This indicates an eswitch port representing a port of PCI
+       virtual function (VF).
+   * - ``DEVLINK_PORT_FLAVOUR_PCI_SF``
+     - This indicates an eswitch port representing a port of PCI
+       subfunction (SF).
+   * - ``DEVLINK_PORT_FLAVOUR_VIRTUAL``
+     - This indicates a virtual port for the PCI virtual function.
+
+Devlink port can have a different type based on the link layer described below.
+
+.. list-table:: List of devlink port types
+   :widths: 23 90
+
+   * - Type
+     - Description
+   * - ``DEVLINK_PORT_TYPE_ETH``
+     - Driver should set this port type when a link layer of the port is
+       Ethernet.
+   * - ``DEVLINK_PORT_TYPE_IB``
+     - Driver should set this port type when a link layer of the port is
+       InfiniBand.
+   * - ``DEVLINK_PORT_TYPE_AUTO``
+     - This type is indicated by the user when driver should detect the port
+       type automatically.
+
+PCI controllers
+---------------
+In most cases a PCI device has only one controller. A controller consists of
+potentially multiple physical, virtual functions and subfunctions. A function
+consists of one or more ports. This port is represented by the devlink eswitch
+port.
+
+A PCI device connected to multiple CPUs or multiple PCI root complexes or a
+SmartNIC, however, may have multiple controllers. For a device with multiple
+controllers, each controller is distinguished by a unique controller number.
+An eswitch is on the PCI device which supports ports of multiple controllers.
+
+An example view of a system with two controllers::
+
+                 ---------------------------------------------------------
+                 |                                                       |
+                 |           --------- ---------         ------- ------- |
+    -----------  |           | vf(s) | | sf(s) |         |vf(s)| |sf(s)| |
+    | server  |  | -------   ----/---- ---/----- ------- ---/--- ---/--- |
+    | pci rc  |=== | pf0 |______/________/       | pf1 |___/_______/     |
+    | connect |  | -------                       -------                 |
+    -----------  |     | controller_num=1 (no eswitch)                   |
+                 ------|--------------------------------------------------
+                 (internal wire)
+                       |
+                 ---------------------------------------------------------
+                 | devlink eswitch ports and reps                        |
+                 | ----------------------------------------------------- |
+                 | |ctrl-0 | ctrl-0 | ctrl-0 | ctrl-0 | ctrl-0 |ctrl-0 | |
+                 | |pf0    | pf0vfN | pf0sfN | pf1    | pf1vfN |pf1sfN | |
+                 | ----------------------------------------------------- |
+                 | |ctrl-1 | ctrl-1 | ctrl-1 | ctrl-1 | ctrl-1 |ctrl-1 | |
+                 | |pf0    | pf0vfN | pf0sfN | pf1    | pf1vfN |pf1sfN | |
+                 | ----------------------------------------------------- |
+                 |                                                       |
+                 |                                                       |
+    -----------  |           --------- ---------         ------- ------- |
+    | smartNIC|  |           | vf(s) | | sf(s) |         |vf(s)| |sf(s)| |
+    | pci rc  |==| -------   ----/---- ---/----- ------- ---/--- ---/--- |
+    | connect |  | | pf0 |______/________/       | pf1 |___/_______/     |
+    -----------  | -------                       -------                 |
+                 |                                                       |
+                 |  local controller_num=0 (eswitch)                     |
+                 ---------------------------------------------------------
+
+In the above example, the external controller (identified by controller number = 1)
+doesn't have the eswitch. Local controller (identified by controller number = 0)
+has the eswitch. The Devlink instance on the local controller has eswitch
+devlink ports for both the controllers.
+
+Function configuration
+======================
+
+A user can configure the function attribute before enumerating the PCI
+function. Usually it means, user should configure function attribute
+before a bus specific device for the function is created. However, when
+SRIOV is enabled, virtual function devices are created on the PCI bus.
+Hence, function attribute should be configured before binding virtual
+function device to the driver. For subfunctions, this means user should
+configure port function attribute before activating the port function.
+
+A user may set the hardware address of the function using
+'devlink port function set hw_addr' command. For Ethernet port function
+this means a MAC address.
+
+Subfunction
+============
+
+Subfunction is a lightweight function that has a parent PCI function on which
+it is deployed. Subfunction is created and deployed in unit of 1. Unlike
+SRIOV VFs, a subfunction doesn't require its own PCI virtual function.
+A subfunction communicates with the hardware through the parent PCI function.
+
+To use a subfunction, 3 steps setup sequence is followed.
+(1) create - create a subfunction;
+(2) configure - configure subfunction attributes;
+(3) deploy - deploy the subfunction;
+
+Subfunction management is done using devlink port user interface.
+User performs setup on the subfunction management device.
+
+(1) Create
+----------
+A subfunction is created using a devlink port interface. A user adds the
+subfunction by adding a devlink port of subfunction flavour. The devlink
+kernel code calls down to subfunction management driver (devlink ops) and asks
+it to create a subfunction devlink port. Driver then instantiates the
+subfunction port and any associated objects such as health reporters and
+representor netdevice.
+
+(2) Configure
+-------------
+A subfunction devlink port is created but it is not active yet. That means the
+entities are created on devlink side, the e-switch port representor is created,
+but the subfunction device itself it not created. A user might use e-switch port
+representor to do settings, putting it into bridge, adding TC rules, etc. A user
+might as well configure the hardware address (such as MAC address) of the
+subfunction while subfunction is inactive.
+
+(3) Deploy
+----------
+Once a subfunction is configured, user must activate it to use it. Upon
+activation, subfunction management driver asks the subfunction management
+device to instantiate the subfunction device on particular PCI function.
+A subfunction device is created on the :ref:`Documentation/driver-api/auxiliary_bus.rst <auxiliary_bus>`.
+At this point a matching subfunction driver binds to the subfunction's auxiliary device.
+
+Terms and Definitions
+=====================
+
+.. list-table:: Terms and Definitions
+   :widths: 22 90
+
+   * - Term
+     - Definitions
+   * - ``PCI device``
+     - A physical PCI device having one or more PCI bus consists of one or
+       more PCI controllers.
+   * - ``PCI controller``
+     -  A controller consists of potentially multiple physical functions,
+        virtual functions and subfunctions.
+   * - ``Port function``
+     -  An object to manage the function of a port.
+   * - ``Subfunction``
+     -  A lightweight function that has parent PCI function on which it is
+        deployed.
+   * - ``Subfunction device``
+     -  A bus device of the subfunction, usually on a auxiliary bus.
+   * - ``Subfunction driver``
+     -  A device driver for the subfunction auxiliary device.
+   * - ``Subfunction management device``
+     -  A PCI physical function that supports subfunction management.
+   * - ``Subfunction management driver``
+     -  A device driver for PCI physical function that supports
+        subfunction management using devlink port interface.
+   * - ``Subfunction host driver``
+     -  A device driver for PCI physical function that hosts subfunction
+        devices. In most cases it is same as subfunction management driver. When
+        subfunction is used on external controller, subfunction management and
+        host drivers are different.
index 93e92d2..3d5ae51 100644 (file)
@@ -23,6 +23,20 @@ current size and related sub resources. To access a sub resource, you
 specify the path of the resource. For example ``/IPv4/fib`` is the id for
 the ``fib`` sub-resource under the ``IPv4`` resource.
 
+Generic Resources
+=================
+
+Generic resources are used to describe resources that can be shared by multiple
+device drivers and their description must be added to the following table:
+
+.. list-table:: List of Generic Resources
+   :widths: 10 90
+
+   * - Name
+     - Description
+   * - ``physical_ports``
+     - A limited capacity of physical ports that the switch ASIC can support
+
 example usage
 -------------
 
index d875f3e..935b639 100644 (file)
@@ -480,6 +480,11 @@ be added to the following table:
      - ``drop``
      - Traps packets that the device decided to drop in case they hit a
        blackhole nexthop
+   * - ``dmac_filter``
+     - ``drop``
+     - Traps incoming packets that the device decided to drop because
+       the destination MAC is not configured in the MAC table and
+       the interface is not in promiscuous mode
 
 Driver-specific Packet Traps
 ============================
index d828747..aab7966 100644 (file)
@@ -18,6 +18,7 @@ general.
    devlink-info
    devlink-flash
    devlink-params
+   devlink-port
    devlink-region
    devlink-resource
    devlink-reload
index debb59e..f6d8f90 100644 (file)
@@ -1006,13 +1006,13 @@ Size modifier is one of ...
 
 Mode modifier is one of::
 
-  BPF_IMM  0x00  /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
-  BPF_ABS  0x20
-  BPF_IND  0x40
-  BPF_MEM  0x60
-  BPF_LEN  0x80  /* classic BPF only, reserved in eBPF */
-  BPF_MSH  0xa0  /* classic BPF only, reserved in eBPF */
-  BPF_XADD 0xc0  /* eBPF only, exclusive add */
+  BPF_IMM     0x00  /* used for 32-bit mov in classic BPF and 64-bit in eBPF */
+  BPF_ABS     0x20
+  BPF_IND     0x40
+  BPF_MEM     0x60
+  BPF_LEN     0x80  /* classic BPF only, reserved in eBPF */
+  BPF_MSH     0xa0  /* classic BPF only, reserved in eBPF */
+  BPF_ATOMIC  0xc0  /* eBPF only, atomic operations */
 
 eBPF has two non-generic instructions: (BPF_ABS | <size> | BPF_LD) and
 (BPF_IND | <size> | BPF_LD) which are used to access packet data.
@@ -1044,11 +1044,50 @@ Unlike classic BPF instruction set, eBPF has generic load/store operations::
     BPF_MEM | <size> | BPF_STX:  *(size *) (dst_reg + off) = src_reg
     BPF_MEM | <size> | BPF_ST:   *(size *) (dst_reg + off) = imm32
     BPF_MEM | <size> | BPF_LDX:  dst_reg = *(size *) (src_reg + off)
-    BPF_XADD | BPF_W  | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
-    BPF_XADD | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
 
-Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW. Note that 1 and
-2 byte atomic increments are not supported.
+Where size is one of: BPF_B or BPF_H or BPF_W or BPF_DW.
+
+It also includes atomic operations, which use the immediate field for extra
+encoding.
+
+   .imm = BPF_ADD, .code = BPF_ATOMIC | BPF_W  | BPF_STX: lock xadd *(u32 *)(dst_reg + off16) += src_reg
+   .imm = BPF_ADD, .code = BPF_ATOMIC | BPF_DW | BPF_STX: lock xadd *(u64 *)(dst_reg + off16) += src_reg
+
+The basic atomic operations supported are:
+
+    BPF_ADD
+    BPF_AND
+    BPF_OR
+    BPF_XOR
+
+Each having equivalent semantics with the ``BPF_ADD`` example, that is: the
+memory location addresed by ``dst_reg + off`` is atomically modified, with
+``src_reg`` as the other operand. If the ``BPF_FETCH`` flag is set in the
+immediate, then these operations also overwrite ``src_reg`` with the
+value that was in memory before it was modified.
+
+The more special operations are:
+
+    BPF_XCHG
+
+This atomically exchanges ``src_reg`` with the value addressed by ``dst_reg +
+off``.
+
+    BPF_CMPXCHG
+
+This atomically compares the value addressed by ``dst_reg + off`` with
+``R0``. If they match it is replaced with ``src_reg``, The value that was there
+before is loaded back to ``R0``.
+
+Note that 1 and 2 byte atomic operations are not supported.
+
+Except ``BPF_ADD`` _without_ ``BPF_FETCH`` (for legacy reasons), all 4 byte
+atomic operations require alu32 mode. Clang enables this mode by default in
+architecture v3 (``-mcpu=v3``). For older versions it can be enabled with
+``-Xclang -target-feature -Xclang +alu32``.
+
+You may encounter BPF_XADD - this is a legacy name for BPF_ATOMIC, referring to
+the exclusive-add operation encoded when the immediate field is zero.
 
 eBPF has one 16-byte instruction: BPF_LD | BPF_DW | BPF_IMM which consists
 of two consecutive ``struct bpf_insn`` 8-byte blocks and interpreted as single
index dd2b12a..f0353fb 100644 (file)
@@ -1807,12 +1807,24 @@ seg6_flowlabel - INTEGER
 ``conf/default/*``:
        Change the interface-specific default settings.
 
+       These settings would be used during creating new interfaces.
+
 
 ``conf/all/*``:
        Change all the interface-specific settings.
 
        [XXX:  Other special features than forwarding?]
 
+conf/all/disable_ipv6 - BOOLEAN
+       Changing this value is same as changing ``conf/default/disable_ipv6``
+       setting and also all per-interface ``disable_ipv6`` settings to the same
+       value.
+
+       Reading this value does not have any particular meaning. It does not say
+       whether IPv6 support is enabled or disabled. Returned value can be 1
+       also in the case when some interface has ``disable_ipv6`` set to 0 and
+       has configured IPv6 addresses.
+
 conf/all/forwarding - BOOLEAN
        Enable global IPv6 forwarding between all interfaces.
 
@@ -1871,6 +1883,16 @@ accept_ra_defrtr - BOOLEAN
                - enabled if accept_ra is enabled.
                - disabled if accept_ra is disabled.
 
+ra_defrtr_metric - UNSIGNED INTEGER
+       Route metric for default route learned in Router Advertisement. This value
+       will be assigned as metric for the default route learned via IPv6 Router
+       Advertisement. Takes affect only if accept_ra_defrtr is enabled.
+
+       Possible values:
+               1 to 0xFFFFFFFF
+
+               Default: IP6_RT_PRIO_USER i.e. 1024.
+
 accept_ra_from_local - BOOLEAN
        Accept RA with source-address that is found on local machine
        if the RA is otherwise proper and able to be accepted.
index 4b9ed58..ae2ae37 100644 (file)
@@ -6,9 +6,9 @@
 netdev FAQ
 ==========
 
-Q: What is netdev?
-------------------
-A: It is a mailing list for all network-related Linux stuff.  This
+What is netdev?
+---------------
+It is a mailing list for all network-related Linux stuff.  This
 includes anything found under net/ (i.e. core code like IPv6) and
 drivers/net (i.e. hardware specific drivers) in the Linux source tree.
 
@@ -25,9 +25,9 @@ Aside from subsystems like that mentioned above, all network-related
 Linux development (i.e. RFC, review, comments, etc.) takes place on
 netdev.
 
-Q: How do the changes posted to netdev make their way into Linux?
------------------------------------------------------------------
-A: There are always two trees (git repositories) in play.  Both are
+How do the changes posted to netdev make their way into Linux?
+--------------------------------------------------------------
+There are always two trees (git repositories) in play.  Both are
 driven by David Miller, the main network maintainer.  There is the
 ``net`` tree, and the ``net-next`` tree.  As you can probably guess from
 the names, the ``net`` tree is for fixes to existing code already in the
@@ -37,9 +37,9 @@ for the future release.  You can find the trees here:
 - https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
 - https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
 
-Q: How often do changes from these trees make it to the mainline Linus tree?
-----------------------------------------------------------------------------
-A: To understand this, you need to know a bit of background information on
+How often do changes from these trees make it to the mainline Linus tree?
+-------------------------------------------------------------------------
+To understand this, you need to know a bit of background information on
 the cadence of Linux development.  Each new release starts off with a
 two week "merge window" where the main maintainers feed their new stuff
 to Linus for merging into the mainline tree.  After the two weeks, the
@@ -81,7 +81,8 @@ focus for ``net`` is on stabilization and bug fixes.
 
 Finally, the vX.Y gets released, and the whole cycle starts over.
 
-Q: So where are we now in this cycle?
+So where are we now in this cycle?
+----------------------------------
 
 Load the mainline (Linus) page here:
 
@@ -91,9 +92,9 @@ and note the top of the "tags" section.  If it is rc1, it is early in
 the dev cycle.  If it was tagged rc7 a week ago, then a release is
 probably imminent.
 
-Q: How do I indicate which tree (net vs. net-next) my patch should be in?
--------------------------------------------------------------------------
-A: Firstly, think whether you have a bug fix or new "next-like" content.
+How do I indicate which tree (net vs. net-next) my patch should be in?
+----------------------------------------------------------------------
+Firstly, think whether you have a bug fix or new "next-like" content.
 Then once decided, assuming that you use git, use the prefix flag, i.e.
 ::
 
@@ -105,48 +106,45 @@ in the above is just the subject text of the outgoing e-mail, and you
 can manually change it yourself with whatever MUA you are comfortable
 with.
 
-Q: I sent a patch and I'm wondering what happened to it?
---------------------------------------------------------
-Q: How can I tell whether it got merged?
-A: Start by looking at the main patchworks queue for netdev:
+I sent a patch and I'm wondering what happened to it - how can I tell whether it got merged?
+--------------------------------------------------------------------------------------------
+Start by looking at the main patchworks queue for netdev:
 
   https://patchwork.kernel.org/project/netdevbpf/list/
 
 The "State" field will tell you exactly where things are at with your
 patch.
 
-Q: The above only says "Under Review".  How can I find out more?
-----------------------------------------------------------------
-A: Generally speaking, the patches get triaged quickly (in less than
+The above only says "Under Review".  How can I find out more?
+-------------------------------------------------------------
+Generally speaking, the patches get triaged quickly (in less than
 48h).  So be patient.  Asking the maintainer for status updates on your
 patch is a good way to ensure your patch is ignored or pushed to the
 bottom of the priority list.
 
-Q: I submitted multiple versions of the patch series
-----------------------------------------------------
-Q: should I directly update patchwork for the previous versions of these
-patch series?
-A: No, please don't interfere with the patch status on patchwork, leave
+I submitted multiple versions of the patch series. Should I directly update patchwork for the previous versions of these patch series?
+--------------------------------------------------------------------------------------------------------------------------------------
+No, please don't interfere with the patch status on patchwork, leave
 it to the maintainer to figure out what is the most recent and current
 version that should be applied. If there is any doubt, the maintainer
 will reply and ask what should be done.
 
-Q: I made changes to only a few patches in a patch series should I resend only those changed?
----------------------------------------------------------------------------------------------
-A: No, please resend the entire patch series and make sure you do number your
+I made changes to only a few patches in a patch series should I resend only those changed?
+------------------------------------------------------------------------------------------
+No, please resend the entire patch series and make sure you do number your
 patches such that it is clear this is the latest and greatest set of patches
 that can be applied.
 
-Q: I submitted multiple versions of a patch series and it looks like a version other than the last one has been accepted, what should I do?
--------------------------------------------------------------------------------------------------------------------------------------------
-A: There is no revert possible, once it is pushed out, it stays like that.
+I submitted multiple versions of a patch series and it looks like a version other than the last one has been accepted, what should I do?
+----------------------------------------------------------------------------------------------------------------------------------------
+There is no revert possible, once it is pushed out, it stays like that.
 Please send incremental versions on top of what has been merged in order to fix
 the patches the way they would look like if your latest patch series was to be
 merged.
 
-Q: How can I tell what patches are queued up for backporting to the various stable releases?
---------------------------------------------------------------------------------------------
-A: Normally Greg Kroah-Hartman collects stable commits himself, but for
+How can I tell what patches are queued up for backporting to the various stable releases?
+-----------------------------------------------------------------------------------------
+Normally Greg Kroah-Hartman collects stable commits himself, but for
 networking, Dave collects up patches he deems critical for the
 networking subsystem, and then hands them off to Greg.
 
@@ -169,11 +167,9 @@ simply clone the repo, and then git grep the mainline commit ID, e.g.
   releases/3.9.8/ipv6-fix-possible-crashes-in-ip6_cork_release.patch
   stable/stable-queue$
 
-Q: I see a network patch and I think it should be backported to stable.
------------------------------------------------------------------------
-Q: Should I request it via stable@vger.kernel.org like the references in
-the kernel's Documentation/process/stable-kernel-rules.rst file say?
-A: No, not for networking.  Check the stable queues as per above first
+I see a network patch and I think it should be backported to stable. Should I request it via stable@vger.kernel.org like the references in the kernel's Documentation/process/stable-kernel-rules.rst file say?
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+No, not for networking.  Check the stable queues as per above first
 to see if it is already queued.  If not, then send a mail to netdev,
 listing the upstream commit ID and why you think it should be a stable
 candidate.
@@ -190,11 +186,9 @@ mainline, the better the odds that it is an OK candidate for stable.  So
 scrambling to request a commit be added the day after it appears should
 be avoided.
 
-Q: I have created a network patch and I think it should be backported to stable.
---------------------------------------------------------------------------------
-Q: Should I add a Cc: stable@vger.kernel.org like the references in the
-kernel's Documentation/ directory say?
-A: No.  See above answer.  In short, if you think it really belongs in
+I have created a network patch and I think it should be backported to stable. Should I add a Cc: stable@vger.kernel.org like the references in the kernel's Documentation/ directory say?
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+No.  See above answer.  In short, if you think it really belongs in
 stable, then ensure you write a decent commit log that describes who
 gets impacted by the bug fix and how it manifests itself, and when the
 bug was introduced.  If you do that properly, then the commit will get
@@ -207,18 +201,18 @@ marker line as described in
 :ref:`Documentation/process/submitting-patches.rst <the_canonical_patch_format>`
 to temporarily embed that information into the patch that you send.
 
-Q: Are all networking bug fixes backported to all stable releases?
-------------------------------------------------------------------
-A: Due to capacity, Dave could only take care of the backports for the
+Are all networking bug fixes backported to all stable releases?
+---------------------------------------------------------------
+Due to capacity, Dave could only take care of the backports for the
 last two stable releases. For earlier stable releases, each stable
 branch maintainer is supposed to take care of them. If you find any
 patch is missing from an earlier stable branch, please notify
 stable@vger.kernel.org with either a commit ID or a formal patch
 backported, and CC Dave and other relevant networking developers.
 
-Q: Is the comment style convention different for the networking content?
-------------------------------------------------------------------------
-A: Yes, in a largely trivial way.  Instead of this::
+Is the comment style convention different for the networking content?
+---------------------------------------------------------------------
+Yes, in a largely trivial way.  Instead of this::
 
   /*
    * foobar blah blah blah
@@ -231,32 +225,30 @@ it is requested that you make it look like this::
    * another line of text
    */
 
-Q: I am working in existing code that has the former comment style and not the latter.
---------------------------------------------------------------------------------------
-Q: Should I submit new code in the former style or the latter?
-A: Make it the latter style, so that eventually all code in the domain
+I am working in existing code that has the former comment style and not the latter. Should I submit new code in the former style or the latter?
+-----------------------------------------------------------------------------------------------------------------------------------------------
+Make it the latter style, so that eventually all code in the domain
 of netdev is of this format.
 
-Q: I found a bug that might have possible security implications or similar.
----------------------------------------------------------------------------
-Q: Should I mail the main netdev maintainer off-list?**
-A: No. The current netdev maintainer has consistently requested that
+I found a bug that might have possible security implications or similar. Should I mail the main netdev maintainer off-list?
+---------------------------------------------------------------------------------------------------------------------------
+No. The current netdev maintainer has consistently requested that
 people use the mailing lists and not reach out directly.  If you aren't
 OK with that, then perhaps consider mailing security@kernel.org or
 reading about http://oss-security.openwall.org/wiki/mailing-lists/distros
 as possible alternative mechanisms.
 
-Q: What level of testing is expected before I submit my change?
----------------------------------------------------------------
-A: If your changes are against ``net-next``, the expectation is that you
+What level of testing is expected before I submit my change?
+------------------------------------------------------------
+If your changes are against ``net-next``, the expectation is that you
 have tested by layering your changes on top of ``net-next``.  Ideally
 you will have done run-time testing specific to your change, but at a
 minimum, your changes should survive an ``allyesconfig`` and an
 ``allmodconfig`` build without new warnings or failures.
 
-Q: How do I post corresponding changes to user space components?
-----------------------------------------------------------------
-A: User space code exercising kernel features should be posted
+How do I post corresponding changes to user space components?
+-------------------------------------------------------------
+User space code exercising kernel features should be posted
 alongside kernel patches. This gives reviewers a chance to see
 how any new interface is used and how well it works.
 
@@ -280,9 +272,9 @@ to the mailing list, e.g.::
 Posting as one thread is discouraged because it confuses patchwork
 (as of patchwork 2.2.2).
 
-Q: Any other tips to help ensure my net/net-next patch gets OK'd?
------------------------------------------------------------------
-A: Attention to detail.  Re-read your own work as if you were the
+Any other tips to help ensure my net/net-next patch gets OK'd?
+--------------------------------------------------------------
+Attention to detail.  Re-read your own work as if you were the
 reviewer.  You can start with using ``checkpatch.pl``, perhaps even with
 the ``--strict`` flag.  But do not be mindlessly robotic in doing so.
 If your change is a bug fix, make sure your commit log indicates the
index 5a85fcc..17bdcb7 100644 (file)
@@ -10,18 +10,177 @@ Introduction
 The following is a random collection of documentation regarding
 network devices.
 
-struct net_device allocation rules
-==================================
+struct net_device lifetime rules
+================================
 Network device structures need to persist even after module is unloaded and
 must be allocated with alloc_netdev_mqs() and friends.
 If device has registered successfully, it will be freed on last use
-by free_netdev(). This is required to handle the pathologic case cleanly
-(example: rmmod mydriver </sys/class/net/myeth/mtu )
+by free_netdev(). This is required to handle the pathological case cleanly
+(example: ``rmmod mydriver </sys/class/net/myeth/mtu``)
 
-alloc_netdev_mqs()/alloc_netdev() reserve extra space for driver
+alloc_netdev_mqs() / alloc_netdev() reserve extra space for driver
 private data which gets freed when the network device is freed. If
 separately allocated data is attached to the network device
-(netdev_priv(dev)) then it is up to the module exit handler to free that.
+(netdev_priv()) then it is up to the module exit handler to free that.
+
+There are two groups of APIs for registering struct net_device.
+First group can be used in normal contexts where ``rtnl_lock`` is not already
+held: register_netdev(), unregister_netdev().
+Second group can be used when ``rtnl_lock`` is already held:
+register_netdevice(), unregister_netdevice(), free_netdevice().
+
+Simple drivers
+--------------
+
+Most drivers (especially device drivers) handle lifetime of struct net_device
+in context where ``rtnl_lock`` is not held (e.g. driver probe and remove paths).
+
+In that case the struct net_device registration is done using
+the register_netdev(), and unregister_netdev() functions:
+
+.. code-block:: c
+
+  int probe()
+  {
+    struct my_device_priv *priv;
+    int err;
+
+    dev = alloc_netdev_mqs(...);
+    if (!dev)
+      return -ENOMEM;
+    priv = netdev_priv(dev);
+
+    /* ... do all device setup before calling register_netdev() ...
+     */
+
+    err = register_netdev(dev);
+    if (err)
+      goto err_undo;
+
+    /* net_device is visible to the user! */
+
+  err_undo:
+    /* ... undo the device setup ... */
+    free_netdev(dev);
+    return err;
+  }
+
+  void remove()
+  {
+    unregister_netdev(dev);
+    free_netdev(dev);
+  }
+
+Note that after calling register_netdev() the device is visible in the system.
+Users can open it and start sending / receiving traffic immediately,
+or run any other callback, so all initialization must be done prior to
+registration.
+
+unregister_netdev() closes the device and waits for all users to be done
+with it. The memory of struct net_device itself may still be referenced
+by sysfs but all operations on that device will fail.
+
+free_netdev() can be called after unregister_netdev() returns on when
+register_netdev() failed.
+
+Device management under RTNL
+----------------------------
+
+Registering struct net_device while in context which already holds
+the ``rtnl_lock`` requires extra care. In those scenarios most drivers
+will want to make use of struct net_device's ``needs_free_netdev``
+and ``priv_destructor`` members for freeing of state.
+
+Example flow of netdev handling under ``rtnl_lock``:
+
+.. code-block:: c
+
+  static void my_setup(struct net_device *dev)
+  {
+    dev->needs_free_netdev = true;
+  }
+
+  static void my_destructor(struct net_device *dev)
+  {
+    some_obj_destroy(priv->obj);
+    some_uninit(priv);
+  }
+
+  int create_link()
+  {
+    struct my_device_priv *priv;
+    int err;
+
+    ASSERT_RTNL();
+
+    dev = alloc_netdev(sizeof(*priv), "net%d", NET_NAME_UNKNOWN, my_setup);
+    if (!dev)
+      return -ENOMEM;
+    priv = netdev_priv(dev);
+
+    /* Implicit constructor */
+    err = some_init(priv);
+    if (err)
+      goto err_free_dev;
+
+    priv->obj = some_obj_create();
+    if (!priv->obj) {
+      err = -ENOMEM;
+      goto err_some_uninit;
+    }
+    /* End of constructor, set the destructor: */
+    dev->priv_destructor = my_destructor;
+
+    err = register_netdevice(dev);
+    if (err)
+      /* register_netdevice() calls destructor on failure */
+      goto err_free_dev;
+
+    /* If anything fails now unregister_netdevice() (or unregister_netdev())
+     * will take care of calling my_destructor and free_netdev().
+     */
+
+    return 0;
+
+  err_some_uninit:
+    some_uninit(priv);
+  err_free_dev:
+    free_netdev(dev);
+    return err;
+  }
+
+If struct net_device.priv_destructor is set it will be called by the core
+some time after unregister_netdevice(), it will also be called if
+register_netdevice() fails. The callback may be invoked with or without
+``rtnl_lock`` held.
+
+There is no explicit constructor callback, driver "constructs" the private
+netdev state after allocating it and before registration.
+
+Setting struct net_device.needs_free_netdev makes core call free_netdevice()
+automatically after unregister_netdevice() when all references to the device
+are gone. It only takes effect after a successful call to register_netdevice()
+so if register_netdevice() fails driver is responsible for calling
+free_netdev().
+
+free_netdev() is safe to call on error paths right after unregister_netdevice()
+or when register_netdevice() fails. Parts of netdev (de)registration process
+happen after ``rtnl_lock`` is released, therefore in those cases free_netdev()
+will defer some of the processing until ``rtnl_lock`` is released.
+
+Devices spawned from struct rtnl_link_ops should never free the
+struct net_device directly.
+
+.ndo_init and .ndo_uninit
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``.ndo_init`` and ``.ndo_uninit`` callbacks are called during net_device
+registration and de-registration, under ``rtnl_lock``. Drivers can use
+those e.g. when parts of their init process need to run under ``rtnl_lock``.
+
+``.ndo_init`` runs before device is visible in the system, ``.ndo_uninit``
+runs during de-registering after device is closed but other subsystems
+may still have outstanding references to the netdevice.
 
 MTU
 ===
@@ -64,8 +223,8 @@ ndo_do_ioctl:
        Context: process
 
 ndo_get_stats:
-       Synchronization: dev_base_lock rwlock.
-       Context: nominally process, but don't sleep inside an rwlock
+       Synchronization: rtnl_lock() semaphore, dev_base_lock rwlock, or RCU.
+       Context: atomic (can't sleep under rwlock or RCU)
 
 ndo_start_xmit:
        Synchronization: __netif_tx_lock spinlock.
index 6c009ce..500ef60 100644 (file)
@@ -8,7 +8,7 @@ Abstract
 ========
 
 This file documents the mmap() facility available with the PACKET
-socket interface on 2.4/2.6/3.x kernels. This type of sockets is used for
+socket interface. This type of sockets is used for
 
 i) capture network traffic with utilities like tcpdump,
 ii) transmit network traffic, or any other that needs raw
@@ -25,12 +25,12 @@ Please send your comments to
 Why use PACKET_MMAP
 ===================
 
-In Linux 2.4/2.6/3.x if PACKET_MMAP is not enabled, the capture process is very
+Non PACKET_MMAP capture process (plain AF_PACKET) is very
 inefficient. It uses very limited buffers and requires one system call to
 capture each packet, it requires two if you want to get packet's timestamp
 (like libpcap always does).
 
-In the other hand PACKET_MMAP is very efficient. PACKET_MMAP provides a size
+On the other hand PACKET_MMAP is very efficient. PACKET_MMAP provides a size
 configurable circular buffer mapped in user space that can be used to either
 send or receive packets. This way reading packets just needs to wait for them,
 most of the time there is no need to issue a single system call. Concerning
@@ -252,8 +252,7 @@ PACKET_MMAP setting constraints
 
 In kernel versions prior to 2.4.26 (for the 2.4 branch) and 2.6.5 (2.6 branch),
 the PACKET_MMAP buffer could hold only 32768 frames in a 32 bit architecture or
-16384 in a 64 bit architecture. For information on these kernel versions
-see http://pusa.uv.es/~ulisses/packet_mmap/packet_mmap.pre-2.4.26_2.6.5.txt
+16384 in a 64 bit architecture.
 
 Block size limit
 ----------------
@@ -437,7 +436,7 @@ and the following flags apply:
 Capture process
 ^^^^^^^^^^^^^^^
 
-     from include/linux/if_packet.h
+From include/linux/if_packet.h::
 
      #define TP_STATUS_COPY          (1 << 1)
      #define TP_STATUS_LOSING        (1 << 2)
index b2f7ec7..399f179 100644 (file)
@@ -286,6 +286,11 @@ Some of the interface modes are described below:
     Note: due to legacy usage, some 10GBASE-R usage incorrectly makes
     use of this definition.
 
+``PHY_INTERFACE_MODE_100BASEX``
+    This defines IEEE 802.3 Clause 24.  The link operates at a fixed data
+    rate of 125Mpbs using a 4B/5B encoding scheme, resulting in an underlying
+    data rate of 100Mpbs.
+
 Pause frames / flow control
 ===========================
 
index 4edd0d3..423d138 100644 (file)
@@ -314,7 +314,7 @@ https://lwn.net/Articles/576263/
 * TcpExtTCPOrigDataSent
 
 This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
-explaination below::
+explanation below::
 
   TCPOrigDataSent: number of outgoing packets with original data (excluding
   retransmission but including data-in-SYN). This counter is different from
@@ -324,7 +324,7 @@ explaination below::
 * TCPSynRetrans
 
 This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
-explaination below::
+explanation below::
 
   TCPSynRetrans: number of SYN and SYN/ACK retransmits to break down
   retransmissions into SYN, fast-retransmits, timeout retransmits, etc.
@@ -332,7 +332,7 @@ explaination below::
 * TCPFastOpenActiveFail
 
 This counter is explained by `kernel commit f19c29e3e391`_, I pasted the
-explaination below::
+explanation below::
 
   TCPFastOpenActiveFail: Fast Open attempts (SYN/data) failed because
   the remote does not accept it or the attempts timed out.
@@ -382,7 +382,7 @@ Defined in `RFC1213 tcpAttemptFails`_.
 
 Defined in `RFC1213 tcpOutRsts`_. The RFC says this counter indicates
 the 'segments sent containing the RST flag', but in linux kernel, this
-couner indicates the segments kerenl tried to send. The sending
+counter indicates the segments kernel tried to send. The sending
 process might be failed due to some errors (e.g. memory alloc failed).
 
 .. _RFC1213 tcpOutRsts: https://tools.ietf.org/html/rfc1213#page-52
@@ -700,7 +700,7 @@ SACK option could have up to 4 blocks, they are checked
 individually. E.g., if 3 blocks of a SACk is invalid, the
 corresponding counter would be updated 3 times. The comment of the
 `Add counters for discarded SACK blocks`_ patch has additional
-explaination:
+explanation:
 
 .. _Add counters for discarded SACK blocks: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=18f02545a9a16c9a89778b91a162ad16d510bb32
 
@@ -829,7 +829,7 @@ PAWS check fails or the received sequence number is out of window.
 
 * TcpExtTCPACKSkippedTimeWait
 
-Tha ACK is skipped in Time-Wait status, the reason would be either
+The ACK is skipped in Time-Wait status, the reason would be either
 PAWS check failed or the received sequence number is out of window.
 
 * TcpExtTCPACKSkippedChallenge
@@ -984,7 +984,7 @@ TcpExtSyncookiesRecv counter wont be updated.
 
 Challenge ACK
 =============
-For details of challenge ACK, please refer the explaination of
+For details of challenge ACK, please refer the explanation of
 TcpExtTCPACKSkippedChallenge.
 
 * TcpExtTCPChallengeACK
@@ -1002,7 +1002,7 @@ prune
 =====
 When a socket is under memory pressure, the TCP stack will try to
 reclaim memory from the receiving queue and out of order queue. One of
-the reclaiming method is 'collapse', which means allocate a big sbk,
+the reclaiming method is 'collapse', which means allocate a big skb,
 copy the contiguous skbs to the single big skb, and free these
 contiguous skbs.
 
@@ -1163,7 +1163,7 @@ The server side nstat output::
   IpExtOutOctets                  52                 0.0
   IpExtInNoECTPkts                1                  0.0
 
-Input a string in nc client side again ('world' in our exmaple)::
+Input a string in nc client side again ('world' in our example)::
 
   nstatuser@nstat-a:~$ nc -v nstat-b 9000
   Connection to nstat-b 9000 port [tcp/*] succeeded!
@@ -1211,7 +1211,7 @@ replied an ACK. But kernel handled them in different ways. When the
 TCP window scale option is not used, kernel will try to enable fast
 path immediately when the connection comes into the established state,
 but if the TCP window scale option is used, kernel will disable the
-fast path at first, and try to enable it after kerenl receives
+fast path at first, and try to enable it after kernel receives
 packets. We could use the 'ss' command to verify whether the window
 scale option is used. e.g. run below command on either server or
 client::
@@ -1343,7 +1343,7 @@ Check TcpExtTCPAbortOnMemory on client::
   nstatuser@nstat-a:~$ nstat | grep -i abort
   TcpExtTCPAbortOnMemory          54                 0.0
 
-Check orphane socket count on client::
+Check orphaned socket count on client::
 
   nstatuser@nstat-a:~$ ss -s
   Total: 131 (kernel 0)
@@ -1685,7 +1685,7 @@ Send 3 SYN repeatly to nstat-b::
 
   nstatuser@nstat-a:~$ for i in {1..3}; do sudo tcpreplay -i ens3 /tmp/syn_fixcsum.pcap; done
 
-Check snmp cunter on nstat-b::
+Check snmp counter on nstat-b::
 
   nstatuser@nstat-b:~$ nstat | grep -i skip
   TcpExtTCPACKSkippedSynRecv      1                  0.0
@@ -1770,7 +1770,7 @@ string 'foo' in our example::
   Connection from nstat-a 42132 received!
   foo
 
-On nstat-a, the tcpdump should have caputred the ACK. We should check
+On nstat-a, the tcpdump should have captured the ACK. We should check
 the source port numbers of the two nc clients::
 
   nstatuser@nstat-a:~$ ss -ta '( dport = :9000 || dport = :9001 )' | tee
@@ -1778,7 +1778,7 @@ the source port numbers of the two nc clients::
   ESTAB  0        0            192.168.122.250:50208       192.168.122.251:9000
   ESTAB  0        0            192.168.122.250:42132       192.168.122.251:9001
 
-Run tcprewrite, change port 9001 to port 9000, chagne port 42132 to
+Run tcprewrite, change port 9001 to port 9000, change port 42132 to
 port 50208::
 
   nstatuser@nstat-a:~$ tcprewrite --infile /tmp/seq_pre.pcap --outfile /tmp/seq.pcap -r 9001:9000 -r 42132:50208 --fixcsum
index 03f7bea..f682e88 100644 (file)
@@ -55,7 +55,8 @@ struct __kernel_sock_timeval format.
 SO_TIMESTAMP_OLD returns incorrect timestamps after the year 2038
 on 32 bit machines.
 
-1.2 SO_TIMESTAMPNS (also SO_TIMESTAMPNS_OLD and SO_TIMESTAMPNS_NEW):
+1.2 SO_TIMESTAMPNS (also SO_TIMESTAMPNS_OLD and SO_TIMESTAMPNS_NEW)
+-------------------------------------------------------------------
 
 This option is identical to SO_TIMESTAMP except for the returned data type.
 Its struct timespec allows for higher resolution (ns) timestamps than the
index 0f55c6d..5f0dea3 100644 (file)
@@ -530,7 +530,10 @@ TLS device feature flags only control adding of new TLS connection
 offloads, old connections will remain active after flags are cleared.
 
 TLS encryption cannot be offloaded to devices without checksum calculation
-offload. Hence, TLS TX device feature flag requires NETIF_F_HW_CSUM being set.
+offload. Hence, TLS TX device feature flag requires TX csum offload being set.
 Disabling the latter implies clearing the former. Disabling TX checksum offload
 should not affect old connections, and drivers should make sure checksum
 calculation does not break for them.
+Similarly, device-offloaded TLS decryption implies doing RXCSUM. If the user
+does not want to enable RX csum offload, TLS RX device feature is disabled
+as well.
index c27e59d..0825dc4 100644 (file)
@@ -249,10 +249,8 @@ features; most of these are found in the "kernel hacking" submenu.  Several
 of these options should be turned on for any kernel used for development or
 testing purposes.  In particular, you should turn on:
 
- - ENABLE_MUST_CHECK and FRAME_WARN to get an
-   extra set of warnings for problems like the use of deprecated interfaces
-   or ignoring an important return value from a function.  The output
-   generated by these warnings can be verbose, but one need not worry about
+ - FRAME_WARN to get warnings for stack frames larger than a given amount.
+   The output generated can be verbose, but one need not worry about
    warnings from other parts of the kernel.
 
  - DEBUG_OBJECTS will add code to track the lifetime of various objects
index fe52c31..b36af65 100644 (file)
@@ -1501,7 +1501,7 @@ Module for Digigram miXart8 sound cards.
 
 This module supports multiple cards.
 Note: One miXart8 board will be represented as 4 alsa cards.
-See MIXART.txt for details.
+See Documentation/sound/cards/mixart.rst for details.
 
 When the driver is compiled as a module and the hotplug firmware
 is supported, the firmware data is loaded via hotplug automatically.
index 73bbd59..e636583 100644 (file)
@@ -71,7 +71,7 @@ core/oss
 The codes for PCM and mixer OSS emulation modules are stored in this
 directory. The rawmidi OSS emulation is included in the ALSA rawmidi
 code since it's quite small. The sequencer code is stored in
-``core/seq/oss`` directory (see `below <#core-seq-oss>`__).
+``core/seq/oss`` directory (see `below <core/seq/oss_>`__).
 
 core/seq
 ~~~~~~~~
@@ -382,7 +382,7 @@ where ``enable[dev]`` is the module option.
 Each time the ``probe`` callback is called, check the availability of
 the device. If not available, simply increment the device index and
 returns. dev will be incremented also later (`step 7
-<#set-the-pci-driver-data-and-return-zero>`__).
+<7) Set the PCI driver data and return zero._>`__).
 
 2) Create a card instance
 ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -450,10 +450,10 @@ field contains the information shown in ``/proc/asound/cards``.
 5) Create other components, such as mixer, MIDI, etc.
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Here you define the basic components such as `PCM <#PCM-Interface>`__,
-mixer (e.g. `AC97 <#API-for-AC97-Codec>`__), MIDI (e.g.
-`MPU-401 <#MIDI-MPU401-UART-Interface>`__), and other interfaces.
-Also, if you want a `proc file <#Proc-Interface>`__, define it here,
+Here you define the basic components such as `PCM <PCM Interface_>`__,
+mixer (e.g. `AC97 <API for AC97 Codec_>`__), MIDI (e.g.
+`MPU-401 <MIDI (MPU401-UART) Interface_>`__), and other interfaces.
+Also, if you want a `proc file <Proc Interface_>`__, define it here,
 too.
 
 6) Register the card instance.
@@ -941,7 +941,7 @@ The allocation of an interrupt source is done like this:
   chip->irq = pci->irq;
 
 where :c:func:`snd_mychip_interrupt()` is the interrupt handler
-defined `later <#pcm-interface-interrupt-handler>`__. Note that
+defined `later <PCM Interrupt Handler_>`__. Note that
 ``chip->irq`` should be defined only when :c:func:`request_irq()`
 succeeded.
 
@@ -3104,7 +3104,7 @@ processing the output stream in the irq handler.
 
 If the MPU-401 interface shares its interrupt with the other logical
 devices on the card, set ``MPU401_INFO_IRQ_HOOK`` (see
-`below <#MIDI-Interrupt-Handler>`__).
+`below <MIDI Interrupt Handler_>`__).
 
 Usually, the port address corresponds to the command port and port + 1
 corresponds to the data port. If not, you may change the ``cport``
index 70254ea..99ceb97 100644 (file)
@@ -360,10 +360,9 @@ since the last call to this ioctl.  Bit 0 is the first page in the
 memory slot.  Ensure the entire structure is cleared to avoid padding
 issues.
 
-If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 specifies
-the address space for which you want to return the dirty bitmap.
-They must be less than the value that KVM_CHECK_EXTENSION returns for
-the KVM_CAP_MULTI_ADDRESS_SPACE capability.
+If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 of slot field specifies
+the address space for which you want to return the dirty bitmap.  See
+KVM_SET_USER_MEMORY_REGION for details on the usage of slot field.
 
 The bits in the dirty bitmap are cleared before the ioctl returns, unless
 KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 is enabled.  For more information,
@@ -392,9 +391,14 @@ This ioctl is obsolete and has been removed.
 
 Errors:
 
-  =====      =============================
+  =======    ==============================================================
   EINTR      an unmasked signal is pending
-  =====      =============================
+  ENOEXEC    the vcpu hasn't been initialized or the guest tried to execute
+             instructions from device memory (arm64)
+  ENOSYS     data abort outside memslots with no syndrome info and
+             KVM_CAP_ARM_NISV_TO_USER not enabled (arm64)
+  EPERM      SVE feature set but not finalized (arm64)
+  =======    ==============================================================
 
 This ioctl is used to run a guest virtual cpu.  While there are no
 explicit parameters, there is an implicit parameter block that can be
@@ -1276,6 +1280,9 @@ field userspace_addr, which must point at user addressable memory for
 the entire memory slot size.  Any object may back this memory, including
 anonymous memory, ordinary files, and hugetlbfs.
 
+On architectures that support a form of address tagging, userspace_addr must
+be an untagged address.
+
 It is recommended that the lower 21 bits of guest_phys_addr and userspace_addr
 be identical.  This allows large pages in the guest to be backed by large
 pages in the host.
@@ -1328,7 +1335,7 @@ documentation when it pops into existence).
 
 :Capability: KVM_CAP_ENABLE_CAP_VM
 :Architectures: all
-:Type: vcpu ioctl
+:Type: vm ioctl
 :Parameters: struct kvm_enable_cap (in)
 :Returns: 0 on success; -1 on error
 
@@ -4427,7 +4434,7 @@ to I/O ports.
 :Capability: KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2
 :Architectures: x86, arm, arm64, mips
 :Type: vm ioctl
-:Parameters: struct kvm_dirty_log (in)
+:Parameters: struct kvm_clear_dirty_log (in)
 :Returns: 0 on success, -1 on error
 
 ::
@@ -4454,10 +4461,9 @@ in KVM's dirty bitmap, and dirty tracking is re-enabled for that page
 (for example via write-protection, or by clearing the dirty bit in
 a page table entry).
 
-If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 specifies
-the address space for which you want to return the dirty bitmap.
-They must be less than the value that KVM_CHECK_EXTENSION returns for
-the KVM_CAP_MULTI_ADDRESS_SPACE capability.
+If KVM_CAP_MULTI_ADDRESS_SPACE is available, bits 16-31 of slot field specifies
+the address space for which you want to clear the dirty status.  See
+KVM_SET_USER_MEMORY_REGION for details on the usage of slot field.
 
 This ioctl is mostly useful when KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2
 is enabled; for more information, see the description of the capability.
index 546aa66..627e90a 100644 (file)
@@ -203,8 +203,8 @@ F:  include/uapi/linux/nl80211.h
 F:     net/wireless/
 
 8169 10/100/1000 GIGABIT ETHERNET DRIVER
-M:     Realtek linux nic maintainers <nic_swsd@realtek.com>
 M:     Heiner Kallweit <hkallweit1@gmail.com>
+M:     nic_swsd@realtek.com
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/realtek/r8169*
@@ -820,7 +820,6 @@ M:  Netanel Belgazal <netanel@amazon.com>
 M:     Arthur Kiyanovski <akiyano@amazon.com>
 R:     Guy Tzalik <gtzalik@amazon.com>
 R:     Saeed Bishara <saeedb@amazon.com>
-R:     Zorik Machulsky <zorik@amazon.com>
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     Documentation/networking/device_drivers/ethernet/amazon/ena.rst
@@ -907,7 +906,7 @@ AMD KFD
 M:     Felix Kuehling <Felix.Kuehling@amd.com>
 L:     amd-gfx@lists.freedesktop.org
 S:     Supported
-T:     git git://people.freedesktop.org/~agd5f/linux
+T:     git https://gitlab.freedesktop.org/agd5f/linux.git
 F:     drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd*.[ch]
 F:     drivers/gpu/drm/amd/amdkfd/
 F:     drivers/gpu/drm/amd/include/cik_structs.h
@@ -2119,7 +2118,7 @@ N:        atmel
 ARM/Microchip Sparx5 SoC support
 M:     Lars Povlsen <lars.povlsen@microchip.com>
 M:     Steen Hegelund <Steen.Hegelund@microchip.com>
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+M:     UNGLinuxDriver@microchip.com
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Supported
 T:     git git://github.com/microchip-ung/linux-upstream.git
@@ -2788,6 +2787,14 @@ F:       arch/arm64/
 F:     tools/testing/selftests/arm64/
 X:     arch/arm64/boot/dts/
 
+ARROW SPEEDCHIPS XRS7000 SERIES ETHERNET SWITCH DRIVER
+M:     George McCollister <george.mccollister@gmail.com>
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/net/dsa/arrow,xrs700x.yaml
+F:     drivers/net/dsa/xrs700x/*
+F:     net/dsa/tag_xrs700x.c
+
 AS3645A LED FLASH CONTROLLER DRIVER
 M:     Sakari Ailus <sakari.ailus@iki.fi>
 L:     linux-leds@vger.kernel.org
@@ -2942,7 +2949,6 @@ S:        Maintained
 F:     drivers/hwmon/asus_atk0110.c
 
 ATLX ETHERNET DRIVERS
-M:     Jay Cliburn <jcliburn@gmail.com>
 M:     Chris Snook <chris.snook@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
@@ -3241,6 +3247,7 @@ L:        netdev@vger.kernel.org
 S:     Supported
 W:     http://sourceforge.net/projects/bonding/
 F:     drivers/net/bonding/
+F:     include/net/bonding.h
 F:     include/uapi/linux/if_bonding.h
 
 BOSCH SENSORTEC BMA400 ACCELEROMETER IIO DRIVER
@@ -3336,7 +3343,7 @@ F:        arch/riscv/net/
 X:     arch/riscv/net/bpf_jit_comp64.c
 
 BPF JIT for RISC-V (64-bit)
-M:     Björn Töpel <bjorn.topel@gmail.com>
+M:     Björn Töpel <bjorn@kernel.org>
 L:     netdev@vger.kernel.org
 L:     bpf@vger.kernel.org
 S:     Maintained
@@ -3400,6 +3407,7 @@ L:        openwrt-devel@lists.openwrt.org (subscribers-only)
 S:     Supported
 F:     Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml
 F:     drivers/net/dsa/b53/*
+F:     include/linux/dsa/brcm.h
 F:     include/linux/platform_data/b53.h
 
 BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
@@ -3413,7 +3421,7 @@ F:        Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
 F:     drivers/pci/controller/pcie-brcmstb.c
 F:     drivers/staging/vc04_services
 N:     bcm2711
-N:     bcm2835
+N:     bcm283*
 
 BROADCOM BCM281XX/BCM11XXX/BCM216XX ARM ARCHITECTURE
 M:     Florian Fainelli <f.fainelli@gmail.com>
@@ -3556,7 +3564,7 @@ S:        Supported
 F:     drivers/net/ethernet/broadcom/bnxt/
 
 BROADCOM BRCM80211 IEEE802.11n WIRELESS DRIVER
-M:     Arend van Spriel <arend.vanspriel@broadcom.com>
+M:     Arend van Spriel <aspriel@gmail.com>
 M:     Franky Lin <franky.lin@broadcom.com>
 M:     Hante Meuleman <hante.meuleman@broadcom.com>
 M:     Chi-hsien Lin <chi-hsien.lin@infineon.com>
@@ -3625,6 +3633,7 @@ S:        Supported
 F:     Documentation/devicetree/bindings/net/brcm,bcmgenet.txt
 F:     Documentation/devicetree/bindings/net/brcm,unimac-mdio.txt
 F:     drivers/net/ethernet/broadcom/genet/
+F:     drivers/net/ethernet/broadcom/unimac.h
 F:     drivers/net/mdio/mdio-bcm-unimac.c
 F:     include/linux/platform_data/bcmgenet.h
 F:     include/linux/platform_data/mdio-bcm-unimac.h
@@ -3658,6 +3667,15 @@ N:       bcm88312
 N:     hr2
 N:     stingray
 
+BROADCOM IPROC GBIT ETHERNET DRIVER
+M:     Rafał Miłecki <rafal@milecki.pl>
+M:     bcm-kernel-feedback-list@broadcom.com
+L:     netdev@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/net/brcm,amac.txt
+F:     drivers/net/ethernet/broadcom/bgmac*
+F:     drivers/net/ethernet/broadcom/unimac.h
+
 BROADCOM KONA GPIO DRIVER
 M:     Ray Jui <rjui@broadcom.com>
 L:     bcm-kernel-feedback-list@broadcom.com
@@ -3737,6 +3755,7 @@ L:        bcm-kernel-feedback-list@broadcom.com
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/ethernet/broadcom/bcmsysport.*
+F:     drivers/net/ethernet/broadcom/unimac.h
 
 BROADCOM TG3 GIGABIT ETHERNET DRIVER
 M:     Siva Reddy Kallam <siva.kallam@broadcom.com>
@@ -3881,9 +3900,9 @@ F:        Documentation/devicetree/bindings/mtd/cadence-nand-controller.txt
 F:     drivers/mtd/nand/raw/cadence-nand-controller.c
 
 CADENCE USB3 DRD IP DRIVER
-M:     Peter Chen <peter.chen@nxp.com>
+M:     Peter Chen <peter.chen@kernel.org>
 M:     Pawel Laszczak <pawell@cadence.com>
-M:     Roger Quadros <rogerq@ti.com>
+R:     Roger Quadros <rogerq@kernel.org>
 R:     Aswath Govindraju <a-govindraju@ti.com>
 L:     linux-usb@vger.kernel.org
 S:     Maintained
@@ -3931,8 +3950,10 @@ T:       git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
 F:     Documentation/devicetree/bindings/net/can/
 F:     drivers/net/can/
+F:     include/linux/can/bittiming.h
 F:     include/linux/can/dev.h
 F:     include/linux/can/led.h
+F:     include/linux/can/length.h
 F:     include/linux/can/platform/
 F:     include/linux/can/rx-offload.h
 F:     include/uapi/linux/can/error.h
@@ -3948,6 +3969,7 @@ W:        https://github.com/linux-can
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next.git
 F:     Documentation/networking/can.rst
+F:     include/linux/can/can-ml.h
 F:     include/linux/can/core.h
 F:     include/linux/can/skb.h
 F:     include/net/netns/can.h
@@ -3961,7 +3983,7 @@ F:        net/can/
 CAN-J1939 NETWORK LAYER
 M:     Robin van der Gracht <robin@protonic.nl>
 M:     Oleksij Rempel <o.rempel@pengutronix.de>
-R:     Pengutronix Kernel Team <kernel@pengutronix.de>
+R:     kernel@pengutronix.de
 L:     linux-can@vger.kernel.org
 S:     Maintained
 F:     Documentation/networking/j1939.rst
@@ -4163,7 +4185,7 @@ S:        Maintained
 F:     Documentation/translations/zh_CN/
 
 CHIPIDEA USB HIGH SPEED DUAL ROLE CONTROLLER
-M:     Peter Chen <Peter.Chen@nxp.com>
+M:     Peter Chen <peter.chen@kernel.org>
 L:     linux-usb@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
@@ -4313,7 +4335,9 @@ W:        https://clangbuiltlinux.github.io/
 B:     https://github.com/ClangBuiltLinux/linux/issues
 C:     irc://chat.freenode.net/clangbuiltlinux
 F:     Documentation/kbuild/llvm.rst
+F:     include/linux/compiler-clang.h
 F:     scripts/clang-tools/
+F:     scripts/clang-version.sh
 F:     scripts/lld-version.sh
 K:     \b(?i:clang|llvm)\b
 
@@ -4588,7 +4612,7 @@ B:        https://bugzilla.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
 F:     Documentation/admin-guide/pm/cpuidle.rst
 F:     Documentation/driver-api/pm/cpuidle.rst
-F:     drivers/cpuidle/*
+F:     drivers/cpuidle/
 F:     include/linux/cpuidle.h
 
 CPU POWER MONITORING SUBSYSTEM
@@ -4922,9 +4946,8 @@ F:        Documentation/scsi/dc395x.rst
 F:     drivers/scsi/dc395x.*
 
 DCCP PROTOCOL
-M:     Gerrit Renker <gerrit@erg.abdn.ac.uk>
 L:     dccp@vger.kernel.org
-S:     Maintained
+S:     Orphan
 W:     http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp
 F:     include/linux/dccp.h
 F:     include/linux/tfrc.h
@@ -7363,7 +7386,6 @@ L:        linux-hardening@vger.kernel.org
 S:     Maintained
 F:     Documentation/kbuild/gcc-plugins.rst
 F:     scripts/Makefile.gcc-plugins
-F:     scripts/gcc-plugin.sh
 F:     scripts/gcc-plugins/
 
 GCOV BASED KERNEL PROFILING
@@ -8435,11 +8457,8 @@ F:       drivers/i3c/
 F:     include/linux/i3c/
 
 IA64 (Itanium) PLATFORM
-M:     Tony Luck <tony.luck@intel.com>
-M:     Fenghua Yu <fenghua.yu@intel.com>
 L:     linux-ia64@vger.kernel.org
-S:     Odd Fixes
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux.git
+S:     Orphan
 F:     Documentation/ia64/
 F:     arch/ia64/
 
@@ -9240,7 +9259,7 @@ F:        tools/testing/selftests/sgx/*
 K:     \bSGX_
 
 INTERCONNECT API
-M:     Georgi Djakov <georgi.djakov@linaro.org>
+M:     Georgi Djakov <djakov@kernel.org>
 L:     linux-pm@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/interconnect/
@@ -9273,7 +9292,7 @@ F:        drivers/net/ethernet/sgi/ioc3-eth.c
 
 IOMAP FILESYSTEM LIBRARY
 M:     Christoph Hellwig <hch@infradead.org>
-M:     Darrick J. Wong <darrick.wong@oracle.com>
+M:     Darrick J. Wong <djwong@kernel.org>
 M:     linux-xfs@vger.kernel.org
 M:     linux-fsdevel@vger.kernel.org
 L:     linux-xfs@vger.kernel.org
@@ -9327,7 +9346,6 @@ W:        http://www.adaptec.com/
 F:     drivers/scsi/ips*
 
 IPVS
-M:     Wensong Zhang <wensong@linux-vs.org>
 M:     Simon Horman <horms@verge.net.au>
 M:     Julian Anastasov <ja@ssi.bg>
 L:     netdev@vger.kernel.org
@@ -9776,7 +9794,7 @@ F:        tools/testing/selftests/kvm/s390x/
 
 KERNEL VIRTUAL MACHINE FOR X86 (KVM/x86)
 M:     Paolo Bonzini <pbonzini@redhat.com>
-R:     Sean Christopherson <sean.j.christopherson@intel.com>
+R:     Sean Christopherson <seanjc@google.com>
 R:     Vitaly Kuznetsov <vkuznets@redhat.com>
 R:     Wanpeng Li <wanpengli@tencent.com>
 R:     Jim Mattson <jmattson@google.com>
@@ -10260,7 +10278,6 @@ S:      Supported
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git dev
 F:     Documentation/atomic_bitops.txt
 F:     Documentation/atomic_t.txt
-F:     Documentation/core-api/atomic_ops.rst
 F:     Documentation/core-api/refcount-vs-atomic.rst
 F:     Documentation/litmus-tests/
 F:     Documentation/memory-barriers.txt
@@ -10847,7 +10864,7 @@ F:      drivers/media/radio/radio-maxiradio*
 
 MCAN MMIO DEVICE DRIVER
 M:     Dan Murphy <dmurphy@ti.com>
-M:     Sriram Dash <sriram.dash@samsung.com>
+M:     Pankaj Sharma <pankj.sharma@samsung.com>
 L:     linux-can@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
@@ -11667,7 +11684,7 @@ F:      drivers/media/platform/atmel/atmel-isi.h
 
 MICROCHIP KSZ SERIES ETHERNET SWITCH DRIVER
 M:     Woojung Huh <woojung.huh@microchip.com>
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+M:     UNGLinuxDriver@microchip.com
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/net/dsa/microchip,ksz.yaml
@@ -11677,7 +11694,7 @@ F:      net/dsa/tag_ksz.c
 
 MICROCHIP LAN743X ETHERNET DRIVER
 M:     Bryan Whitehead <bryan.whitehead@microchip.com>
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+M:     UNGLinuxDriver@microchip.com
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ethernet/microchip/lan743x_*
@@ -11771,7 +11788,7 @@ F:      drivers/net/wireless/microchip/wilc1000/
 
 MICROSEMI MIPS SOCS
 M:     Alexandre Belloni <alexandre.belloni@bootlin.com>
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+M:     UNGLinuxDriver@microchip.com
 L:     linux-mips@vger.kernel.org
 S:     Supported
 F:     Documentation/devicetree/bindings/mips/mscc.txt
@@ -12418,8 +12435,8 @@ F:      tools/testing/selftests/net/ipsec.c
 
 NETWORKING [IPv4/IPv6]
 M:     "David S. Miller" <davem@davemloft.net>
-M:     Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
 M:     Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
+M:     David Ahern <dsahern@kernel.org>
 L:     netdev@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
@@ -12475,7 +12492,6 @@ F:      net/ipv6/tcp*.c
 
 NETWORKING [TLS]
 M:     Boris Pismenny <borisp@nvidia.com>
-M:     Aviad Yehezkel <aviadye@nvidia.com>
 M:     John Fastabend <john.fastabend@gmail.com>
 M:     Daniel Borkmann <daniel@iogearbox.net>
 M:     Jakub Kicinski <kuba@kernel.org>
@@ -12524,6 +12540,14 @@ F:     include/net/nfc/
 F:     include/uapi/linux/nfc.h
 F:     net/nfc/
 
+NFC VIRTUAL NCI DEVICE DRIVER
+M:     Bongsu Jeon <bongsu.jeon@samsung.com>
+L:     netdev@vger.kernel.org
+L:     linux-nfc@lists.01.org (moderated for non-subscribers)
+S:     Supported
+F:     drivers/nfc/virtual_ncidev.c
+F:     tools/testing/selftests/nci/
+
 NFS, SUNRPC, AND LOCKD CLIENTS
 M:     Trond Myklebust <trond.myklebust@hammerspace.com>
 M:     Anna Schumaker <anna.schumaker@netapp.com>
@@ -12825,10 +12849,10 @@ F:    tools/objtool/
 F:     include/linux/objtool.h
 
 OCELOT ETHERNET SWITCH DRIVER
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
 M:     Vladimir Oltean <vladimir.oltean@nxp.com>
 M:     Claudiu Manoil <claudiu.manoil@nxp.com>
 M:     Alexandre Belloni <alexandre.belloni@bootlin.com>
+M:     UNGLinuxDriver@microchip.com
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     drivers/net/dsa/ocelot/*
@@ -12850,7 +12874,7 @@ F:      include/misc/ocxl*
 F:     include/uapi/misc/ocxl.h
 
 OMAP AUDIO SUPPORT
-M:     Peter Ujfalusi <peter.ujfalusi@ti.com>
+M:     Peter Ujfalusi <peter.ujfalusi@gmail.com>
 M:     Jarkko Nikula <jarkko.nikula@bitmer.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:     linux-omap@vger.kernel.org
@@ -13890,7 +13914,7 @@ F:      drivers/platform/x86/peaq-wmi.c
 
 PENSANDO ETHERNET DRIVERS
 M:     Shannon Nelson <snelson@pensando.io>
-M:     Pensando Drivers <drivers@pensando.io>
+M:     drivers@pensando.io
 L:     netdev@vger.kernel.org
 S:     Supported
 F:     Documentation/networking/device_drivers/ethernet/pensando/ionic.rst
@@ -14512,10 +14536,18 @@ S:    Supported
 F:     drivers/crypto/qat/
 
 QCOM AUDIO (ASoC) DRIVERS
-M:     Patrick Lai <plai@codeaurora.org>
+M:     Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
 M:     Banajit Goswami <bgoswami@codeaurora.org>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Supported
+F:     sound/soc/codecs/lpass-va-macro.c
+F:     sound/soc/codecs/lpass-wsa-macro.*
+F:     sound/soc/codecs/msm8916-wcd-analog.c
+F:     sound/soc/codecs/msm8916-wcd-digital.c
+F:     sound/soc/codecs/wcd9335.*
+F:     sound/soc/codecs/wcd934x.c
+F:     sound/soc/codecs/wcd-clsh-v2.*
+F:     sound/soc/codecs/wsa881x.c
 F:     sound/soc/qcom/
 
 QCOM IPA DRIVER
@@ -14669,7 +14701,7 @@ T:      git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
 F:     drivers/net/wireless/ath/ath11k/
 
 QUALCOMM ATHEROS ATH9K WIRELESS DRIVER
-M:     QCA ath9k Development <ath9k-devel@qca.qualcomm.com>
+M:     ath9k-devel@qca.qualcomm.com
 L:     linux-wireless@vger.kernel.org
 S:     Supported
 W:     https://wireless.wiki.kernel.org/en/users/Drivers/ath9k
@@ -14820,7 +14852,7 @@ M:      Alex Deucher <alexander.deucher@amd.com>
 M:     Christian König <christian.koenig@amd.com>
 L:     amd-gfx@lists.freedesktop.org
 S:     Supported
-T:     git git://people.freedesktop.org/~agd5f/linux
+T:     git https://gitlab.freedesktop.org/agd5f/linux.git
 F:     drivers/gpu/drm/amd/
 F:     drivers/gpu/drm/radeon/
 F:     include/uapi/drm/amdgpu_drm.h
@@ -16321,6 +16353,7 @@ M:      Pekka Enberg <penberg@kernel.org>
 M:     David Rientjes <rientjes@google.com>
 M:     Joonsoo Kim <iamjoonsoo.kim@lge.com>
 M:     Andrew Morton <akpm@linux-foundation.org>
+M:     Vlastimil Babka <vbabka@suse.cz>
 L:     linux-mm@kvack.org
 S:     Maintained
 F:     include/linux/sl?b*.h
@@ -16710,6 +16743,8 @@ M:      Samuel Thibault <samuel.thibault@ens-lyon.org>
 L:     speakup@linux-speakup.org
 S:     Odd Fixes
 W:     http://www.linux-speakup.org/
+W:     https://github.com/linux-speakup/speakup
+B:     https://github.com/linux-speakup/speakup/issues
 F:     drivers/accessibility/speakup/
 
 SPEAR CLOCK FRAMEWORK SUPPORT
@@ -16964,7 +16999,7 @@ M:      Olivier Moysan <olivier.moysan@st.com>
 M:     Arnaud Pouliquen <arnaud.pouliquen@st.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Maintained
-F:     Documentation/devicetree/bindings/sound/st,stm32-*.txt
+F:     Documentation/devicetree/bindings/iio/adc/st,stm32-*.yaml
 F:     sound/soc/stm/
 
 STM32 TIMER/LPTIMER DRIVERS
@@ -17541,7 +17576,7 @@ F:      arch/xtensa/
 F:     drivers/irqchip/irq-xtensa-*
 
 TEXAS INSTRUMENTS ASoC DRIVERS
-M:     Peter Ujfalusi <peter.ujfalusi@ti.com>
+M:     Peter Ujfalusi <peter.ujfalusi@gmail.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Maintained
 F:     sound/soc/ti/
@@ -17553,6 +17588,19 @@ S:     Supported
 F:     Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt
 F:     drivers/iio/dac/ti-dac7612.c
 
+TEXAS INSTRUMENTS DMA DRIVERS
+M:     Peter Ujfalusi <peter.ujfalusi@gmail.com>
+L:     dmaengine@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/dma/ti-dma-crossbar.txt
+F:     Documentation/devicetree/bindings/dma/ti-edma.txt
+F:     Documentation/devicetree/bindings/dma/ti/
+F:     drivers/dma/ti/
+X:     drivers/dma/ti/cppi41.c
+F:     include/linux/dma/k3-udma-glue.h
+F:     include/linux/dma/ti-cppi5.h
+F:     include/linux/dma/k3-psil.h
+
 TEXAS INSTRUMENTS' SYSTEM CONTROL INTERFACE (TISCI) PROTOCOL DRIVER
 M:     Nishanth Menon <nm@ti.com>
 M:     Tero Kristo <t-kristo@ti.com>
@@ -17827,7 +17875,7 @@ M:      Dan Murphy <dmurphy@ti.com>
 L:     linux-can@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/net/can/tcan4x5x.txt
-F:     drivers/net/can/m_can/tcan4x5x.c
+F:     drivers/net/can/m_can/tcan4x5x*
 
 TI TRF7970A NFC DRIVER
 M:     Mark Greer <mgreer@animalcreek.com>
@@ -17838,7 +17886,7 @@ F:      Documentation/devicetree/bindings/net/nfc/trf7970a.txt
 F:     drivers/nfc/trf7970a.c
 
 TI TWL4030 SERIES SOC CODEC DRIVER
-M:     Peter Ujfalusi <peter.ujfalusi@ti.com>
+M:     Peter Ujfalusi <peter.ujfalusi@gmail.com>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:     Maintained
 F:     sound/soc/codecs/twl4030*
@@ -18370,7 +18418,7 @@ F:      include/linux/usb/isp116x.h
 
 USB LAN78XX ETHERNET DRIVER
 M:     Woojung Huh <woojung.huh@microchip.com>
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+M:     UNGLinuxDriver@microchip.com
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     Documentation/devicetree/bindings/net/microchip,lan78xx.txt
@@ -18404,7 +18452,7 @@ F:      Documentation/usb/ohci.rst
 F:     drivers/usb/host/ohci*
 
 USB OTG FSM (Finite State Machine)
-M:     Peter Chen <Peter.Chen@nxp.com>
+M:     Peter Chen <peter.chen@kernel.org>
 L:     linux-usb@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
@@ -18484,7 +18532,7 @@ F:      drivers/net/usb/smsc75xx.*
 
 USB SMSC95XX ETHERNET DRIVER
 M:     Steve Glendinning <steve.glendinning@shawell.net>
-M:     Microchip Linux Driver Support <UNGLinuxDriver@microchip.com>
+M:     UNGLinuxDriver@microchip.com
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/usb/smsc95xx.*
@@ -19031,7 +19079,7 @@ F:      drivers/input/mouse/vmmouse.h
 
 VMWARE VMXNET3 ETHERNET DRIVER
 M:     Ronak Doshi <doshir@vmware.com>
-M:     "VMware, Inc." <pv-drivers@vmware.com>
+M:     pv-drivers@vmware.com
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     drivers/net/vmxnet3/
@@ -19058,7 +19106,6 @@ K:      regulator_get_optional
 
 VRF
 M:     David Ahern <dsahern@kernel.org>
-M:     Shrijeet Mukherjee <shrijeet@gmail.com>
 L:     netdev@vger.kernel.org
 S:     Maintained
 F:     Documentation/networking/vrf.rst
@@ -19409,7 +19456,7 @@ F:      drivers/net/ethernet/*/*/*xdp*
 K:     (?:\b|_)xdp(?:\b|_)
 
 XDP SOCKETS (AF_XDP)
-M:     Björn Töpel <bjorn.topel@intel.com>
+M:     Björn Töpel <bjorn@kernel.org>
 M:     Magnus Karlsson <magnus.karlsson@intel.com>
 R:     Jonathan Lemon <jonathan.lemon@gmail.com>
 L:     netdev@vger.kernel.org
@@ -19505,7 +19552,7 @@ F:      arch/x86/xen/*swiotlb*
 F:     drivers/xen/*swiotlb*
 
 XFS FILESYSTEM
-M:     Darrick J. Wong <darrick.wong@oracle.com>
+M:     Darrick J. Wong <djwong@kernel.org>
 M:     linux-xfs@vger.kernel.org
 L:     linux-xfs@vger.kernel.org
 S:     Supported
index 3d328b7..917f536 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 VERSION = 5
 PATCHLEVEL = 11
 SUBLEVEL = 0
-EXTRAVERSION = -rc1
+EXTRAVERSION = -rc5
 NAME = Kleptomaniac Octopus
 
 # *DOCUMENTATION*
@@ -649,7 +649,8 @@ ifeq ($(KBUILD_EXTMOD),)
 core-y         := init/ usr/
 drivers-y      := drivers/ sound/
 drivers-$(CONFIG_SAMPLES) += samples/
-drivers-y      += net/ virt/
+drivers-$(CONFIG_NET) += net/
+drivers-y      += virt/
 libs-y         := lib/
 endif # KBUILD_EXTMOD
 
index 78c6f05..24862d1 100644 (file)
@@ -1105,6 +1105,12 @@ config HAVE_ARCH_PFN_VALID
 config ARCH_SUPPORTS_DEBUG_PAGEALLOC
        bool
 
+config ARCH_SPLIT_ARG64
+       bool
+       help
+          If a 32-bit architecture requires 64-bit arguments to be split into
+          pairs of 32-bit arguments, select this option.
+
 source "kernel/gcov/Kconfig"
 
 source "scripts/gcc-plugins/Kconfig"
diff --git a/arch/alpha/include/asm/local64.h b/arch/alpha/include/asm/local64.h
deleted file mode 100644 (file)
index 36c93b5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
index 0c6bf0d..578bdbb 100644 (file)
@@ -102,16 +102,22 @@ libs-y            += arch/arc/lib/ $(LIBGCC)
 
 boot           := arch/arc/boot
 
-#default target for make without any arguments.
-KBUILD_IMAGE   := $(boot)/bootpImage
-
-all:   bootpImage
-bootpImage: vmlinux
-
-boot_targets += uImage uImage.bin uImage.gz
+boot_targets := uImage.bin uImage.gz uImage.lzma
 
+PHONY += $(boot_targets)
 $(boot_targets): vmlinux
        $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
 
+uimage-default-y                       := uImage.bin
+uimage-default-$(CONFIG_KERNEL_GZIP)   := uImage.gz
+uimage-default-$(CONFIG_KERNEL_LZMA)   := uImage.lzma
+
+PHONY += uImage
+uImage: $(uimage-default-y)
+       @ln -sf $< $(boot)/uImage
+       @$(kecho) '  Image $(boot)/uImage is ready'
+
+CLEAN_FILES += $(boot)/uImage
+
 archclean:
        $(Q)$(MAKE) $(clean)=$(boot)
index 538b92f..5648748 100644 (file)
@@ -1,5 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-targets := vmlinux.bin vmlinux.bin.gz uImage
 
 # uImage build relies on mkimage being availble on your host for ARC target
 # You will need to build u-boot for ARC, rename mkimage to arc-elf32-mkimage
@@ -7,23 +6,18 @@ targets := vmlinux.bin vmlinux.bin.gz uImage
 
 OBJCOPYFLAGS= -O binary -R .note -R .note.gnu.build-id -R .comment -S
 
-LINUX_START_TEXT = $$(readelf -h vmlinux | \
+LINUX_START_TEXT = $$($(READELF) -h vmlinux | \
                        grep "Entry point address" | grep -o 0x.*)
 
 UIMAGE_LOADADDR    = $(CONFIG_LINUX_LINK_BASE)
 UIMAGE_ENTRYADDR   = $(LINUX_START_TEXT)
 
-suffix-y := bin
-suffix-$(CONFIG_KERNEL_GZIP)   := gz
-suffix-$(CONFIG_KERNEL_LZMA)   := lzma
-
-targets += uImage
+targets += vmlinux.bin
+targets += vmlinux.bin.gz
+targets += vmlinux.bin.lzma
 targets += uImage.bin
 targets += uImage.gz
 targets += uImage.lzma
-extra-y += vmlinux.bin
-extra-y += vmlinux.bin.gz
-extra-y += vmlinux.bin.lzma
 
 $(obj)/vmlinux.bin: vmlinux FORCE
        $(call if_changed,objcopy)
@@ -42,7 +36,3 @@ $(obj)/uImage.gz: $(obj)/vmlinux.bin.gz FORCE
 
 $(obj)/uImage.lzma: $(obj)/vmlinux.bin.lzma FORCE
        $(call if_changed,uimage,lzma)
-
-$(obj)/uImage: $(obj)/uImage.$(suffix-y)
-       @ln -sf $(notdir $<) $@
-       @echo '  Image $@ is ready'
index 81f4ede..3c1afa5 100644 (file)
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 generic-y += extable.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += parport.h
 generic-y += user.h
index 23e41e8..ad9b7fe 100644 (file)
@@ -10,6 +10,7 @@
 #ifndef __ASSEMBLY__
 
 #define clear_page(paddr)              memset((paddr), 0, PAGE_SIZE)
+#define copy_user_page(to, from, vaddr, pg)    copy_page(to, from)
 #define copy_page(to, from)            memcpy((to), (from), PAGE_SIZE)
 
 struct vm_area_struct;
index 1f5308a..1743506 100644 (file)
@@ -307,7 +307,7 @@ resume_user_mode_begin:
        mov r0, sp      ; pt_regs for arg to do_signal()/do_notify_resume()
 
        GET_CURR_THR_INFO_FLAGS   r9
-       and.f  0,  r9, TIF_SIGPENDING|TIF_NOTIFY_SIGNAL
+       and.f  0,  r9, _TIF_SIGPENDING|_TIF_NOTIFY_SIGNAL
        bz .Lchk_notify_resume
 
        ; Normal Trap/IRQ entry only saves Scratch (caller-saved) regs
index 6b5c545..a2d10c2 100644 (file)
@@ -7,6 +7,7 @@ menuconfig ARC_SOC_HSDK
        depends on ISA_ARCV2
        select ARC_HAS_ACCL_REGS
        select ARC_IRQ_NO_AUTOSAVE
+       select ARC_FPU_SAVE_RESTORE
        select CLK_HSDK
        select RESET_CONTROLLER
        select RESET_HSDK
index 861e05d..343364d 100644 (file)
                stdout-path = &uart1;
        };
 
+       aliases {
+               mmc0 = &usdhc2;
+               mmc1 = &usdhc3;
+               mmc2 = &usdhc4;
+               /delete-property/ mmc3;
+       };
+
        memory@10000000 {
                device_type = "memory";
                reg = <0x10000000 0x80000000>;
index 736074f..959d8ac 100644 (file)
 
                        /* VDD_AUD_1P8: Audio codec */
                        reg_aud_1p8v: ldo3 {
-                               regulator-name = "vdd1p8";
+                               regulator-name = "vdd1p8a";
                                regulator-min-microvolt = <1800000>;
                                regulator-max-microvolt = <1800000>;
                                regulator-boot-on;
index d6df598..b167b33 100644 (file)
 
        lcd_backlight: lcd-backlight {
                compatible = "pwm-backlight";
-               pwms = <&pwm4 0 5000000>;
+               pwms = <&pwm4 0 5000000 0>;
                pwm-names = "LCD_BKLT_PWM";
 
                brightness-levels = <0 10 20 30 40 50 60 70 80 90 100>;
                i2c-gpio,delay-us = <2>; /* ~100 kHz */
                #address-cells = <1>;
                #size-cells = <0>;
-               status = "disabld";
+               status = "disabled";
        };
 
        i2c_cam: i2c-gpio-cam {
                i2c-gpio,delay-us = <2>; /* ~100 kHz */
                #address-cells = <1>;
                #size-cells = <0>;
-               status = "disabld";
+               status = "disabled";
        };
 };
 
index b065778..7e4e5fd 100644 (file)
@@ -53,7 +53,6 @@
 &fec {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_microsom_enet_ar8035>;
-       phy-handle = <&phy>;
        phy-mode = "rgmii-id";
        phy-reset-duration = <2>;
        phy-reset-gpios = <&gpio4 15 GPIO_ACTIVE_LOW>;
                #address-cells = <1>;
                #size-cells = <0>;
 
-               phy: ethernet-phy@0 {
+               /*
+                * The PHY can appear at either address 0 or 4 due to the
+                * configuration (LED) pin not being pulled sufficiently.
+                */
+               ethernet-phy@0 {
                        reg = <0>;
                        qca,clk-out-frequency = <125000000>;
                };
+
+               ethernet-phy@4 {
+                       reg = <4>;
+                       qca,clk-out-frequency = <125000000>;
+               };
        };
 };
 
index 84b0952..bd6b528 100644 (file)
                compatible = "nxp,pcf2127";
                reg = <0>;
                spi-max-frequency = <2000000>;
+               reset-source;
        };
 };
 
index 11d41e8..7dde9fb 100644 (file)
                clock-names = "sysclk";
        };
 };
+
+&aes1_target {
+       status = "disabled";
+};
+
+&aes2_target {
+       status = "disabled";
+};
index c4c6c7e..5898879 100644 (file)
                emac: gem@30000 {
                        compatible = "cadence,gem";
                        reg = <0x30000 0x10000>;
+                       interrupt-parent = <&vic0>;
                        interrupts = <31>;
                };
 
                dmac1: dmac@40000 {
                        compatible = "snps,dw-dmac";
                        reg = <0x40000 0x10000>;
+                       interrupt-parent = <&vic0>;
                        interrupts = <25>;
                };
 
                dmac2: dmac@50000 {
                        compatible = "snps,dw-dmac";
                        reg = <0x50000 0x10000>;
+                       interrupt-parent = <&vic0>;
                        interrupts = <26>;
                };
 
                axi2pico@c0000000 {
                        compatible = "picochip,axi2pico-pc3x2";
                        reg = <0xc0000000 0x10000>;
+                       interrupt-parent = <&vic0>;
                        interrupts = <13 14 15 16 17 18 19 20 21>;
                };
        };
index d309fad..344d298 100644 (file)
                                            200000 0>;
                };
        };
+
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               /* Modem trace memory */
+               ram@06000000 {
+                       reg = <0x06000000 0x00f00000>;
+                       no-map;
+               };
+
+               /* Modem shared memory */
+               ram@06f00000 {
+                       reg = <0x06f00000 0x00100000>;
+                       no-map;
+               };
+
+               /* Modem private memory */
+               ram@07000000 {
+                       reg = <0x07000000 0x01000000>;
+                       no-map;
+               };
+
+               /*
+                * Initial Secure Software ISSW memory
+                *
+                * This is probably only used if the kernel tries
+                * to actually call into trustzone to run secure
+                * applications, which the mainline kernel probably
+                * will not do on this old chipset. But you can never
+                * be too careful, so reserve this memory anyway.
+                */
+               ram@17f00000 {
+                       reg = <0x17f00000 0x00100000>;
+                       no-map;
+               };
+       };
 };
index 48bd872..287804e 100644 (file)
                                            200000 0>;
                };
        };
+
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               /* Modem trace memory */
+               ram@06000000 {
+                       reg = <0x06000000 0x00f00000>;
+                       no-map;
+               };
+
+               /* Modem shared memory */
+               ram@06f00000 {
+                       reg = <0x06f00000 0x00100000>;
+                       no-map;
+               };
+
+               /* Modem private memory */
+               ram@07000000 {
+                       reg = <0x07000000 0x01000000>;
+                       no-map;
+               };
+
+               /*
+                * Initial Secure Software ISSW memory
+                *
+                * This is probably only used if the kernel tries
+                * to actually call into trustzone to run secure
+                * applications, which the mainline kernel probably
+                * will not do on this old chipset. But you can never
+                * be too careful, so reserve this memory anyway.
+                */
+               ram@17f00000 {
+                       reg = <0x17f00000 0x00100000>;
+                       no-map;
+               };
+       };
 };
diff --git a/arch/arm/boot/dts/ste-db9500.dtsi b/arch/arm/boot/dts/ste-db9500.dtsi
new file mode 100644 (file)
index 0000000..0afff70
--- /dev/null
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "ste-dbx5x0.dtsi"
+
+/ {
+       cpus {
+               cpu@300 {
+                       /* cpufreq controls */
+                       operating-points = <1152000 0
+                                           800000 0
+                                           400000 0
+                                           200000 0>;
+               };
+       };
+
+       reserved-memory {
+               #address-cells = <1>;
+               #size-cells = <1>;
+               ranges;
+
+               /*
+                * Initial Secure Software ISSW memory
+                *
+                * This is probably only used if the kernel tries
+                * to actually call into trustzone to run secure
+                * applications, which the mainline kernel probably
+                * will not do on this old chipset. But you can never
+                * be too careful, so reserve this memory anyway.
+                */
+               ram@17f00000 {
+                       reg = <0x17f00000 0x00100000>;
+                       no-map;
+               };
+       };
+};
index be90e73..27d8a07 100644 (file)
@@ -4,7 +4,7 @@
  */
 
 /dts-v1/;
-#include "ste-db8500.dtsi"
+#include "ste-db9500.dtsi"
 #include "ste-href-ab8500.dtsi"
 #include "ste-href-family-pinctrl.dtsi"
 
index 496f9d3..60fe618 100644 (file)
                                panel@0 {
                                        compatible = "samsung,s6e63m0";
                                        reg = <0>;
+                                       max-brightness = <15>;
                                        vdd3-supply = <&panel_reg_3v0>;
                                        vci-supply = <&panel_reg_1v8>;
                                        reset-gpios = <&gpio4 11 GPIO_ACTIVE_LOW>;
index 1c11d15..b515c31 100644 (file)
@@ -279,6 +279,7 @@ CONFIG_SERIAL_OMAP_CONSOLE=y
 CONFIG_SERIAL_DEV_BUS=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_SPI=y
+CONFIG_SPI_GPIO=m
 CONFIG_SPI_OMAP24XX=y
 CONFIG_SPI_TI_QSPI=m
 CONFIG_HSI=m
@@ -296,7 +297,6 @@ CONFIG_GPIO_TWL4030=y
 CONFIG_W1=m
 CONFIG_HDQ_MASTER_OMAP=m
 CONFIG_W1_SLAVE_DS250X=m
-CONFIG_POWER_AVS=y
 CONFIG_POWER_RESET=y
 CONFIG_POWER_RESET_GPIO=y
 CONFIG_BATTERY_BQ27XXX=m
index 7b5cf84..cdde8fd 100644 (file)
@@ -60,6 +60,7 @@ static void chacha_doneon(u32 *state, u8 *dst, const u8 *src,
                chacha_block_xor_neon(state, d, s, nrounds);
                if (d != dst)
                        memcpy(dst, buf, bytes);
+               state[12]++;
        }
 }
 
index 4a0848a..03657ff 100644 (file)
@@ -2,7 +2,6 @@
 generic-y += early_ioremap.h
 generic-y += extable.h
 generic-y += flat.h
-generic-y += local64.h
 generic-y += parport.h
 
 generated-y += mach-types.h
index 1eabf2d..e06f946 100644 (file)
@@ -67,6 +67,7 @@
 #define MX6Q_CCM_CCR   0x0
 
        .align 3
+       .arm
 
        .macro  sync_l2_cache
 
index f319170..56d6814 100644 (file)
@@ -230,10 +230,12 @@ static int _omap_device_notifier_call(struct notifier_block *nb,
                break;
        case BUS_NOTIFY_BIND_DRIVER:
                od = to_omap_device(pdev);
-               if (od && (od->_state == OMAP_DEVICE_STATE_ENABLED) &&
-                   pm_runtime_status_suspended(dev)) {
+               if (od) {
                        od->_driver_status = BUS_NOTIFY_BIND_DRIVER;
-                       pm_runtime_set_active(dev);
+                       if (od->_state == OMAP_DEVICE_STATE_ENABLED &&
+                           pm_runtime_status_suspended(dev)) {
+                               pm_runtime_set_active(dev);
+                       }
                }
                break;
        case BUS_NOTIFY_ADD_DEVICE:
index eab281a..09076ad 100644 (file)
@@ -71,7 +71,7 @@ static struct omap_voltdm_pmic omap_cpcap_iva = {
        .vp_vstepmin = OMAP4_VP_VSTEPMIN_VSTEPMIN,
        .vp_vstepmax = OMAP4_VP_VSTEPMAX_VSTEPMAX,
        .vddmin = 900000,
-       .vddmax = 1350000,
+       .vddmax = 1375000,
        .vp_timeout_us = OMAP4_VP_VLIMITTO_TIMEOUT_US,
        .i2c_slave_addr = 0x44,
        .volt_reg_addr = 0x0,
index 0207b6e..897634d 100644 (file)
@@ -1620,10 +1620,9 @@ exit:
                }
                emit_str_r(dst_lo, tmp2, off, ctx, BPF_SIZE(code));
                break;
-       /* STX XADD: lock *(u32 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_W:
-       /* STX XADD: lock *(u64 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_DW:
+       /* Atomic ops */
+       case BPF_STX | BPF_ATOMIC | BPF_W:
+       case BPF_STX | BPF_ATOMIC | BPF_DW:
                goto notyet;
        /* STX: *(size *)(dst + off) = src */
        case BPF_STX | BPF_MEM | BPF_W:
index 60e901c..5a957a9 100644 (file)
@@ -371,7 +371,7 @@ static int __init xen_guest_init(void)
        }
        gnttab_init();
        if (!xen_initial_domain())
-               xenbus_probe(NULL);
+               xenbus_probe();
 
        /*
         * Making sure board specific code will not set up ops for
index 05e1735..f39568b 100644 (file)
@@ -174,8 +174,6 @@ config ARM64
        select HAVE_NMI
        select HAVE_PATA_PLATFORM
        select HAVE_PERF_EVENTS
-       select HAVE_PERF_EVENTS_NMI if ARM64_PSEUDO_NMI && HW_PERF_EVENTS
-       select HAVE_HARDLOCKUP_DETECTOR_PERF if PERF_EVENTS && HAVE_PERF_EVENTS_NMI
        select HAVE_PERF_REGS
        select HAVE_PERF_USER_STACK_DUMP
        select HAVE_REGS_AND_STACK_ACCESS_API
index 6be9b37..9030920 100644 (file)
@@ -10,7 +10,7 @@
 #
 # Copyright (C) 1995-2001 by Russell King
 
-LDFLAGS_vmlinux        :=--no-undefined -X -z norelro
+LDFLAGS_vmlinux        :=--no-undefined -X
 
 ifeq ($(CONFIG_RELOCATABLE), y)
 # Pass --no-apply-dynamic-relocs to restore pre-binutils-2.27 behaviour
@@ -115,16 +115,20 @@ KBUILD_CPPFLAGS   += -mbig-endian
 CHECKFLAGS     += -D__AARCH64EB__
 # Prefer the baremetal ELF build target, but not all toolchains include
 # it so fall back to the standard linux version if needed.
-KBUILD_LDFLAGS += -EB $(call ld-option, -maarch64elfb, -maarch64linuxb)
+KBUILD_LDFLAGS += -EB $(call ld-option, -maarch64elfb, -maarch64linuxb -z norelro)
 UTS_MACHINE    := aarch64_be
 else
 KBUILD_CPPFLAGS        += -mlittle-endian
 CHECKFLAGS     += -D__AARCH64EL__
 # Same as above, prefer ELF but fall back to linux target if needed.
-KBUILD_LDFLAGS += -EL $(call ld-option, -maarch64elf, -maarch64linux)
+KBUILD_LDFLAGS += -EL $(call ld-option, -maarch64elf, -maarch64linux -z norelro)
 UTS_MACHINE    := aarch64
 endif
 
+ifeq ($(CONFIG_LD_IS_LLD), y)
+KBUILD_LDFLAGS += -z norelro
+endif
+
 CHECKFLAGS     += -D__aarch64__
 
 ifeq ($(CONFIG_DYNAMIC_FTRACE_WITH_REGS),y)
index fa6e690..53a9b76 100644 (file)
                                compatible = "snps,dw-apb-gpio-port";
                                gpio-controller;
                                #gpio-cells = <2>;
-                               snps,nr-gpios = <32>;
+                               ngpios = <32>;
                                reg = <0>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
                                compatible = "snps,dw-apb-gpio-port";
                                gpio-controller;
                                #gpio-cells = <2>;
-                               snps,nr-gpios = <32>;
+                               ngpios = <32>;
                                reg = <0>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
                                compatible = "snps,dw-apb-gpio-port";
                                gpio-controller;
                                #gpio-cells = <2>;
-                               snps,nr-gpios = <8>;
+                               ngpios = <8>;
                                reg = <0>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
index aef8f2b..5401a64 100644 (file)
@@ -4,11 +4,16 @@
  */
        usb {
                compatible = "simple-bus";
-               dma-ranges;
                #address-cells = <2>;
                #size-cells = <2>;
                ranges = <0x0 0x0 0x0 0x68500000 0x0 0x00400000>;
 
+               /*
+                * Internally, USB bus to the interconnect can only address up
+                * to 40-bit
+                */
+               dma-ranges = <0 0 0 0 0x100 0x0>;
+
                usbphy0: usb-phy@0 {
                        compatible = "brcm,sr-usb-combo-phy";
                        reg = <0x0 0x00000000 0x0 0x100>;
index 60ff19f..6c8a61c 100644 (file)
        reboot {
                compatible ="syscon-reboot";
                regmap = <&rst>;
-               offset = <0xb0>;
+               offset = <0>;
                mask = <0x02>;
        };
 
index ee17902..2a79e89 100644 (file)
                        #size-cells = <1>;
                        ranges;
 
-                       spba: bus@30000000 {
+                       spba: spba-bus@30000000 {
                                compatible = "fsl,spba-bus", "simple-bus";
                                #address-cells = <1>;
                                #size-cells = <1>;
index ecccfbb..23f5a5e 100644 (file)
                                #gpio-cells = <2>;
                                interrupt-controller;
                                #interrupt-cells = <2>;
-                               gpio-ranges = <&iomuxc 0 56 26>, <&iomuxc 0 144 4>;
+                               gpio-ranges = <&iomuxc 0 56 26>, <&iomuxc 26 144 4>;
                        };
 
                        gpio4: gpio@30230000 {
index 22b832f..003309f 100644 (file)
                        qcom,smem-state-names = "ipa-clock-enabled-valid",
                                                "ipa-clock-enabled";
 
-                       modem-remoteproc = <&remoteproc_mpss>;
-
                        status = "disabled";
                };
 
index bcf8883..04b2490 100644 (file)
                        qcom,smem-state-names = "ipa-clock-enabled-valid",
                                                "ipa-clock-enabled";
 
-                       modem-remoteproc = <&mss_pil>;
-
                        status = "disabled";
                };
 
index 8383016..a0bcf02 100644 (file)
@@ -991,8 +991,6 @@ CONFIG_ARCH_TEGRA_210_SOC=y
 CONFIG_ARCH_TEGRA_186_SOC=y
 CONFIG_ARCH_TEGRA_194_SOC=y
 CONFIG_ARCH_TEGRA_234_SOC=y
-CONFIG_ARCH_K3_AM6_SOC=y
-CONFIG_ARCH_K3_J721E_SOC=y
 CONFIG_TI_SCI_PM_DOMAINS=y
 CONFIG_EXTCON_PTN5150=m
 CONFIG_EXTCON_USB_GPIO=y
index ff9cbb6..07ac208 100644 (file)
@@ -1,6 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 generic-y += early_ioremap.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += qrwlock.h
 generic-y += qspinlock.h
index 015ddff..b56a4b2 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/lse.h>
 
 #define ATOMIC_OP(op)                                                  \
-static inline void arch_##op(int i, atomic_t *v)                       \
+static __always_inline void arch_##op(int i, atomic_t *v)              \
 {                                                                      \
        __lse_ll_sc_body(op, i, v);                                     \
 }
@@ -32,7 +32,7 @@ ATOMIC_OP(atomic_sub)
 #undef ATOMIC_OP
 
 #define ATOMIC_FETCH_OP(name, op)                                      \
-static inline int arch_##op##name(int i, atomic_t *v)                  \
+static __always_inline int arch_##op##name(int i, atomic_t *v)         \
 {                                                                      \
        return __lse_ll_sc_body(op##name, i, v);                        \
 }
@@ -56,7 +56,7 @@ ATOMIC_FETCH_OPS(atomic_sub_return)
 #undef ATOMIC_FETCH_OPS
 
 #define ATOMIC64_OP(op)                                                        \
-static inline void arch_##op(long i, atomic64_t *v)                    \
+static __always_inline void arch_##op(long i, atomic64_t *v)           \
 {                                                                      \
        __lse_ll_sc_body(op, i, v);                                     \
 }
@@ -71,7 +71,7 @@ ATOMIC64_OP(atomic64_sub)
 #undef ATOMIC64_OP
 
 #define ATOMIC64_FETCH_OP(name, op)                                    \
-static inline long arch_##op##name(long i, atomic64_t *v)              \
+static __always_inline long arch_##op##name(long i, atomic64_t *v)     \
 {                                                                      \
        return __lse_ll_sc_body(op##name, i, v);                        \
 }
@@ -94,7 +94,7 @@ ATOMIC64_FETCH_OPS(atomic64_sub_return)
 #undef ATOMIC64_FETCH_OP
 #undef ATOMIC64_FETCH_OPS
 
-static inline long arch_atomic64_dec_if_positive(atomic64_t *v)
+static __always_inline long arch_atomic64_dec_if_positive(atomic64_t *v)
 {
        return __lse_ll_sc_body(atomic64_dec_if_positive, v);
 }
index 11beda8..8fcfab0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/jump_label.h>
 #include <linux/kvm_types.h>
 #include <linux/percpu.h>
+#include <linux/psci.h>
 #include <asm/arch_gicv3.h>
 #include <asm/barrier.h>
 #include <asm/cpufeature.h>
@@ -240,6 +241,28 @@ struct kvm_host_data {
        struct kvm_pmu_events pmu_events;
 };
 
+struct kvm_host_psci_config {
+       /* PSCI version used by host. */
+       u32 version;
+
+       /* Function IDs used by host if version is v0.1. */
+       struct psci_0_1_function_ids function_ids_0_1;
+
+       bool psci_0_1_cpu_suspend_implemented;
+       bool psci_0_1_cpu_on_implemented;
+       bool psci_0_1_cpu_off_implemented;
+       bool psci_0_1_migrate_implemented;
+};
+
+extern struct kvm_host_psci_config kvm_nvhe_sym(kvm_host_psci_config);
+#define kvm_host_psci_config CHOOSE_NVHE_SYM(kvm_host_psci_config)
+
+extern s64 kvm_nvhe_sym(hyp_physvirt_offset);
+#define hyp_physvirt_offset CHOOSE_NVHE_SYM(hyp_physvirt_offset)
+
+extern u64 kvm_nvhe_sym(hyp_cpu_logical_map)[NR_CPUS];
+#define hyp_cpu_logical_map CHOOSE_NVHE_SYM(hyp_cpu_logical_map)
+
 struct vcpu_reset_state {
        unsigned long   pc;
        unsigned long   r0;
index 6f986e0..f0fe0cc 100644 (file)
@@ -176,10 +176,21 @@ static inline void __uaccess_enable_hw_pan(void)
  * The Tag check override (TCO) bit disables temporarily the tag checking
  * preventing the issue.
  */
-static inline void uaccess_disable_privileged(void)
+static inline void __uaccess_disable_tco(void)
 {
        asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(0),
                                 ARM64_MTE, CONFIG_KASAN_HW_TAGS));
+}
+
+static inline void __uaccess_enable_tco(void)
+{
+       asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(1),
+                                ARM64_MTE, CONFIG_KASAN_HW_TAGS));
+}
+
+static inline void uaccess_disable_privileged(void)
+{
+       __uaccess_disable_tco();
 
        if (uaccess_ttbr0_disable())
                return;
@@ -189,8 +200,7 @@ static inline void uaccess_disable_privileged(void)
 
 static inline void uaccess_enable_privileged(void)
 {
-       asm volatile(ALTERNATIVE("nop", SET_PSTATE_TCO(1),
-                                ARM64_MTE, CONFIG_KASAN_HW_TAGS));
+       __uaccess_enable_tco();
 
        if (uaccess_ttbr0_enable())
                return;
index f42fd9e..3017844 100644 (file)
@@ -75,7 +75,7 @@ int main(void)
   DEFINE(S_SDEI_TTBR1,         offsetof(struct pt_regs, sdei_ttbr1));
   DEFINE(S_PMR_SAVE,           offsetof(struct pt_regs, pmr_save));
   DEFINE(S_STACKFRAME,         offsetof(struct pt_regs, stackframe));
-  DEFINE(S_FRAME_SIZE,         sizeof(struct pt_regs));
+  DEFINE(PT_REGS_SIZE,         sizeof(struct pt_regs));
   BLANK();
 #ifdef CONFIG_COMPAT
   DEFINE(COMPAT_SIGFRAME_REGS_OFFSET,          offsetof(struct compat_sigframe, uc.uc_mcontext.arm_r0));
index 7ffb5f1..e99edde 100644 (file)
@@ -2568,7 +2568,7 @@ static void verify_hyp_capabilities(void)
        int parange, ipa_max;
        unsigned int safe_vmid_bits, vmid_bits;
 
-       if (!IS_ENABLED(CONFIG_KVM) || !IS_ENABLED(CONFIG_KVM_ARM_HOST))
+       if (!IS_ENABLED(CONFIG_KVM))
                return;
 
        safe_mmfr1 = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
index a338f40..b3e4f9a 100644 (file)
@@ -35,7 +35,7 @@
  */
        .macro  ftrace_regs_entry, allregs=0
        /* Make room for pt_regs, plus a callee frame */
-       sub     sp, sp, #(S_FRAME_SIZE + 16)
+       sub     sp, sp, #(PT_REGS_SIZE + 16)
 
        /* Save function arguments (and x9 for simplicity) */
        stp     x0, x1, [sp, #S_X0]
        .endif
 
        /* Save the callsite's SP and LR */
-       add     x10, sp, #(S_FRAME_SIZE + 16)
+       add     x10, sp, #(PT_REGS_SIZE + 16)
        stp     x9, x10, [sp, #S_LR]
 
        /* Save the PC after the ftrace callsite */
        str     x30, [sp, #S_PC]
 
        /* Create a frame record for the callsite above pt_regs */
-       stp     x29, x9, [sp, #S_FRAME_SIZE]
-       add     x29, sp, #S_FRAME_SIZE
+       stp     x29, x9, [sp, #PT_REGS_SIZE]
+       add     x29, sp, #PT_REGS_SIZE
 
        /* Create our frame record within pt_regs. */
        stp     x29, x30, [sp, #S_STACKFRAME]
@@ -120,7 +120,7 @@ ftrace_common_return:
        ldr     x9, [sp, #S_PC]
 
        /* Restore the callsite's SP */
-       add     sp, sp, #S_FRAME_SIZE + 16
+       add     sp, sp, #PT_REGS_SIZE + 16
 
        ret     x9
 SYM_CODE_END(ftrace_common)
@@ -130,7 +130,7 @@ SYM_CODE_START(ftrace_graph_caller)
        ldr     x0, [sp, #S_PC]
        sub     x0, x0, #AARCH64_INSN_SIZE      // ip (callsite's BL insn)
        add     x1, sp, #S_LR                   // parent_ip (callsite's LR)
-       ldr     x2, [sp, #S_FRAME_SIZE]         // parent fp (callsite's FP)
+       ldr     x2, [sp, #PT_REGS_SIZE]         // parent fp (callsite's FP)
        bl      prepare_ftrace_return
        b       ftrace_common_return
 SYM_CODE_END(ftrace_graph_caller)
index 2a93fa5..c9bae73 100644 (file)
@@ -75,7 +75,7 @@ alternative_else_nop_endif
        .endif
 #endif
 
-       sub     sp, sp, #S_FRAME_SIZE
+       sub     sp, sp, #PT_REGS_SIZE
 #ifdef CONFIG_VMAP_STACK
        /*
         * Test whether the SP has overflowed, without corrupting a GPR.
@@ -96,7 +96,7 @@ alternative_else_nop_endif
         * userspace, and can clobber EL0 registers to free up GPRs.
         */
 
-       /* Stash the original SP (minus S_FRAME_SIZE) in tpidr_el0. */
+       /* Stash the original SP (minus PT_REGS_SIZE) in tpidr_el0. */
        msr     tpidr_el0, x0
 
        /* Recover the original x0 value and stash it in tpidrro_el0 */
@@ -182,7 +182,6 @@ alternative_else_nop_endif
        mrs_s   \tmp2, SYS_GCR_EL1
        bfi     \tmp2, \tmp, #0, #16
        msr_s   SYS_GCR_EL1, \tmp2
-       isb
 #endif
        .endm
 
@@ -194,6 +193,7 @@ alternative_else_nop_endif
        ldr_l   \tmp, gcr_kernel_excl
 
        mte_set_gcr \tmp, \tmp2
+       isb
 1:
 #endif
        .endm
@@ -253,7 +253,7 @@ alternative_else_nop_endif
 
        scs_load tsk, x20
        .else
-       add     x21, sp, #S_FRAME_SIZE
+       add     x21, sp, #PT_REGS_SIZE
        get_current_task tsk
        .endif /* \el == 0 */
        mrs     x22, elr_el1
@@ -377,7 +377,7 @@ alternative_else_nop_endif
        ldp     x26, x27, [sp, #16 * 13]
        ldp     x28, x29, [sp, #16 * 14]
        ldr     lr, [sp, #S_LR]
-       add     sp, sp, #S_FRAME_SIZE           // restore sp
+       add     sp, sp, #PT_REGS_SIZE           // restore sp
 
        .if     \el == 0
 alternative_insn eret, nop, ARM64_UNMAP_KERNEL_AT_EL0
@@ -580,12 +580,12 @@ __bad_stack:
 
        /*
         * Store the original GPRs to the new stack. The orginal SP (minus
-        * S_FRAME_SIZE) was stashed in tpidr_el0 by kernel_ventry.
+        * PT_REGS_SIZE) was stashed in tpidr_el0 by kernel_ventry.
         */
-       sub     sp, sp, #S_FRAME_SIZE
+       sub     sp, sp, #PT_REGS_SIZE
        kernel_entry 1
        mrs     x0, tpidr_el0
-       add     x0, x0, #S_FRAME_SIZE
+       add     x0, x0, #PT_REGS_SIZE
        str     x0, [sp, #S_SP]
 
        /* Stash the regs for handle_bad_stack */
index 38bb07e..3605f77 100644 (file)
@@ -23,8 +23,6 @@
 #include <linux/platform_device.h>
 #include <linux/sched_clock.h>
 #include <linux/smp.h>
-#include <linux/nmi.h>
-#include <linux/cpufreq.h>
 
 /* ARMv8 Cortex-A53 specific event types. */
 #define ARMV8_A53_PERFCTR_PREF_LINEFILL                                0xC2
@@ -1250,21 +1248,10 @@ static struct platform_driver armv8_pmu_driver = {
 
 static int __init armv8_pmu_driver_init(void)
 {
-       int ret;
-
        if (acpi_disabled)
-               ret = platform_driver_register(&armv8_pmu_driver);
+               return platform_driver_register(&armv8_pmu_driver);
        else
-               ret = arm_pmu_acpi_probe(armv8_pmuv3_init);
-
-       /*
-        * Try to re-initialize lockup detector after PMU init in
-        * case PMU events are triggered via NMIs.
-        */
-       if (ret == 0 && arm_pmu_irq_is_nmi())
-               lockup_detector_init();
-
-       return ret;
+               return arm_pmu_acpi_probe(armv8_pmuv3_init);
 }
 device_initcall(armv8_pmu_driver_init)
 
@@ -1322,27 +1309,3 @@ void arch_perf_update_userpage(struct perf_event *event,
        userpg->cap_user_time_zero = 1;
        userpg->cap_user_time_short = 1;
 }
-
-#ifdef CONFIG_HARDLOCKUP_DETECTOR_PERF
-/*
- * Safe maximum CPU frequency in case a particular platform doesn't implement
- * cpufreq driver. Although, architecture doesn't put any restrictions on
- * maximum frequency but 5 GHz seems to be safe maximum given the available
- * Arm CPUs in the market which are clocked much less than 5 GHz. On the other
- * hand, we can't make it much higher as it would lead to a large hard-lockup
- * detection timeout on parts which are running slower (eg. 1GHz on
- * Developerbox) and doesn't possess a cpufreq driver.
- */
-#define SAFE_MAX_CPU_FREQ      5000000000UL // 5 GHz
-u64 hw_nmi_get_sample_period(int watchdog_thresh)
-{
-       unsigned int cpu = smp_processor_id();
-       unsigned long max_cpu_freq;
-
-       max_cpu_freq = cpufreq_get_hw_max_freq(cpu) * 1000UL;
-       if (!max_cpu_freq)
-               max_cpu_freq = SAFE_MAX_CPU_FREQ;
-
-       return (u64)max_cpu_freq * watchdog_thresh;
-}
-#endif
index 89c64ad..66aac28 100644 (file)
@@ -352,8 +352,8 @@ kprobe_breakpoint_ss_handler(struct pt_regs *regs, unsigned int esr)
        unsigned long addr = instruction_pointer(regs);
        struct kprobe *cur = kprobe_running();
 
-       if (cur && (kcb->kprobe_status == KPROBE_HIT_SS)
-           && ((unsigned long)&cur->ainsn.api.insn[1] == addr)) {
+       if (cur && (kcb->kprobe_status & (KPROBE_HIT_SS | KPROBE_REENTER)) &&
+           ((unsigned long)&cur->ainsn.api.insn[1] == addr)) {
                kprobes_restore_local_irqflag(kcb, regs);
                post_kprobe_handler(cur, kcb, regs);
 
index 890ca72..288a84e 100644 (file)
@@ -25,7 +25,7 @@
        stp x24, x25, [sp, #S_X24]
        stp x26, x27, [sp, #S_X26]
        stp x28, x29, [sp, #S_X28]
-       add x0, sp, #S_FRAME_SIZE
+       add x0, sp, #PT_REGS_SIZE
        stp lr, x0, [sp, #S_LR]
        /*
         * Construct a useful saved PSTATE
@@ -62,7 +62,7 @@
        .endm
 
 SYM_CODE_START(kretprobe_trampoline)
-       sub sp, sp, #S_FRAME_SIZE
+       sub sp, sp, #PT_REGS_SIZE
 
        save_all_base_regs
 
@@ -76,7 +76,7 @@ SYM_CODE_START(kretprobe_trampoline)
 
        restore_all_base_regs
 
-       add sp, sp, #S_FRAME_SIZE
+       add sp, sp, #PT_REGS_SIZE
        ret
 
 SYM_CODE_END(kretprobe_trampoline)
index f71d6ce..6237486 100644 (file)
@@ -914,13 +914,6 @@ static void do_signal(struct pt_regs *regs)
 asmlinkage void do_notify_resume(struct pt_regs *regs,
                                 unsigned long thread_flags)
 {
-       /*
-        * The assembly code enters us with IRQs off, but it hasn't
-        * informed the tracing code of that for efficiency reasons.
-        * Update the trace code with the current status.
-        */
-       trace_hardirqs_off();
-
        do {
                if (thread_flags & _TIF_NEED_RESCHED) {
                        /* Unmask Debug and SError for the next task */
index 6bc3a36..ad00f99 100644 (file)
@@ -434,7 +434,7 @@ static void __init hyp_mode_check(void)
                           "CPU: CPUs started in inconsistent modes");
        else
                pr_info("CPU: All CPU(s) started at EL1\n");
-       if (IS_ENABLED(CONFIG_KVM))
+       if (IS_ENABLED(CONFIG_KVM) && !is_kernel_in_hyp_mode())
                kvm_compute_layout();
 }
 
@@ -807,7 +807,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)
        unsigned int cpu, i;
 
        for (i = 0; i < NR_IPI; i++) {
-               unsigned int irq = irq_desc_get_irq(ipi_desc[i]);
                seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
                           prec >= 4 ? " " : "");
                for_each_online_cpu(cpu)
index f61e9d8..c2877c3 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <asm/daifflags.h>
 #include <asm/debug-monitors.h>
+#include <asm/exception.h>
 #include <asm/fpsimd.h>
 #include <asm/syscall.h>
 #include <asm/thread_info.h>
@@ -165,15 +166,8 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
        if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
                local_daif_mask();
                flags = current_thread_info()->flags;
-               if (!has_syscall_work(flags) && !(flags & _TIF_SINGLESTEP)) {
-                       /*
-                        * We're off to userspace, where interrupts are
-                        * always enabled after we restore the flags from
-                        * the SPSR.
-                        */
-                       trace_hardirqs_on();
+               if (!has_syscall_work(flags) && !(flags & _TIF_SINGLESTEP))
                        return;
-               }
                local_daif_restore(DAIF_PROCCTX);
        }
 
index 08156be..6895ce7 100644 (file)
@@ -42,7 +42,6 @@
 #include <asm/smp.h>
 #include <asm/stack_pointer.h>
 #include <asm/stacktrace.h>
-#include <asm/exception.h>
 #include <asm/system_misc.h>
 #include <asm/sysreg.h>
 
index a8f8e40..cd9c3fa 100644 (file)
@@ -24,8 +24,7 @@ btildflags-$(CONFIG_ARM64_BTI_KERNEL) += -z force-bti
 # routines, as x86 does (see 6f121e548f83 ("x86, vdso: Reimplement vdso.so
 # preparation in build-time C")).
 ldflags-y := -shared -nostdlib -soname=linux-vdso.so.1 --hash-style=sysv       \
-            -Bsymbolic $(call ld-option, --no-eh-frame-hdr) --build-id=sha1 -n \
-            $(btildflags-y) -T
+            -Bsymbolic --build-id=sha1 -n $(btildflags-y) -T
 
 ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18
 ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
index d808ad3..61dbb4c 100644 (file)
@@ -40,9 +40,6 @@ SECTIONS
        PROVIDE (_etext = .);
        PROVIDE (etext = .);
 
-       .eh_frame_hdr   : { *(.eh_frame_hdr) }          :text   :eh_frame_hdr
-       .eh_frame       : { KEEP (*(.eh_frame)) }       :text
-
        .dynamic        : { *(.dynamic) }               :text   :dynamic
 
        .rodata         : { *(.rodata*) }               :text
@@ -54,6 +51,7 @@ SECTIONS
                *(.note.GNU-stack)
                *(.data .data.* .gnu.linkonce.d.* .sdata*)
                *(.bss .sbss .dynbss .dynsbss)
+               *(.eh_frame .eh_frame_hdr)
        }
 }
 
@@ -66,7 +64,6 @@ PHDRS
        text            PT_LOAD         FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
        dynamic         PT_DYNAMIC      FLAGS(4);               /* PF_R */
        note            PT_NOTE         FLAGS(4);               /* PF_R */
-       eh_frame_hdr    PT_GNU_EH_FRAME;
 }
 
 /*
index 043756d..3964acf 100644 (file)
@@ -49,14 +49,6 @@ if KVM
 
 source "virt/kvm/Kconfig"
 
-config KVM_ARM_PMU
-       bool "Virtual Performance Monitoring Unit (PMU) support"
-       depends on HW_PERF_EVENTS
-       default y
-       help
-         Adds support for a virtual Performance Monitoring Unit (PMU) in
-         virtual machines.
-
 endif # KVM
 
 endif # VIRTUALIZATION
index 60fd181..13b0172 100644 (file)
@@ -24,4 +24,4 @@ kvm-y := $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/eventfd.o \
         vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
         vgic/vgic-its.o vgic/vgic-debug.o
 
-kvm-$(CONFIG_KVM_ARM_PMU)  += pmu-emul.o
+kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o
index 32ba6fb..74e0699 100644 (file)
@@ -1129,9 +1129,10 @@ int kvm_timer_enable(struct kvm_vcpu *vcpu)
        if (!irqchip_in_kernel(vcpu->kvm))
                goto no_vgic;
 
-       if (!vgic_initialized(vcpu->kvm))
-               return -ENODEV;
-
+       /*
+        * At this stage, we have the guarantee that the vgic is both
+        * available and initialized.
+        */
        if (!timer_irqs_are_valid(vcpu)) {
                kvm_debug("incorrectly configured timer irqs\n");
                return -EINVAL;
index 6e637d2..fe60d25 100644 (file)
@@ -65,10 +65,6 @@ static bool vgic_present;
 static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
 DEFINE_STATIC_KEY_FALSE(userspace_irqchip_in_use);
 
-extern u64 kvm_nvhe_sym(__cpu_logical_map)[NR_CPUS];
-extern u32 kvm_nvhe_sym(kvm_host_psci_version);
-extern struct psci_0_1_function_ids kvm_nvhe_sym(kvm_host_psci_0_1_function_ids);
-
 int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
 {
        return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
@@ -584,11 +580,9 @@ static int kvm_vcpu_first_run_init(struct kvm_vcpu *vcpu)
                 * Map the VGIC hardware resources before running a vcpu the
                 * first time on this VM.
                 */
-               if (unlikely(!vgic_ready(kvm))) {
-                       ret = kvm_vgic_map_resources(kvm);
-                       if (ret)
-                               return ret;
-               }
+               ret = kvm_vgic_map_resources(kvm);
+               if (ret)
+                       return ret;
        } else {
                /*
                 * Tell the rest of the code that there are userspace irqchip
@@ -1402,8 +1396,9 @@ static void cpu_init_hyp_mode(void)
         * Calculate the raw per-cpu offset without a translation from the
         * kernel's mapping to the linear mapping, and store it in tpidr_el2
         * so that we can use adr_l to access per-cpu variables in EL2.
+        * Also drop the KASAN tag which gets in the way...
         */
-       params->tpidr_el2 = (unsigned long)this_cpu_ptr_nvhe_sym(__per_cpu_start) -
+       params->tpidr_el2 = (unsigned long)kasan_reset_tag(this_cpu_ptr_nvhe_sym(__per_cpu_start)) -
                            (unsigned long)kvm_ksym_ref(CHOOSE_NVHE_SYM(__per_cpu_start));
 
        params->mair_el2 = read_sysreg(mair_el1);
@@ -1574,12 +1569,12 @@ static struct notifier_block hyp_init_cpu_pm_nb = {
        .notifier_call = hyp_init_cpu_pm_notifier,
 };
 
-static void __init hyp_cpu_pm_init(void)
+static void hyp_cpu_pm_init(void)
 {
        if (!is_protected_kvm_enabled())
                cpu_pm_register_notifier(&hyp_init_cpu_pm_nb);
 }
-static void __init hyp_cpu_pm_exit(void)
+static void hyp_cpu_pm_exit(void)
 {
        if (!is_protected_kvm_enabled())
                cpu_pm_unregister_notifier(&hyp_init_cpu_pm_nb);
@@ -1604,9 +1599,12 @@ static void init_cpu_logical_map(void)
         * allow any other CPUs from the `possible` set to boot.
         */
        for_each_online_cpu(cpu)
-               kvm_nvhe_sym(__cpu_logical_map)[cpu] = cpu_logical_map(cpu);
+               hyp_cpu_logical_map[cpu] = cpu_logical_map(cpu);
 }
 
+#define init_psci_0_1_impl_state(config, what) \
+       config.psci_0_1_ ## what ## _implemented = psci_ops.what
+
 static bool init_psci_relay(void)
 {
        /*
@@ -1618,8 +1616,15 @@ static bool init_psci_relay(void)
                return false;
        }
 
-       kvm_nvhe_sym(kvm_host_psci_version) = psci_ops.get_version();
-       kvm_nvhe_sym(kvm_host_psci_0_1_function_ids) = get_psci_0_1_function_ids();
+       kvm_host_psci_config.version = psci_ops.get_version();
+
+       if (kvm_host_psci_config.version == PSCI_VERSION(0, 1)) {
+               kvm_host_psci_config.function_ids_0_1 = get_psci_0_1_function_ids();
+               init_psci_0_1_impl_state(kvm_host_psci_config, cpu_suspend);
+               init_psci_0_1_impl_state(kvm_host_psci_config, cpu_on);
+               init_psci_0_1_impl_state(kvm_host_psci_config, cpu_off);
+               init_psci_0_1_impl_state(kvm_host_psci_config, migrate);
+       }
        return true;
 }
 
index b1f6092..6171635 100644 (file)
@@ -59,4 +59,13 @@ static inline void __adjust_pc(struct kvm_vcpu *vcpu)
        }
 }
 
+/*
+ * Skip an instruction while host sysregs are live.
+ * Assumes host is always 64-bit.
+ */
+static inline void kvm_skip_host_instr(void)
+{
+       write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR);
+}
+
 #endif
index bde658d..a906f9e 100644 (file)
@@ -157,11 +157,6 @@ static void default_host_smc_handler(struct kvm_cpu_context *host_ctxt)
        __kvm_hyp_host_forward_smc(host_ctxt);
 }
 
-static void skip_host_instruction(void)
-{
-       write_sysreg_el2(read_sysreg_el2(SYS_ELR) + 4, SYS_ELR);
-}
-
 static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
 {
        bool handled;
@@ -170,11 +165,8 @@ static void handle_host_smc(struct kvm_cpu_context *host_ctxt)
        if (!handled)
                default_host_smc_handler(host_ctxt);
 
-       /*
-        * Unlike HVC, the return address of an SMC is the instruction's PC.
-        * Move the return address past the instruction.
-        */
-       skip_host_instruction();
+       /* SMC was trapped, move ELR past the current PC. */
+       kvm_skip_host_instr();
 }
 
 void handle_trap(struct kvm_cpu_context *host_ctxt)
index cbab0c6..2997aa1 100644 (file)
  * Other CPUs should not be allowed to boot because their features were
  * not checked against the finalized system capabilities.
  */
-u64 __ro_after_init __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
+u64 __ro_after_init hyp_cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
 
 u64 cpu_logical_map(unsigned int cpu)
 {
-       if (cpu >= ARRAY_SIZE(__cpu_logical_map))
+       if (cpu >= ARRAY_SIZE(hyp_cpu_logical_map))
                hyp_panic();
 
-       return __cpu_logical_map[cpu];
+       return hyp_cpu_logical_map[cpu];
 }
 
 unsigned long __hyp_per_cpu_offset(unsigned int cpu)
index 08dc9de..8e7128c 100644 (file)
@@ -7,11 +7,8 @@
 #include <asm/kvm_asm.h>
 #include <asm/kvm_hyp.h>
 #include <asm/kvm_mmu.h>
-#include <kvm/arm_hypercalls.h>
 #include <linux/arm-smccc.h>
 #include <linux/kvm_host.h>
-#include <linux/psci.h>
-#include <kvm/arm_psci.h>
 #include <uapi/linux/psci.h>
 
 #include <nvhe/trap_handler.h>
@@ -22,9 +19,8 @@ void kvm_hyp_cpu_resume(unsigned long r0);
 void __noreturn __host_enter(struct kvm_cpu_context *host_ctxt);
 
 /* Config options set by the host. */
-__ro_after_init u32 kvm_host_psci_version;
-__ro_after_init struct psci_0_1_function_ids kvm_host_psci_0_1_function_ids;
-__ro_after_init s64 hyp_physvirt_offset;
+struct kvm_host_psci_config __ro_after_init kvm_host_psci_config;
+s64 __ro_after_init hyp_physvirt_offset;
 
 #define __hyp_pa(x) ((phys_addr_t)((x)) + hyp_physvirt_offset)
 
@@ -47,19 +43,16 @@ struct psci_boot_args {
 static DEFINE_PER_CPU(struct psci_boot_args, cpu_on_args) = PSCI_BOOT_ARGS_INIT;
 static DEFINE_PER_CPU(struct psci_boot_args, suspend_args) = PSCI_BOOT_ARGS_INIT;
 
-static u64 get_psci_func_id(struct kvm_cpu_context *host_ctxt)
-{
-       DECLARE_REG(u64, func_id, host_ctxt, 0);
-
-       return func_id;
-}
+#define        is_psci_0_1(what, func_id)                                      \
+       (kvm_host_psci_config.psci_0_1_ ## what ## _implemented &&      \
+        (func_id) == kvm_host_psci_config.function_ids_0_1.what)
 
 static bool is_psci_0_1_call(u64 func_id)
 {
-       return (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend) ||
-              (func_id == kvm_host_psci_0_1_function_ids.cpu_on) ||
-              (func_id == kvm_host_psci_0_1_function_ids.cpu_off) ||
-              (func_id == kvm_host_psci_0_1_function_ids.migrate);
+       return (is_psci_0_1(cpu_suspend, func_id) ||
+               is_psci_0_1(cpu_on, func_id) ||
+               is_psci_0_1(cpu_off, func_id) ||
+               is_psci_0_1(migrate, func_id));
 }
 
 static bool is_psci_0_2_call(u64 func_id)
@@ -69,16 +62,6 @@ static bool is_psci_0_2_call(u64 func_id)
               (PSCI_0_2_FN64(0) <= func_id && func_id <= PSCI_0_2_FN64(31));
 }
 
-static bool is_psci_call(u64 func_id)
-{
-       switch (kvm_host_psci_version) {
-       case PSCI_VERSION(0, 1):
-               return is_psci_0_1_call(func_id);
-       default:
-               return is_psci_0_2_call(func_id);
-       }
-}
-
 static unsigned long psci_call(unsigned long fn, unsigned long arg0,
                               unsigned long arg1, unsigned long arg2)
 {
@@ -94,12 +77,6 @@ static unsigned long psci_forward(struct kvm_cpu_context *host_ctxt)
                         cpu_reg(host_ctxt, 2), cpu_reg(host_ctxt, 3));
 }
 
-static __noreturn unsigned long psci_forward_noreturn(struct kvm_cpu_context *host_ctxt)
-{
-       psci_forward(host_ctxt);
-       hyp_panic(); /* unreachable */
-}
-
 static unsigned int find_cpu_id(u64 mpidr)
 {
        unsigned int i;
@@ -248,15 +225,14 @@ asmlinkage void __noreturn kvm_host_psci_cpu_entry(bool is_cpu_on)
 
 static unsigned long psci_0_1_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
 {
-       if ((func_id == kvm_host_psci_0_1_function_ids.cpu_off) ||
-           (func_id == kvm_host_psci_0_1_function_ids.migrate))
+       if (is_psci_0_1(cpu_off, func_id) || is_psci_0_1(migrate, func_id))
                return psci_forward(host_ctxt);
-       else if (func_id == kvm_host_psci_0_1_function_ids.cpu_on)
+       if (is_psci_0_1(cpu_on, func_id))
                return psci_cpu_on(func_id, host_ctxt);
-       else if (func_id == kvm_host_psci_0_1_function_ids.cpu_suspend)
+       if (is_psci_0_1(cpu_suspend, func_id))
                return psci_cpu_suspend(func_id, host_ctxt);
-       else
-               return PSCI_RET_NOT_SUPPORTED;
+
+       return PSCI_RET_NOT_SUPPORTED;
 }
 
 static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context *host_ctxt)
@@ -269,10 +245,13 @@ static unsigned long psci_0_2_handler(u64 func_id, struct kvm_cpu_context *host_
        case PSCI_0_2_FN_MIGRATE_INFO_TYPE:
        case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU:
                return psci_forward(host_ctxt);
+       /*
+        * SYSTEM_OFF/RESET should not return according to the spec.
+        * Allow it so as to stay robust to broken firmware.
+        */
        case PSCI_0_2_FN_SYSTEM_OFF:
        case PSCI_0_2_FN_SYSTEM_RESET:
-               psci_forward_noreturn(host_ctxt);
-               unreachable();
+               return psci_forward(host_ctxt);
        case PSCI_0_2_FN64_CPU_SUSPEND:
                return psci_cpu_suspend(func_id, host_ctxt);
        case PSCI_0_2_FN64_CPU_ON:
@@ -298,20 +277,23 @@ static unsigned long psci_1_0_handler(u64 func_id, struct kvm_cpu_context *host_
 
 bool kvm_host_psci_handler(struct kvm_cpu_context *host_ctxt)
 {
-       u64 func_id = get_psci_func_id(host_ctxt);
+       DECLARE_REG(u64, func_id, host_ctxt, 0);
        unsigned long ret;
 
-       if (!is_psci_call(func_id))
-               return false;
-
-       switch (kvm_host_psci_version) {
+       switch (kvm_host_psci_config.version) {
        case PSCI_VERSION(0, 1):
+               if (!is_psci_0_1_call(func_id))
+                       return false;
                ret = psci_0_1_handler(func_id, host_ctxt);
                break;
        case PSCI_VERSION(0, 2):
+               if (!is_psci_0_2_call(func_id))
+                       return false;
                ret = psci_0_2_handler(func_id, host_ctxt);
                break;
        default:
+               if (!is_psci_0_2_call(func_id))
+                       return false;
                ret = psci_1_0_handler(func_id, host_ctxt);
                break;
        }
index 398f6df..247422a 100644 (file)
@@ -788,7 +788,7 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
 {
        unsigned long *bmap = vcpu->kvm->arch.pmu_filter;
        u64 val, mask = 0;
-       int base, i;
+       int base, i, nr_events;
 
        if (!pmceid1) {
                val = read_sysreg(pmceid0_el0);
@@ -801,13 +801,17 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1)
        if (!bmap)
                return val;
 
+       nr_events = kvm_pmu_event_mask(vcpu->kvm) + 1;
+
        for (i = 0; i < 32; i += 8) {
                u64 byte;
 
                byte = bitmap_get_value8(bmap, base + i);
                mask |= byte << i;
-               byte = bitmap_get_value8(bmap, 0x4000 + base + i);
-               mask |= byte << (32 + i);
+               if (nr_events >= (0x4000 + base + 32)) {
+                       byte = bitmap_get_value8(bmap, 0x4000 + base + i);
+                       mask |= byte << (32 + i);
+               }
        }
 
        return val & mask;
@@ -850,8 +854,6 @@ int kvm_arm_pmu_v3_enable(struct kvm_vcpu *vcpu)
                   return -EINVAL;
        }
 
-       kvm_pmu_vcpu_reset(vcpu);
-
        return 0;
 }
 
index 3313ded..7c4f795 100644 (file)
  * 64bit interface.
  */
 
+#define reg_to_encoding(x)                                             \
+       sys_reg((u32)(x)->Op0, (u32)(x)->Op1,                           \
+               (u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2)
+
 static bool read_from_write_only(struct kvm_vcpu *vcpu,
                                 struct sys_reg_params *params,
                                 const struct sys_reg_desc *r)
@@ -273,8 +277,7 @@ static bool trap_loregion(struct kvm_vcpu *vcpu,
                          const struct sys_reg_desc *r)
 {
        u64 val = read_sanitised_ftr_reg(SYS_ID_AA64MMFR1_EL1);
-       u32 sr = sys_reg((u32)r->Op0, (u32)r->Op1,
-                        (u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
+       u32 sr = reg_to_encoding(r);
 
        if (!(val & (0xfUL << ID_AA64MMFR1_LOR_SHIFT))) {
                kvm_inject_undefined(vcpu);
@@ -590,10 +593,23 @@ static void reset_mpidr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
        vcpu_write_sys_reg(vcpu, (1ULL << 31) | mpidr, MPIDR_EL1);
 }
 
+static unsigned int pmu_visibility(const struct kvm_vcpu *vcpu,
+                                  const struct sys_reg_desc *r)
+{
+       if (kvm_vcpu_has_pmu(vcpu))
+               return 0;
+
+       return REG_HIDDEN;
+}
+
 static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 {
        u64 pmcr, val;
 
+       /* No PMU available, PMCR_EL0 may UNDEF... */
+       if (!kvm_arm_support_pmu_v3())
+               return;
+
        pmcr = read_sysreg(pmcr_el0);
        /*
         * Writable bits of PMCR_EL0 (ARMV8_PMU_PMCR_MASK) are reset to UNKNOWN
@@ -609,9 +625,8 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 static bool check_pmu_access_disabled(struct kvm_vcpu *vcpu, u64 flags)
 {
        u64 reg = __vcpu_sys_reg(vcpu, PMUSERENR_EL0);
-       bool enabled = kvm_vcpu_has_pmu(vcpu);
+       bool enabled = (reg & flags) || vcpu_mode_priv(vcpu);
 
-       enabled &= (reg & flags) || vcpu_mode_priv(vcpu);
        if (!enabled)
                kvm_inject_undefined(vcpu);
 
@@ -896,11 +911,6 @@ static bool access_pmswinc(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
 static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
                             const struct sys_reg_desc *r)
 {
-       if (!kvm_vcpu_has_pmu(vcpu)) {
-               kvm_inject_undefined(vcpu);
-               return false;
-       }
-
        if (p->is_write) {
                if (!vcpu_mode_priv(vcpu)) {
                        kvm_inject_undefined(vcpu);
@@ -917,10 +927,6 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
        return true;
 }
 
-#define reg_to_encoding(x)                                             \
-       sys_reg((u32)(x)->Op0, (u32)(x)->Op1,                           \
-               (u32)(x)->CRn, (u32)(x)->CRm, (u32)(x)->Op2);
-
 /* Silly macro to expand the DBG{BCR,BVR,WVR,WCR}n_EL1 registers in one go */
 #define DBG_BCR_BVR_WCR_WVR_EL1(n)                                     \
        { SYS_DESC(SYS_DBGBVRn_EL1(n)),                                 \
@@ -932,15 +938,18 @@ static bool access_pmuserenr(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
        { SYS_DESC(SYS_DBGWCRn_EL1(n)),                                 \
          trap_wcr, reset_wcr, 0, 0,  get_wcr, set_wcr }
 
+#define PMU_SYS_REG(r)                                         \
+       SYS_DESC(r), .reset = reset_unknown, .visibility = pmu_visibility
+
 /* Macro to expand the PMEVCNTRn_EL0 register */
 #define PMU_PMEVCNTR_EL0(n)                                            \
-       { SYS_DESC(SYS_PMEVCNTRn_EL0(n)),                                       \
-         access_pmu_evcntr, reset_unknown, (PMEVCNTR0_EL0 + n), }
+       { PMU_SYS_REG(SYS_PMEVCNTRn_EL0(n)),                            \
+         .access = access_pmu_evcntr, .reg = (PMEVCNTR0_EL0 + n), }
 
 /* Macro to expand the PMEVTYPERn_EL0 register */
 #define PMU_PMEVTYPER_EL0(n)                                           \
-       { SYS_DESC(SYS_PMEVTYPERn_EL0(n)),                                      \
-         access_pmu_evtyper, reset_unknown, (PMEVTYPER0_EL0 + n), }
+       { PMU_SYS_REG(SYS_PMEVTYPERn_EL0(n)),                           \
+         .access = access_pmu_evtyper, .reg = (PMEVTYPER0_EL0 + n), }
 
 static bool undef_access(struct kvm_vcpu *vcpu, struct sys_reg_params *p,
                         const struct sys_reg_desc *r)
@@ -1016,8 +1025,7 @@ static bool access_arch_timer(struct kvm_vcpu *vcpu,
 static u64 read_id_reg(const struct kvm_vcpu *vcpu,
                struct sys_reg_desc const *r, bool raz)
 {
-       u32 id = sys_reg((u32)r->Op0, (u32)r->Op1,
-                        (u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
+       u32 id = reg_to_encoding(r);
        u64 val = raz ? 0 : read_sanitised_ftr_reg(id);
 
        if (id == SYS_ID_AA64PFR0_EL1) {
@@ -1058,8 +1066,7 @@ static u64 read_id_reg(const struct kvm_vcpu *vcpu,
 static unsigned int id_visibility(const struct kvm_vcpu *vcpu,
                                  const struct sys_reg_desc *r)
 {
-       u32 id = sys_reg((u32)r->Op0, (u32)r->Op1,
-                        (u32)r->CRn, (u32)r->CRm, (u32)r->Op2);
+       u32 id = reg_to_encoding(r);
 
        switch (id) {
        case SYS_ID_AA64ZFR0_EL1:
@@ -1482,8 +1489,10 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        { SYS_DESC(SYS_FAR_EL1), access_vm_reg, reset_unknown, FAR_EL1 },
        { SYS_DESC(SYS_PAR_EL1), NULL, reset_unknown, PAR_EL1 },
 
-       { SYS_DESC(SYS_PMINTENSET_EL1), access_pminten, reset_unknown, PMINTENSET_EL1 },
-       { SYS_DESC(SYS_PMINTENCLR_EL1), access_pminten, reset_unknown, PMINTENSET_EL1 },
+       { PMU_SYS_REG(SYS_PMINTENSET_EL1),
+         .access = access_pminten, .reg = PMINTENSET_EL1 },
+       { PMU_SYS_REG(SYS_PMINTENCLR_EL1),
+         .access = access_pminten, .reg = PMINTENSET_EL1 },
 
        { SYS_DESC(SYS_MAIR_EL1), access_vm_reg, reset_unknown, MAIR_EL1 },
        { SYS_DESC(SYS_AMAIR_EL1), access_vm_reg, reset_amair_el1, AMAIR_EL1 },
@@ -1522,23 +1531,36 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 },
        { SYS_DESC(SYS_CTR_EL0), access_ctr },
 
-       { SYS_DESC(SYS_PMCR_EL0), access_pmcr, reset_pmcr, PMCR_EL0 },
-       { SYS_DESC(SYS_PMCNTENSET_EL0), access_pmcnten, reset_unknown, PMCNTENSET_EL0 },
-       { SYS_DESC(SYS_PMCNTENCLR_EL0), access_pmcnten, reset_unknown, PMCNTENSET_EL0 },
-       { SYS_DESC(SYS_PMOVSCLR_EL0), access_pmovs, reset_unknown, PMOVSSET_EL0 },
-       { SYS_DESC(SYS_PMSWINC_EL0), access_pmswinc, reset_unknown, PMSWINC_EL0 },
-       { SYS_DESC(SYS_PMSELR_EL0), access_pmselr, reset_unknown, PMSELR_EL0 },
-       { SYS_DESC(SYS_PMCEID0_EL0), access_pmceid },
-       { SYS_DESC(SYS_PMCEID1_EL0), access_pmceid },
-       { SYS_DESC(SYS_PMCCNTR_EL0), access_pmu_evcntr, reset_unknown, PMCCNTR_EL0 },
-       { SYS_DESC(SYS_PMXEVTYPER_EL0), access_pmu_evtyper },
-       { SYS_DESC(SYS_PMXEVCNTR_EL0), access_pmu_evcntr },
+       { PMU_SYS_REG(SYS_PMCR_EL0), .access = access_pmcr,
+         .reset = reset_pmcr, .reg = PMCR_EL0 },
+       { PMU_SYS_REG(SYS_PMCNTENSET_EL0),
+         .access = access_pmcnten, .reg = PMCNTENSET_EL0 },
+       { PMU_SYS_REG(SYS_PMCNTENCLR_EL0),
+         .access = access_pmcnten, .reg = PMCNTENSET_EL0 },
+       { PMU_SYS_REG(SYS_PMOVSCLR_EL0),
+         .access = access_pmovs, .reg = PMOVSSET_EL0 },
+       { PMU_SYS_REG(SYS_PMSWINC_EL0),
+         .access = access_pmswinc, .reg = PMSWINC_EL0 },
+       { PMU_SYS_REG(SYS_PMSELR_EL0),
+         .access = access_pmselr, .reg = PMSELR_EL0 },
+       { PMU_SYS_REG(SYS_PMCEID0_EL0),
+         .access = access_pmceid, .reset = NULL },
+       { PMU_SYS_REG(SYS_PMCEID1_EL0),
+         .access = access_pmceid, .reset = NULL },
+       { PMU_SYS_REG(SYS_PMCCNTR_EL0),
+         .access = access_pmu_evcntr, .reg = PMCCNTR_EL0 },
+       { PMU_SYS_REG(SYS_PMXEVTYPER_EL0),
+         .access = access_pmu_evtyper, .reset = NULL },
+       { PMU_SYS_REG(SYS_PMXEVCNTR_EL0),
+         .access = access_pmu_evcntr, .reset = NULL },
        /*
         * PMUSERENR_EL0 resets as unknown in 64bit mode while it resets as zero
         * in 32bit mode. Here we choose to reset it as zero for consistency.
         */
-       { SYS_DESC(SYS_PMUSERENR_EL0), access_pmuserenr, reset_val, PMUSERENR_EL0, 0 },
-       { SYS_DESC(SYS_PMOVSSET_EL0), access_pmovs, reset_unknown, PMOVSSET_EL0 },
+       { PMU_SYS_REG(SYS_PMUSERENR_EL0), .access = access_pmuserenr,
+         .reset = reset_val, .reg = PMUSERENR_EL0, .val = 0 },
+       { PMU_SYS_REG(SYS_PMOVSSET_EL0),
+         .access = access_pmovs, .reg = PMOVSSET_EL0 },
 
        { SYS_DESC(SYS_TPIDR_EL0), NULL, reset_unknown, TPIDR_EL0 },
        { SYS_DESC(SYS_TPIDRRO_EL0), NULL, reset_unknown, TPIDRRO_EL0 },
@@ -1690,7 +1712,8 @@ static const struct sys_reg_desc sys_reg_descs[] = {
         * PMCCFILTR_EL0 resets as unknown in 64bit mode while it resets as zero
         * in 32bit mode. Here we choose to reset it as zero for consistency.
         */
-       { SYS_DESC(SYS_PMCCFILTR_EL0), access_pmu_evtyper, reset_val, PMCCFILTR_EL0, 0 },
+       { PMU_SYS_REG(SYS_PMCCFILTR_EL0), .access = access_pmu_evtyper,
+         .reset = reset_val, .reg = PMCCFILTR_EL0, .val = 0 },
 
        { SYS_DESC(SYS_DACR32_EL2), NULL, reset_unknown, DACR32_EL2 },
        { SYS_DESC(SYS_IFSR32_EL2), NULL, reset_unknown, IFSR32_EL2 },
index d8cc51b..70fcd6a 100644 (file)
@@ -34,17 +34,16 @@ static u64 __early_kern_hyp_va(u64 addr)
 }
 
 /*
- * Store a hyp VA <-> PA offset into a hyp-owned variable.
+ * Store a hyp VA <-> PA offset into a EL2-owned variable.
  */
 static void init_hyp_physvirt_offset(void)
 {
-       extern s64 kvm_nvhe_sym(hyp_physvirt_offset);
        u64 kern_va, hyp_va;
 
        /* Compute the offset from the hyp VA and PA of a random symbol. */
-       kern_va = (u64)kvm_ksym_ref(__hyp_text_start);
+       kern_va = (u64)lm_alias(__hyp_text_start);
        hyp_va = __early_kern_hyp_va(kern_va);
-       CHOOSE_NVHE_SYM(hyp_physvirt_offset) = (s64)__pa(kern_va) - (s64)hyp_va;
+       hyp_physvirt_offset = (s64)__pa(kern_va) - (s64)hyp_va;
 }
 
 /*
index 32e32d6..052917d 100644 (file)
@@ -419,7 +419,8 @@ int vgic_lazy_init(struct kvm *kvm)
  * Map the MMIO regions depending on the VGIC model exposed to the guest
  * called on the first VCPU run.
  * Also map the virtual CPU interface into the VM.
- * v2/v3 derivatives call vgic_init if not already done.
+ * v2 calls vgic_init() if not already done.
+ * v3 and derivatives return an error if the VGIC is not initialized.
  * vgic_ready() returns true if this function has succeeded.
  * @kvm: kvm struct pointer
  */
@@ -428,7 +429,13 @@ int kvm_vgic_map_resources(struct kvm *kvm)
        struct vgic_dist *dist = &kvm->arch.vgic;
        int ret = 0;
 
+       if (likely(vgic_ready(kvm)))
+               return 0;
+
        mutex_lock(&kvm->lock);
+       if (vgic_ready(kvm))
+               goto out;
+
        if (!irqchip_in_kernel(kvm))
                goto out;
 
@@ -439,6 +446,8 @@ int kvm_vgic_map_resources(struct kvm *kvm)
 
        if (ret)
                __kvm_vgic_destroy(kvm);
+       else
+               dist->ready = true;
 
 out:
        mutex_unlock(&kvm->lock);
index ebf53a4..11934c2 100644 (file)
@@ -306,20 +306,15 @@ int vgic_v2_map_resources(struct kvm *kvm)
        struct vgic_dist *dist = &kvm->arch.vgic;
        int ret = 0;
 
-       if (vgic_ready(kvm))
-               goto out;
-
        if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base) ||
            IS_VGIC_ADDR_UNDEF(dist->vgic_cpu_base)) {
                kvm_err("Need to set vgic cpu and dist addresses first\n");
-               ret = -ENXIO;
-               goto out;
+               return -ENXIO;
        }
 
        if (!vgic_v2_check_base(dist->vgic_dist_base, dist->vgic_cpu_base)) {
                kvm_err("VGIC CPU and dist frames overlap\n");
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
        /*
@@ -329,13 +324,13 @@ int vgic_v2_map_resources(struct kvm *kvm)
        ret = vgic_init(kvm);
        if (ret) {
                kvm_err("Unable to initialize VGIC dynamic data structures\n");
-               goto out;
+               return ret;
        }
 
        ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V2);
        if (ret) {
                kvm_err("Unable to register VGIC MMIO regions\n");
-               goto out;
+               return ret;
        }
 
        if (!static_branch_unlikely(&vgic_v2_cpuif_trap)) {
@@ -344,14 +339,11 @@ int vgic_v2_map_resources(struct kvm *kvm)
                                            KVM_VGIC_V2_CPU_SIZE, true);
                if (ret) {
                        kvm_err("Unable to remap VGIC CPU to VCPU\n");
-                       goto out;
+                       return ret;
                }
        }
 
-       dist->ready = true;
-
-out:
-       return ret;
+       return 0;
 }
 
 DEFINE_STATIC_KEY_FALSE(vgic_v2_cpuif_trap);
index 9cdf39a..52915b3 100644 (file)
@@ -500,29 +500,23 @@ int vgic_v3_map_resources(struct kvm *kvm)
        int ret = 0;
        int c;
 
-       if (vgic_ready(kvm))
-               goto out;
-
        kvm_for_each_vcpu(c, vcpu, kvm) {
                struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
 
                if (IS_VGIC_ADDR_UNDEF(vgic_cpu->rd_iodev.base_addr)) {
                        kvm_debug("vcpu %d redistributor base not set\n", c);
-                       ret = -ENXIO;
-                       goto out;
+                       return -ENXIO;
                }
        }
 
        if (IS_VGIC_ADDR_UNDEF(dist->vgic_dist_base)) {
                kvm_err("Need to set vgic distributor addresses first\n");
-               ret = -ENXIO;
-               goto out;
+               return -ENXIO;
        }
 
        if (!vgic_v3_check_base(kvm)) {
                kvm_err("VGIC redist and dist frames overlap\n");
-               ret = -EINVAL;
-               goto out;
+               return -EINVAL;
        }
 
        /*
@@ -530,22 +524,19 @@ int vgic_v3_map_resources(struct kvm *kvm)
         * the VGIC before we need to use it.
         */
        if (!vgic_initialized(kvm)) {
-               ret = -EBUSY;
-               goto out;
+               return -EBUSY;
        }
 
        ret = vgic_register_dist_iodev(kvm, dist->vgic_dist_base, VGIC_V3);
        if (ret) {
                kvm_err("Unable to register VGICv3 dist MMIO regions\n");
-               goto out;
+               return ret;
        }
 
        if (kvm_vgic_global_state.has_gicv4_1)
                vgic_v4_configure_vsgis(kvm);
-       dist->ready = true;
 
-out:
-       return ret;
+       return 0;
 }
 
 DEFINE_STATIC_KEY_FALSE(vgic_v3_cpuif_trap);
index 3c40da4..35d75c6 100644 (file)
@@ -709,10 +709,11 @@ static int do_tag_check_fault(unsigned long far, unsigned int esr,
                              struct pt_regs *regs)
 {
        /*
-        * The architecture specifies that bits 63:60 of FAR_EL1 are UNKNOWN for tag
-        * check faults. Mask them out now so that userspace doesn't see them.
+        * The architecture specifies that bits 63:60 of FAR_EL1 are UNKNOWN
+        * for tag check faults. Set them to corresponding bits in the untagged
+        * address.
         */
-       far &= (1UL << 60) - 1;
+       far = (__untagged_addr(far) & ~MTE_TAG_MASK) | (far & MTE_TAG_MASK);
        do_bad_area(far, esr, regs);
        return 0;
 }
index 75addb3..709d98f 100644 (file)
@@ -53,13 +53,13 @@ s64 memstart_addr __ro_after_init = -1;
 EXPORT_SYMBOL(memstart_addr);
 
 /*
- * We create both ZONE_DMA and ZONE_DMA32. ZONE_DMA covers the first 1G of
- * memory as some devices, namely the Raspberry Pi 4, have peripherals with
- * this limited view of the memory. ZONE_DMA32 will cover the rest of the 32
- * bit addressable memory area.
+ * If the corresponding config options are enabled, we create both ZONE_DMA
+ * and ZONE_DMA32. By default ZONE_DMA covers the 32-bit addressable memory
+ * unless restricted on specific platforms (e.g. 30-bit on Raspberry Pi 4).
+ * In such case, ZONE_DMA32 covers the rest of the 32-bit addressable memory,
+ * otherwise it is empty.
  */
 phys_addr_t arm64_dma_phys_limit __ro_after_init;
-static phys_addr_t arm64_dma32_phys_limit __ro_after_init;
 
 #ifdef CONFIG_KEXEC_CORE
 /*
@@ -84,7 +84,7 @@ static void __init reserve_crashkernel(void)
 
        if (crash_base == 0) {
                /* Current arm64 boot protocol requires 2MB alignment */
-               crash_base = memblock_find_in_range(0, arm64_dma32_phys_limit,
+               crash_base = memblock_find_in_range(0, arm64_dma_phys_limit,
                                crash_size, SZ_2M);
                if (crash_base == 0) {
                        pr_warn("cannot allocate crashkernel (size:0x%llx)\n",
@@ -196,6 +196,7 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
        unsigned long max_zone_pfns[MAX_NR_ZONES]  = {0};
        unsigned int __maybe_unused acpi_zone_dma_bits;
        unsigned int __maybe_unused dt_zone_dma_bits;
+       phys_addr_t __maybe_unused dma32_phys_limit = max_zone_phys(32);
 
 #ifdef CONFIG_ZONE_DMA
        acpi_zone_dma_bits = fls64(acpi_iort_dma_get_max_cpu_address());
@@ -205,8 +206,12 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
        max_zone_pfns[ZONE_DMA] = PFN_DOWN(arm64_dma_phys_limit);
 #endif
 #ifdef CONFIG_ZONE_DMA32
-       max_zone_pfns[ZONE_DMA32] = PFN_DOWN(arm64_dma32_phys_limit);
+       max_zone_pfns[ZONE_DMA32] = PFN_DOWN(dma32_phys_limit);
+       if (!arm64_dma_phys_limit)
+               arm64_dma_phys_limit = dma32_phys_limit;
 #endif
+       if (!arm64_dma_phys_limit)
+               arm64_dma_phys_limit = PHYS_MASK + 1;
        max_zone_pfns[ZONE_NORMAL] = max;
 
        free_area_init(max_zone_pfns);
@@ -394,16 +399,9 @@ void __init arm64_memblock_init(void)
 
        early_init_fdt_scan_reserved_mem();
 
-       if (IS_ENABLED(CONFIG_ZONE_DMA32))
-               arm64_dma32_phys_limit = max_zone_phys(32);
-       else
-               arm64_dma32_phys_limit = PHYS_MASK + 1;
-
        reserve_elfcorehdr();
 
        high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
-
-       dma_contiguous_reserve(arm64_dma32_phys_limit);
 }
 
 void __init bootmem_init(void)
@@ -438,6 +436,11 @@ void __init bootmem_init(void)
        sparse_init();
        zone_sizes_init(min, max);
 
+       /*
+        * Reserve the CMA area after arm64_dma_phys_limit was initialised.
+        */
+       dma_contiguous_reserve(arm64_dma_phys_limit);
+
        /*
         * request_standard_resources() depends on crashkernel's memory being
         * reserved, so do it here.
@@ -455,7 +458,7 @@ void __init bootmem_init(void)
 void __init mem_init(void)
 {
        if (swiotlb_force == SWIOTLB_FORCE ||
-           max_pfn > PFN_DOWN(arm64_dma_phys_limit ? : arm64_dma32_phys_limit))
+           max_pfn > PFN_DOWN(arm64_dma_phys_limit))
                swiotlb_init(1);
        else
                swiotlb_force = SWIOTLB_NO_FORCE;
index 37a54b5..1f7ee8c 100644 (file)
@@ -46,7 +46,7 @@
 #endif
 
 #ifdef CONFIG_KASAN_HW_TAGS
-#define TCR_KASAN_HW_FLAGS SYS_TCR_EL1_TCMA1 | TCR_TBI1
+#define TCR_KASAN_HW_FLAGS SYS_TCR_EL1_TCMA1 | TCR_TBI1 | TCR_TBID1
 #else
 #define TCR_KASAN_HW_FLAGS 0
 #endif
index ef9f1d5..f7b1948 100644 (file)
@@ -875,10 +875,18 @@ emit_cond_jmp:
                }
                break;
 
-       /* STX XADD: lock *(u32 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_W:
-       /* STX XADD: lock *(u64 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_DW:
+       case BPF_STX | BPF_ATOMIC | BPF_W:
+       case BPF_STX | BPF_ATOMIC | BPF_DW:
+               if (insn->imm != BPF_ADD) {
+                       pr_err_once("unknown atomic op code %02x\n", insn->imm);
+                       return -EINVAL;
+               }
+
+               /* STX XADD: lock *(u32 *)(dst + off) += src
+                * and
+                * STX XADD: lock *(u64 *)(dst + off) += src
+                */
+
                if (!off) {
                        reg = dst;
                } else {
index 9337225..cc24bb8 100644 (file)
@@ -2,7 +2,6 @@
 generic-y += asm-offsets.h
 generic-y += gpio.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += qrwlock.h
 generic-y += user.h
 generic-y += vmlinux.lds.h
index ddf04f3..60ee7f0 100644 (file)
@@ -2,7 +2,6 @@
 generic-y += asm-offsets.h
 generic-y += extable.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += parport.h
 generic-y += spinlock.h
index 373964b..3ece3c9 100644 (file)
@@ -2,5 +2,4 @@
 generic-y += extable.h
 generic-y += iomap.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
diff --git a/arch/ia64/include/asm/local64.h b/arch/ia64/include/asm/local64.h
deleted file mode 100644 (file)
index 36c93b5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
index dd8c166..42ed524 100644 (file)
@@ -3,6 +3,7 @@
 #define _ASM_IA64_SPARSEMEM_H
 
 #ifdef CONFIG_SPARSEMEM
+#include <asm/page.h>
 /*
  * SECTION_SIZE_BITS            2^N: how big each section will be
  * MAX_PHYSMEM_BITS             2^N: how much memory we can have in that space
index d69c979..5d90307 100644 (file)
@@ -54,7 +54,7 @@ extern void ia64_xchg_called_with_bad_pointer(void);
 })
 
 #define xchg(ptr, x)                                                   \
-((__typeof__(*(ptr))) __xchg((unsigned long) (x), (ptr), sizeof(*(ptr))))
+({(__typeof__(*(ptr))) __xchg((unsigned long) (x), (ptr), sizeof(*(ptr)));})
 
 /*
  * Atomic compare and exchange.  Compare OLD with MEM, if identical,
index ed9fc3d..43e8050 100644 (file)
@@ -171,29 +171,34 @@ void vtime_account_hardirq(struct task_struct *tsk)
 static irqreturn_t
 timer_interrupt (int irq, void *dev_id)
 {
-       unsigned long cur_itm, new_itm, ticks;
+       unsigned long new_itm;
 
        if (cpu_is_offline(smp_processor_id())) {
                return IRQ_HANDLED;
        }
 
        new_itm = local_cpu_data->itm_next;
-       cur_itm = ia64_get_itc();
 
-       if (!time_after(cur_itm, new_itm)) {
+       if (!time_after(ia64_get_itc(), new_itm))
                printk(KERN_ERR "Oops: timer tick before it's due (itc=%lx,itm=%lx)\n",
-                      cur_itm, new_itm);
-               ticks = 1;
-       } else {
-               ticks = DIV_ROUND_UP(cur_itm - new_itm,
-                                    local_cpu_data->itm_delta);
-               new_itm += ticks * local_cpu_data->itm_delta;
-       }
+                      ia64_get_itc(), new_itm);
+
+       while (1) {
+               new_itm += local_cpu_data->itm_delta;
+
+               legacy_timer_tick(smp_processor_id() == time_keeper_id);
 
-       if (smp_processor_id() != time_keeper_id)
-               ticks = 0;
+               local_cpu_data->itm_next = new_itm;
 
-       legacy_timer_tick(ticks);
+               if (time_after(new_itm, ia64_get_itc()))
+                       break;
+
+               /*
+                * Allow IPIs to interrupt the timer loop.
+                */
+               local_irq_enable();
+               local_irq_disable();
+       }
 
        do {
                /*
index 9b5acf8..e76386a 100644 (file)
@@ -536,7 +536,7 @@ virtual_memmap_init(u64 start, u64 end, void *arg)
 
        if (map_start < map_end)
                memmap_init_zone((unsigned long)(map_end - map_start),
-                                args->nid, args->zone, page_to_pfn(map_start),
+                                args->nid, args->zone, page_to_pfn(map_start), page_to_pfn(map_end),
                                 MEMINIT_EARLY, NULL, MIGRATE_MOVABLE);
        return 0;
 }
@@ -546,7 +546,7 @@ memmap_init (unsigned long size, int nid, unsigned long zone,
             unsigned long start_pfn)
 {
        if (!vmem_map) {
-               memmap_init_zone(size, nid, zone, start_pfn,
+               memmap_init_zone(size, nid, zone, start_pfn, start_pfn + size,
                                 MEMINIT_EARLY, NULL, MIGRATE_MOVABLE);
        } else {
                struct page *start;
index 1bff55a..0dbf9c5 100644 (file)
@@ -2,6 +2,5 @@
 generated-y += syscall_table.h
 generic-y += extable.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += spinlock.h
index 63bce83..29b0e55 100644 (file)
@@ -2,7 +2,6 @@
 generated-y += syscall_table.h
 generic-y += extable.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += parport.h
 generic-y += syscalls.h
index c61c641..e3946b0 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/libfdt.h>
 
 #include <asm/addrspace.h>
+#include <asm/unaligned.h>
 
 /*
  * These two variables specify the free mem region
@@ -117,7 +118,7 @@ void decompress_kernel(unsigned long boot_heap_start)
                dtb_size = fdt_totalsize((void *)&__appended_dtb);
 
                /* last four bytes is always image size in little endian */
-               image_size = le32_to_cpup((void *)&__image_end - 4);
+               image_size = get_unaligned_le32((void *)&__image_end - 4);
 
                /* copy dtb to where the booted kernel will expect it */
                memcpy((void *)VMLINUX_LOAD_ADDRESS_ULL + image_size,
index bd47e15..be5d4af 100644 (file)
@@ -1444,7 +1444,7 @@ static void octeon_irq_setup_secondary_ciu2(void)
 static int __init octeon_irq_init_ciu(
        struct device_node *ciu_node, struct device_node *parent)
 {
-       unsigned int i, r;
+       int i, r;
        struct irq_chip *chip;
        struct irq_chip *chip_edge;
        struct irq_chip *chip_mbox;
index 198b3ba..95b4fa7 100644 (file)
@@ -6,7 +6,6 @@ generated-y += syscall_table_64_n64.h
 generated-y += syscall_table_64_o32.h
 generic-y += export.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += parport.h
 generic-y += qrwlock.h
index 19edf8e..292d042 100644 (file)
@@ -51,6 +51,7 @@ extern void kmap_flush_tlb(unsigned long addr);
 
 #define flush_cache_kmaps()    BUG_ON(cpu_has_dc_aliases)
 
+#define arch_kmap_local_set_pte(mm, vaddr, ptep, ptev) set_pte(ptep, ptev)
 #define arch_kmap_local_post_map(vaddr, pteval)        local_flush_tlb_one(vaddr)
 #define arch_kmap_local_post_unmap(vaddr)      local_flush_tlb_one(vaddr)
 
index 6ee3f72..c444141 100644 (file)
@@ -103,4 +103,11 @@ jiffies_to_old_timeval32(unsigned long jiffies, struct old_timeval32 *value)
 #undef ns_to_kernel_old_timeval
 #define ns_to_kernel_old_timeval ns_to_old_timeval32
 
+/*
+ * Some data types as stored in coredump.
+ */
+#define user_long_t             compat_long_t
+#define user_siginfo_t          compat_siginfo_t
+#define copy_siginfo_to_external        copy_siginfo_to_external32
+
 #include "../../../fs/binfmt_elf.c"
index 6dd103d..7b2a23f 100644 (file)
@@ -106,4 +106,11 @@ jiffies_to_old_timeval32(unsigned long jiffies, struct old_timeval32 *value)
 #undef ns_to_kernel_old_timeval
 #define ns_to_kernel_old_timeval ns_to_old_timeval32
 
+/*
+ * Some data types as stored in coredump.
+ */
+#define user_long_t             compat_long_t
+#define user_siginfo_t          compat_siginfo_t
+#define copy_siginfo_to_external        copy_siginfo_to_external32
+
 #include "../../../fs/binfmt_elf.c"
index 47aeb33..0e365b7 100644 (file)
@@ -187,8 +187,14 @@ static int __init relocate_exception_table(long offset)
 static inline __init unsigned long rotate_xor(unsigned long hash,
                                              const void *area, size_t size)
 {
-       size_t i;
-       unsigned long *ptr = (unsigned long *)area;
+       const typeof(hash) *ptr = PTR_ALIGN(area, sizeof(hash));
+       size_t diff, i;
+
+       diff = (void *)ptr - area;
+       if (unlikely(size < diff + sizeof(hash)))
+               return hash;
+
+       size = ALIGN_DOWN(size - diff, sizeof(hash));
 
        for (i = 0; i < size / sizeof(hash); i++) {
                /* Rotate by odd number of bits and XOR. */
index 561154c..939dd06 100644 (file)
@@ -1423,8 +1423,8 @@ jeq_common:
        case BPF_STX | BPF_H | BPF_MEM:
        case BPF_STX | BPF_W | BPF_MEM:
        case BPF_STX | BPF_DW | BPF_MEM:
-       case BPF_STX | BPF_W | BPF_XADD:
-       case BPF_STX | BPF_DW | BPF_XADD:
+       case BPF_STX | BPF_W | BPF_ATOMIC:
+       case BPF_STX | BPF_DW | BPF_ATOMIC:
                if (insn->dst_reg == BPF_REG_10) {
                        ctx->flags |= EBPF_SEEN_FP;
                        dst = MIPS_R_SP;
@@ -1438,7 +1438,12 @@ jeq_common:
                src = ebpf_to_mips_reg(ctx, insn, src_reg_no_fp);
                if (src < 0)
                        return src;
-               if (BPF_MODE(insn->code) == BPF_XADD) {
+               if (BPF_MODE(insn->code) == BPF_ATOMIC) {
+                       if (insn->imm != BPF_ADD) {
+                               pr_err("ATOMIC OP %02x NOT HANDLED\n", insn->imm);
+                               return -EINVAL;
+                       }
+
                        /*
                         * If mem_off does not fit within the 9 bit ll/sc
                         * instruction immediate field, use a temp reg.
index ff1e942..82a4453 100644 (file)
@@ -4,6 +4,5 @@ generic-y += cmpxchg.h
 generic-y += export.h
 generic-y += gpio.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += parport.h
 generic-y += user.h
index 442f3d3..ca5987e 100644 (file)
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
 generic-y += extable.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += qspinlock_types.h
 generic-y += qspinlock.h
index 7d6b4a7..c298061 100644 (file)
@@ -31,7 +31,7 @@
 void __iomem *ioremap(phys_addr_t offset, unsigned long size);
 
 #define iounmap iounmap
-extern void iounmap(void *addr);
+extern void iounmap(void __iomem *addr);
 
 #include <asm-generic/io.h>
 
index 5aed97a..daae13a 100644 (file)
@@ -77,7 +77,7 @@ void __iomem *__ref ioremap(phys_addr_t addr, unsigned long size)
 }
 EXPORT_SYMBOL(ioremap);
 
-void iounmap(void *addr)
+void iounmap(void __iomem *addr)
 {
        /* If the page is from the fixmap pool then we just clear out
         * the fixmap mapping.
index 78b1762..2784621 100644 (file)
@@ -202,9 +202,8 @@ config PREFETCH
        depends on PA8X00 || PA7200
 
 config MLONGCALLS
-       bool "Enable the -mlong-calls compiler option for big kernels"
-       default y if !MODULES || UBSAN || FTRACE
-       default n
+       def_bool y if !MODULES || UBSAN || FTRACE
+       bool "Enable the -mlong-calls compiler option for big kernels" if MODULES && !UBSAN && !FTRACE
        depends on PA8X00
        help
          If you configure the kernel to include many drivers built-in instead
index f16c4db..4406475 100644 (file)
@@ -3,6 +3,5 @@ generated-y += syscall_table_32.h
 generated-y += syscall_table_64.h
 generated-y += syscall_table_c32.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += user.h
index 959e79c..378f63c 100644 (file)
@@ -47,7 +47,4 @@ extern unsigned long txn_affinity_addr(unsigned int irq, int cpu);
 extern int cpu_claim_irq(unsigned int irq, struct irq_chip *, void *);
 extern int cpu_check_affinity(struct irq_data *d, const struct cpumask *dest);
 
-/* soft power switch support (power.c) */
-extern struct tasklet_struct power_tasklet;
-
 #endif /* _ASM_PARISC_IRQ_H */
index beba981..4d37cc9 100644 (file)
@@ -997,10 +997,17 @@ intr_do_preempt:
        bb,<,n  %r20, 31 - PSW_SM_I, intr_restore
        nop
 
+       /* ssm PSW_SM_I done later in intr_restore */
+#ifdef CONFIG_MLONGCALLS
+       ldil    L%intr_restore, %r2
+       load32  preempt_schedule_irq, %r1
+       bv      %r0(%r1)
+       ldo     R%intr_restore(%r2), %r2
+#else
+       ldil    L%intr_restore, %r1
        BL      preempt_schedule_irq, %r2
-       nop
-
-       b,n     intr_restore            /* ssm PSW_SM_I done by intr_restore */
+       ldo     R%intr_restore(%r1), %r2
+#endif
 #endif /* CONFIG_PREEMPTION */
 
        /*
index 90cd5c5..e1f9b4e 100644 (file)
@@ -5,7 +5,6 @@ generated-y += syscall_table_c32.h
 generated-y += syscall_table_spu.h
 generic-y += export.h
 generic-y += kvm_types.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += qrwlock.h
 generic-y += vtime.h
index 1d32b17..c1a8aac 100644 (file)
        nop;                                                            \
        nop;
 
+#define SCV_ENTRY_FLUSH_SLOT                                           \
+       SCV_ENTRY_FLUSH_FIXUP_SECTION;                                  \
+       nop;                                                            \
+       nop;                                                            \
+       nop;
+
 /*
  * r10 must be free to use, r13 must be paca
  */
        STF_ENTRY_BARRIER_SLOT;                                         \
        ENTRY_FLUSH_SLOT
 
+/*
+ * r10, ctr must be free to use, r13 must be paca
+ */
+#define SCV_INTERRUPT_TO_KERNEL                                                \
+       STF_ENTRY_BARRIER_SLOT;                                         \
+       SCV_ENTRY_FLUSH_SLOT
+
 /*
  * Macros for annotating the expected destination of (h)rfid
  *
index f6d2acb..ac605fc 100644 (file)
@@ -240,6 +240,14 @@ label##3:                                          \
        FTR_ENTRY_OFFSET 957b-958b;                     \
        .popsection;
 
+#define SCV_ENTRY_FLUSH_FIXUP_SECTION                  \
+957:                                                   \
+       .pushsection __scv_entry_flush_fixup,"a";       \
+       .align 2;                                       \
+958:                                                   \
+       FTR_ENTRY_OFFSET 957b-958b;                     \
+       .popsection;
+
 #define RFI_FLUSH_FIXUP_SECTION                                \
 951:                                                   \
        .pushsection __rfi_flush_fixup,"a";             \
@@ -273,10 +281,12 @@ label##3:                                         \
 
 extern long stf_barrier_fallback;
 extern long entry_flush_fallback;
+extern long scv_entry_flush_fallback;
 extern long __start___stf_entry_barrier_fixup, __stop___stf_entry_barrier_fixup;
 extern long __start___stf_exit_barrier_fixup, __stop___stf_exit_barrier_fixup;
 extern long __start___uaccess_flush_fixup, __stop___uaccess_flush_fixup;
 extern long __start___entry_flush_fixup, __stop___entry_flush_fixup;
+extern long __start___scv_entry_flush_fixup, __stop___scv_entry_flush_fixup;
 extern long __start___rfi_flush_fixup, __stop___rfi_flush_fixup;
 extern long __start___barrier_nospec_fixup, __stop___barrier_nospec_fixup;
 extern long __start__btb_flush_fixup, __stop__btb_flush_fixup;
index 80a5ae7..c0fcd1b 100644 (file)
@@ -58,6 +58,8 @@ extern pte_t *pkmap_page_table;
 
 #define flush_cache_kmaps()    flush_cache_all()
 
+#define arch_kmap_local_set_pte(mm, vaddr, ptep, ptev) \
+       __set_pte_at(mm, vaddr, ptep, ptev, 1)
 #define arch_kmap_local_post_map(vaddr, pteval)        \
        local_flush_tlb_page(NULL, vaddr)
 #define arch_kmap_local_post_unmap(vaddr)      \
index 81671aa..77c635c 100644 (file)
@@ -103,6 +103,8 @@ int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz
        return do_syscall_2(__NR_gettimeofday, (unsigned long)_tv, (unsigned long)_tz);
 }
 
+#ifdef __powerpc64__
+
 static __always_inline
 int clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
 {
@@ -115,10 +117,22 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
        return do_syscall_2(__NR_clock_getres, _clkid, (unsigned long)_ts);
 }
 
-#ifdef CONFIG_VDSO32
+#else
 
 #define BUILD_VDSO32           1
 
+static __always_inline
+int clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
+{
+       return do_syscall_2(__NR_clock_gettime64, _clkid, (unsigned long)_ts);
+}
+
+static __always_inline
+int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
+{
+       return do_syscall_2(__NR_clock_getres_time64, _clkid, (unsigned long)_ts);
+}
+
 static __always_inline
 int clock_gettime32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
 {
index aa1af13..33ddfee 100644 (file)
@@ -75,7 +75,7 @@ BEGIN_FTR_SECTION
        bne     .Ltabort_syscall
 END_FTR_SECTION_IFSET(CPU_FTR_TM)
 #endif
-       INTERRUPT_TO_KERNEL
+       SCV_INTERRUPT_TO_KERNEL
        mr      r10,r1
        ld      r1,PACAKSAVE(r13)
        std     r10,0(r1)
index e02ad6f..6e53f76 100644 (file)
@@ -2993,6 +2993,25 @@ TRAMP_REAL_BEGIN(entry_flush_fallback)
        ld      r11,PACA_EXRFI+EX_R11(r13)
        blr
 
+/*
+ * The SCV entry flush happens with interrupts enabled, so it must disable
+ * to prevent EXRFI being clobbered by NMIs (e.g., soft_nmi_common). r10
+ * (containing LR) does not need to be preserved here because scv entry
+ * puts 0 in the pt_regs, CTR can be clobbered for the same reason.
+ */
+TRAMP_REAL_BEGIN(scv_entry_flush_fallback)
+       li      r10,0
+       mtmsrd  r10,1
+       lbz     r10,PACAIRQHAPPENED(r13)
+       ori     r10,r10,PACA_IRQ_HARD_DIS
+       stb     r10,PACAIRQHAPPENED(r13)
+       std     r11,PACA_EXRFI+EX_R11(r13)
+       L1D_DISPLACEMENT_FLUSH
+       ld      r11,PACA_EXRFI+EX_R11(r13)
+       li      r10,MSR_RI
+       mtmsrd  r10,1
+       blr
+
 TRAMP_REAL_BEGIN(rfi_flush_fallback)
        SET_SCRATCH0(r13);
        GET_PACA(r13);
index 349bf3f..858fbc8 100644 (file)
@@ -260,10 +260,19 @@ __secondary_hold_acknowledge:
 MachineCheck:
        EXCEPTION_PROLOG_0
 #ifdef CONFIG_PPC_CHRP
+#ifdef CONFIG_VMAP_STACK
+       mtspr   SPRN_SPRG_SCRATCH2,r1
+       mfspr   r1, SPRN_SPRG_THREAD
+       lwz     r1, RTAS_SP(r1)
+       cmpwi   cr1, r1, 0
+       bne     cr1, 7f
+       mfspr   r1, SPRN_SPRG_SCRATCH2
+#else
        mfspr   r11, SPRN_SPRG_THREAD
        lwz     r11, RTAS_SP(r11)
        cmpwi   cr1, r11, 0
        bne     cr1, 7f
+#endif
 #endif /* CONFIG_PPC_CHRP */
        EXCEPTION_PROLOG_1 for_rtas=1
 7:     EXCEPTION_PROLOG_2
index 0318ba4..72fa3c0 100644 (file)
@@ -85,7 +85,7 @@ SECTIONS
                ALIGN_FUNCTION();
 #endif
                /* careful! __ftr_alt_* sections need to be close to .text */
-               *(.text.hot TEXT_MAIN .text.fixup .text.unlikely .fixup __ftr_alt_* .ref.text);
+               *(.text.hot .text.hot.* TEXT_MAIN .text.fixup .text.unlikely .text.unlikely.* .fixup __ftr_alt_* .ref.text);
 #ifdef CONFIG_PPC64
                *(.tramp.ftrace.text);
 #endif
@@ -145,6 +145,13 @@ SECTIONS
                __stop___entry_flush_fixup = .;
        }
 
+       . = ALIGN(8);
+       __scv_entry_flush_fixup : AT(ADDR(__scv_entry_flush_fixup) - LOAD_OFFSET) {
+               __start___scv_entry_flush_fixup = .;
+               *(__scv_entry_flush_fixup)
+               __stop___scv_entry_flush_fixup = .;
+       }
+
        . = ALIGN(8);
        __stf_exit_barrier_fixup : AT(ADDR(__stf_exit_barrier_fixup) - LOAD_OFFSET) {
                __start___stf_exit_barrier_fixup = .;
@@ -187,6 +194,12 @@ SECTIONS
        .init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {
                _sinittext = .;
                INIT_TEXT
+
+               /*
+                *.init.text might be RO so we must ensure this section ends on
+                * a page boundary.
+                */
+               . = ALIGN(PAGE_SIZE);
                _einittext = .;
 #ifdef CONFIG_PPC64
                *(.tramp.ftrace.init);
@@ -200,6 +213,8 @@ SECTIONS
                EXIT_TEXT
        }
 
+       . = ALIGN(PAGE_SIZE);
+
        INIT_DATA_SECTION(16)
 
        . = ALIGN(8);
index 4782105..1fd31b4 100644 (file)
@@ -290,9 +290,6 @@ void do_entry_flush_fixups(enum l1d_flush_type types)
        long *start, *end;
        int i;
 
-       start = PTRRELOC(&__start___entry_flush_fixup);
-       end = PTRRELOC(&__stop___entry_flush_fixup);
-
        instrs[0] = 0x60000000; /* nop */
        instrs[1] = 0x60000000; /* nop */
        instrs[2] = 0x60000000; /* nop */
@@ -312,6 +309,8 @@ void do_entry_flush_fixups(enum l1d_flush_type types)
        if (types & L1D_FLUSH_MTTRIG)
                instrs[i++] = 0x7c12dba6; /* mtspr TRIG2,r0 (SPR #882) */
 
+       start = PTRRELOC(&__start___entry_flush_fixup);
+       end = PTRRELOC(&__stop___entry_flush_fixup);
        for (i = 0; start < end; start++, i++) {
                dest = (void *)start + *start;
 
@@ -328,6 +327,25 @@ void do_entry_flush_fixups(enum l1d_flush_type types)
                patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
        }
 
+       start = PTRRELOC(&__start___scv_entry_flush_fixup);
+       end = PTRRELOC(&__stop___scv_entry_flush_fixup);
+       for (; start < end; start++, i++) {
+               dest = (void *)start + *start;
+
+               pr_devel("patching dest %lx\n", (unsigned long)dest);
+
+               patch_instruction((struct ppc_inst *)dest, ppc_inst(instrs[0]));
+
+               if (types == L1D_FLUSH_FALLBACK)
+                       patch_branch((struct ppc_inst *)(dest + 1), (unsigned long)&scv_entry_flush_fallback,
+                                    BRANCH_SET_LINK);
+               else
+                       patch_instruction((struct ppc_inst *)(dest + 1), ppc_inst(instrs[1]));
+
+               patch_instruction((struct ppc_inst *)(dest + 2), ppc_inst(instrs[2]));
+       }
+
+
        printk(KERN_DEBUG "entry-flush: patched %d locations (%s flush)\n", i,
                (types == L1D_FLUSH_NONE)       ? "no" :
                (types == L1D_FLUSH_FALLBACK)   ? "fallback displacement" :
index 022103c..aaf1a88 100644 (file)
@@ -683,10 +683,18 @@ emit_clear:
                        break;
 
                /*
-                * BPF_STX XADD (atomic_add)
+                * BPF_STX ATOMIC (atomic ops)
                 */
-               /* *(u32 *)(dst + off) += src */
-               case BPF_STX | BPF_XADD | BPF_W:
+               case BPF_STX | BPF_ATOMIC | BPF_W:
+                       if (insn->imm != BPF_ADD) {
+                               pr_err_ratelimited(
+                                       "eBPF filter atomic op code %02x (@%d) unsupported\n",
+                                       code, i);
+                               return -ENOTSUPP;
+                       }
+
+                       /* *(u32 *)(dst + off) += src */
+
                        /* Get EA into TMP_REG_1 */
                        EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], dst_reg, off));
                        tmp_idx = ctx->idx * 4;
@@ -699,8 +707,15 @@ emit_clear:
                        /* we're done if this succeeded */
                        PPC_BCC_SHORT(COND_NE, tmp_idx);
                        break;
-               /* *(u64 *)(dst + off) += src */
-               case BPF_STX | BPF_XADD | BPF_DW:
+               case BPF_STX | BPF_ATOMIC | BPF_DW:
+                       if (insn->imm != BPF_ADD) {
+                               pr_err_ratelimited(
+                                       "eBPF filter atomic op code %02x (@%d) unsupported\n",
+                                       code, i);
+                               return -ENOTSUPP;
+                       }
+                       /* *(u64 *)(dst + off) += src */
+
                        EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], dst_reg, off));
                        tmp_idx = ctx->idx * 4;
                        EMIT(PPC_RAW_LDARX(b2p[TMP_REG_2], 0, b2p[TMP_REG_1], 0));
index 81b76d4..e9e2c1f 100644 (file)
@@ -137,7 +137,7 @@ config PA_BITS
 
 config PAGE_OFFSET
        hex
-       default 0xC0000000 if 32BIT && MAXPHYSMEM_2GB
+       default 0xC0000000 if 32BIT && MAXPHYSMEM_1GB
        default 0x80000000 if 64BIT && !MMU
        default 0xffffffff80000000 if 64BIT && MAXPHYSMEM_2GB
        default 0xffffffe000000000 if 64BIT && MAXPHYSMEM_128GB
@@ -247,10 +247,12 @@ config MODULE_SECTIONS
 
 choice
        prompt "Maximum Physical Memory"
-       default MAXPHYSMEM_2GB if 32BIT
+       default MAXPHYSMEM_1GB if 32BIT
        default MAXPHYSMEM_2GB if 64BIT && CMODEL_MEDLOW
        default MAXPHYSMEM_128GB if 64BIT && CMODEL_MEDANY
 
+       config MAXPHYSMEM_1GB
+               bool "1GiB"
        config MAXPHYSMEM_2GB
                bool "2GiB"
        config MAXPHYSMEM_128GB
index 4a2729f..24d75a1 100644 (file)
@@ -88,7 +88,9 @@
        phy-mode = "gmii";
        phy-handle = <&phy0>;
        phy0: ethernet-phy@0 {
+               compatible = "ethernet-phy-id0007.0771";
                reg = <0>;
+               reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>;
        };
 };
 
index d222d35..8c3d1e4 100644 (file)
@@ -64,6 +64,8 @@ CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_VIRTIO=y
 CONFIG_SPI=y
 CONFIG_SPI_SIFIVE=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SIFIVE=y
 # CONFIG_PTP_1588_CLOCK is not set
 CONFIG_POWER_RESET=y
 CONFIG_DRM=y
index 59dd7be..445ccc9 100644 (file)
@@ -3,6 +3,5 @@ generic-y += early_ioremap.h
 generic-y += extable.h
 generic-y += flat.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += user.h
 generic-y += vmlinux.lds.h
index 41a7286..251e1db 100644 (file)
@@ -99,7 +99,6 @@
                                | _PAGE_DIRTY)
 
 #define PAGE_KERNEL            __pgprot(_PAGE_KERNEL)
-#define PAGE_KERNEL_EXEC       __pgprot(_PAGE_KERNEL | _PAGE_EXEC)
 #define PAGE_KERNEL_READ       __pgprot(_PAGE_KERNEL & ~_PAGE_WRITE)
 #define PAGE_KERNEL_EXEC       __pgprot(_PAGE_KERNEL | _PAGE_EXEC)
 #define PAGE_KERNEL_READ_EXEC  __pgprot((_PAGE_KERNEL & ~_PAGE_WRITE) \
index 8454f74..1453a2f 100644 (file)
@@ -10,7 +10,7 @@
 
 #include <linux/types.h>
 
-#ifndef GENERIC_TIME_VSYSCALL
+#ifndef CONFIG_GENERIC_TIME_VSYSCALL
 struct vdso_data {
 };
 #endif
index de59dd4..d867813 100644 (file)
@@ -26,7 +26,16 @@ cache_get_priv_group(struct cacheinfo *this_leaf)
 
 static struct cacheinfo *get_cacheinfo(u32 level, enum cache_type type)
 {
-       struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(smp_processor_id());
+       /*
+        * Using raw_smp_processor_id() elides a preemptability check, but this
+        * is really indicative of a larger problem: the cacheinfo UABI assumes
+        * that cores have a homonogenous view of the cache hierarchy.  That
+        * happens to be the case for the current set of RISC-V systems, but
+        * likely won't be true in general.  Since there's no way to provide
+        * correct information for these systems via the current UABI we're
+        * just eliding the check for now.
+        */
+       struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(raw_smp_processor_id());
        struct cacheinfo *this_leaf;
        int index;
 
index 524d918..744f320 100644 (file)
@@ -124,15 +124,15 @@ skip_context_tracking:
        REG_L a1, (a1)
        jr a1
 1:
-#ifdef CONFIG_TRACE_IRQFLAGS
-       call trace_hardirqs_on
-#endif
        /*
         * Exceptions run with interrupts enabled or disabled depending on the
         * state of SR_PIE in m/sstatus.
         */
        andi t0, s1, SR_PIE
        beqz t0, 1f
+#ifdef CONFIG_TRACE_IRQFLAGS
+       call trace_hardirqs_on
+#endif
        csrs CSR_STATUS, SR_IE
 
 1:
@@ -155,6 +155,15 @@ skip_context_tracking:
        tail do_trap_unknown
 
 handle_syscall:
+#ifdef CONFIG_RISCV_M_MODE
+       /*
+        * When running is M-Mode (no MMU config), MPIE does not get set.
+        * As a result, we need to force enable interrupts here because
+        * handle_exception did not do set SR_IE as it always sees SR_PIE
+        * being cleared.
+        */
+       csrs CSR_STATUS, SR_IE
+#endif
 #if defined(CONFIG_TRACE_IRQFLAGS) || defined(CONFIG_CONTEXT_TRACKING)
        /* Recover a0 - a7 for system calls */
        REG_L a0, PT_A0(sp)
@@ -186,14 +195,7 @@ check_syscall_nr:
         * Syscall number held in a7.
         * If syscall number is above allowed value, redirect to ni_syscall.
         */
-       bge a7, t0, 1f
-       /*
-        * Check if syscall is rejected by tracer, i.e., a7 == -1.
-        * If yes, we pretend it was executed.
-        */
-       li t1, -1
-       beq a7, t1, ret_from_syscall_rejected
-       blt a7, t1, 1f
+       bgeu a7, t0, 1f
        /* Call syscall */
        la s0, sys_call_table
        slli t0, a7, RISCV_LGPTR
index 1d85e9b..3fa3f26 100644 (file)
@@ -127,7 +127,9 @@ static void __init init_resources(void)
 {
        struct memblock_region *region = NULL;
        struct resource *res = NULL;
-       int ret = 0;
+       struct resource *mem_res = NULL;
+       size_t mem_res_sz = 0;
+       int ret = 0, i = 0;
 
        code_res.start = __pa_symbol(_text);
        code_res.end = __pa_symbol(_etext) - 1;
@@ -145,16 +147,17 @@ static void __init init_resources(void)
        bss_res.end = __pa_symbol(__bss_stop) - 1;
        bss_res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
 
+       mem_res_sz = (memblock.memory.cnt + memblock.reserved.cnt) * sizeof(*mem_res);
+       mem_res = memblock_alloc(mem_res_sz, SMP_CACHE_BYTES);
+       if (!mem_res)
+               panic("%s: Failed to allocate %zu bytes\n", __func__, mem_res_sz);
        /*
         * Start by adding the reserved regions, if they overlap
         * with /memory regions, insert_resource later on will take
         * care of it.
         */
        for_each_reserved_mem_region(region) {
-               res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES);
-               if (!res)
-                       panic("%s: Failed to allocate %zu bytes\n", __func__,
-                             sizeof(struct resource));
+               res = &mem_res[i++];
 
                res->name = "Reserved";
                res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
@@ -171,8 +174,10 @@ static void __init init_resources(void)
                 * Ignore any other reserved regions within
                 * system memory.
                 */
-               if (memblock_is_memory(res->start))
+               if (memblock_is_memory(res->start)) {
+                       memblock_free((phys_addr_t) res, sizeof(struct resource));
                        continue;
+               }
 
                ret = add_resource(&iomem_resource, res);
                if (ret < 0)
@@ -181,10 +186,7 @@ static void __init init_resources(void)
 
        /* Add /memory regions to the resource tree */
        for_each_mem_region(region) {
-               res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES);
-               if (!res)
-                       panic("%s: Failed to allocate %zu bytes\n", __func__,
-                             sizeof(struct resource));
+               res = &mem_res[i++];
 
                if (unlikely(memblock_is_nomap(region))) {
                        res->name = "Reserved";
@@ -205,9 +207,9 @@ static void __init init_resources(void)
        return;
 
  error:
-       memblock_free((phys_addr_t) res, sizeof(struct resource));
        /* Better an empty resource tree than an inconsistent one */
        release_child_resources(&iomem_resource);
+       memblock_free((phys_addr_t) mem_res, mem_res_sz);
 }
 
 
index 48b870a..df5d2da 100644 (file)
@@ -14,7 +14,7 @@
 
 #include <asm/stacktrace.h>
 
-register unsigned long sp_in_global __asm__("sp");
+register const unsigned long sp_in_global __asm__("sp");
 
 #ifdef CONFIG_FRAME_POINTER
 
@@ -28,9 +28,8 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
                sp = user_stack_pointer(regs);
                pc = instruction_pointer(regs);
        } else if (task == NULL || task == current) {
-               const register unsigned long current_sp = sp_in_global;
                fp = (unsigned long)__builtin_frame_address(0);
-               sp = current_sp;
+               sp = sp_in_global;
                pc = (unsigned long)walk_stackframe;
        } else {
                /* task blocked in __switch_to */
index 4d3a104..8a5cf99 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2017 SiFive
  */
 
+#include <linux/of_clk.h>
 #include <linux/clocksource.h>
 #include <linux/delay.h>
 #include <asm/sbi.h>
@@ -24,6 +25,8 @@ void __init time_init(void)
        riscv_timebase = prop;
 
        lpj_fine = riscv_timebase / HZ;
+
+       of_clk_init(NULL);
        timer_probe();
 }
 
index 6782042..3f1d35e 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/binfmts.h>
 #include <linux/err.h>
 #include <asm/page.h>
-#ifdef GENERIC_TIME_VSYSCALL
+#ifdef CONFIG_GENERIC_TIME_VSYSCALL
 #include <vdso/datapage.h>
 #else
 #include <asm/vdso.h>
index bf53791..7cd4993 100644 (file)
@@ -157,9 +157,10 @@ disable:
 void __init setup_bootmem(void)
 {
        phys_addr_t mem_start = 0;
-       phys_addr_t start, end = 0;
+       phys_addr_t start, dram_end, end = 0;
        phys_addr_t vmlinux_end = __pa_symbol(&_end);
        phys_addr_t vmlinux_start = __pa_symbol(&_start);
+       phys_addr_t max_mapped_addr = __pa(~(ulong)0);
        u64 i;
 
        /* Find the memory region containing the kernel */
@@ -181,7 +182,18 @@ void __init setup_bootmem(void)
        /* Reserve from the start of the kernel to the end of the kernel */
        memblock_reserve(vmlinux_start, vmlinux_end - vmlinux_start);
 
-       max_pfn = PFN_DOWN(memblock_end_of_DRAM());
+       dram_end = memblock_end_of_DRAM();
+
+       /*
+        * memblock allocator is not aware of the fact that last 4K bytes of
+        * the addressable memory can not be mapped because of IS_ERR_VALUE
+        * macro. Make sure that last 4k bytes are not usable by memblock
+        * if end of dram is equal to maximum addressable memory.
+        */
+       if (max_mapped_addr == (dram_end - 1))
+               memblock_set_current_limit(max_mapped_addr - 4096);
+
+       max_pfn = PFN_DOWN(dram_end);
        max_low_pfn = max_pfn;
        dma32_phys_limit = min(4UL * SZ_1G, (unsigned long)PFN_PHYS(max_low_pfn));
        set_max_mapnr(max_low_pfn);
index 12ddd1f..a8a2ffd 100644 (file)
@@ -93,8 +93,8 @@ void __init kasan_init(void)
                                                                VMALLOC_END));
 
        for_each_mem_range(i, &_start, &_end) {
-               void *start = (void *)_start;
-               void *end = (void *)_end;
+               void *start = (void *)__va(_start);
+               void *end = (void *)__va(_end);
 
                if (start >= end)
                        break;
index 579575f..81de865 100644 (file)
@@ -881,7 +881,7 @@ static int emit_store_r64(const s8 *dst, const s8 *src, s16 off,
        const s8 *rd = bpf_get_reg64(dst, tmp1, ctx);
        const s8 *rs = bpf_get_reg64(src, tmp2, ctx);
 
-       if (mode == BPF_XADD && size != BPF_W)
+       if (mode == BPF_ATOMIC && size != BPF_W)
                return -1;
 
        emit_imm(RV_REG_T0, off, ctx);
@@ -899,7 +899,7 @@ static int emit_store_r64(const s8 *dst, const s8 *src, s16 off,
                case BPF_MEM:
                        emit(rv_sw(RV_REG_T0, 0, lo(rs)), ctx);
                        break;
-               case BPF_XADD:
+               case BPF_ATOMIC: /* Only BPF_ADD supported */
                        emit(rv_amoadd_w(RV_REG_ZERO, lo(rs), RV_REG_T0, 0, 0),
                             ctx);
                        break;
@@ -1260,7 +1260,6 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
        case BPF_STX | BPF_MEM | BPF_H:
        case BPF_STX | BPF_MEM | BPF_W:
        case BPF_STX | BPF_MEM | BPF_DW:
-       case BPF_STX | BPF_XADD | BPF_W:
                if (BPF_CLASS(code) == BPF_ST) {
                        emit_imm32(tmp2, imm, ctx);
                        src = tmp2;
@@ -1271,8 +1270,21 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
                        return -1;
                break;
 
+       case BPF_STX | BPF_ATOMIC | BPF_W:
+               if (insn->imm != BPF_ADD) {
+                       pr_info_once(
+                               "bpf-jit: not supported: atomic operation %02x ***\n",
+                               insn->imm);
+                       return -EFAULT;
+               }
+
+               if (emit_store_r64(dst, src, off, ctx, BPF_SIZE(code),
+                                  BPF_MODE(code)))
+                       return -1;
+               break;
+
        /* No hardware support for 8-byte atomics in RV32. */
-       case BPF_STX | BPF_XADD | BPF_DW:
+       case BPF_STX | BPF_ATOMIC | BPF_DW:
                /* Fallthrough. */
 
 notsupported:
index 8a56b52..b44ff52 100644 (file)
@@ -1027,10 +1027,18 @@ out_be:
                emit_add(RV_REG_T1, RV_REG_T1, rd, ctx);
                emit_sd(RV_REG_T1, 0, rs, ctx);
                break;
-       /* STX XADD: lock *(u32 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_W:
-       /* STX XADD: lock *(u64 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_DW:
+       case BPF_STX | BPF_ATOMIC | BPF_W:
+       case BPF_STX | BPF_ATOMIC | BPF_DW:
+               if (insn->imm != BPF_ADD) {
+                       pr_err("bpf-jit: not supported: atomic operation %02x ***\n",
+                              insn->imm);
+                       return -EINVAL;
+               }
+
+               /* atomic_add: lock *(u32 *)(dst + off) += src
+                * atomic_add: lock *(u64 *)(dst + off) += src
+                */
+
                if (off) {
                        if (is_12b_int(off)) {
                                emit_addi(RV_REG_T1, rd, off, ctx);
index e84bdd1..c72874f 100644 (file)
@@ -54,17 +54,23 @@ config KASAN_SHADOW_OFFSET
 
 config S390
        def_bool y
+       #
+       # Note: keep this list sorted alphabetically
+       #
+       imply IMA_SECURE_AND_OR_TRUSTED_BOOT
        select ARCH_BINFMT_ELF_STATE
        select ARCH_HAS_DEBUG_VM_PGTABLE
        select ARCH_HAS_DEBUG_WX
        select ARCH_HAS_DEVMEM_IS_ALLOWED
        select ARCH_HAS_ELF_RANDOMIZE
+       select ARCH_HAS_FORCE_DMA_UNENCRYPTED
        select ARCH_HAS_FORTIFY_SOURCE
        select ARCH_HAS_GCOV_PROFILE_ALL
        select ARCH_HAS_GIGANTIC_PAGE
        select ARCH_HAS_KCOV
        select ARCH_HAS_MEM_ENCRYPT
        select ARCH_HAS_PTE_SPECIAL
+       select ARCH_HAS_SCALED_CPUTIME
        select ARCH_HAS_SET_MEMORY
        select ARCH_HAS_STRICT_KERNEL_RWX
        select ARCH_HAS_STRICT_MODULE_RWX
@@ -111,8 +117,10 @@ config S390
        select ARCH_WANT_IPC_PARSE_VERSION
        select BUILDTIME_TABLE_SORT
        select CLONE_BACKWARDS2
+       select CPU_NO_EFFICIENT_FFS if !HAVE_MARCH_Z9_109_FEATURES
        select DMA_OPS if PCI
        select DYNAMIC_FTRACE if FUNCTION_TRACER
+       select GENERIC_ALLOCATOR
        select GENERIC_CPU_AUTOPROBE
        select GENERIC_CPU_VULNERABILITIES
        select GENERIC_FIND_FIRST_BIT
@@ -126,22 +134,21 @@ config S390
        select HAVE_ARCH_JUMP_LABEL_RELATIVE
        select HAVE_ARCH_KASAN
        select HAVE_ARCH_KASAN_VMALLOC
-       select CPU_NO_EFFICIENT_FFS if !HAVE_MARCH_Z9_109_FEATURES
        select HAVE_ARCH_SECCOMP_FILTER
        select HAVE_ARCH_SOFT_DIRTY
        select HAVE_ARCH_TRACEHOOK
        select HAVE_ARCH_TRANSPARENT_HUGEPAGE
        select HAVE_ARCH_VMAP_STACK
        select HAVE_ASM_MODVERSIONS
-       select HAVE_EBPF_JIT if PACK_STACK && HAVE_MARCH_Z196_FEATURES
        select HAVE_CMPXCHG_DOUBLE
        select HAVE_CMPXCHG_LOCAL
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DMA_CONTIGUOUS
        select HAVE_DYNAMIC_FTRACE
        select HAVE_DYNAMIC_FTRACE_WITH_REGS
-       select HAVE_FAST_GUP
+       select HAVE_EBPF_JIT if PACK_STACK && HAVE_MARCH_Z196_FEATURES
        select HAVE_EFFICIENT_UNALIGNED_ACCESS
+       select HAVE_FAST_GUP
        select HAVE_FENTRY
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_FUNCTION_ERROR_INJECTION
@@ -163,16 +170,15 @@ config S390
        select HAVE_KRETPROBES
        select HAVE_KVM
        select HAVE_LIVEPATCH
-       select HAVE_PERF_REGS
-       select HAVE_PERF_USER_STACK_DUMP
        select HAVE_MEMBLOCK_PHYS_MAP
-       select MMU_GATHER_NO_GATHER
        select HAVE_MOD_ARCH_SPECIFIC
+       select HAVE_NMI
        select HAVE_NOP_MCOUNT
        select HAVE_OPROFILE
        select HAVE_PCI
        select HAVE_PERF_EVENTS
-       select MMU_GATHER_RCU_TABLE_FREE
+       select HAVE_PERF_REGS
+       select HAVE_PERF_USER_STACK_DUMP
        select HAVE_REGS_AND_STACK_ACCESS_API
        select HAVE_RELIABLE_STACKTRACE
        select HAVE_RSEQ
@@ -181,6 +187,8 @@ config S390
        select HAVE_VIRT_CPU_ACCOUNTING_IDLE
        select IOMMU_HELPER             if PCI
        select IOMMU_SUPPORT            if PCI
+       select MMU_GATHER_NO_GATHER
+       select MMU_GATHER_RCU_TABLE_FREE
        select MODULES_USE_ELF_RELA
        select NEED_DMA_MAP_STATE       if PCI
        select NEED_SG_DMA_LENGTH       if PCI
@@ -190,17 +198,12 @@ config S390
        select PCI_MSI                  if PCI
        select PCI_MSI_ARCH_FALLBACKS   if PCI_MSI
        select SPARSE_IRQ
+       select SWIOTLB
        select SYSCTL_EXCEPTION_TRACE
        select THREAD_INFO_IN_TASK
        select TTY
        select VIRT_CPU_ACCOUNTING
-       select ARCH_HAS_SCALED_CPUTIME
-       select HAVE_NMI
-       select ARCH_HAS_FORCE_DMA_UNENCRYPTED
-       select SWIOTLB
-       select GENERIC_ALLOCATOR
-       imply IMA_SECURE_AND_OR_TRUSTED_BOOT
-
+       # Note: keep the above list sorted alphabetically
 
 config SCHED_OMIT_FRAME_POINTER
        def_bool y
index 1be32fc..c4f6ff9 100644 (file)
@@ -61,7 +61,9 @@ CONFIG_OPROFILE=m
 CONFIG_KPROBES=y
 CONFIG_JUMP_LABEL=y
 CONFIG_STATIC_KEYS_SELFTEST=y
+CONFIG_SECCOMP_CACHE_DEBUG=y
 CONFIG_LOCK_EVENT_COUNTS=y
+# CONFIG_GCC_PLUGINS is not set
 CONFIG_MODULES=y
 CONFIG_MODULE_FORCE_LOAD=y
 CONFIG_MODULE_UNLOAD=y
@@ -410,12 +412,12 @@ CONFIG_SCSI_ENCLOSURE=m
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SPI_ATTRS=m
-CONFIG_SCSI_FC_ATTRS=y
+CONFIG_SCSI_FC_ATTRS=m
 CONFIG_SCSI_SAS_LIBSAS=m
 CONFIG_SCSI_SRP_ATTRS=m
 CONFIG_ISCSI_TCP=m
 CONFIG_SCSI_DEBUG=m
-CONFIG_ZFCP=y
+CONFIG_ZFCP=m
 CONFIG_SCSI_VIRTIO=m
 CONFIG_SCSI_DH=y
 CONFIG_SCSI_DH_RDAC=m
@@ -444,6 +446,7 @@ CONFIG_DM_MULTIPATH=m
 CONFIG_DM_MULTIPATH_QL=m
 CONFIG_DM_MULTIPATH_ST=m
 CONFIG_DM_MULTIPATH_HST=m
+CONFIG_DM_MULTIPATH_IOA=m
 CONFIG_DM_DELAY=m
 CONFIG_DM_UEVENT=y
 CONFIG_DM_FLAKEY=m
@@ -542,7 +545,6 @@ CONFIG_INPUT_EVDEV=y
 # CONFIG_INPUT_MOUSE is not set
 # CONFIG_SERIO is not set
 CONFIG_LEGACY_PTY_COUNT=0
-CONFIG_NULL_TTY=m
 CONFIG_VIRTIO_CONSOLE=y
 CONFIG_HW_RANDOM_VIRTIO=m
 CONFIG_RAW_DRIVER=m
@@ -574,6 +576,7 @@ CONFIG_VIRTIO_BALLOON=m
 CONFIG_VIRTIO_INPUT=y
 CONFIG_VHOST_NET=m
 CONFIG_VHOST_VSOCK=m
+# CONFIG_SURFACE_PLATFORMS is not set
 CONFIG_S390_CCW_IOMMU=y
 CONFIG_S390_AP_IOMMU=y
 CONFIG_EXT4_FS=y
@@ -655,6 +658,7 @@ CONFIG_CIFS_XATTR=y
 CONFIG_CIFS_POSIX=y
 # CONFIG_CIFS_DEBUG is not set
 CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_SWN_UPCALL=y
 CONFIG_NLS_DEFAULT="utf8"
 CONFIG_NLS_CODEPAGE_437=m
 CONFIG_NLS_CODEPAGE_850=m
@@ -826,6 +830,8 @@ CONFIG_FTRACE_SYSCALLS=y
 CONFIG_BLK_DEV_IO_TRACE=y
 CONFIG_BPF_KPROBE_OVERRIDE=y
 CONFIG_HIST_TRIGGERS=y
+CONFIG_FTRACE_STARTUP_TEST=y
+# CONFIG_EVENT_TRACE_STARTUP_TEST is not set
 CONFIG_DEBUG_USER_ASCE=y
 CONFIG_NOTIFIER_ERROR_INJECTION=m
 CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m
index e2171a0..5113589 100644 (file)
@@ -58,6 +58,7 @@ CONFIG_S390_UNWIND_SELFTEST=m
 CONFIG_OPROFILE=m
 CONFIG_KPROBES=y
 CONFIG_JUMP_LABEL=y
+# CONFIG_GCC_PLUGINS is not set
 CONFIG_MODULES=y
 CONFIG_MODULE_FORCE_LOAD=y
 CONFIG_MODULE_UNLOAD=y
@@ -95,7 +96,6 @@ CONFIG_ZSMALLOC_STAT=y
 CONFIG_DEFERRED_STRUCT_PAGE_INIT=y
 CONFIG_IDLE_PAGE_TRACKING=y
 CONFIG_PERCPU_STATS=y
-CONFIG_GUP_TEST=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_PACKET_DIAG=m
@@ -403,12 +403,12 @@ CONFIG_SCSI_ENCLOSURE=m
 CONFIG_SCSI_CONSTANTS=y
 CONFIG_SCSI_LOGGING=y
 CONFIG_SCSI_SPI_ATTRS=m
-CONFIG_SCSI_FC_ATTRS=y
+CONFIG_SCSI_FC_ATTRS=m
 CONFIG_SCSI_SAS_LIBSAS=m
 CONFIG_SCSI_SRP_ATTRS=m
 CONFIG_ISCSI_TCP=m
 CONFIG_SCSI_DEBUG=m
-CONFIG_ZFCP=y
+CONFIG_ZFCP=m
 CONFIG_SCSI_VIRTIO=m
 CONFIG_SCSI_DH=y
 CONFIG_SCSI_DH_RDAC=m
@@ -437,6 +437,7 @@ CONFIG_DM_MULTIPATH=m
 CONFIG_DM_MULTIPATH_QL=m
 CONFIG_DM_MULTIPATH_ST=m
 CONFIG_DM_MULTIPATH_HST=m
+CONFIG_DM_MULTIPATH_IOA=m
 CONFIG_DM_DELAY=m
 CONFIG_DM_UEVENT=y
 CONFIG_DM_FLAKEY=m
@@ -536,7 +537,6 @@ CONFIG_INPUT_EVDEV=y
 # CONFIG_INPUT_MOUSE is not set
 # CONFIG_SERIO is not set
 CONFIG_LEGACY_PTY_COUNT=0
-CONFIG_NULL_TTY=m
 CONFIG_VIRTIO_CONSOLE=y
 CONFIG_HW_RANDOM_VIRTIO=m
 CONFIG_RAW_DRIVER=m
@@ -568,6 +568,7 @@ CONFIG_VIRTIO_BALLOON=m
 CONFIG_VIRTIO_INPUT=y
 CONFIG_VHOST_NET=m
 CONFIG_VHOST_VSOCK=m
+# CONFIG_SURFACE_PLATFORMS is not set
 CONFIG_S390_CCW_IOMMU=y
 CONFIG_S390_AP_IOMMU=y
 CONFIG_EXT4_FS=y
@@ -645,6 +646,7 @@ CONFIG_CIFS_XATTR=y
 CONFIG_CIFS_POSIX=y
 # CONFIG_CIFS_DEBUG is not set
 CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_CIFS_SWN_UPCALL=y
 CONFIG_NLS_DEFAULT="utf8"
 CONFIG_NLS_CODEPAGE_437=m
 CONFIG_NLS_CODEPAGE_850=m
@@ -778,6 +780,7 @@ CONFIG_FTRACE_SYSCALLS=y
 CONFIG_BLK_DEV_IO_TRACE=y
 CONFIG_BPF_KPROBE_OVERRIDE=y
 CONFIG_HIST_TRIGGERS=y
+CONFIG_DEBUG_USER_ASCE=y
 CONFIG_LKDTM=m
 CONFIG_PERCPU_TEST=m
 CONFIG_ATOMIC64_SELFTEST=y
index a302630..1ef211d 100644 (file)
@@ -22,6 +22,7 @@ CONFIG_CRASH_DUMP=y
 # CONFIG_VIRTUALIZATION is not set
 # CONFIG_S390_GUEST is not set
 # CONFIG_SECCOMP is not set
+# CONFIG_GCC_PLUGINS is not set
 CONFIG_PARTITION_ADVANCED=y
 CONFIG_IBM_PARTITION=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
@@ -58,6 +59,7 @@ CONFIG_RAW_DRIVER=y
 # CONFIG_HID is not set
 # CONFIG_VIRTIO_MENU is not set
 # CONFIG_VHOST_MENU is not set
+# CONFIG_SURFACE_PLATFORMS is not set
 # CONFIG_IOMMU_SUPPORT is not set
 # CONFIG_DNOTIFY is not set
 # CONFIG_INOTIFY_USER is not set
index 319efa0..1a18d7b 100644 (file)
@@ -7,5 +7,4 @@ generated-y += unistd_nr.h
 generic-y += asm-offsets.h
 generic-y += export.h
 generic-y += kvm_types.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
index 0a41827..f973e2e 100644 (file)
@@ -1205,18 +1205,23 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
                jit->seen |= SEEN_MEM;
                break;
        /*
-        * BPF_STX XADD (atomic_add)
+        * BPF_ATOMIC
         */
-       case BPF_STX | BPF_XADD | BPF_W: /* *(u32 *)(dst + off) += src */
-               /* laal %w0,%src,off(%dst) */
-               EMIT6_DISP_LH(0xeb000000, 0x00fa, REG_W0, src_reg,
-                             dst_reg, off);
-               jit->seen |= SEEN_MEM;
-               break;
-       case BPF_STX | BPF_XADD | BPF_DW: /* *(u64 *)(dst + off) += src */
-               /* laalg %w0,%src,off(%dst) */
-               EMIT6_DISP_LH(0xeb000000, 0x00ea, REG_W0, src_reg,
-                             dst_reg, off);
+       case BPF_STX | BPF_ATOMIC | BPF_DW:
+       case BPF_STX | BPF_ATOMIC | BPF_W:
+               if (insn->imm != BPF_ADD) {
+                       pr_err("Unknown atomic operation %02x\n", insn->imm);
+                       return -1;
+               }
+
+               /* *(u32/u64 *)(dst + off) += src
+                *
+                * BFW_W:  laal  %w0,%src,off(%dst)
+                * BPF_DW: laalg %w0,%src,off(%dst)
+                */
+               EMIT6_DISP_LH(0xeb000000,
+                             BPF_SIZE(insn->code) == BPF_W ? 0x00fa : 0x00ea,
+                             REG_W0, src_reg, dst_reg, off);
                jit->seen |= SEEN_MEM;
                break;
        /*
index 5fa5802..52646f5 100644 (file)
@@ -29,7 +29,6 @@ config SUPERH
        select HAVE_ARCH_KGDB
        select HAVE_ARCH_SECCOMP_FILTER
        select HAVE_ARCH_TRACEHOOK
-       select HAVE_COPY_THREAD_TLS
        select HAVE_DEBUG_BUGVERBOSE
        select HAVE_DEBUG_KMEMLEAK
        select HAVE_DYNAMIC_FTRACE
index 8b23ed7..7fb4748 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/sched.h>
 #include <linux/time.h>
 #include <linux/bcd.h>
-#include <linux/rtc.h>
 #include <linux/spinlock.h>
 #include <linux/io.h>
 #include <linux/rtc.h>
index ba6ec04..e6c5ddf 100644 (file)
@@ -27,13 +27,12 @@ CONFIG_NETFILTER=y
 CONFIG_ATALK=m
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDECD=y
-CONFIG_BLK_DEV_OFFBOARD=y
-CONFIG_BLK_DEV_GENERIC=y
-CONFIG_BLK_DEV_AEC62XX=y
+CONFIG_ATA=y
+CONFIG_ATA_GENERIC=y
+CONFIG_PATA_ATP867X=y
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
 CONFIG_SCSI_MULTI_LUN=y
 CONFIG_MD=y
 CONFIG_BLK_DEV_MD=m
index c65667d..e982519 100644 (file)
@@ -20,8 +20,6 @@ CONFIG_IP_PNP=y
 # CONFIG_IPV6 is not set
 # CONFIG_FW_LOADER is not set
 CONFIG_BLK_DEV_RAM=y
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDECD=y
 CONFIG_NETDEVICES=y
 CONFIG_NET_ETHERNET=y
 CONFIG_SMC91X=y
index d10a041..d00376e 100644 (file)
@@ -44,16 +44,14 @@ CONFIG_NET_SCHED=y
 CONFIG_PARPORT=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDECD=y
-CONFIG_BLK_DEV_PLATFORM=y
-CONFIG_BLK_DEV_GENERIC=y
 CONFIG_BLK_DEV_SD=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_CHR_DEV_SG=y
 CONFIG_SCSI_SPI_ATTRS=y
 CONFIG_SCSI_FC_ATTRS=y
 CONFIG_ATA=y
+CONFIG_ATA_GENERIC=y
+CONFIG_PATA_PLATFORM=y
 CONFIG_MD=y
 CONFIG_BLK_DEV_DM=y
 CONFIG_NETDEVICES=y
index 61bec46..4a44cac 100644 (file)
@@ -116,9 +116,6 @@ CONFIG_MTD_UBI_GLUEBI=m
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_CRYPTOLOOP=y
 CONFIG_BLK_DEV_RAM=y
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDECD=y
-CONFIG_BLK_DEV_PLATFORM=y
 CONFIG_BLK_DEV_SD=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_SCSI_MULTI_LUN=y
index 3f1c137..4defc76 100644 (file)
@@ -29,7 +29,6 @@ CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_AMDSTD=y
 CONFIG_MTD_ROM=y
-CONFIG_IDE=y
 CONFIG_SCSI=y
 CONFIG_NETDEVICES=y
 CONFIG_NET_ETHERNET=y
index f0073ed..48b457d 100644 (file)
@@ -39,9 +39,6 @@ CONFIG_IP_PNP_RARP=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_NBD=y
 CONFIG_BLK_DEV_RAM=y
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDECD=m
-CONFIG_BLK_DEV_IDETAPE=m
 CONFIG_SCSI=m
 CONFIG_BLK_DEV_SD=m
 CONFIG_BLK_DEV_SR=m
index d0de378..7d54f28 100644 (file)
@@ -63,8 +63,7 @@ config PVR2_DMA
 
 config G2_DMA
        tristate "G2 Bus DMA support"
-       depends on SH_DREAMCAST
-       select SH_DMA_API
+       depends on SH_DREAMCAST && SH_DMA_API
        help
          This enables support for the DMA controller for the Dreamcast's
          G2 bus. Drivers that want this will generally enable this on
index 7435182..fc44d9c 100644 (file)
@@ -1,6 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 generated-y += syscall_table.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += parport.h
index 3519188..d643250 100644 (file)
@@ -16,7 +16,6 @@
 #include <cpu/gpio.h>
 #endif
 
-#define ARCH_NR_GPIOS 512
 #include <asm-generic/gpio.h>
 
 #ifdef CONFIG_GPIOLIB
index 25eb809..e48b3dd 100644 (file)
@@ -14,7 +14,6 @@
 #include <cpu/mmu_context.h>
 #include <asm/page.h>
 #include <asm/cache.h>
-#include <asm/thread_info.h>
 
 ! NOTE:
 ! GNU as (as of 2.9.1) changes bf/s into bt/s and bra, when the address
index 703d306..77aa2f8 100644 (file)
@@ -105,7 +105,7 @@ config VSYSCALL
          (the default value) say Y.
 
 config NUMA
-       bool "Non Uniform Memory Access (NUMA) Support"
+       bool "Non-Uniform Memory Access (NUMA) Support"
        depends on MMU && SYS_SUPPORTS_NUMA
        select ARCH_WANT_NUMA_VARIABLE_LOCALITY
        default n
index 4c1ca19..d16d6f5 100644 (file)
@@ -26,7 +26,7 @@
 #include <asm/processor.h>
 #include <asm/mmu_context.h>
 
-static int asids_seq_show(struct seq_file *file, void *iter)
+static int asids_debugfs_show(struct seq_file *file, void *iter)
 {
        struct task_struct *p;
 
@@ -48,18 +48,7 @@ static int asids_seq_show(struct seq_file *file, void *iter)
        return 0;
 }
 
-static int asids_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, asids_seq_show, inode->i_private);
-}
-
-static const struct file_operations asids_debugfs_fops = {
-       .owner          = THIS_MODULE,
-       .open           = asids_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(asids_debugfs);
 
 static int __init asids_debugfs_init(void)
 {
index 17d7807..b0f1851 100644 (file)
@@ -22,7 +22,7 @@ enum cache_type {
        CACHE_TYPE_UNIFIED,
 };
 
-static int cache_seq_show(struct seq_file *file, void *iter)
+static int cache_debugfs_show(struct seq_file *file, void *iter)
 {
        unsigned int cache_type = (unsigned int)file->private;
        struct cache_info *cache;
@@ -94,18 +94,7 @@ static int cache_seq_show(struct seq_file *file, void *iter)
        return 0;
 }
 
-static int cache_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, cache_seq_show, inode->i_private);
-}
-
-static const struct file_operations cache_debugfs_fops = {
-       .owner          = THIS_MODULE,
-       .open           = cache_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(cache_debugfs);
 
 static int __init cache_debugfs_init(void)
 {
index b20aba6..68eb7cc 100644 (file)
@@ -812,7 +812,7 @@ bool __in_29bit_mode(void)
         return (__raw_readl(PMB_PASCR) & PASCR_SE) == 0;
 }
 
-static int pmb_seq_show(struct seq_file *file, void *iter)
+static int pmb_debugfs_show(struct seq_file *file, void *iter)
 {
        int i;
 
@@ -846,18 +846,7 @@ static int pmb_seq_show(struct seq_file *file, void *iter)
        return 0;
 }
 
-static int pmb_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, pmb_seq_show, NULL);
-}
-
-static const struct file_operations pmb_debugfs_fops = {
-       .owner          = THIS_MODULE,
-       .open           = pmb_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(pmb_debugfs);
 
 static int __init pmb_debugfs_init(void)
 {
index 5269a70..3688fda 100644 (file)
@@ -6,5 +6,4 @@ generated-y += syscall_table_64.h
 generated-y += syscall_table_c32.h
 generic-y += export.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
index 8751162..c7b2e20 100644 (file)
@@ -50,10 +50,11 @@ extern pte_t *pkmap_page_table;
 
 #define flush_cache_kmaps()    flush_cache_all()
 
-/* FIXME: Use __flush_tlb_one(vaddr) instead of flush_cache_all() -- Anton */
-#define arch_kmap_local_post_map(vaddr, pteval)        flush_cache_all()
-#define arch_kmap_local_post_unmap(vaddr)      flush_cache_all()
-
+/* FIXME: Use __flush_*_one(vaddr) instead of flush_*_all() -- Anton */
+#define arch_kmap_local_pre_map(vaddr, pteval) flush_cache_all()
+#define arch_kmap_local_pre_unmap(vaddr)       flush_cache_all()
+#define arch_kmap_local_post_map(vaddr, pteval)        flush_tlb_all()
+#define arch_kmap_local_post_unmap(vaddr)      flush_tlb_all()
 
 #endif /* __KERNEL__ */
 
index 3364e2a..4b8d3c6 100644 (file)
@@ -1366,12 +1366,18 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
                break;
        }
 
-       /* STX XADD: lock *(u32 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_W: {
+       case BPF_STX | BPF_ATOMIC | BPF_W: {
                const u8 tmp = bpf2sparc[TMP_REG_1];
                const u8 tmp2 = bpf2sparc[TMP_REG_2];
                const u8 tmp3 = bpf2sparc[TMP_REG_3];
 
+               if (insn->imm != BPF_ADD) {
+                       pr_err_once("unknown atomic op %02x\n", insn->imm);
+                       return -EINVAL;
+               }
+
+               /* lock *(u32 *)(dst + off) += src */
+
                if (insn->dst_reg == BPF_REG_FP)
                        ctx->saw_frame_pointer = true;
 
@@ -1390,11 +1396,16 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx)
                break;
        }
        /* STX XADD: lock *(u64 *)(dst + off) += src */
-       case BPF_STX | BPF_XADD | BPF_DW: {
+       case BPF_STX | BPF_ATOMIC | BPF_DW: {
                const u8 tmp = bpf2sparc[TMP_REG_1];
                const u8 tmp2 = bpf2sparc[TMP_REG_2];
                const u8 tmp3 = bpf2sparc[TMP_REG_3];
 
+               if (insn->imm != BPF_ADD) {
+                       pr_err_once("unknown atomic op %02x\n", insn->imm);
+                       return -EINVAL;
+               }
+
                if (insn->dst_reg == BPF_REG_FP)
                        ctx->saw_frame_pointer = true;
 
index 7b6dd10..21f8511 100644 (file)
@@ -19,6 +19,7 @@ config X86_32
        select KMAP_LOCAL
        select MODULES_USE_ELF_REL
        select OLD_SIGACTION
+       select ARCH_SPLIT_ARG64
 
 config X86_64
        def_bool y
index 18d8f17..0904f56 100644 (file)
@@ -73,10 +73,8 @@ static __always_inline void do_syscall_32_irqs_on(struct pt_regs *regs,
                                                  unsigned int nr)
 {
        if (likely(nr < IA32_NR_syscalls)) {
-               instrumentation_begin();
                nr = array_index_nospec(nr, IA32_NR_syscalls);
                regs->ax = ia32_sys_call_table[nr](regs);
-               instrumentation_end();
        }
 }
 
@@ -91,8 +89,11 @@ __visible noinstr void do_int80_syscall_32(struct pt_regs *regs)
         * or may not be necessary, but it matches the old asm behavior.
         */
        nr = (unsigned int)syscall_enter_from_user_mode(regs, nr);
+       instrumentation_begin();
 
        do_syscall_32_irqs_on(regs, nr);
+
+       instrumentation_end();
        syscall_exit_to_user_mode(regs);
 }
 
@@ -121,11 +122,12 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
                res = get_user(*(u32 *)&regs->bp,
                       (u32 __user __force *)(unsigned long)(u32)regs->sp);
        }
-       instrumentation_end();
 
        if (res) {
                /* User code screwed up. */
                regs->ax = -EFAULT;
+
+               instrumentation_end();
                syscall_exit_to_user_mode(regs);
                return false;
        }
@@ -135,6 +137,8 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
 
        /* Now this is just like a normal syscall. */
        do_syscall_32_irqs_on(regs, nr);
+
+       instrumentation_end();
        syscall_exit_to_user_mode(regs);
        return true;
 }
index e04d90a..6375967 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/hyperv-tlfs.h>
 #include <asm/mshyperv.h>
 #include <asm/idtentry.h>
+#include <linux/kexec.h>
 #include <linux/version.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
@@ -26,6 +27,8 @@
 #include <linux/syscore_ops.h>
 #include <clocksource/hyperv_timer.h>
 
+int hyperv_init_cpuhp;
+
 void *hv_hypercall_pg;
 EXPORT_SYMBOL_GPL(hv_hypercall_pg);
 
@@ -312,6 +315,25 @@ static struct syscore_ops hv_syscore_ops = {
        .resume         = hv_resume,
 };
 
+static void (* __initdata old_setup_percpu_clockev)(void);
+
+static void __init hv_stimer_setup_percpu_clockev(void)
+{
+       /*
+        * Ignore any errors in setting up stimer clockevents
+        * as we can run with the LAPIC timer as a fallback.
+        */
+       (void)hv_stimer_alloc();
+
+       /*
+        * Still register the LAPIC timer, because the direct-mode STIMER is
+        * not supported by old versions of Hyper-V. This also allows users
+        * to switch to LAPIC timer via /sys, if they want to.
+        */
+       if (old_setup_percpu_clockev)
+               old_setup_percpu_clockev();
+}
+
 /*
  * This function is to be invoked early in the boot sequence after the
  * hypervisor has been detected.
@@ -390,10 +412,14 @@ void __init hyperv_init(void)
        wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
 
        /*
-        * Ignore any errors in setting up stimer clockevents
-        * as we can run with the LAPIC timer as a fallback.
+        * hyperv_init() is called before LAPIC is initialized: see
+        * apic_intr_mode_init() -> x86_platform.apic_post_init() and
+        * apic_bsp_setup() -> setup_local_APIC(). The direct-mode STIMER
+        * depends on LAPIC, so hv_stimer_alloc() should be called from
+        * x86_init.timers.setup_percpu_clockev.
         */
-       (void)hv_stimer_alloc();
+       old_setup_percpu_clockev = x86_init.timers.setup_percpu_clockev;
+       x86_init.timers.setup_percpu_clockev = hv_stimer_setup_percpu_clockev;
 
        hv_apic_init();
 
@@ -401,6 +427,7 @@ void __init hyperv_init(void)
 
        register_syscore_ops(&hv_syscore_ops);
 
+       hyperv_init_cpuhp = cpuhp;
        return;
 
 remove_cpuhp_state:
index 5208ba4..2c87350 100644 (file)
@@ -66,11 +66,17 @@ static void hyperv_flush_tlb_others(const struct cpumask *cpus,
        if (!hv_hypercall_pg)
                goto do_native;
 
-       if (cpumask_empty(cpus))
-               return;
-
        local_irq_save(flags);
 
+       /*
+        * Only check the mask _after_ interrupt has been disabled to avoid the
+        * mask changing under our feet.
+        */
+       if (cpumask_empty(cpus)) {
+               local_irq_restore(flags);
+               return;
+       }
+
        flush_pcpu = (struct hv_tlb_flush **)
                     this_cpu_ptr(hyperv_pcpu_input_arg);
 
index a5aba4a..67a4f1c 100644 (file)
  * Use kernel_fpu_begin/end() if you intend to use FPU in kernel context. It
  * disables preemption so be careful if you intend to use it for long periods
  * of time.
- * If you intend to use the FPU in softirq you need to check first with
+ * If you intend to use the FPU in irq/softirq you need to check first with
  * irq_fpu_usable() if it is possible.
  */
-extern void kernel_fpu_begin(void);
+
+/* Kernel FPU states to initialize in kernel_fpu_begin_mask() */
+#define KFPU_387       _BITUL(0)       /* 387 state will be initialized */
+#define KFPU_MXCSR     _BITUL(1)       /* MXCSR will be initialized */
+
+extern void kernel_fpu_begin_mask(unsigned int kfpu_mask);
 extern void kernel_fpu_end(void);
 extern bool irq_fpu_usable(void);
 extern void fpregs_mark_activate(void);
 
+/* Code that is unaware of kernel_fpu_begin_mask() can use this */
+static inline void kernel_fpu_begin(void)
+{
+       kernel_fpu_begin_mask(KFPU_387 | KFPU_MXCSR);
+}
+
 /*
  * Use fpregs_lock() while editing CPU's FPU registers or fpu->state.
  * A context switch will (and softirq might) save CPU's FPU registers to
index 247a60a..f656aab 100644 (file)
@@ -613,6 +613,7 @@ DECLARE_IDTENTRY_VC(X86_TRAP_VC,    exc_vmm_communication);
 
 #ifdef CONFIG_XEN_PV
 DECLARE_IDTENTRY_XENCB(X86_TRAP_OTHER, exc_xen_hypervisor_callback);
+DECLARE_IDTENTRY_RAW(X86_TRAP_OTHER,   exc_xen_unknown_trap);
 #endif
 
 /* Device interrupts common/spurious */
index 5e658ba..9abe842 100644 (file)
@@ -97,6 +97,7 @@
 
 #define        INTEL_FAM6_LAKEFIELD            0x8A
 #define INTEL_FAM6_ALDERLAKE           0x97
+#define INTEL_FAM6_ALDERLAKE_L         0x9A
 
 /* "Small Core" Processors (Atom) */
 
index 3ab7b46..3d6616f 100644 (file)
@@ -1010,9 +1010,21 @@ struct kvm_arch {
         */
        bool tdp_mmu_enabled;
 
-       /* List of struct tdp_mmu_pages being used as roots */
+       /*
+        * List of struct kvmp_mmu_pages being used as roots.
+        * All struct kvm_mmu_pages in the list should have
+        * tdp_mmu_page set.
+        * All struct kvm_mmu_pages in the list should have a positive
+        * root_count except when a thread holds the MMU lock and is removing
+        * an entry from the list.
+        */
        struct list_head tdp_mmu_roots;
-       /* List of struct tdp_mmu_pages not being used as roots */
+
+       /*
+        * List of struct kvmp_mmu_pages not being used as roots.
+        * All struct kvm_mmu_pages in the list should have
+        * tdp_mmu_page set and a root_count of 0.
+        */
        struct list_head tdp_mmu_pages;
 };
 
@@ -1287,6 +1299,8 @@ struct kvm_x86_ops {
        void (*migrate_timers)(struct kvm_vcpu *vcpu);
        void (*msr_filter_changed)(struct kvm_vcpu *vcpu);
        int (*complete_emulated_msr)(struct kvm_vcpu *vcpu, int err);
+
+       void (*vcpu_deliver_sipi_vector)(struct kvm_vcpu *vcpu, u8 vector);
 };
 
 struct kvm_x86_nested_ops {
@@ -1468,6 +1482,7 @@ int kvm_fast_pio(struct kvm_vcpu *vcpu, int size, unsigned short port, int in);
 int kvm_emulate_cpuid(struct kvm_vcpu *vcpu);
 int kvm_emulate_halt(struct kvm_vcpu *vcpu);
 int kvm_vcpu_halt(struct kvm_vcpu *vcpu);
+int kvm_emulate_ap_reset_hold(struct kvm_vcpu *vcpu);
 int kvm_emulate_wbinvd(struct kvm_vcpu *vcpu);
 
 void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
diff --git a/arch/x86/include/asm/local64.h b/arch/x86/include/asm/local64.h
deleted file mode 100644 (file)
index 36c93b5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
index ffc2899..30f76b9 100644 (file)
@@ -74,6 +74,8 @@ static inline void hv_disable_stimer0_percpu_irq(int irq) {}
 
 
 #if IS_ENABLED(CONFIG_HYPERV)
+extern int hyperv_init_cpuhp;
+
 extern void *hv_hypercall_pg;
 extern void  __percpu  **hyperv_pcpu_input_arg;
 
index 0b4920a..e16cccd 100644 (file)
@@ -86,7 +86,7 @@ static inline void do_trace_rdpmc(unsigned int msr, u64 val, int failed) {}
  * think of extending them - you will be slapped with a stinking trout or a frozen
  * shark will reach you, wherever you are! You've been warned.
  */
-static inline unsigned long long notrace __rdmsr(unsigned int msr)
+static __always_inline unsigned long long __rdmsr(unsigned int msr)
 {
        DECLARE_ARGS(val, low, high);
 
@@ -98,7 +98,7 @@ static inline unsigned long long notrace __rdmsr(unsigned int msr)
        return EAX_EDX_VAL(val, low, high);
 }
 
-static inline void notrace __wrmsr(unsigned int msr, u32 low, u32 high)
+static __always_inline void __wrmsr(unsigned int msr, u32 low, u32 high)
 {
        asm volatile("1: wrmsr\n"
                     "2:\n"
index 488a8e8..9239399 100644 (file)
@@ -110,6 +110,8 @@ extern const struct cpumask *cpu_coregroup_mask(int cpu);
 #define topology_die_id(cpu)                   (cpu_data(cpu).cpu_die_id)
 #define topology_core_id(cpu)                  (cpu_data(cpu).cpu_core_id)
 
+extern unsigned int __max_die_per_package;
+
 #ifdef CONFIG_SMP
 #define topology_die_cpumask(cpu)              (per_cpu(cpu_die_map, cpu))
 #define topology_core_cpumask(cpu)             (per_cpu(cpu_core_map, cpu))
@@ -118,8 +120,6 @@ extern const struct cpumask *cpu_coregroup_mask(int cpu);
 extern unsigned int __max_logical_packages;
 #define topology_max_packages()                        (__max_logical_packages)
 
-extern unsigned int __max_die_per_package;
-
 static inline int topology_max_die_per_package(void)
 {
        return __max_die_per_package;
index f8ca66f..347a956 100644 (file)
@@ -542,12 +542,12 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
                u32 ecx;
 
                ecx = cpuid_ecx(0x8000001e);
-               nodes_per_socket = ((ecx >> 8) & 7) + 1;
+               __max_die_per_package = nodes_per_socket = ((ecx >> 8) & 7) + 1;
        } else if (boot_cpu_has(X86_FEATURE_NODEID_MSR)) {
                u64 value;
 
                rdmsrl(MSR_FAM10H_NODE_ID, value);
-               nodes_per_socket = ((value >> 3) & 7) + 1;
+               __max_die_per_package = nodes_per_socket = ((value >> 3) & 7) + 1;
        }
 
        if (!boot_cpu_has(X86_FEATURE_AMD_SSBD) &&
index 13d3f1c..e133ce1 100644 (file)
@@ -1992,10 +1992,9 @@ static __always_inline void exc_machine_check_kernel(struct pt_regs *regs)
         * that out because it's an indirect call. Annotate it.
         */
        instrumentation_begin();
-       trace_hardirqs_off_finish();
+
        machine_check_vector(regs);
-       if (regs->flags & X86_EFLAGS_IF)
-               trace_hardirqs_on_prepare();
+
        instrumentation_end();
        irqentry_nmi_exit(regs, irq_state);
 }
@@ -2004,7 +2003,9 @@ static __always_inline void exc_machine_check_user(struct pt_regs *regs)
 {
        irqentry_enter_from_user_mode(regs);
        instrumentation_begin();
+
        machine_check_vector(regs);
+
        instrumentation_end();
        irqentry_exit_to_user_mode(regs);
 }
index f628e3d..43b54be 100644 (file)
@@ -135,14 +135,32 @@ static void hv_machine_shutdown(void)
 {
        if (kexec_in_progress && hv_kexec_handler)
                hv_kexec_handler();
+
+       /*
+        * Call hv_cpu_die() on all the CPUs, otherwise later the hypervisor
+        * corrupts the old VP Assist Pages and can crash the kexec kernel.
+        */
+       if (kexec_in_progress && hyperv_init_cpuhp > 0)
+               cpuhp_remove_state(hyperv_init_cpuhp);
+
+       /* The function calls stop_other_cpus(). */
        native_machine_shutdown();
+
+       /* Disable the hypercall page when there is only 1 active CPU. */
+       if (kexec_in_progress)
+               hyperv_cleanup();
 }
 
 static void hv_machine_crash_shutdown(struct pt_regs *regs)
 {
        if (hv_crash_handler)
                hv_crash_handler(regs);
+
+       /* The function calls crash_smp_send_stop(). */
        native_machine_crash_shutdown(regs);
+
+       /* Disable the hypercall page when there is only 1 active CPU. */
+       hyperv_cleanup();
 }
 #endif /* CONFIG_KEXEC_CORE */
 #endif /* CONFIG_HYPERV */
index 23ad8e9..a29997e 100644 (file)
@@ -167,9 +167,6 @@ static u8 mtrr_type_lookup_variable(u64 start, u64 end, u64 *partial_end,
        *repeat = 0;
        *uniform = 1;
 
-       /* Make end inclusive instead of exclusive */
-       end--;
-
        prev_match = MTRR_TYPE_INVALID;
        for (i = 0; i < num_var_ranges; ++i) {
                unsigned short start_state, end_state, inclusive;
@@ -261,6 +258,9 @@ u8 mtrr_type_lookup(u64 start, u64 end, u8 *uniform)
        int repeat;
        u64 partial_end;
 
+       /* Make end inclusive instead of exclusive */
+       end--;
+
        if (!mtrr_state_set)
                return MTRR_TYPE_INVALID;
 
index 29ffb95..460f3e0 100644 (file)
@@ -525,89 +525,70 @@ static void rdtgroup_remove(struct rdtgroup *rdtgrp)
        kfree(rdtgrp);
 }
 
-struct task_move_callback {
-       struct callback_head    work;
-       struct rdtgroup         *rdtgrp;
-};
-
-static void move_myself(struct callback_head *head)
+static void _update_task_closid_rmid(void *task)
 {
-       struct task_move_callback *callback;
-       struct rdtgroup *rdtgrp;
-
-       callback = container_of(head, struct task_move_callback, work);
-       rdtgrp = callback->rdtgrp;
-
        /*
-        * If resource group was deleted before this task work callback
-        * was invoked, then assign the task to root group and free the
-        * resource group.
+        * If the task is still current on this CPU, update PQR_ASSOC MSR.
+        * Otherwise, the MSR is updated when the task is scheduled in.
         */
-       if (atomic_dec_and_test(&rdtgrp->waitcount) &&
-           (rdtgrp->flags & RDT_DELETED)) {
-               current->closid = 0;
-               current->rmid = 0;
-               rdtgroup_remove(rdtgrp);
-       }
-
-       if (unlikely(current->flags & PF_EXITING))
-               goto out;
-
-       preempt_disable();
-       /* update PQR_ASSOC MSR to make resource group go into effect */
-       resctrl_sched_in();
-       preempt_enable();
+       if (task == current)
+               resctrl_sched_in();
+}
 
-out:
-       kfree(callback);
+static void update_task_closid_rmid(struct task_struct *t)
+{
+       if (IS_ENABLED(CONFIG_SMP) && task_curr(t))
+               smp_call_function_single(task_cpu(t), _update_task_closid_rmid, t, 1);
+       else
+               _update_task_closid_rmid(t);
 }
 
 static int __rdtgroup_move_task(struct task_struct *tsk,
                                struct rdtgroup *rdtgrp)
 {
-       struct task_move_callback *callback;
-       int ret;
-
-       callback = kzalloc(sizeof(*callback), GFP_KERNEL);
-       if (!callback)
-               return -ENOMEM;
-       callback->work.func = move_myself;
-       callback->rdtgrp = rdtgrp;
+       /* If the task is already in rdtgrp, no need to move the task. */
+       if ((rdtgrp->type == RDTCTRL_GROUP && tsk->closid == rdtgrp->closid &&
+            tsk->rmid == rdtgrp->mon.rmid) ||
+           (rdtgrp->type == RDTMON_GROUP && tsk->rmid == rdtgrp->mon.rmid &&
+            tsk->closid == rdtgrp->mon.parent->closid))
+               return 0;
 
        /*
-        * Take a refcount, so rdtgrp cannot be freed before the
-        * callback has been invoked.
+        * Set the task's closid/rmid before the PQR_ASSOC MSR can be
+        * updated by them.
+        *
+        * For ctrl_mon groups, move both closid and rmid.
+        * For monitor groups, can move the tasks only from
+        * their parent CTRL group.
         */
-       atomic_inc(&rdtgrp->waitcount);
-       ret = task_work_add(tsk, &callback->work, TWA_RESUME);
-       if (ret) {
-               /*
-                * Task is exiting. Drop the refcount and free the callback.
-                * No need to check the refcount as the group cannot be
-                * deleted before the write function unlocks rdtgroup_mutex.
-                */
-               atomic_dec(&rdtgrp->waitcount);
-               kfree(callback);
-               rdt_last_cmd_puts("Task exited\n");
-       } else {
-               /*
-                * For ctrl_mon groups move both closid and rmid.
-                * For monitor groups, can move the tasks only from
-                * their parent CTRL group.
-                */
-               if (rdtgrp->type == RDTCTRL_GROUP) {
-                       tsk->closid = rdtgrp->closid;
+
+       if (rdtgrp->type == RDTCTRL_GROUP) {
+               tsk->closid = rdtgrp->closid;
+               tsk->rmid = rdtgrp->mon.rmid;
+       } else if (rdtgrp->type == RDTMON_GROUP) {
+               if (rdtgrp->mon.parent->closid == tsk->closid) {
                        tsk->rmid = rdtgrp->mon.rmid;
-               } else if (rdtgrp->type == RDTMON_GROUP) {
-                       if (rdtgrp->mon.parent->closid == tsk->closid) {
-                               tsk->rmid = rdtgrp->mon.rmid;
-                       } else {
-                               rdt_last_cmd_puts("Can't move task to different control group\n");
-                               ret = -EINVAL;
-                       }
+               } else {
+                       rdt_last_cmd_puts("Can't move task to different control group\n");
+                       return -EINVAL;
                }
        }
-       return ret;
+
+       /*
+        * Ensure the task's closid and rmid are written before determining if
+        * the task is current that will decide if it will be interrupted.
+        */
+       barrier();
+
+       /*
+        * By now, the task's closid and rmid are set. If the task is current
+        * on a CPU, the PQR_ASSOC MSR needs to be updated to make the resource
+        * group go into effect. If the task is not current, the MSR will be
+        * updated when the task is scheduled in.
+        */
+       update_task_closid_rmid(tsk);
+
+       return 0;
 }
 
 static bool is_closid_match(struct task_struct *t, struct rdtgroup *r)
index 1068002..8678864 100644 (file)
 #define BITS_SHIFT_NEXT_LEVEL(eax)     ((eax) & 0x1f)
 #define LEVEL_MAX_SIBLINGS(ebx)                ((ebx) & 0xffff)
 
-#ifdef CONFIG_SMP
 unsigned int __max_die_per_package __read_mostly = 1;
 EXPORT_SYMBOL(__max_die_per_package);
 
+#ifdef CONFIG_SMP
 /*
  * Check if given CPUID extended toplogy "leaf" is implemented
  */
index eb86a2b..571220a 100644 (file)
@@ -121,7 +121,7 @@ int copy_fpregs_to_fpstate(struct fpu *fpu)
 }
 EXPORT_SYMBOL(copy_fpregs_to_fpstate);
 
-void kernel_fpu_begin(void)
+void kernel_fpu_begin_mask(unsigned int kfpu_mask)
 {
        preempt_disable();
 
@@ -141,13 +141,14 @@ void kernel_fpu_begin(void)
        }
        __cpu_invalidate_fpregs_state();
 
-       if (boot_cpu_has(X86_FEATURE_XMM))
+       /* Put sane initial values into the control registers. */
+       if (likely(kfpu_mask & KFPU_MXCSR) && boot_cpu_has(X86_FEATURE_XMM))
                ldmxcsr(MXCSR_DEFAULT);
 
-       if (boot_cpu_has(X86_FEATURE_FPU))
+       if (unlikely(kfpu_mask & KFPU_387) && boot_cpu_has(X86_FEATURE_FPU))
                asm volatile ("fninit");
 }
-EXPORT_SYMBOL_GPL(kernel_fpu_begin);
+EXPORT_SYMBOL_GPL(kernel_fpu_begin_mask);
 
 void kernel_fpu_end(void)
 {
index 740f3bd..3412c45 100644 (file)
@@ -660,17 +660,6 @@ static void __init trim_platform_memory_ranges(void)
 
 static void __init trim_bios_range(void)
 {
-       /*
-        * A special case is the first 4Kb of memory;
-        * This is a BIOS owned area, not kernel ram, but generally
-        * not listed as such in the E820 table.
-        *
-        * This typically reserves additional memory (64KiB by default)
-        * since some BIOSes are known to corrupt low memory.  See the
-        * Kconfig help text for X86_RESERVE_LOW.
-        */
-       e820__range_update(0, PAGE_SIZE, E820_TYPE_RAM, E820_TYPE_RESERVED);
-
        /*
         * special case: Some BIOSes report the PC BIOS
         * area (640Kb -> 1Mb) as RAM even though it is not.
@@ -728,6 +717,15 @@ early_param("reservelow", parse_reservelow);
 
 static void __init trim_low_memory_range(void)
 {
+       /*
+        * A special case is the first 4Kb of memory;
+        * This is a BIOS owned area, not kernel ram, but generally
+        * not listed as such in the E820 table.
+        *
+        * This typically reserves additional memory (64KiB by default)
+        * since some BIOSes are known to corrupt low memory.  See the
+        * Kconfig help text for X86_RESERVE_LOW.
+        */
        memblock_reserve(0, ALIGN(reserve_low, PAGE_SIZE));
 }
        
index 7d04b35..cdc04d0 100644 (file)
@@ -305,14 +305,14 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
        case 0xe4:
        case 0xe5:
                *exitinfo |= IOIO_TYPE_IN;
-               *exitinfo |= (u64)insn->immediate.value << 16;
+               *exitinfo |= (u8)insn->immediate.value << 16;
                break;
 
        /* OUT immediate opcodes */
        case 0xe6:
        case 0xe7:
                *exitinfo |= IOIO_TYPE_OUT;
-               *exitinfo |= (u64)insn->immediate.value << 16;
+               *exitinfo |= (u8)insn->immediate.value << 16;
                break;
 
        /* IN register opcodes */
index 0bd1a0f..84c1821 100644 (file)
@@ -225,7 +225,7 @@ static inline u64 sev_es_rd_ghcb_msr(void)
        return __rdmsr(MSR_AMD64_SEV_ES_GHCB);
 }
 
-static inline void sev_es_wr_ghcb_msr(u64 val)
+static __always_inline void sev_es_wr_ghcb_msr(u64 val)
 {
        u32 low, high;
 
@@ -286,6 +286,12 @@ static enum es_result vc_write_mem(struct es_em_ctxt *ctxt,
        u16 d2;
        u8  d1;
 
+       /* If instruction ran in kernel mode and the I/O buffer is in kernel space */
+       if (!user_mode(ctxt->regs) && !access_ok(target, size)) {
+               memcpy(dst, buf, size);
+               return ES_OK;
+       }
+
        switch (size) {
        case 1:
                memcpy(&d1, buf, 1);
@@ -335,6 +341,12 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
        u16 d2;
        u8  d1;
 
+       /* If instruction ran in kernel mode and the I/O buffer is in kernel space */
+       if (!user_mode(ctxt->regs) && !access_ok(s, size)) {
+               memcpy(buf, src, size);
+               return ES_OK;
+       }
+
        switch (size) {
        case 1:
                if (get_user(d1, s))
index 8ca66af..117e24f 100644 (file)
@@ -56,6 +56,7 @@
 #include <linux/numa.h>
 #include <linux/pgtable.h>
 #include <linux/overflow.h>
+#include <linux/syscore_ops.h>
 
 #include <asm/acpi.h>
 #include <asm/desc.h>
@@ -2083,6 +2084,23 @@ static void init_counter_refs(void)
        this_cpu_write(arch_prev_mperf, mperf);
 }
 
+#ifdef CONFIG_PM_SLEEP
+static struct syscore_ops freq_invariance_syscore_ops = {
+       .resume = init_counter_refs,
+};
+
+static void register_freq_invariance_syscore_ops(void)
+{
+       /* Bail out if registered already. */
+       if (freq_invariance_syscore_ops.node.prev)
+               return;
+
+       register_syscore_ops(&freq_invariance_syscore_ops);
+}
+#else
+static inline void register_freq_invariance_syscore_ops(void) {}
+#endif
+
 static void init_freq_invariance(bool secondary, bool cppc_ready)
 {
        bool ret = false;
@@ -2109,6 +2127,7 @@ static void init_freq_invariance(bool secondary, bool cppc_ready)
        if (ret) {
                init_counter_refs();
                static_branch_enable(&arch_scale_freq_key);
+               register_freq_invariance_syscore_ops();
                pr_info("Estimated ratio of average max frequency by base frequency (times 1024): %llu\n", arch_max_freq_ratio);
        } else {
                pr_debug("Couldn't determine max cpu frequency, necessary for scale-invariant accounting.\n");
index f15bc16..a889563 100644 (file)
@@ -9,31 +9,6 @@
        (X86_CR4_PVI | X86_CR4_DE | X86_CR4_PCE | X86_CR4_OSFXSR  \
         | X86_CR4_OSXMMEXCPT | X86_CR4_PGE | X86_CR4_TSD | X86_CR4_FSGSBASE)
 
-static inline bool kvm_register_is_available(struct kvm_vcpu *vcpu,
-                                            enum kvm_reg reg)
-{
-       return test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
-}
-
-static inline bool kvm_register_is_dirty(struct kvm_vcpu *vcpu,
-                                        enum kvm_reg reg)
-{
-       return test_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty);
-}
-
-static inline void kvm_register_mark_available(struct kvm_vcpu *vcpu,
-                                              enum kvm_reg reg)
-{
-       __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
-}
-
-static inline void kvm_register_mark_dirty(struct kvm_vcpu *vcpu,
-                                          enum kvm_reg reg)
-{
-       __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
-       __set_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty);
-}
-
 #define BUILD_KVM_GPR_ACCESSORS(lname, uname)                                \
 static __always_inline unsigned long kvm_##lname##_read(struct kvm_vcpu *vcpu)\
 {                                                                            \
@@ -43,7 +18,6 @@ static __always_inline void kvm_##lname##_write(struct kvm_vcpu *vcpu,              \
                                                unsigned long val)            \
 {                                                                            \
        vcpu->arch.regs[VCPU_REGS_##uname] = val;                             \
-       kvm_register_mark_dirty(vcpu, VCPU_REGS_##uname);                     \
 }
 BUILD_KVM_GPR_ACCESSORS(rax, RAX)
 BUILD_KVM_GPR_ACCESSORS(rbx, RBX)
@@ -63,6 +37,31 @@ BUILD_KVM_GPR_ACCESSORS(r14, R14)
 BUILD_KVM_GPR_ACCESSORS(r15, R15)
 #endif
 
+static inline bool kvm_register_is_available(struct kvm_vcpu *vcpu,
+                                            enum kvm_reg reg)
+{
+       return test_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
+}
+
+static inline bool kvm_register_is_dirty(struct kvm_vcpu *vcpu,
+                                        enum kvm_reg reg)
+{
+       return test_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty);
+}
+
+static inline void kvm_register_mark_available(struct kvm_vcpu *vcpu,
+                                              enum kvm_reg reg)
+{
+       __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
+}
+
+static inline void kvm_register_mark_dirty(struct kvm_vcpu *vcpu,
+                                          enum kvm_reg reg)
+{
+       __set_bit(reg, (unsigned long *)&vcpu->arch.regs_avail);
+       __set_bit(reg, (unsigned long *)&vcpu->arch.regs_dirty);
+}
+
 static inline unsigned long kvm_register_read(struct kvm_vcpu *vcpu, int reg)
 {
        if (WARN_ON_ONCE((unsigned int)reg >= NR_VCPU_REGS))
index 3136e05..43ccead 100644 (file)
@@ -674,7 +674,7 @@ static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu)
                           (unsigned long long)vcpu->arch.pv_eoi.msr_val);
                return false;
        }
-       return val & 0x1;
+       return val & KVM_PV_EOI_ENABLED;
 }
 
 static void pv_eoi_set_pending(struct kvm_vcpu *vcpu)
@@ -2898,7 +2898,7 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu)
                        /* evaluate pending_events before reading the vector */
                        smp_rmb();
                        sipi_vector = apic->sipi_vector;
-                       kvm_vcpu_deliver_sipi_vector(vcpu, sipi_vector);
+                       kvm_x86_ops.vcpu_deliver_sipi_vector(vcpu, sipi_vector);
                        vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
                }
        }
index 9c4a9c8..261be1d 100644 (file)
 #define PT32_ROOT_LEVEL 2
 #define PT32E_ROOT_LEVEL 3
 
-static inline u64 rsvd_bits(int s, int e)
+static __always_inline u64 rsvd_bits(int s, int e)
 {
+       BUILD_BUG_ON(__builtin_constant_p(e) && __builtin_constant_p(s) && e < s);
+
+       if (__builtin_constant_p(e))
+               BUILD_BUG_ON(e > 63);
+       else
+               e &= 63;
+
        if (e < s)
                return 0;
 
-       return ((1ULL << (e - s + 1)) - 1) << s;
+       return ((2ULL << (e - s)) - 1) << s;
 }
 
 void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 access_mask);
index c478904..6d16481 100644 (file)
@@ -3493,26 +3493,25 @@ static bool mmio_info_in_cache(struct kvm_vcpu *vcpu, u64 addr, bool direct)
  * Return the level of the lowest level SPTE added to sptes.
  * That SPTE may be non-present.
  */
-static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
+static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes, int *root_level)
 {
        struct kvm_shadow_walk_iterator iterator;
-       int leaf = vcpu->arch.mmu->root_level;
+       int leaf = -1;
        u64 spte;
 
-
        walk_shadow_page_lockless_begin(vcpu);
 
-       for (shadow_walk_init(&iterator, vcpu, addr);
+       for (shadow_walk_init(&iterator, vcpu, addr),
+            *root_level = iterator.level;
             shadow_walk_okay(&iterator);
             __shadow_walk_next(&iterator, spte)) {
                leaf = iterator.level;
                spte = mmu_spte_get_lockless(iterator.sptep);
 
-               sptes[leaf - 1] = spte;
+               sptes[leaf] = spte;
 
                if (!is_shadow_present_pte(spte))
                        break;
-
        }
 
        walk_shadow_page_lockless_end(vcpu);
@@ -3520,14 +3519,12 @@ static int get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
        return leaf;
 }
 
-/* return true if reserved bit is detected on spte. */
+/* return true if reserved bit(s) are detected on a valid, non-MMIO SPTE. */
 static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
 {
-       u64 sptes[PT64_ROOT_MAX_LEVEL];
+       u64 sptes[PT64_ROOT_MAX_LEVEL + 1];
        struct rsvd_bits_validate *rsvd_check;
-       int root = vcpu->arch.mmu->shadow_root_level;
-       int leaf;
-       int level;
+       int root, leaf, level;
        bool reserved = false;
 
        if (!VALID_PAGE(vcpu->arch.mmu->root_hpa)) {
@@ -3536,35 +3533,45 @@ static bool get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep)
        }
 
        if (is_tdp_mmu_root(vcpu->kvm, vcpu->arch.mmu->root_hpa))
-               leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes);
+               leaf = kvm_tdp_mmu_get_walk(vcpu, addr, sptes, &root);
        else
-               leaf = get_walk(vcpu, addr, sptes);
+               leaf = get_walk(vcpu, addr, sptes, &root);
+
+       if (unlikely(leaf < 0)) {
+               *sptep = 0ull;
+               return reserved;
+       }
+
+       *sptep = sptes[leaf];
+
+       /*
+        * Skip reserved bits checks on the terminal leaf if it's not a valid
+        * SPTE.  Note, this also (intentionally) skips MMIO SPTEs, which, by
+        * design, always have reserved bits set.  The purpose of the checks is
+        * to detect reserved bits on non-MMIO SPTEs. i.e. buggy SPTEs.
+        */
+       if (!is_shadow_present_pte(sptes[leaf]))
+               leaf++;
 
        rsvd_check = &vcpu->arch.mmu->shadow_zero_check;
 
-       for (level = root; level >= leaf; level--) {
-               if (!is_shadow_present_pte(sptes[level - 1]))
-                       break;
+       for (level = root; level >= leaf; level--)
                /*
                 * Use a bitwise-OR instead of a logical-OR to aggregate the
                 * reserved bit and EPT's invalid memtype/XWR checks to avoid
                 * adding a Jcc in the loop.
                 */
-               reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level - 1]) |
-                           __is_rsvd_bits_set(rsvd_check, sptes[level - 1],
-                                              level);
-       }
+               reserved |= __is_bad_mt_xwr(rsvd_check, sptes[level]) |
+                           __is_rsvd_bits_set(rsvd_check, sptes[level], level);
 
        if (reserved) {
                pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n",
                       __func__, addr);
                for (level = root; level >= leaf; level--)
                        pr_err("------ spte 0x%llx level %d.\n",
-                              sptes[level - 1], level);
+                              sptes[level], level);
        }
 
-       *sptep = sptes[leaf - 1];
-
        return reserved;
 }
 
index 4bd2f1d..2ef8615 100644 (file)
@@ -44,7 +44,48 @@ void kvm_mmu_uninit_tdp_mmu(struct kvm *kvm)
        WARN_ON(!list_empty(&kvm->arch.tdp_mmu_roots));
 }
 
-#define for_each_tdp_mmu_root(_kvm, _root)                         \
+static void tdp_mmu_put_root(struct kvm *kvm, struct kvm_mmu_page *root)
+{
+       if (kvm_mmu_put_root(kvm, root))
+               kvm_tdp_mmu_free_root(kvm, root);
+}
+
+static inline bool tdp_mmu_next_root_valid(struct kvm *kvm,
+                                          struct kvm_mmu_page *root)
+{
+       lockdep_assert_held(&kvm->mmu_lock);
+
+       if (list_entry_is_head(root, &kvm->arch.tdp_mmu_roots, link))
+               return false;
+
+       kvm_mmu_get_root(kvm, root);
+       return true;
+
+}
+
+static inline struct kvm_mmu_page *tdp_mmu_next_root(struct kvm *kvm,
+                                                    struct kvm_mmu_page *root)
+{
+       struct kvm_mmu_page *next_root;
+
+       next_root = list_next_entry(root, link);
+       tdp_mmu_put_root(kvm, root);
+       return next_root;
+}
+
+/*
+ * Note: this iterator gets and puts references to the roots it iterates over.
+ * This makes it safe to release the MMU lock and yield within the loop, but
+ * if exiting the loop early, the caller must drop the reference to the most
+ * recent root. (Unless keeping a live reference is desirable.)
+ */
+#define for_each_tdp_mmu_root_yield_safe(_kvm, _root)                          \
+       for (_root = list_first_entry(&_kvm->arch.tdp_mmu_roots,        \
+                                     typeof(*_root), link);            \
+            tdp_mmu_next_root_valid(_kvm, _root);                      \
+            _root = tdp_mmu_next_root(_kvm, _root))
+
+#define for_each_tdp_mmu_root(_kvm, _root)                             \
        list_for_each_entry(_root, &_kvm->arch.tdp_mmu_roots, link)
 
 bool is_tdp_mmu_root(struct kvm *kvm, hpa_t hpa)
@@ -447,18 +488,9 @@ bool kvm_tdp_mmu_zap_gfn_range(struct kvm *kvm, gfn_t start, gfn_t end)
        struct kvm_mmu_page *root;
        bool flush = false;
 
-       for_each_tdp_mmu_root(kvm, root) {
-               /*
-                * Take a reference on the root so that it cannot be freed if
-                * this thread releases the MMU lock and yields in this loop.
-                */
-               kvm_mmu_get_root(kvm, root);
-
+       for_each_tdp_mmu_root_yield_safe(kvm, root)
                flush |= zap_gfn_range(kvm, root, start, end, true);
 
-               kvm_mmu_put_root(kvm, root);
-       }
-
        return flush;
 }
 
@@ -619,13 +651,7 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start,
        int ret = 0;
        int as_id;
 
-       for_each_tdp_mmu_root(kvm, root) {
-               /*
-                * Take a reference on the root so that it cannot be freed if
-                * this thread releases the MMU lock and yields in this loop.
-                */
-               kvm_mmu_get_root(kvm, root);
-
+       for_each_tdp_mmu_root_yield_safe(kvm, root) {
                as_id = kvm_mmu_page_as_id(root);
                slots = __kvm_memslots(kvm, as_id);
                kvm_for_each_memslot(memslot, slots) {
@@ -647,8 +673,6 @@ static int kvm_tdp_mmu_handle_hva_range(struct kvm *kvm, unsigned long start,
                        ret |= handler(kvm, memslot, root, gfn_start,
                                       gfn_end, data);
                }
-
-               kvm_mmu_put_root(kvm, root);
        }
 
        return ret;
@@ -838,21 +862,13 @@ bool kvm_tdp_mmu_wrprot_slot(struct kvm *kvm, struct kvm_memory_slot *slot,
        int root_as_id;
        bool spte_set = false;
 
-       for_each_tdp_mmu_root(kvm, root) {
+       for_each_tdp_mmu_root_yield_safe(kvm, root) {
                root_as_id = kvm_mmu_page_as_id(root);
                if (root_as_id != slot->as_id)
                        continue;
 
-               /*
-                * Take a reference on the root so that it cannot be freed if
-                * this thread releases the MMU lock and yields in this loop.
-                */
-               kvm_mmu_get_root(kvm, root);
-
                spte_set |= wrprot_gfn_range(kvm, root, slot->base_gfn,
                             slot->base_gfn + slot->npages, min_level);
-
-               kvm_mmu_put_root(kvm, root);
        }
 
        return spte_set;
@@ -906,21 +922,13 @@ bool kvm_tdp_mmu_clear_dirty_slot(struct kvm *kvm, struct kvm_memory_slot *slot)
        int root_as_id;
        bool spte_set = false;
 
-       for_each_tdp_mmu_root(kvm, root) {
+       for_each_tdp_mmu_root_yield_safe(kvm, root) {
                root_as_id = kvm_mmu_page_as_id(root);
                if (root_as_id != slot->as_id)
                        continue;
 
-               /*
-                * Take a reference on the root so that it cannot be freed if
-                * this thread releases the MMU lock and yields in this loop.
-                */
-               kvm_mmu_get_root(kvm, root);
-
                spte_set |= clear_dirty_gfn_range(kvm, root, slot->base_gfn,
                                slot->base_gfn + slot->npages);
-
-               kvm_mmu_put_root(kvm, root);
        }
 
        return spte_set;
@@ -1029,21 +1037,13 @@ bool kvm_tdp_mmu_slot_set_dirty(struct kvm *kvm, struct kvm_memory_slot *slot)
        int root_as_id;
        bool spte_set = false;
 
-       for_each_tdp_mmu_root(kvm, root) {
+       for_each_tdp_mmu_root_yield_safe(kvm, root) {
                root_as_id = kvm_mmu_page_as_id(root);
                if (root_as_id != slot->as_id)
                        continue;
 
-               /*
-                * Take a reference on the root so that it cannot be freed if
-                * this thread releases the MMU lock and yields in this loop.
-                */
-               kvm_mmu_get_root(kvm, root);
-
                spte_set |= set_dirty_gfn_range(kvm, root, slot->base_gfn,
                                slot->base_gfn + slot->npages);
-
-               kvm_mmu_put_root(kvm, root);
        }
        return spte_set;
 }
@@ -1089,21 +1089,13 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
        struct kvm_mmu_page *root;
        int root_as_id;
 
-       for_each_tdp_mmu_root(kvm, root) {
+       for_each_tdp_mmu_root_yield_safe(kvm, root) {
                root_as_id = kvm_mmu_page_as_id(root);
                if (root_as_id != slot->as_id)
                        continue;
 
-               /*
-                * Take a reference on the root so that it cannot be freed if
-                * this thread releases the MMU lock and yields in this loop.
-                */
-               kvm_mmu_get_root(kvm, root);
-
                zap_collapsible_spte_range(kvm, root, slot->base_gfn,
                                           slot->base_gfn + slot->npages);
-
-               kvm_mmu_put_root(kvm, root);
        }
 }
 
@@ -1160,16 +1152,19 @@ bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
  * Return the level of the lowest level SPTE added to sptes.
  * That SPTE may be non-present.
  */
-int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes)
+int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes,
+                        int *root_level)
 {
        struct tdp_iter iter;
        struct kvm_mmu *mmu = vcpu->arch.mmu;
-       int leaf = vcpu->arch.mmu->shadow_root_level;
        gfn_t gfn = addr >> PAGE_SHIFT;
+       int leaf = -1;
+
+       *root_level = vcpu->arch.mmu->shadow_root_level;
 
        tdp_mmu_for_each_pte(iter, mmu, gfn, gfn + 1) {
                leaf = iter.level;
-               sptes[leaf - 1] = iter.old_spte;
+               sptes[leaf] = iter.old_spte;
        }
 
        return leaf;
index 556e065..cbbdbad 100644 (file)
@@ -44,5 +44,7 @@ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm,
 bool kvm_tdp_mmu_write_protect_gfn(struct kvm *kvm,
                                   struct kvm_memory_slot *slot, gfn_t gfn);
 
-int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes);
+int kvm_tdp_mmu_get_walk(struct kvm_vcpu *vcpu, u64 addr, u64 *sptes,
+                        int *root_level);
+
 #endif /* __KVM_X86_MMU_TDP_MMU_H */
index b0b6674..7a605ad 100644 (file)
@@ -199,6 +199,10 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
 static bool svm_get_nested_state_pages(struct kvm_vcpu *vcpu)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
+
+       if (WARN_ON(!is_guest_mode(vcpu)))
+               return true;
+
        if (!nested_svm_vmrun_msrpm(svm)) {
                vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
                vcpu->run->internal.suberror =
@@ -595,6 +599,8 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
        svm->nested.vmcb12_gpa = 0;
        WARN_ON_ONCE(svm->nested.nested_run_pending);
 
+       kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu);
+
        /* in case we halted in L2 */
        svm->vcpu.arch.mp_state = KVM_MP_STATE_RUNNABLE;
 
@@ -754,6 +760,7 @@ void svm_leave_nested(struct vcpu_svm *svm)
                leave_guest_mode(&svm->vcpu);
                copy_vmcb_control_area(&vmcb->control, &hsave->control);
                nested_svm_uninit_mmu_context(&svm->vcpu);
+               vmcb_mark_all_dirty(svm->vmcb);
        }
 
        kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, &svm->vcpu);
@@ -1194,6 +1201,10 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu,
         * in the registers, the save area of the nested state instead
         * contains saved L1 state.
         */
+
+       svm->nested.nested_run_pending =
+               !!(kvm_state->flags & KVM_STATE_NESTED_RUN_PENDING);
+
        copy_vmcb_control_area(&hsave->control, &svm->vmcb->control);
        hsave->save = *save;
 
index 9858d5a..ac652bc 100644 (file)
@@ -1415,16 +1415,13 @@ static void sev_es_sync_to_ghcb(struct vcpu_svm *svm)
         * to be returned:
         *   GPRs RAX, RBX, RCX, RDX
         *
-        * Copy their values to the GHCB if they are dirty.
+        * Copy their values, even if they may not have been written during the
+        * VM-Exit.  It's the guest's responsibility to not consume random data.
         */
-       if (kvm_register_is_dirty(vcpu, VCPU_REGS_RAX))
-               ghcb_set_rax(ghcb, vcpu->arch.regs[VCPU_REGS_RAX]);
-       if (kvm_register_is_dirty(vcpu, VCPU_REGS_RBX))
-               ghcb_set_rbx(ghcb, vcpu->arch.regs[VCPU_REGS_RBX]);
-       if (kvm_register_is_dirty(vcpu, VCPU_REGS_RCX))
-               ghcb_set_rcx(ghcb, vcpu->arch.regs[VCPU_REGS_RCX]);
-       if (kvm_register_is_dirty(vcpu, VCPU_REGS_RDX))
-               ghcb_set_rdx(ghcb, vcpu->arch.regs[VCPU_REGS_RDX]);
+       ghcb_set_rax(ghcb, vcpu->arch.regs[VCPU_REGS_RAX]);
+       ghcb_set_rbx(ghcb, vcpu->arch.regs[VCPU_REGS_RBX]);
+       ghcb_set_rcx(ghcb, vcpu->arch.regs[VCPU_REGS_RCX]);
+       ghcb_set_rdx(ghcb, vcpu->arch.regs[VCPU_REGS_RDX]);
 }
 
 static void sev_es_sync_from_ghcb(struct vcpu_svm *svm)
@@ -1563,6 +1560,7 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
                        goto vmgexit_err;
                break;
        case SVM_VMGEXIT_NMI_COMPLETE:
+       case SVM_VMGEXIT_AP_HLT_LOOP:
        case SVM_VMGEXIT_AP_JUMP_TABLE:
        case SVM_VMGEXIT_UNSUPPORTED_EVENT:
                break;
@@ -1888,6 +1886,9 @@ int sev_handle_vmgexit(struct vcpu_svm *svm)
        case SVM_VMGEXIT_NMI_COMPLETE:
                ret = svm_invoke_exit_handler(svm, SVM_EXIT_IRET);
                break;
+       case SVM_VMGEXIT_AP_HLT_LOOP:
+               ret = kvm_emulate_ap_reset_hold(&svm->vcpu);
+               break;
        case SVM_VMGEXIT_AP_JUMP_TABLE: {
                struct kvm_sev_info *sev = &to_kvm_svm(svm->vcpu.kvm)->sev_info;
 
@@ -2001,7 +2002,7 @@ void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu)
         * of which one step is to perform a VMLOAD. Since hardware does not
         * perform a VMSAVE on VMRUN, the host savearea must be updated.
         */
-       asm volatile(__ex("vmsave") : : "a" (__sme_page_pa(sd->save_area)) : "memory");
+       asm volatile(__ex("vmsave %0") : : "a" (__sme_page_pa(sd->save_area)) : "memory");
 
        /*
         * Certain MSRs are restored on VMEXIT, only save ones that aren't
@@ -2040,3 +2041,21 @@ void sev_es_vcpu_put(struct vcpu_svm *svm)
                wrmsrl(host_save_user_msrs[i].index, svm->host_user_msrs[i]);
        }
 }
+
+void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       /* First SIPI: Use the values as initially set by the VMM */
+       if (!svm->received_first_sipi) {
+               svm->received_first_sipi = true;
+               return;
+       }
+
+       /*
+        * Subsequent SIPI: Return from an AP Reset Hold VMGEXIT, where
+        * the guest will set the CS and RIP. Set SW_EXIT_INFO_2 to a
+        * non-zero value.
+        */
+       ghcb_set_sw_exit_info_2(svm->ghcb, 1);
+}
index cce0143..f923e14 100644 (file)
@@ -3677,8 +3677,6 @@ static fastpath_t svm_exit_handlers_fastpath(struct kvm_vcpu *vcpu)
        return EXIT_FASTPATH_NONE;
 }
 
-void __svm_vcpu_run(unsigned long vmcb_pa, unsigned long *regs);
-
 static noinstr void svm_vcpu_enter_exit(struct kvm_vcpu *vcpu,
                                        struct vcpu_svm *svm)
 {
@@ -3741,6 +3739,8 @@ static __no_kcsan fastpath_t svm_vcpu_run(struct kvm_vcpu *vcpu)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
+       trace_kvm_entry(vcpu);
+
        svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
        svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
        svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP];
@@ -4384,6 +4384,14 @@ static bool svm_apic_init_signal_blocked(struct kvm_vcpu *vcpu)
                   (vmcb_is_intercept(&svm->vmcb->control, INTERCEPT_INIT));
 }
 
+static void svm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
+{
+       if (!sev_es_guest(vcpu->kvm))
+               return kvm_vcpu_deliver_sipi_vector(vcpu, vector);
+
+       sev_vcpu_deliver_sipi_vector(vcpu, vector);
+}
+
 static void svm_vm_destroy(struct kvm *kvm)
 {
        avic_vm_destroy(kvm);
@@ -4526,6 +4534,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
 
        .msr_filter_changed = svm_msr_filter_changed,
        .complete_emulated_msr = svm_complete_emulated_msr,
+
+       .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
 };
 
 static struct kvm_x86_init_ops svm_init_ops __initdata = {
index 5431e63..0fe874a 100644 (file)
@@ -185,6 +185,7 @@ struct vcpu_svm {
        struct vmcb_save_area *vmsa;
        struct ghcb *ghcb;
        struct kvm_host_map ghcb_map;
+       bool received_first_sipi;
 
        /* SEV-ES scratch area support */
        void *ghcb_sa;
@@ -591,6 +592,7 @@ void sev_es_init_vmcb(struct vcpu_svm *svm);
 void sev_es_create_vcpu(struct vcpu_svm *svm);
 void sev_es_vcpu_load(struct vcpu_svm *svm, int cpu);
 void sev_es_vcpu_put(struct vcpu_svm *svm);
+void sev_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector);
 
 /* vmenter.S */
 
index e2f2656..f2b9bfb 100644 (file)
@@ -3124,13 +3124,9 @@ static int nested_vmx_check_vmentry_hw(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
+static bool nested_get_evmcs_page(struct kvm_vcpu *vcpu)
 {
-       struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
        struct vcpu_vmx *vmx = to_vmx(vcpu);
-       struct kvm_host_map *map;
-       struct page *page;
-       u64 hpa;
 
        /*
         * hv_evmcs may end up being not mapped after migration (when
@@ -3153,6 +3149,17 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
                }
        }
 
+       return true;
+}
+
+static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
+{
+       struct vmcs12 *vmcs12 = get_vmcs12(vcpu);
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+       struct kvm_host_map *map;
+       struct page *page;
+       u64 hpa;
+
        if (nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES)) {
                /*
                 * Translate L1 physical address to host physical
@@ -3221,6 +3228,18 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu)
                exec_controls_setbit(vmx, CPU_BASED_USE_MSR_BITMAPS);
        else
                exec_controls_clearbit(vmx, CPU_BASED_USE_MSR_BITMAPS);
+
+       return true;
+}
+
+static bool vmx_get_nested_state_pages(struct kvm_vcpu *vcpu)
+{
+       if (!nested_get_evmcs_page(vcpu))
+               return false;
+
+       if (is_guest_mode(vcpu) && !nested_get_vmcs12_pages(vcpu))
+               return false;
+
        return true;
 }
 
@@ -4442,6 +4461,8 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
        /* trying to cancel vmlaunch/vmresume is a bug */
        WARN_ON_ONCE(vmx->nested.nested_run_pending);
 
+       kvm_clear_request(KVM_REQ_GET_NESTED_STATE_PAGES, vcpu);
+
        /* Service the TLB flush request for L2 before switching to L1. */
        if (kvm_check_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu))
                kvm_vcpu_flush_tlb_current(vcpu);
@@ -6075,11 +6096,14 @@ static int vmx_get_nested_state(struct kvm_vcpu *vcpu,
        if (is_guest_mode(vcpu)) {
                sync_vmcs02_to_vmcs12(vcpu, vmcs12);
                sync_vmcs02_to_vmcs12_rare(vcpu, vmcs12);
-       } else if (!vmx->nested.need_vmcs12_to_shadow_sync) {
-               if (vmx->nested.hv_evmcs)
-                       copy_enlightened_to_vmcs12(vmx);
-               else if (enable_shadow_vmcs)
-                       copy_shadow_to_vmcs12(vmx);
+       } else  {
+               copy_vmcs02_to_vmcs12_rare(vcpu, get_vmcs12(vcpu));
+               if (!vmx->nested.need_vmcs12_to_shadow_sync) {
+                       if (vmx->nested.hv_evmcs)
+                               copy_enlightened_to_vmcs12(vmx);
+                       else if (enable_shadow_vmcs)
+                               copy_shadow_to_vmcs12(vmx);
+               }
        }
 
        BUILD_BUG_ON(sizeof(user_vmx_nested_state->vmcs12) < VMCS12_SIZE);
@@ -6600,7 +6624,7 @@ struct kvm_x86_nested_ops vmx_nested_ops = {
        .hv_timer_pending = nested_vmx_preemption_timer_pending,
        .get_state = vmx_get_nested_state,
        .set_state = vmx_set_nested_state,
-       .get_nested_state_pages = nested_get_vmcs12_pages,
+       .get_nested_state_pages = vmx_get_nested_state_pages,
        .write_log_dirty = nested_vmx_write_pml_buffer,
        .enable_evmcs = nested_enable_evmcs,
        .get_evmcs_version = nested_get_evmcs_version,
index a886a47..cdf5f34 100644 (file)
@@ -29,7 +29,7 @@ static struct kvm_event_hw_type_mapping intel_arch_events[] = {
        [4] = { 0x2e, 0x41, PERF_COUNT_HW_CACHE_MISSES },
        [5] = { 0xc4, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
        [6] = { 0xc5, 0x00, PERF_COUNT_HW_BRANCH_MISSES },
-       [7] = { 0x00, 0x30, PERF_COUNT_HW_REF_CPU_CYCLES },
+       [7] = { 0x00, 0x03, PERF_COUNT_HW_REF_CPU_CYCLES },
 };
 
 /* mapping between fixed pmc index and intel_arch_events array */
@@ -345,7 +345,9 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
 
        pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters,
                                         x86_pmu.num_counters_gp);
+       eax.split.bit_width = min_t(int, eax.split.bit_width, x86_pmu.bit_width_gp);
        pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1;
+       eax.split.mask_length = min_t(int, eax.split.mask_length, x86_pmu.events_mask_len);
        pmu->available_event_types = ~entry->ebx &
                                        ((1ull << eax.split.mask_length) - 1);
 
@@ -355,6 +357,8 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu)
                pmu->nr_arch_fixed_counters =
                        min_t(int, edx.split.num_counters_fixed,
                              x86_pmu.num_counters_fixed);
+               edx.split.bit_width_fixed = min_t(int,
+                       edx.split.bit_width_fixed, x86_pmu.bit_width_fixed);
                pmu->counter_bitmask[KVM_PMC_FIXED] =
                        ((u64)1 << edx.split.bit_width_fixed) - 1;
        }
index 75c9c6a..cc60b1f 100644 (file)
@@ -6653,6 +6653,8 @@ reenter_guest:
        if (vmx->emulation_required)
                return EXIT_FASTPATH_NONE;
 
+       trace_kvm_entry(vcpu);
+
        if (vmx->ple_window_dirty) {
                vmx->ple_window_dirty = false;
                vmcs_write32(PLE_WINDOW, vmx->ple_window);
@@ -7707,6 +7709,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = {
        .msr_filter_changed = vmx_msr_filter_changed,
        .complete_emulated_msr = kvm_complete_insn_gp,
        .cpu_dirty_log_size = vmx_cpu_dirty_log_size,
+
+       .vcpu_deliver_sipi_vector = kvm_vcpu_deliver_sipi_vector,
 };
 
 static __init int hardware_setup(void)
index 3f7c1fc..76bce83 100644 (file)
@@ -105,6 +105,7 @@ static u64 __read_mostly cr4_reserved_bits = CR4_RESERVED_BITS;
 
 static void update_cr8_intercept(struct kvm_vcpu *vcpu);
 static void process_nmi(struct kvm_vcpu *vcpu);
+static void process_smi(struct kvm_vcpu *vcpu);
 static void enter_smm(struct kvm_vcpu *vcpu);
 static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags);
 static void store_regs(struct kvm_vcpu *vcpu);
@@ -4230,6 +4231,9 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 {
        process_nmi(vcpu);
 
+       if (kvm_check_request(KVM_REQ_SMI, vcpu))
+               process_smi(vcpu);
+
        /*
         * In guest mode, payload delivery should be deferred,
         * so that the L1 hypervisor can intercept #PF before
@@ -7976,17 +7980,22 @@ void kvm_arch_exit(void)
        kmem_cache_destroy(x86_fpu_cache);
 }
 
-int kvm_vcpu_halt(struct kvm_vcpu *vcpu)
+static int __kvm_vcpu_halt(struct kvm_vcpu *vcpu, int state, int reason)
 {
        ++vcpu->stat.halt_exits;
        if (lapic_in_kernel(vcpu)) {
-               vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
+               vcpu->arch.mp_state = state;
                return 1;
        } else {
-               vcpu->run->exit_reason = KVM_EXIT_HLT;
+               vcpu->run->exit_reason = reason;
                return 0;
        }
 }
+
+int kvm_vcpu_halt(struct kvm_vcpu *vcpu)
+{
+       return __kvm_vcpu_halt(vcpu, KVM_MP_STATE_HALTED, KVM_EXIT_HLT);
+}
 EXPORT_SYMBOL_GPL(kvm_vcpu_halt);
 
 int kvm_emulate_halt(struct kvm_vcpu *vcpu)
@@ -8000,6 +8009,14 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu)
 }
 EXPORT_SYMBOL_GPL(kvm_emulate_halt);
 
+int kvm_emulate_ap_reset_hold(struct kvm_vcpu *vcpu)
+{
+       int ret = kvm_skip_emulated_instruction(vcpu);
+
+       return __kvm_vcpu_halt(vcpu, KVM_MP_STATE_AP_RESET_HOLD, KVM_EXIT_AP_RESET_HOLD) && ret;
+}
+EXPORT_SYMBOL_GPL(kvm_emulate_ap_reset_hold);
+
 #ifdef CONFIG_X86_64
 static int kvm_pv_clock_pairing(struct kvm_vcpu *vcpu, gpa_t paddr,
                                unsigned long clock_type)
@@ -8973,8 +8990,6 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                kvm_x86_ops.request_immediate_exit(vcpu);
        }
 
-       trace_kvm_entry(vcpu);
-
        fpregs_assert_state_consistent();
        if (test_thread_flag(TIF_NEED_FPU_LOAD))
                switch_fpu_return();
@@ -9094,6 +9109,7 @@ static inline int vcpu_block(struct kvm *kvm, struct kvm_vcpu *vcpu)
        kvm_apic_accept_events(vcpu);
        switch(vcpu->arch.mp_state) {
        case KVM_MP_STATE_HALTED:
+       case KVM_MP_STATE_AP_RESET_HOLD:
                vcpu->arch.pv.pv_unhalted = false;
                vcpu->arch.mp_state =
                        KVM_MP_STATE_RUNNABLE;
@@ -9520,8 +9536,9 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
                kvm_load_guest_fpu(vcpu);
 
        kvm_apic_accept_events(vcpu);
-       if (vcpu->arch.mp_state == KVM_MP_STATE_HALTED &&
-                                       vcpu->arch.pv.pv_unhalted)
+       if ((vcpu->arch.mp_state == KVM_MP_STATE_HALTED ||
+            vcpu->arch.mp_state == KVM_MP_STATE_AP_RESET_HOLD) &&
+           vcpu->arch.pv.pv_unhalted)
                mp_state->mp_state = KVM_MP_STATE_RUNNABLE;
        else
                mp_state->mp_state = vcpu->arch.mp_state;
@@ -10152,6 +10169,7 @@ void kvm_vcpu_deliver_sipi_vector(struct kvm_vcpu *vcpu, u8 vector)
        kvm_set_segment(vcpu, &cs, VCPU_SREG_CS);
        kvm_rip_write(vcpu, 0);
 }
+EXPORT_SYMBOL_GPL(kvm_vcpu_deliver_sipi_vector);
 
 int kvm_arch_hardware_enable(void)
 {
@@ -11538,6 +11556,7 @@ int kvm_sev_es_string_io(struct kvm_vcpu *vcpu, unsigned int size,
 }
 EXPORT_SYMBOL_GPL(kvm_sev_es_string_io);
 
+EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_entry);
 EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
 EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_fast_mmio);
 EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq);
index 4321fa0..419365c 100644 (file)
 #include <asm/fpu/api.h>
 #include <asm/asm.h>
 
+/*
+ * Use KFPU_387.  MMX instructions are not affected by MXCSR,
+ * but both AMD and Intel documentation states that even integer MMX
+ * operations will result in #MF if an exception is pending in FCW.
+ *
+ * EMMS is not needed afterwards because, after calling kernel_fpu_end(),
+ * any subsequent user of the 387 stack will reinitialize it using
+ * KFPU_387.
+ */
+
 void *_mmx_memcpy(void *to, const void *from, size_t len)
 {
        void *p;
@@ -37,7 +47,7 @@ void *_mmx_memcpy(void *to, const void *from, size_t len)
        p = to;
        i = len >> 6; /* len/64 */
 
-       kernel_fpu_begin();
+       kernel_fpu_begin_mask(KFPU_387);
 
        __asm__ __volatile__ (
                "1: prefetch (%0)\n"            /* This set is 28 bytes */
@@ -127,7 +137,7 @@ static void fast_clear_page(void *page)
 {
        int i;
 
-       kernel_fpu_begin();
+       kernel_fpu_begin_mask(KFPU_387);
 
        __asm__ __volatile__ (
                "  pxor %%mm0, %%mm0\n" : :
@@ -160,7 +170,7 @@ static void fast_copy_page(void *to, void *from)
 {
        int i;
 
-       kernel_fpu_begin();
+       kernel_fpu_begin_mask(KFPU_387);
 
        /*
         * maybe the prefetch stuff can go before the expensive fnsave...
@@ -247,7 +257,7 @@ static void fast_clear_page(void *page)
 {
        int i;
 
-       kernel_fpu_begin();
+       kernel_fpu_begin_mask(KFPU_387);
 
        __asm__ __volatile__ (
                "  pxor %%mm0, %%mm0\n" : :
@@ -282,7 +292,7 @@ static void fast_copy_page(void *to, void *from)
 {
        int i;
 
-       kernel_fpu_begin();
+       kernel_fpu_begin_mask(KFPU_387);
 
        __asm__ __volatile__ (
                "1: prefetch (%0)\n"
index dfd82f5..f6a9e2e 100644 (file)
@@ -829,6 +829,8 @@ int pud_free_pmd_page(pud_t *pud, unsigned long addr)
        }
 
        free_page((unsigned long)pmd_sv);
+
+       pgtable_pmd_page_dtor(virt_to_page(pmd));
        free_page((unsigned long)pmd);
 
        return 1;
index 796506d..1d4d501 100644 (file)
@@ -205,6 +205,18 @@ static u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg)
        return byte + reg2hex[dst_reg] + (reg2hex[src_reg] << 3);
 }
 
+/* Some 1-byte opcodes for binary ALU operations */
+static u8 simple_alu_opcodes[] = {
+       [BPF_ADD] = 0x01,
+       [BPF_SUB] = 0x29,
+       [BPF_AND] = 0x21,
+       [BPF_OR] = 0x09,
+       [BPF_XOR] = 0x31,
+       [BPF_LSH] = 0xE0,
+       [BPF_RSH] = 0xE8,
+       [BPF_ARSH] = 0xF8,
+};
+
 static void jit_fill_hole(void *area, unsigned int size)
 {
        /* Fill whole space with INT3 instructions */
@@ -681,6 +693,42 @@ static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)
        *pprog = prog;
 }
 
+/* Emit the suffix (ModR/M etc) for addressing *(ptr_reg + off) and val_reg */
+static void emit_insn_suffix(u8 **pprog, u32 ptr_reg, u32 val_reg, int off)
+{
+       u8 *prog = *pprog;
+       int cnt = 0;
+
+       if (is_imm8(off)) {
+               /* 1-byte signed displacement.
+                *
+                * If off == 0 we could skip this and save one extra byte, but
+                * special case of x86 R13 which always needs an offset is not
+                * worth the hassle
+                */
+               EMIT2(add_2reg(0x40, ptr_reg, val_reg), off);
+       } else {
+               /* 4-byte signed displacement */
+               EMIT1_off32(add_2reg(0x80, ptr_reg, val_reg), off);
+       }
+       *pprog = prog;
+}
+
+/*
+ * Emit a REX byte if it will be necessary to address these registers
+ */
+static void maybe_emit_mod(u8 **pprog, u32 dst_reg, u32 src_reg, bool is64)
+{
+       u8 *prog = *pprog;
+       int cnt = 0;
+
+       if (is64)
+               EMIT1(add_2mod(0x48, dst_reg, src_reg));
+       else if (is_ereg(dst_reg) || is_ereg(src_reg))
+               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+       *pprog = prog;
+}
+
 /* LDX: dst_reg = *(u8*)(src_reg + off) */
 static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
 {
@@ -708,15 +756,7 @@ static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
                EMIT2(add_2mod(0x48, src_reg, dst_reg), 0x8B);
                break;
        }
-       /*
-        * If insn->off == 0 we can save one extra byte, but
-        * special case of x86 R13 which always needs an offset
-        * is not worth the hassle
-        */
-       if (is_imm8(off))
-               EMIT2(add_2reg(0x40, src_reg, dst_reg), off);
-       else
-               EMIT1_off32(add_2reg(0x80, src_reg, dst_reg), off);
+       emit_insn_suffix(&prog, src_reg, dst_reg, off);
        *pprog = prog;
 }
 
@@ -751,13 +791,53 @@ static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
                EMIT2(add_2mod(0x48, dst_reg, src_reg), 0x89);
                break;
        }
-       if (is_imm8(off))
-               EMIT2(add_2reg(0x40, dst_reg, src_reg), off);
-       else
-               EMIT1_off32(add_2reg(0x80, dst_reg, src_reg), off);
+       emit_insn_suffix(&prog, dst_reg, src_reg, off);
        *pprog = prog;
 }
 
+static int emit_atomic(u8 **pprog, u8 atomic_op,
+                      u32 dst_reg, u32 src_reg, s16 off, u8 bpf_size)
+{
+       u8 *prog = *pprog;
+       int cnt = 0;
+
+       EMIT1(0xF0); /* lock prefix */
+
+       maybe_emit_mod(&prog, dst_reg, src_reg, bpf_size == BPF_DW);
+
+       /* emit opcode */
+       switch (atomic_op) {
+       case BPF_ADD:
+       case BPF_SUB:
+       case BPF_AND:
+       case BPF_OR:
+       case BPF_XOR:
+               /* lock *(u32/u64*)(dst_reg + off) <op>= src_reg */
+               EMIT1(simple_alu_opcodes[atomic_op]);
+               break;
+       case BPF_ADD | BPF_FETCH:
+               /* src_reg = atomic_fetch_add(dst_reg + off, src_reg); */
+               EMIT2(0x0F, 0xC1);
+               break;
+       case BPF_XCHG:
+               /* src_reg = atomic_xchg(dst_reg + off, src_reg); */
+               EMIT1(0x87);
+               break;
+       case BPF_CMPXCHG:
+               /* r0 = atomic_cmpxchg(dst_reg + off, r0, src_reg); */
+               EMIT2(0x0F, 0xB1);
+               break;
+       default:
+               pr_err("bpf_jit: unknown atomic opcode %02x\n", atomic_op);
+               return -EFAULT;
+       }
+
+       emit_insn_suffix(&prog, dst_reg, src_reg, off);
+
+       *pprog = prog;
+       return 0;
+}
+
 static bool ex_handler_bpf(const struct exception_table_entry *x,
                           struct pt_regs *regs, int trapnr,
                           unsigned long error_code, unsigned long fault_addr)
@@ -802,6 +882,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
        int i, cnt = 0, excnt = 0;
        int proglen = 0;
        u8 *prog = temp;
+       int err;
 
        detect_reg_usage(insn, insn_cnt, callee_regs_used,
                         &tail_call_seen);
@@ -837,17 +918,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                case BPF_ALU64 | BPF_AND | BPF_X:
                case BPF_ALU64 | BPF_OR | BPF_X:
                case BPF_ALU64 | BPF_XOR | BPF_X:
-                       switch (BPF_OP(insn->code)) {
-                       case BPF_ADD: b2 = 0x01; break;
-                       case BPF_SUB: b2 = 0x29; break;
-                       case BPF_AND: b2 = 0x21; break;
-                       case BPF_OR: b2 = 0x09; break;
-                       case BPF_XOR: b2 = 0x31; break;
-                       }
-                       if (BPF_CLASS(insn->code) == BPF_ALU64)
-                               EMIT1(add_2mod(0x48, dst_reg, src_reg));
-                       else if (is_ereg(dst_reg) || is_ereg(src_reg))
-                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       maybe_emit_mod(&prog, dst_reg, src_reg,
+                                      BPF_CLASS(insn->code) == BPF_ALU64);
+                       b2 = simple_alu_opcodes[BPF_OP(insn->code)];
                        EMIT2(b2, add_2reg(0xC0, dst_reg, src_reg));
                        break;
 
@@ -1027,12 +1100,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                        else if (is_ereg(dst_reg))
                                EMIT1(add_1mod(0x40, dst_reg));
 
-                       switch (BPF_OP(insn->code)) {
-                       case BPF_LSH: b3 = 0xE0; break;
-                       case BPF_RSH: b3 = 0xE8; break;
-                       case BPF_ARSH: b3 = 0xF8; break;
-                       }
-
+                       b3 = simple_alu_opcodes[BPF_OP(insn->code)];
                        if (imm32 == 1)
                                EMIT2(0xD1, add_1reg(b3, dst_reg));
                        else
@@ -1066,11 +1134,7 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image,
                        else if (is_ereg(dst_reg))
                                EMIT1(add_1mod(0x40, dst_reg));
 
-                       switch (BPF_OP(insn->code)) {
-                       case BPF_LSH: b3 = 0xE0; break;
-                       case BPF_RSH: b3 = 0xE8; break;
-                       case BPF_ARSH: b3 = 0xF8; break;
-                       }
+                       b3 = simple_alu_opcodes[BPF_OP(insn->code)];
                        EMIT2(0xD3, add_1reg(b3, dst_reg));
 
                        if (src_reg != BPF_REG_4)
@@ -1230,21 +1294,56 @@ st:                     if (is_imm8(insn->off))
                        }
                        break;
 
-                       /* STX XADD: lock *(u32*)(dst_reg + off) += src_reg */
-               case BPF_STX | BPF_XADD | BPF_W:
-                       /* Emit 'lock add dword ptr [rax + off], eax' */
-                       if (is_ereg(dst_reg) || is_ereg(src_reg))
-                               EMIT3(0xF0, add_2mod(0x40, dst_reg, src_reg), 0x01);
-                       else
-                               EMIT2(0xF0, 0x01);
-                       goto xadd;
-               case BPF_STX | BPF_XADD | BPF_DW:
-                       EMIT3(0xF0, add_2mod(0x48, dst_reg, src_reg), 0x01);
-xadd:                  if (is_imm8(insn->off))
-                               EMIT2(add_2reg(0x40, dst_reg, src_reg), insn->off);
-                       else
-                               EMIT1_off32(add_2reg(0x80, dst_reg, src_reg),
-                                           insn->off);
+               case BPF_STX | BPF_ATOMIC | BPF_W:
+               case BPF_STX | BPF_ATOMIC | BPF_DW:
+                       if (insn->imm == (BPF_AND | BPF_FETCH) ||
+                           insn->imm == (BPF_OR | BPF_FETCH) ||
+                           insn->imm == (BPF_XOR | BPF_FETCH)) {
+                               u8 *branch_target;
+                               bool is64 = BPF_SIZE(insn->code) == BPF_DW;
+
+                               /*
+                                * Can't be implemented with a single x86 insn.
+                                * Need to do a CMPXCHG loop.
+                                */
+
+                               /* Will need RAX as a CMPXCHG operand so save R0 */
+                               emit_mov_reg(&prog, true, BPF_REG_AX, BPF_REG_0);
+                               branch_target = prog;
+                               /* Load old value */
+                               emit_ldx(&prog, BPF_SIZE(insn->code),
+                                        BPF_REG_0, dst_reg, insn->off);
+                               /*
+                                * Perform the (commutative) operation locally,
+                                * put the result in the AUX_REG.
+                                */
+                               emit_mov_reg(&prog, is64, AUX_REG, BPF_REG_0);
+                               maybe_emit_mod(&prog, AUX_REG, src_reg, is64);
+                               EMIT2(simple_alu_opcodes[BPF_OP(insn->imm)],
+                                     add_2reg(0xC0, AUX_REG, src_reg));
+                               /* Attempt to swap in new value */
+                               err = emit_atomic(&prog, BPF_CMPXCHG,
+                                                 dst_reg, AUX_REG, insn->off,
+                                                 BPF_SIZE(insn->code));
+                               if (WARN_ON(err))
+                                       return err;
+                               /*
+                                * ZF tells us whether we won the race. If it's
+                                * cleared we need to try again.
+                                */
+                               EMIT2(X86_JNE, -(prog - branch_target) - 2);
+                               /* Return the pre-modification value */
+                               emit_mov_reg(&prog, is64, src_reg, BPF_REG_0);
+                               /* Restore R0 after clobbering RAX */
+                               emit_mov_reg(&prog, true, BPF_REG_0, BPF_REG_AX);
+                               break;
+
+                       }
+
+                       err = emit_atomic(&prog, insn->imm, dst_reg, src_reg,
+                                                 insn->off, BPF_SIZE(insn->code));
+                       if (err)
+                               return err;
                        break;
 
                        /* call */
@@ -1295,20 +1394,16 @@ xadd:                   if (is_imm8(insn->off))
                case BPF_JMP32 | BPF_JSGE | BPF_X:
                case BPF_JMP32 | BPF_JSLE | BPF_X:
                        /* cmp dst_reg, src_reg */
-                       if (BPF_CLASS(insn->code) == BPF_JMP)
-                               EMIT1(add_2mod(0x48, dst_reg, src_reg));
-                       else if (is_ereg(dst_reg) || is_ereg(src_reg))
-                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       maybe_emit_mod(&prog, dst_reg, src_reg,
+                                      BPF_CLASS(insn->code) == BPF_JMP);
                        EMIT2(0x39, add_2reg(0xC0, dst_reg, src_reg));
                        goto emit_cond_jmp;
 
                case BPF_JMP | BPF_JSET | BPF_X:
                case BPF_JMP32 | BPF_JSET | BPF_X:
                        /* test dst_reg, src_reg */
-                       if (BPF_CLASS(insn->code) == BPF_JMP)
-                               EMIT1(add_2mod(0x48, dst_reg, src_reg));
-                       else if (is_ereg(dst_reg) || is_ereg(src_reg))
-                               EMIT1(add_2mod(0x40, dst_reg, src_reg));
+                       maybe_emit_mod(&prog, dst_reg, src_reg,
+                                      BPF_CLASS(insn->code) == BPF_JMP);
                        EMIT2(0x85, add_2reg(0xC0, dst_reg, src_reg));
                        goto emit_cond_jmp;
 
@@ -1344,10 +1439,8 @@ xadd:                    if (is_imm8(insn->off))
                case BPF_JMP32 | BPF_JSLE | BPF_K:
                        /* test dst_reg, dst_reg to save one extra byte */
                        if (imm32 == 0) {
-                               if (BPF_CLASS(insn->code) == BPF_JMP)
-                                       EMIT1(add_2mod(0x48, dst_reg, dst_reg));
-                               else if (is_ereg(dst_reg))
-                                       EMIT1(add_2mod(0x40, dst_reg, dst_reg));
+                               maybe_emit_mod(&prog, dst_reg, dst_reg,
+                                              BPF_CLASS(insn->code) == BPF_JMP);
                                EMIT2(0x85, add_2reg(0xC0, dst_reg, dst_reg));
                                goto emit_cond_jmp;
                        }
index 96fde03..d17b67c 100644 (file)
@@ -2243,10 +2243,8 @@ emit_jmp:
                                return -EFAULT;
                        }
                        break;
-               /* STX XADD: lock *(u32 *)(dst + off) += src */
-               case BPF_STX | BPF_XADD | BPF_W:
-               /* STX XADD: lock *(u64 *)(dst + off) += src */
-               case BPF_STX | BPF_XADD | BPF_DW:
+               case BPF_STX | BPF_ATOMIC | BPF_W:
+               case BPF_STX | BPF_ATOMIC | BPF_DW:
                        goto notyet;
                case BPF_JMP | BPF_EXIT:
                        if (seen_exit) {
index 9e87ab0..e68ea5f 100644 (file)
@@ -164,10 +164,10 @@ static int xen_cpu_up_prepare_hvm(unsigned int cpu)
        else
                per_cpu(xen_vcpu_id, cpu) = cpu;
        rc = xen_vcpu_setup(cpu);
-       if (rc)
+       if (rc || !xen_have_vector_callback)
                return rc;
 
-       if (xen_have_vector_callback && xen_feature(XENFEAT_hvm_safe_pvclock))
+       if (xen_feature(XENFEAT_hvm_safe_pvclock))
                xen_setup_timer(cpu);
 
        rc = xen_smp_intr_init(cpu);
@@ -188,6 +188,8 @@ static int xen_cpu_dead_hvm(unsigned int cpu)
        return 0;
 }
 
+static bool no_vector_callback __initdata;
+
 static void __init xen_hvm_guest_init(void)
 {
        if (xen_pv_domain())
@@ -207,7 +209,7 @@ static void __init xen_hvm_guest_init(void)
 
        xen_panic_handler_init();
 
-       if (xen_feature(XENFEAT_hvm_callback_vector))
+       if (!no_vector_callback && xen_feature(XENFEAT_hvm_callback_vector))
                xen_have_vector_callback = 1;
 
        xen_hvm_smp_init();
@@ -233,6 +235,13 @@ static __init int xen_parse_nopv(char *arg)
 }
 early_param("xen_nopv", xen_parse_nopv);
 
+static __init int xen_parse_no_vector_callback(char *arg)
+{
+       no_vector_callback = true;
+       return 0;
+}
+early_param("xen_no_vector_callback", xen_parse_no_vector_callback);
+
 bool __init xen_hvm_need_lapic(void)
 {
        if (xen_pv_domain())
index 4409306..9a5a50c 100644 (file)
@@ -583,6 +583,13 @@ DEFINE_IDTENTRY_RAW(xenpv_exc_debug)
                exc_debug(regs);
 }
 
+DEFINE_IDTENTRY_RAW(exc_xen_unknown_trap)
+{
+       /* This should never happen and there is no way to handle it. */
+       pr_err("Unknown trap in Xen PV mode.");
+       BUG();
+}
+
 struct trap_array_entry {
        void (*orig)(void);
        void (*xen)(void);
@@ -631,6 +638,7 @@ static bool __ref get_trap_addr(void **addr, unsigned int ist)
 {
        unsigned int nr;
        bool ist_okay = false;
+       bool found = false;
 
        /*
         * Replace trap handler addresses by Xen specific ones.
@@ -645,6 +653,7 @@ static bool __ref get_trap_addr(void **addr, unsigned int ist)
                if (*addr == entry->orig) {
                        *addr = entry->xen;
                        ist_okay = entry->ist_okay;
+                       found = true;
                        break;
                }
        }
@@ -655,9 +664,13 @@ static bool __ref get_trap_addr(void **addr, unsigned int ist)
                nr = (*addr - (void *)early_idt_handler_array[0]) /
                     EARLY_IDT_HANDLER_SIZE;
                *addr = (void *)xen_early_idt_handler_array[nr];
+               found = true;
        }
 
-       if (WARN_ON(ist != 0 && !ist_okay))
+       if (!found)
+               *addr = (void *)xen_asm_exc_xen_unknown_trap;
+
+       if (WARN_ON(found && ist != 0 && !ist_okay))
                return false;
 
        return true;
index f5e7db4..6ff3c88 100644 (file)
@@ -33,9 +33,11 @@ static void __init xen_hvm_smp_prepare_cpus(unsigned int max_cpus)
        int cpu;
 
        native_smp_prepare_cpus(max_cpus);
-       WARN_ON(xen_smp_intr_init(0));
 
-       xen_init_lock_cpu(0);
+       if (xen_have_vector_callback) {
+               WARN_ON(xen_smp_intr_init(0));
+               xen_init_lock_cpu(0);
+       }
 
        for_each_possible_cpu(cpu) {
                if (cpu == 0)
@@ -50,9 +52,11 @@ static void __init xen_hvm_smp_prepare_cpus(unsigned int max_cpus)
 static void xen_hvm_cpu_die(unsigned int cpu)
 {
        if (common_cpu_die(cpu) == 0) {
-               xen_smp_intr_free(cpu);
-               xen_uninit_lock_cpu(cpu);
-               xen_teardown_timer(cpu);
+               if (xen_have_vector_callback) {
+                       xen_smp_intr_free(cpu);
+                       xen_uninit_lock_cpu(cpu);
+                       xen_teardown_timer(cpu);
+               }
        }
 }
 #else
@@ -64,14 +68,19 @@ static void xen_hvm_cpu_die(unsigned int cpu)
 
 void __init xen_hvm_smp_init(void)
 {
-       if (!xen_have_vector_callback)
+       smp_ops.smp_prepare_boot_cpu = xen_hvm_smp_prepare_boot_cpu;
+       smp_ops.smp_prepare_cpus = xen_hvm_smp_prepare_cpus;
+       smp_ops.smp_cpus_done = xen_smp_cpus_done;
+       smp_ops.cpu_die = xen_hvm_cpu_die;
+
+       if (!xen_have_vector_callback) {
+#ifdef CONFIG_PARAVIRT_SPINLOCKS
+               nopvspin = true;
+#endif
                return;
+       }
 
-       smp_ops.smp_prepare_cpus = xen_hvm_smp_prepare_cpus;
        smp_ops.smp_send_reschedule = xen_smp_send_reschedule;
-       smp_ops.cpu_die = xen_hvm_cpu_die;
        smp_ops.send_call_func_ipi = xen_smp_send_call_function_ipi;
        smp_ops.send_call_func_single_ipi = xen_smp_send_call_function_single_ipi;
-       smp_ops.smp_prepare_boot_cpu = xen_hvm_smp_prepare_boot_cpu;
-       smp_ops.smp_cpus_done = xen_smp_cpus_done;
 }
index 1cb0e84..53cf8aa 100644 (file)
@@ -178,6 +178,7 @@ xen_pv_trap asm_exc_simd_coprocessor_error
 #ifdef CONFIG_IA32_EMULATION
 xen_pv_trap entry_INT80_compat
 #endif
+xen_pv_trap asm_exc_xen_unknown_trap
 xen_pv_trap asm_exc_xen_hypervisor_callback
 
        __INIT
index 9718e95..854c5e0 100644 (file)
@@ -2,7 +2,6 @@
 generated-y += syscall_table.h
 generic-y += extable.h
 generic-y += kvm_para.h
-generic-y += local64.h
 generic-y += mcs_spinlock.h
 generic-y += param.h
 generic-y += qrwlock.h
index 9e81d10..9e4eb0f 100644 (file)
@@ -6332,13 +6332,13 @@ static unsigned int bfq_update_depths(struct bfq_data *bfqd,
         * limit 'something'.
         */
        /* no more than 50% of tags for async I/O */
-       bfqd->word_depths[0][0] = max((1U << bt->sb.shift) >> 1, 1U);
+       bfqd->word_depths[0][0] = max(bt->sb.depth >> 1, 1U);
        /*
         * no more than 75% of tags for sync writes (25% extra tags
         * w.r.t. async I/O, to prevent async I/O from starving sync
         * writes)
         */
-       bfqd->word_depths[0][1] = max(((1U << bt->sb.shift) * 3) >> 2, 1U);
+       bfqd->word_depths[0][1] = max((bt->sb.depth * 3) >> 2, 1U);
 
        /*
         * In-word depths in case some bfq_queue is being weight-
@@ -6348,9 +6348,9 @@ static unsigned int bfq_update_depths(struct bfq_data *bfqd,
         * shortage.
         */
        /* no more than ~18% of tags for async I/O */
-       bfqd->word_depths[1][0] = max(((1U << bt->sb.shift) * 3) >> 4, 1U);
+       bfqd->word_depths[1][0] = max((bt->sb.depth * 3) >> 4, 1U);
        /* no more than ~37% of tags for sync writes (~20% extra tags) */
-       bfqd->word_depths[1][1] = max(((1U << bt->sb.shift) * 6) >> 4, 1U);
+       bfqd->word_depths[1][1] = max((bt->sb.depth * 6) >> 4, 1U);
 
        for (i = 0; i < 2; i++)
                for (j = 0; j < 2; j++)
index 96e5fcd..7663a9b 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/bio.h>
 #include <linux/blkdev.h>
 #include <linux/blk-mq.h>
+#include <linux/blk-pm.h>
 #include <linux/highmem.h>
 #include <linux/mm.h>
 #include <linux/pagemap.h>
@@ -424,11 +425,11 @@ EXPORT_SYMBOL(blk_cleanup_queue);
 /**
  * blk_queue_enter() - try to increase q->q_usage_counter
  * @q: request queue pointer
- * @flags: BLK_MQ_REQ_NOWAIT and/or BLK_MQ_REQ_PREEMPT
+ * @flags: BLK_MQ_REQ_NOWAIT and/or BLK_MQ_REQ_PM
  */
 int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags)
 {
-       const bool pm = flags & BLK_MQ_REQ_PREEMPT;
+       const bool pm = flags & BLK_MQ_REQ_PM;
 
        while (true) {
                bool success = false;
@@ -440,7 +441,8 @@ int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags)
                         * responsible for ensuring that that counter is
                         * globally visible before the queue is unfrozen.
                         */
-                       if (pm || !blk_queue_pm_only(q)) {
+                       if ((pm && queue_rpm_status(q) != RPM_SUSPENDED) ||
+                           !blk_queue_pm_only(q)) {
                                success = true;
                        } else {
                                percpu_ref_put(&q->q_usage_counter);
@@ -465,8 +467,7 @@ int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags)
 
                wait_event(q->mq_freeze_wq,
                           (!q->mq_freeze_depth &&
-                           (pm || (blk_pm_request_resume(q),
-                                   !blk_queue_pm_only(q)))) ||
+                           blk_pm_resume_queue(pm, q)) ||
                           blk_queue_dying(q));
                if (blk_queue_dying(q))
                        return -ENODEV;
@@ -630,7 +631,7 @@ struct request *blk_get_request(struct request_queue *q, unsigned int op,
        struct request *req;
 
        WARN_ON_ONCE(op & REQ_NOWAIT);
-       WARN_ON_ONCE(flags & ~(BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_PREEMPT));
+       WARN_ON_ONCE(flags & ~(BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_PM));
 
        req = blk_mq_alloc_request(q, op, flags);
        if (!IS_ERR(req) && q->mq_ops->initialize_rq_fn)
index ac6078a..98d656b 100644 (file)
@@ -2551,8 +2551,8 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio)
        bool use_debt, ioc_locked;
        unsigned long flags;
 
-       /* bypass IOs if disabled or for root cgroup */
-       if (!ioc->enabled || !iocg->level)
+       /* bypass IOs if disabled, still initializing, or for root cgroup */
+       if (!ioc->enabled || !iocg || !iocg->level)
                return;
 
        /* calculate the absolute vtime cost */
@@ -2679,14 +2679,14 @@ static void ioc_rqos_merge(struct rq_qos *rqos, struct request *rq,
                           struct bio *bio)
 {
        struct ioc_gq *iocg = blkg_to_iocg(bio->bi_blkg);
-       struct ioc *ioc = iocg->ioc;
+       struct ioc *ioc = rqos_to_ioc(rqos);
        sector_t bio_end = bio_end_sector(bio);
        struct ioc_now now;
        u64 vtime, abs_cost, cost;
        unsigned long flags;
 
-       /* bypass if disabled or for root cgroup */
-       if (!ioc->enabled || !iocg->level)
+       /* bypass if disabled, still initializing, or for root cgroup */
+       if (!ioc->enabled || !iocg || !iocg->level)
                return;
 
        abs_cost = calc_vtime_cost(bio, iocg, true);
@@ -2863,6 +2863,12 @@ static int blk_iocost_init(struct request_queue *q)
        ioc_refresh_params(ioc, true);
        spin_unlock_irq(&ioc->lock);
 
+       /*
+        * rqos must be added before activation to allow iocg_pd_init() to
+        * lookup the ioc from q. This means that the rqos methods may get
+        * called before policy activation completion, can't assume that the
+        * target bio has an iocg associated and need to test for NULL iocg.
+        */
        rq_qos_add(q, rqos);
        ret = blkcg_activate_policy(q, &blkcg_policy_iocost);
        if (ret) {
index 3094542..4de03da 100644 (file)
@@ -129,6 +129,7 @@ static const char *const blk_queue_flag_name[] = {
        QUEUE_FLAG_NAME(PCI_P2PDMA),
        QUEUE_FLAG_NAME(ZONE_RESETALL),
        QUEUE_FLAG_NAME(RQ_ALLOC_TIME),
+       QUEUE_FLAG_NAME(NOWAIT),
 };
 #undef QUEUE_FLAG_NAME
 
@@ -245,6 +246,7 @@ static const char *const hctx_flag_name[] = {
        HCTX_FLAG_NAME(BLOCKING),
        HCTX_FLAG_NAME(NO_SCHED),
        HCTX_FLAG_NAME(STACKING),
+       HCTX_FLAG_NAME(TAG_HCTX_SHARED),
 };
 #undef HCTX_FLAG_NAME
 
@@ -297,7 +299,6 @@ static const char *const rqf_name[] = {
        RQF_NAME(MIXED_MERGE),
        RQF_NAME(MQ_INFLIGHT),
        RQF_NAME(DONTPREP),
-       RQF_NAME(PREEMPT),
        RQF_NAME(FAILED),
        RQF_NAME(QUIET),
        RQF_NAME(ELVPRIV),
index c338c9b..f285a91 100644 (file)
@@ -294,8 +294,8 @@ static struct request *blk_mq_rq_ctx_init(struct blk_mq_alloc_data *data,
        rq->mq_hctx = data->hctx;
        rq->rq_flags = 0;
        rq->cmd_flags = data->cmd_flags;
-       if (data->flags & BLK_MQ_REQ_PREEMPT)
-               rq->rq_flags |= RQF_PREEMPT;
+       if (data->flags & BLK_MQ_REQ_PM)
+               rq->rq_flags |= RQF_PM;
        if (blk_queue_io_stat(data->q))
                rq->rq_flags |= RQF_IO_STAT;
        INIT_LIST_HEAD(&rq->queuelist);
index b85234d..17bd020 100644 (file)
@@ -67,6 +67,10 @@ int blk_pre_runtime_suspend(struct request_queue *q)
 
        WARN_ON_ONCE(q->rpm_status != RPM_ACTIVE);
 
+       spin_lock_irq(&q->queue_lock);
+       q->rpm_status = RPM_SUSPENDING;
+       spin_unlock_irq(&q->queue_lock);
+
        /*
         * Increase the pm_only counter before checking whether any
         * non-PM blk_queue_enter() calls are in progress to avoid that any
@@ -89,15 +93,14 @@ int blk_pre_runtime_suspend(struct request_queue *q)
        /* Switch q_usage_counter back to per-cpu mode. */
        blk_mq_unfreeze_queue(q);
 
-       spin_lock_irq(&q->queue_lock);
-       if (ret < 0)
+       if (ret < 0) {
+               spin_lock_irq(&q->queue_lock);
+               q->rpm_status = RPM_ACTIVE;
                pm_runtime_mark_last_busy(q->dev);
-       else
-               q->rpm_status = RPM_SUSPENDING;
-       spin_unlock_irq(&q->queue_lock);
+               spin_unlock_irq(&q->queue_lock);
 
-       if (ret)
                blk_clear_pm_only(q);
+       }
 
        return ret;
 }
index ea5507d..a2283cc 100644 (file)
@@ -6,11 +6,14 @@
 #include <linux/pm_runtime.h>
 
 #ifdef CONFIG_PM
-static inline void blk_pm_request_resume(struct request_queue *q)
+static inline int blk_pm_resume_queue(const bool pm, struct request_queue *q)
 {
-       if (q->dev && (q->rpm_status == RPM_SUSPENDED ||
-                      q->rpm_status == RPM_SUSPENDING))
-               pm_request_resume(q->dev);
+       if (!q->dev || !blk_queue_pm_only(q))
+               return 1;       /* Nothing to do */
+       if (pm && q->rpm_status != RPM_SUSPENDED)
+               return 1;       /* Request allowed */
+       pm_request_resume(q->dev);
+       return 0;
 }
 
 static inline void blk_pm_mark_last_busy(struct request *rq)
@@ -44,8 +47,9 @@ static inline void blk_pm_put_request(struct request *rq)
                --rq->q->nr_pending;
 }
 #else
-static inline void blk_pm_request_resume(struct request_queue *q)
+static inline int blk_pm_resume_queue(const bool pm, struct request_queue *q)
 {
+       return 1;
 }
 
 static inline void blk_pm_mark_last_busy(struct request *rq)
index 73faec4..419548e 100644 (file)
@@ -246,15 +246,18 @@ struct block_device *disk_part_iter_next(struct disk_part_iter *piter)
                part = rcu_dereference(ptbl->part[piter->idx]);
                if (!part)
                        continue;
+               piter->part = bdgrab(part);
+               if (!piter->part)
+                       continue;
                if (!bdev_nr_sectors(part) &&
                    !(piter->flags & DISK_PITER_INCL_EMPTY) &&
                    !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 &&
-                     piter->idx == 0))
+                     piter->idx == 0)) {
+                       bdput(piter->part);
+                       piter->part = NULL;
                        continue;
+               }
 
-               piter->part = bdgrab(part);
-               if (!piter->part)
-                       continue;
                piter->idx += inc;
                break;
        }
index 511932a..0959613 100644 (file)
@@ -354,7 +354,7 @@ static uint32_t derive_pub_key(const void *pub_key, uint32_t len, uint8_t *buf)
        memcpy(cur, e, sizeof(e));
        cur += sizeof(e);
        /* Zero parameters to satisfy set_pub_key ABI. */
-       memset(cur, 0, SETKEY_PARAMS_SIZE);
+       memzero_explicit(cur, SETKEY_PARAMS_SIZE);
 
        return cur - buf;
 }
index 8892908..788a4ba 100644 (file)
@@ -356,7 +356,8 @@ int public_key_verify_signature(const struct public_key *pkey,
        if (ret)
                goto error_free_key;
 
-       if (strcmp(sig->pkey_algo, "sm2") == 0 && sig->data_size) {
+       if (sig->pkey_algo && strcmp(sig->pkey_algo, "sm2") == 0 &&
+           sig->data_size) {
                ret = cert_sig_digest_update(sig, tfm);
                if (ret)
                        goto error_free_key;
index d56b860..96f80c8 100644 (file)
@@ -39,7 +39,8 @@ static int ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
        struct ecdh params;
        unsigned int ndigits;
 
-       if (crypto_ecdh_decode_key(buf, len, &params) < 0)
+       if (crypto_ecdh_decode_key(buf, len, &params) < 0 ||
+           params.key_size > sizeof(ctx->private_key))
                return -EINVAL;
 
        ndigits = ecdh_supported_curve(params.curve_id);
index eacbf4f..8f899f8 100644 (file)
@@ -107,6 +107,8 @@ do_xor_speed(struct xor_block_template *tmpl, void *b1, void *b2)
        preempt_enable();
 
        // bytes/ns == GB/s, multiply by 1000 to get MB/s [not MiB/s]
+       if (!min)
+               min = 1;
        speed = (1000 * REPS * BENCH_SIZE) / (unsigned int)ktime_to_ns(min);
        tmpl->speed = speed;
 
index edf1558..ebcf534 100644 (file)
@@ -395,9 +395,6 @@ config ACPI_CONTAINER
 
          This helps support hotplug of nodes, CPUs, and memory.
 
-         To compile this driver as a module, choose M here:
-         the module will be called container.
-
 config ACPI_HOTPLUG_MEMORY
        bool "Memory Hotplug"
        depends on MEMORY_HOTPLUG
@@ -411,9 +408,6 @@ config ACPI_HOTPLUG_MEMORY
          removing memory devices at runtime, you need not enable
          this driver.
 
-         To compile this driver as a module, choose M here:
-         the module will be called acpi_memhotplug.
-
 config ACPI_HOTPLUG_IOAPIC
        bool
        depends on PCI
index cb229e2..e6a5d99 100644 (file)
@@ -97,7 +97,7 @@ void acpi_scan_table_handler(u32 event, void *table, void *context);
 extern struct list_head acpi_bus_id_list;
 
 struct acpi_device_bus_id {
-       char bus_id[15];
+       const char *bus_id;
        unsigned int instance_no;
        struct list_head node;
 };
index 80b668c..1db063b 100644 (file)
@@ -486,6 +486,7 @@ static void acpi_device_del(struct acpi_device *device)
                                acpi_device_bus_id->instance_no--;
                        else {
                                list_del(&acpi_device_bus_id->node);
+                               kfree_const(acpi_device_bus_id->bus_id);
                                kfree(acpi_device_bus_id);
                        }
                        break;
@@ -585,6 +586,8 @@ static int acpi_get_device_data(acpi_handle handle, struct acpi_device **device,
        if (!device)
                return -EINVAL;
 
+       *device = NULL;
+
        status = acpi_get_data_full(handle, acpi_scan_drop_device,
                                    (void **)device, callback);
        if (ACPI_FAILURE(status) || !*device) {
@@ -674,7 +677,14 @@ int acpi_device_add(struct acpi_device *device,
        }
        if (!found) {
                acpi_device_bus_id = new_bus_id;
-               strcpy(acpi_device_bus_id->bus_id, acpi_device_hid(device));
+               acpi_device_bus_id->bus_id =
+                       kstrdup_const(acpi_device_hid(device), GFP_KERNEL);
+               if (!acpi_device_bus_id->bus_id) {
+                       pr_err(PREFIX "Memory allocation error for bus id\n");
+                       result = -ENOMEM;
+                       goto err_free_new_bus_id;
+               }
+
                acpi_device_bus_id->instance_no = 0;
                list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);
        }
@@ -709,6 +719,11 @@ int acpi_device_add(struct acpi_device *device,
        if (device->parent)
                list_del(&device->node);
        list_del(&device->wakeup_list);
+
+ err_free_new_bus_id:
+       if (!found)
+               kfree(new_bus_id);
+
        mutex_unlock(&acpi_device_lock);
 
  err_detach:
index 25fea34..2b69536 100644 (file)
@@ -105,18 +105,8 @@ static void lpi_device_get_constraints_amd(void)
 
        for (i = 0; i < out_obj->package.count; i++) {
                union acpi_object *package = &out_obj->package.elements[i];
-               struct lpi_device_info_amd info = { };
 
-               if (package->type == ACPI_TYPE_INTEGER) {
-                       switch (i) {
-                       case 0:
-                               info.revision = package->integer.value;
-                               break;
-                       case 1:
-                               info.count = package->integer.value;
-                               break;
-                       }
-               } else if (package->type == ACPI_TYPE_PACKAGE) {
+               if (package->type == ACPI_TYPE_PACKAGE) {
                        lpi_constraints_table = kcalloc(package->package.count,
                                                        sizeof(*lpi_constraints_table),
                                                        GFP_KERNEL);
@@ -135,12 +125,10 @@ static void lpi_device_get_constraints_amd(void)
 
                                for (k = 0; k < info_obj->package.count; ++k) {
                                        union acpi_object *obj = &info_obj->package.elements[k];
-                                       union acpi_object *obj_new;
 
                                        list = &lpi_constraints_table[lpi_constraints_table_size];
                                        list->min_dstate = -1;
 
-                                       obj_new = &obj[k];
                                        switch (k) {
                                        case 0:
                                                dev_info.enabled = obj->integer.value;
index 65a3886..5f0472c 100644 (file)
@@ -3607,7 +3607,7 @@ static int idt77252_init_one(struct pci_dev *pcidev,
 
        if ((err = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)))) {
                printk("idt77252: can't enable DMA for PCI device at %s\n", pci_name(pcidev));
-               return err;
+               goto err_out_disable_pdev;
        }
 
        card = kzalloc(sizeof(struct idt77252_dev), GFP_KERNEL);
index 25e08e5..6eb4c7a 100644 (file)
@@ -208,6 +208,16 @@ int device_links_read_lock_held(void)
 #endif
 #endif /* !CONFIG_SRCU */
 
+static bool device_is_ancestor(struct device *dev, struct device *target)
+{
+       while (target->parent) {
+               target = target->parent;
+               if (dev == target)
+                       return true;
+       }
+       return false;
+}
+
 /**
  * device_is_dependent - Check if one device depends on another one
  * @dev: Device to check dependencies for.
@@ -221,7 +231,12 @@ int device_is_dependent(struct device *dev, void *target)
        struct device_link *link;
        int ret;
 
-       if (dev == target)
+       /*
+        * The "ancestors" check is needed to catch the case when the target
+        * device has not been completely initialized yet and it is still
+        * missing from the list of children of its parent device.
+        */
+       if (dev == target || device_is_ancestor(dev, target))
                return 1;
 
        ret = device_for_each_child(dev, target, device_is_dependent);
@@ -456,7 +471,9 @@ static int devlink_add_symlinks(struct device *dev,
        struct device *con = link->consumer;
        char *buf;
 
-       len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
+       len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)),
+                 strlen(dev_bus_name(con)) + strlen(dev_name(con)));
+       len += strlen(":");
        len += strlen("supplier:") + 1;
        buf = kzalloc(len, GFP_KERNEL);
        if (!buf)
@@ -470,12 +487,12 @@ static int devlink_add_symlinks(struct device *dev,
        if (ret)
                goto err_con;
 
-       snprintf(buf, len, "consumer:%s", dev_name(con));
+       snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
        ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf);
        if (ret)
                goto err_con_dev;
 
-       snprintf(buf, len, "supplier:%s", dev_name(sup));
+       snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
        ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf);
        if (ret)
                goto err_sup_dev;
@@ -483,7 +500,7 @@ static int devlink_add_symlinks(struct device *dev,
        goto out;
 
 err_sup_dev:
-       snprintf(buf, len, "consumer:%s", dev_name(con));
+       snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
        sysfs_remove_link(&sup->kobj, buf);
 err_con_dev:
        sysfs_remove_link(&link->link_dev.kobj, "consumer");
@@ -506,7 +523,9 @@ static void devlink_remove_symlinks(struct device *dev,
        sysfs_remove_link(&link->link_dev.kobj, "consumer");
        sysfs_remove_link(&link->link_dev.kobj, "supplier");
 
-       len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
+       len = max(strlen(dev_bus_name(sup)) + strlen(dev_name(sup)),
+                 strlen(dev_bus_name(con)) + strlen(dev_name(con)));
+       len += strlen(":");
        len += strlen("supplier:") + 1;
        buf = kzalloc(len, GFP_KERNEL);
        if (!buf) {
@@ -514,9 +533,9 @@ static void devlink_remove_symlinks(struct device *dev,
                return;
        }
 
-       snprintf(buf, len, "supplier:%s", dev_name(sup));
+       snprintf(buf, len, "supplier:%s:%s", dev_bus_name(sup), dev_name(sup));
        sysfs_remove_link(&con->kobj, buf);
-       snprintf(buf, len, "consumer:%s", dev_name(con));
+       snprintf(buf, len, "consumer:%s:%s", dev_bus_name(con), dev_name(con));
        sysfs_remove_link(&sup->kobj, buf);
        kfree(buf);
 }
@@ -737,8 +756,9 @@ struct device_link *device_link_add(struct device *consumer,
 
        link->link_dev.class = &devlink_class;
        device_set_pm_not_required(&link->link_dev);
-       dev_set_name(&link->link_dev, "%s--%s",
-                    dev_name(supplier), dev_name(consumer));
+       dev_set_name(&link->link_dev, "%s:%s--%s:%s",
+                    dev_bus_name(supplier), dev_name(supplier),
+                    dev_bus_name(consumer), dev_name(consumer));
        if (device_register(&link->link_dev)) {
                put_device(consumer);
                put_device(supplier);
@@ -1808,9 +1828,7 @@ const char *dev_driver_string(const struct device *dev)
         * never change once they are set, so they don't need special care.
         */
        drv = READ_ONCE(dev->driver);
-       return drv ? drv->name :
-                       (dev->bus ? dev->bus->name :
-                       (dev->class ? dev->class->name : ""));
+       return drv ? drv->name : dev_bus_name(dev);
 }
 EXPORT_SYMBOL(dev_driver_string);
 
@@ -4414,6 +4432,12 @@ static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
  *
  * Set the device's firmware node pointer to @fwnode, but if a secondary
  * firmware node of the device is present, preserve it.
+ *
+ * Valid fwnode cases are:
+ *  - primary --> secondary --> -ENODEV
+ *  - primary --> NULL
+ *  - secondary --> -ENODEV
+ *  - NULL
  */
 void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
 {
@@ -4432,8 +4456,9 @@ void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode)
        } else {
                if (fwnode_is_primary(fn)) {
                        dev->fwnode = fn->secondary;
+                       /* Set fn->secondary = NULL, so fn remains the primary fwnode */
                        if (!(parent && fn == parent->fwnode))
-                               fn->secondary = ERR_PTR(-ENODEV);
+                               fn->secondary = NULL;
                } else {
                        dev->fwnode = NULL;
                }
index 2f32f38..9179825 100644 (file)
@@ -370,13 +370,6 @@ static void driver_bound(struct device *dev)
 
        device_pm_check_callbacks(dev);
 
-       /*
-        * Reorder successfully probed devices to the end of the device list.
-        * This ensures that suspend/resume order matches probe order, which
-        * is usually what drivers rely on.
-        */
-       device_pm_move_to_tail(dev);
-
        /*
         * Make sure the device is no longer in one of the deferred lists and
         * kick off retrying all pending devices
@@ -619,6 +612,8 @@ dev_groups_failed:
        else if (drv->remove)
                drv->remove(dev);
 probe_failed:
+       kfree(dev->dma_range_map);
+       dev->dma_range_map = NULL;
        if (dev->bus)
                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                             BUS_NOTIFY_DRIVER_NOT_BOUND, dev);
index 95fd154..8456d83 100644 (file)
@@ -366,6 +366,8 @@ int devm_platform_get_irqs_affinity(struct platform_device *dev,
                return -ERANGE;
 
        nvec = platform_irq_count(dev);
+       if (nvec < 0)
+               return nvec;
 
        if (nvec < minvec)
                return -ENOSPC;
index 8dfac7f..ff2ee87 100644 (file)
@@ -582,8 +582,12 @@ void regmap_debugfs_init(struct regmap *map)
                devname = dev_name(map->dev);
 
        if (name) {
-               map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
+               if (!map->debugfs_name) {
+                       map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
                                              devname, name);
+                       if (!map->debugfs_name)
+                               return;
+               }
                name = map->debugfs_name;
        } else {
                name = devname;
@@ -591,9 +595,10 @@ void regmap_debugfs_init(struct regmap *map)
 
        if (!strcmp(name, "dummy")) {
                kfree(map->debugfs_name);
-
                map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d",
                                                dummy_index);
+               if (!map->debugfs_name)
+                               return;
                name = map->debugfs_name;
                dummy_index++;
        }
index 2623269..583b671 100644 (file)
@@ -445,6 +445,7 @@ config BLK_DEV_RBD
 config BLK_DEV_RSXX
        tristate "IBM Flash Adapter 900GB Full Height PCIe Device Driver"
        depends on PCI
+       select CRC32
        help
          Device driver for IBM's high speed PCIe SSD
          storage device: Flash Adapter 900GB Full Height.
index 4b6d3d8..2ff05a0 100644 (file)
@@ -7,6 +7,7 @@ config BLK_DEV_RNBD_CLIENT
        tristate "RDMA Network Block Device driver client"
        depends on INFINIBAND_RTRS_CLIENT
        select BLK_DEV_RNBD
+       select SG_POOL
        help
          RNBD client is a network block device driver using rdma transport.
 
index 1773c0a..080f58a 100644 (file)
@@ -90,3 +90,4 @@ Kleber Souza <kleber.souza@profitbricks.com>
 Lutz Pogrell <lutz.pogrell@cloud.ionos.com>
 Milind Dumbare <Milind.dumbare@gmail.com>
 Roman Penyaev <roman.penyaev@profitbricks.com>
+Swapnil Ingle <ingleswapnil@gmail.com>
index 96e3f9f..45a4700 100644 (file)
@@ -375,12 +375,19 @@ static struct rnbd_iu *rnbd_get_iu(struct rnbd_clt_session *sess,
        init_waitqueue_head(&iu->comp.wait);
        iu->comp.errno = INT_MAX;
 
+       if (sg_alloc_table(&iu->sgt, 1, GFP_KERNEL)) {
+               rnbd_put_permit(sess, permit);
+               kfree(iu);
+               return NULL;
+       }
+
        return iu;
 }
 
 static void rnbd_put_iu(struct rnbd_clt_session *sess, struct rnbd_iu *iu)
 {
        if (atomic_dec_and_test(&iu->refcount)) {
+               sg_free_table(&iu->sgt);
                rnbd_put_permit(sess, iu->permit);
                kfree(iu);
        }
@@ -487,8 +494,6 @@ static int send_msg_close(struct rnbd_clt_dev *dev, u32 device_id, bool wait)
        iu->buf = NULL;
        iu->dev = dev;
 
-       sg_alloc_table(&iu->sgt, 1, GFP_KERNEL);
-
        msg.hdr.type    = cpu_to_le16(RNBD_MSG_CLOSE);
        msg.device_id   = cpu_to_le32(device_id);
 
@@ -502,7 +507,6 @@ static int send_msg_close(struct rnbd_clt_dev *dev, u32 device_id, bool wait)
                err = errno;
        }
 
-       sg_free_table(&iu->sgt);
        rnbd_put_iu(sess, iu);
        return err;
 }
@@ -575,7 +579,6 @@ static int send_msg_open(struct rnbd_clt_dev *dev, bool wait)
        iu->buf = rsp;
        iu->dev = dev;
 
-       sg_alloc_table(&iu->sgt, 1, GFP_KERNEL);
        sg_init_one(iu->sgt.sgl, rsp, sizeof(*rsp));
 
        msg.hdr.type    = cpu_to_le16(RNBD_MSG_OPEN);
@@ -594,7 +597,6 @@ static int send_msg_open(struct rnbd_clt_dev *dev, bool wait)
                err = errno;
        }
 
-       sg_free_table(&iu->sgt);
        rnbd_put_iu(sess, iu);
        return err;
 }
@@ -622,8 +624,6 @@ static int send_msg_sess_info(struct rnbd_clt_session *sess, bool wait)
 
        iu->buf = rsp;
        iu->sess = sess;
-
-       sg_alloc_table(&iu->sgt, 1, GFP_KERNEL);
        sg_init_one(iu->sgt.sgl, rsp, sizeof(*rsp));
 
        msg.hdr.type = cpu_to_le16(RNBD_MSG_SESS_INFO);
@@ -650,7 +650,6 @@ put_iu:
        } else {
                err = errno;
        }
-       sg_free_table(&iu->sgt);
        rnbd_put_iu(sess, iu);
        return err;
 }
@@ -1698,7 +1697,8 @@ static void rnbd_destroy_sessions(void)
         */
 
        list_for_each_entry_safe(sess, sn, &sess_list, list) {
-               WARN_ON(!rnbd_clt_get_sess(sess));
+               if (!rnbd_clt_get_sess(sess))
+                       continue;
                close_rtrs(sess);
                list_for_each_entry_safe(dev, tn, &sess->devs_list, list) {
                        /*
index b8e4433..a6a68d4 100644 (file)
@@ -338,10 +338,12 @@ static int rnbd_srv_link_ev(struct rtrs_srv *rtrs,
 
 void rnbd_srv_sess_dev_force_close(struct rnbd_srv_sess_dev *sess_dev)
 {
-       mutex_lock(&sess_dev->sess->lock);
-       rnbd_srv_destroy_dev_session_sysfs(sess_dev);
-       mutex_unlock(&sess_dev->sess->lock);
+       struct rnbd_srv_session *sess = sess_dev->sess;
+
        sess_dev->keep_id = true;
+       mutex_lock(&sess->lock);
+       rnbd_srv_destroy_dev_session_sysfs(sess_dev);
+       mutex_unlock(&sess->lock);
 }
 
 static int process_msg_close(struct rtrs_srv *rtrs,
index 5265975..e1c6798 100644 (file)
@@ -945,7 +945,8 @@ static void blkif_set_queue_limits(struct blkfront_info *info)
        if (info->feature_discard) {
                blk_queue_flag_set(QUEUE_FLAG_DISCARD, rq);
                blk_queue_max_discard_sectors(rq, get_capacity(gd));
-               rq->limits.discard_granularity = info->discard_granularity;
+               rq->limits.discard_granularity = info->discard_granularity ?:
+                                                info->physical_sector_size;
                rq->limits.discard_alignment = info->discard_alignment;
                if (info->feature_secdiscard)
                        blk_queue_flag_set(QUEUE_FLAG_SECERASE, rq);
@@ -2179,19 +2180,12 @@ static void blkfront_closing(struct blkfront_info *info)
 
 static void blkfront_setup_discard(struct blkfront_info *info)
 {
-       int err;
-       unsigned int discard_granularity;
-       unsigned int discard_alignment;
-
        info->feature_discard = 1;
-       err = xenbus_gather(XBT_NIL, info->xbdev->otherend,
-               "discard-granularity", "%u", &discard_granularity,
-               "discard-alignment", "%u", &discard_alignment,
-               NULL);
-       if (!err) {
-               info->discard_granularity = discard_granularity;
-               info->discard_alignment = discard_alignment;
-       }
+       info->discard_granularity = xenbus_read_unsigned(info->xbdev->otherend,
+                                                        "discard-granularity",
+                                                        0);
+       info->discard_alignment = xenbus_read_unsigned(info->xbdev->otherend,
+                                                      "discard-alignment", 0);
        info->feature_secdiscard =
                !!xenbus_read_unsigned(info->xbdev->otherend, "discard-secure",
                                       0);
index 845b6c4..2344d56 100644 (file)
@@ -54,6 +54,7 @@ static int integrator_lm_populate(int num, struct device *dev)
                        ret = of_platform_default_populate(child, NULL, dev);
                        if (ret) {
                                dev_err(dev, "failed to populate module\n");
+                               of_node_put(child);
                                return ret;
                        }
                }
index b8e6acd..8af978b 100644 (file)
@@ -840,6 +840,15 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev)
        endpoint_desc.id = endpoint2.id;
        endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev);
 
+       /*
+        * We know that the device has an endpoint because we verified by
+        * interrogating the firmware. This is the case when the device was not
+        * yet discovered by the fsl-mc bus, thus the lookup returned NULL.
+        * Differentiate this case by returning EPROBE_DEFER.
+        */
+       if (!endpoint)
+               return ERR_PTR(-EPROBE_DEFER);
+
        return endpoint;
 }
 EXPORT_SYMBOL_GPL(fsl_mc_get_endpoint);
index 3061896..47d9ec3 100644 (file)
@@ -6,8 +6,6 @@ config MXC_CLK
 
 config MXC_CLK_SCU
        tristate
-       depends on ARCH_MXC
-       depends on IMX_SCU && HAVE_ARM_SMCCC
 
 config CLK_IMX1
        def_bool SOC_IMX1
index eea69d4..7aa7f4a 100644 (file)
@@ -392,7 +392,8 @@ static int mmp2_audio_clk_remove(struct platform_device *pdev)
        return 0;
 }
 
-static int __maybe_unused mmp2_audio_clk_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int mmp2_audio_clk_suspend(struct device *dev)
 {
        struct mmp2_audio_clk *priv = dev_get_drvdata(dev);
 
@@ -404,7 +405,7 @@ static int __maybe_unused mmp2_audio_clk_suspend(struct device *dev)
        return 0;
 }
 
-static int __maybe_unused mmp2_audio_clk_resume(struct device *dev)
+static int mmp2_audio_clk_resume(struct device *dev)
 {
        struct mmp2_audio_clk *priv = dev_get_drvdata(dev);
 
@@ -415,6 +416,7 @@ static int __maybe_unused mmp2_audio_clk_resume(struct device *dev)
 
        return 0;
 }
+#endif
 
 static const struct dev_pm_ops mmp2_audio_clk_pm_ops = {
        SET_RUNTIME_PM_OPS(mmp2_audio_clk_suspend, mmp2_audio_clk_resume, NULL)
index d82d725..b05901b 100644 (file)
@@ -891,21 +891,6 @@ static struct clk_branch gcc_boot_rom_ahb_clk = {
        },
 };
 
-static struct clk_branch gcc_camera_ahb_clk = {
-       .halt_reg = 0xb008,
-       .halt_check = BRANCH_HALT,
-       .hwcg_reg = 0xb008,
-       .hwcg_bit = 1,
-       .clkr = {
-               .enable_reg = 0xb008,
-               .enable_mask = BIT(0),
-               .hw.init = &(struct clk_init_data){
-                       .name = "gcc_camera_ahb_clk",
-                       .ops = &clk_branch2_ops,
-               },
-       },
-};
-
 static struct clk_branch gcc_camera_hf_axi_clk = {
        .halt_reg = 0xb020,
        .halt_check = BRANCH_HALT,
@@ -2317,7 +2302,6 @@ static struct clk_regmap *gcc_sc7180_clocks[] = {
        [GCC_AGGRE_UFS_PHY_AXI_CLK] = &gcc_aggre_ufs_phy_axi_clk.clkr,
        [GCC_AGGRE_USB3_PRIM_AXI_CLK] = &gcc_aggre_usb3_prim_axi_clk.clkr,
        [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr,
-       [GCC_CAMERA_AHB_CLK] = &gcc_camera_ahb_clk.clkr,
        [GCC_CAMERA_HF_AXI_CLK] = &gcc_camera_hf_axi_clk.clkr,
        [GCC_CAMERA_THROTTLE_HF_AXI_CLK] = &gcc_camera_throttle_hf_axi_clk.clkr,
        [GCC_CAMERA_XO_CLK] = &gcc_camera_xo_clk.clkr,
@@ -2519,11 +2503,12 @@ static int gcc_sc7180_probe(struct platform_device *pdev)
 
        /*
         * Keep the clocks always-ON
-        * GCC_CPUSS_GNOC_CLK, GCC_VIDEO_AHB_CLK, GCC_DISP_AHB_CLK
-        * GCC_GPU_CFG_AHB_CLK
+        * GCC_CPUSS_GNOC_CLK, GCC_VIDEO_AHB_CLK, GCC_CAMERA_AHB_CLK,
+        * GCC_DISP_AHB_CLK, GCC_GPU_CFG_AHB_CLK
         */
        regmap_update_bits(regmap, 0x48004, BIT(0), BIT(0));
        regmap_update_bits(regmap, 0x0b004, BIT(0), BIT(0));
+       regmap_update_bits(regmap, 0x0b008, BIT(0), BIT(0));
        regmap_update_bits(regmap, 0x0b00c, BIT(0), BIT(0));
        regmap_update_bits(regmap, 0x71004, BIT(0), BIT(0));
 
index 6cb6617..ab594a0 100644 (file)
@@ -722,7 +722,7 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = {
                .name = "gcc_sdcc2_apps_clk_src",
                .parent_data = gcc_parent_data_4,
                .num_parents = 5,
-               .ops = &clk_rcg2_ops,
+               .ops = &clk_rcg2_floor_ops,
        },
 };
 
@@ -745,7 +745,7 @@ static struct clk_rcg2 gcc_sdcc4_apps_clk_src = {
                .name = "gcc_sdcc4_apps_clk_src",
                .parent_data = gcc_parent_data_0,
                .num_parents = 3,
-               .ops = &clk_rcg2_ops,
+               .ops = &clk_rcg2_floor_ops,
        },
 };
 
index 37244a7..9cf249c 100644 (file)
@@ -1256,6 +1256,8 @@ static struct tegra_clk_init_table init_table[] __initdata = {
        { TEGRA30_CLK_I2S3_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
        { TEGRA30_CLK_I2S4_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
        { TEGRA30_CLK_VIMCLK_SYNC, TEGRA30_CLK_CLK_MAX, 24000000, 0 },
+       { TEGRA30_CLK_HDA, TEGRA30_CLK_PLL_P, 102000000, 0 },
+       { TEGRA30_CLK_HDA2CODEC_2X, TEGRA30_CLK_PLL_P, 48000000, 0 },
        /* must be the last entry */
        { TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
 };
index a60aee1..65df9ef 100644 (file)
@@ -235,36 +235,6 @@ static ssize_t ti_eqep_position_ceiling_write(struct counter_device *counter,
        return len;
 }
 
-static ssize_t ti_eqep_position_floor_read(struct counter_device *counter,
-                                          struct counter_count *count,
-                                          void *ext_priv, char *buf)
-{
-       struct ti_eqep_cnt *priv = counter->priv;
-       u32 qposinit;
-
-       regmap_read(priv->regmap32, QPOSINIT, &qposinit);
-
-       return sprintf(buf, "%u\n", qposinit);
-}
-
-static ssize_t ti_eqep_position_floor_write(struct counter_device *counter,
-                                           struct counter_count *count,
-                                           void *ext_priv, const char *buf,
-                                           size_t len)
-{
-       struct ti_eqep_cnt *priv = counter->priv;
-       int err;
-       u32 res;
-
-       err = kstrtouint(buf, 0, &res);
-       if (err < 0)
-               return err;
-
-       regmap_write(priv->regmap32, QPOSINIT, res);
-
-       return len;
-}
-
 static ssize_t ti_eqep_position_enable_read(struct counter_device *counter,
                                            struct counter_count *count,
                                            void *ext_priv, char *buf)
@@ -301,11 +271,6 @@ static struct counter_count_ext ti_eqep_position_ext[] = {
                .read   = ti_eqep_position_ceiling_read,
                .write  = ti_eqep_position_ceiling_write,
        },
-       {
-               .name   = "floor",
-               .read   = ti_eqep_position_floor_read,
-               .write  = ti_eqep_position_floor_write,
-       },
        {
                .name   = "enable",
                .read   = ti_eqep_position_enable_read,
index 6e23376..be05e03 100644 (file)
@@ -76,11 +76,6 @@ static inline int ceiling_fp(int32_t x)
        return ret;
 }
 
-static inline int32_t percent_fp(int percent)
-{
-       return div_fp(percent, 100);
-}
-
 static inline u64 mul_ext_fp(u64 x, u64 y)
 {
        return (x * y) >> EXT_FRAC_BITS;
@@ -91,11 +86,6 @@ static inline u64 div_ext_fp(u64 x, u64 y)
        return div64_u64(x << EXT_FRAC_BITS, y);
 }
 
-static inline int32_t percent_ext_fp(int percent)
-{
-       return div_ext_fp(percent, 100);
-}
-
 /**
  * struct sample -     Store performance sample
  * @core_avg_perf:     Ratio of APERF/MPERF which is the actual average
@@ -2653,12 +2643,13 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum,
                                      unsigned long capacity)
 {
        struct cpudata *cpu = all_cpu_data[cpunum];
+       u64 hwp_cap = READ_ONCE(cpu->hwp_cap_cached);
        int old_pstate = cpu->pstate.current_pstate;
        int cap_pstate, min_pstate, max_pstate, target_pstate;
 
        update_turbo_state();
-       cap_pstate = global.turbo_disabled ? cpu->pstate.max_pstate :
-                                            cpu->pstate.turbo_pstate;
+       cap_pstate = global.turbo_disabled ? HWP_GUARANTEED_PERF(hwp_cap) :
+                                            HWP_HIGHEST_PERF(hwp_cap);
 
        /* Optimization: Avoid unnecessary divisions. */
 
@@ -3086,7 +3077,6 @@ static int __init intel_pstate_init(void)
                        intel_pstate.attr = hwp_cpufreq_attrs;
                        intel_cpufreq.attr = hwp_cpufreq_attrs;
                        intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS;
-                       intel_cpufreq.fast_switch = NULL;
                        intel_cpufreq.adjust_perf = intel_cpufreq_adjust_perf;
                        if (!default_driver)
                                default_driver = &intel_pstate;
index 0acc9e2..b9ccb6a 100644 (file)
@@ -878,9 +878,9 @@ static int get_transition_latency(struct powernow_k8_data *data)
 
 /* Take a frequency, and issue the fid/vid transition command */
 static int transition_frequency_fidvid(struct powernow_k8_data *data,
-               unsigned int index)
+               unsigned int index,
+               struct cpufreq_policy *policy)
 {
-       struct cpufreq_policy *policy;
        u32 fid = 0;
        u32 vid = 0;
        int res;
@@ -912,9 +912,6 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
        freqs.old = find_khz_freq_from_fid(data->currfid);
        freqs.new = find_khz_freq_from_fid(fid);
 
-       policy = cpufreq_cpu_get(smp_processor_id());
-       cpufreq_cpu_put(policy);
-
        cpufreq_freq_transition_begin(policy, &freqs);
        res = transition_fid_vid(data, fid, vid);
        cpufreq_freq_transition_end(policy, &freqs, res);
@@ -969,7 +966,7 @@ static long powernowk8_target_fn(void *arg)
 
        powernow_k8_acpi_pst_values(data, newstate);
 
-       ret = transition_frequency_fidvid(data, newstate);
+       ret = transition_frequency_fidvid(data, newstate, pol);
 
        if (ret) {
                pr_err("transition frequency failed\n");
index bbd5170..e535f28 100644 (file)
@@ -366,6 +366,7 @@ if CRYPTO_DEV_OMAP
 config CRYPTO_DEV_OMAP_SHAM
        tristate "Support for OMAP MD5/SHA1/SHA2 hw accelerator"
        depends on ARCH_OMAP2PLUS
+       select CRYPTO_ENGINE
        select CRYPTO_SHA1
        select CRYPTO_MD5
        select CRYPTO_SHA256
index fabfaac..fa56b45 100644 (file)
@@ -300,11 +300,11 @@ struct mv_cesa_tdma_desc {
        __le32 byte_cnt;
        union {
                __le32 src;
-               dma_addr_t src_dma;
+               u32 src_dma;
        };
        union {
                __le32 dst;
-               dma_addr_t dst_dma;
+               u32 dst_dma;
        };
        __le32 next_dma;
 
index e63684d..9ad6397 100644 (file)
@@ -76,10 +76,6 @@ static void dma_buf_release(struct dentry *dentry)
 
        dmabuf->ops->release(dmabuf);
 
-       mutex_lock(&db_list.lock);
-       list_del(&dmabuf->list_node);
-       mutex_unlock(&db_list.lock);
-
        if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
                dma_resv_fini(dmabuf->resv);
 
@@ -88,6 +84,22 @@ static void dma_buf_release(struct dentry *dentry)
        kfree(dmabuf);
 }
 
+static int dma_buf_file_release(struct inode *inode, struct file *file)
+{
+       struct dma_buf *dmabuf;
+
+       if (!is_dma_buf_file(file))
+               return -EINVAL;
+
+       dmabuf = file->private_data;
+
+       mutex_lock(&db_list.lock);
+       list_del(&dmabuf->list_node);
+       mutex_unlock(&db_list.lock);
+
+       return 0;
+}
+
 static const struct dentry_operations dma_buf_dentry_ops = {
        .d_dname = dmabuffs_dname,
        .d_release = dma_buf_release,
@@ -413,6 +425,7 @@ static void dma_buf_show_fdinfo(struct seq_file *m, struct file *file)
 }
 
 static const struct file_operations dma_buf_fops = {
+       .release        = dma_buf_file_release,
        .mmap           = dma_buf_mmap_internal,
        .llseek         = dma_buf_llseek,
        .poll           = dma_buf_poll,
index 3c4e343..364fc2f 100644 (file)
@@ -251,6 +251,9 @@ static void cma_heap_dma_buf_release(struct dma_buf *dmabuf)
                buffer->vaddr = NULL;
        }
 
+       /* free page list */
+       kfree(buffer->pages);
+       /* release memory */
        cma_release(cma_heap->cma, buffer->cma_pages, buffer->pagecount);
        kfree(buffer);
 }
index b971505..08d71da 100644 (file)
@@ -86,12 +86,12 @@ static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
 
        if (desc->chunk) {
                /* Create and add new element into the linked list */
-               desc->chunks_alloc++;
-               list_add_tail(&chunk->list, &desc->chunk->list);
                if (!dw_edma_alloc_burst(chunk)) {
                        kfree(chunk);
                        return NULL;
                }
+               desc->chunks_alloc++;
+               list_add_tail(&chunk->list, &desc->chunk->list);
        } else {
                /* List head */
                chunk->burst = NULL;
index 266423a..4dbb03c 100644 (file)
@@ -434,7 +434,7 @@ int idxd_register_driver(void)
        return 0;
 
 drv_fail:
-       for (; i > 0; i--)
+       while (--i >= 0)
                driver_unregister(&idxd_drvs[i]->drv);
        return rc;
 }
@@ -1840,7 +1840,7 @@ int idxd_register_bus_type(void)
        return 0;
 
 bus_err:
-       for (; i > 0; i--)
+       while (--i >= 0)
                bus_unregister(idxd_bus_types[i]);
        return rc;
 }
index f133ae8..6ad8afb 100644 (file)
@@ -1007,6 +1007,7 @@ static int mtk_hsdma_probe(struct platform_device *pdev)
        return 0;
 
 err_free:
+       mtk_hsdma_hw_deinit(hsdma);
        of_dma_controller_free(pdev->dev.of_node);
 err_unregister:
        dma_async_device_unregister(dd);
index 584c931..d29d01e 100644 (file)
@@ -350,7 +350,7 @@ static int milbeaut_xdmac_probe(struct platform_device *pdev)
 
        ret = dma_async_device_register(ddev);
        if (ret)
-               return ret;
+               goto disable_xdmac;
 
        ret = of_dma_controller_register(dev->of_node,
                                         of_dma_simple_xlate, mdev);
@@ -363,6 +363,8 @@ static int milbeaut_xdmac_probe(struct platform_device *pdev)
 
 unregister_dmac:
        dma_async_device_unregister(ddev);
+disable_xdmac:
+       disable_xdmac(mdev);
        return ret;
 }
 
index d5773d4..8857985 100644 (file)
@@ -630,7 +630,7 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
                             GFP_NOWAIT);
 
        if (!async_desc)
-               goto err_out;
+               return NULL;
 
        if (flags & DMA_PREP_FENCE)
                async_desc->flags |= DESC_FLAG_NWD;
@@ -670,10 +670,6 @@ static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan,
        }
 
        return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags);
-
-err_out:
-       kfree(async_desc);
-       return NULL;
 }
 
 /**
index d2334f5..1a0bf6b 100644 (file)
@@ -1416,7 +1416,7 @@ static int gpi_alloc_ring(struct gpi_ring *ring, u32 elements,
        len = 1 << bit;
        ring->alloc_size = (len + (len - 1));
        dev_dbg(gpii->gpi_dev->dev,
-               "#el:%u el_size:%u len:%u actual_len:%llu alloc_size:%lu\n",
+               "#el:%u el_size:%u len:%u actual_len:%llu alloc_size:%zu\n",
                  elements, el_size, (elements * el_size), len,
                  ring->alloc_size);
 
@@ -1424,7 +1424,7 @@ static int gpi_alloc_ring(struct gpi_ring *ring, u32 elements,
                                               ring->alloc_size,
                                               &ring->dma_handle, GFP_KERNEL);
        if (!ring->pre_aligned) {
-               dev_err(gpii->gpi_dev->dev, "could not alloc size:%lu mem for ring\n",
+               dev_err(gpii->gpi_dev->dev, "could not alloc size:%zu mem for ring\n",
                        ring->alloc_size);
                return -ENOMEM;
        }
@@ -1444,8 +1444,8 @@ static int gpi_alloc_ring(struct gpi_ring *ring, u32 elements,
        smp_wmb();
 
        dev_dbg(gpii->gpi_dev->dev,
-               "phy_pre:0x%0llx phy_alig:0x%0llx len:%u el_size:%u elements:%u\n",
-               ring->dma_handle, ring->phys_addr, ring->len,
+               "phy_pre:%pad phy_alig:%pa len:%u el_size:%u elements:%u\n",
+               &ring->dma_handle, &ring->phys_addr, ring->len,
                ring->el_size, ring->elements);
 
        return 0;
@@ -1948,7 +1948,7 @@ static int gpi_ch_init(struct gchan *gchan)
        return ret;
 
 error_start_chan:
-       for (i = i - 1; i >= 0; i++) {
+       for (i = i - 1; i >= 0; i--) {
                gpi_stop_chan(&gpii->gchan[i]);
                gpi_send_cmd(gpii, gchan, GPI_CH_CMD_RESET);
        }
index e4637ec..36ba8b4 100644 (file)
 #define STM32_MDMA_MAX_CHANNELS                63
 #define STM32_MDMA_MAX_REQUESTS                256
 #define STM32_MDMA_MAX_BURST           128
-#define STM32_MDMA_VERY_HIGH_PRIORITY  0x11
+#define STM32_MDMA_VERY_HIGH_PRIORITY  0x3
 
 enum stm32_mdma_trigger_mode {
        STM32_MDMA_BUFFER,
index 87157cb..2984604 100644 (file)
@@ -4698,9 +4698,9 @@ static int pktdma_setup_resources(struct udma_dev *ud)
                ud->tchan_tpl.levels = 1;
        }
 
-       ud->tchan_tpl.levels = ud->tchan_tpl.levels;
-       ud->tchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0];
-       ud->tchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1];
+       ud->rchan_tpl.levels = ud->tchan_tpl.levels;
+       ud->rchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0];
+       ud->rchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1];
 
        ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
                                           sizeof(unsigned long), GFP_KERNEL);
index 22faea6..7977755 100644 (file)
@@ -2781,7 +2781,7 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
                has_dre = false;
 
        if (!has_dre)
-               xdev->common.copy_align = fls(width - 1);
+               xdev->common.copy_align = (enum dmaengine_alignment)fls(width - 1);
 
        if (of_device_is_compatible(node, "xlnx,axi-vdma-mm2s-channel") ||
            of_device_is_compatible(node, "xlnx,axi-dma-mm2s-channel") ||
@@ -2900,7 +2900,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
 static int xilinx_dma_child_probe(struct xilinx_dma_device *xdev,
                                    struct device_node *node)
 {
-       int ret, i, nr_channels = 1;
+       int ret, i;
+       u32 nr_channels = 1;
 
        ret = of_property_read_u32(node, "dma-channels", &nr_channels);
        if (xdev->dma_config->dmatype == XDMA_TYPE_AXIMCDMA && ret < 0)
@@ -3112,7 +3113,11 @@ static int xilinx_dma_probe(struct platform_device *pdev)
        }
 
        /* Register the DMA engine with the core */
-       dma_async_device_register(&xdev->common);
+       err = dma_async_device_register(&xdev->common);
+       if (err) {
+               dev_err(xdev->dev, "failed to register the dma device\n");
+               goto error;
+       }
 
        err = of_dma_controller_register(node, of_dma_xilinx_xlate,
                                         xdev);
index 1d2e5b8..c027d99 100644 (file)
@@ -13,6 +13,7 @@ config IMX_DSP
 config IMX_SCU
        bool "IMX SCU Protocol driver"
        depends on IMX_MBOX
+       select SOC_BUS
        help
          The System Controller Firmware (SCFW) is a low-level system function
          which runs on a dedicated Cortex-M core to provide power, clock, and
index c70f46e..dea65d8 100644 (file)
@@ -521,7 +521,8 @@ config GPIO_SAMA5D2_PIOBU
 
 config GPIO_SIFIVE
        bool "SiFive GPIO support"
-       depends on OF_GPIO && IRQ_DOMAIN_HIERARCHY
+       depends on OF_GPIO
+       select IRQ_DOMAIN_HIERARCHY
        select GPIO_GENERIC
        select GPIOLIB_IRQCHIP
        select REGMAP_MMIO
@@ -597,6 +598,8 @@ config GPIO_TEGRA
        default ARCH_TEGRA
        depends on ARCH_TEGRA || COMPILE_TEST
        depends on OF_GPIO
+       select GPIOLIB_IRQCHIP
+       select IRQ_DOMAIN_HIERARCHY
        help
          Say yes here to support GPIO pins on NVIDIA Tegra SoCs.
 
index 672681a..a912a8f 100644 (file)
@@ -676,20 +676,17 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
        else
                state->duty_cycle = 1;
 
+       val = (unsigned long long) u; /* on duration */
        regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u);
-       val = (unsigned long long) u * NSEC_PER_SEC;
+       val += (unsigned long long) u; /* period = on + off duration */
+       val *= NSEC_PER_SEC;
        do_div(val, mvpwm->clk_rate);
-       if (val < state->duty_cycle) {
+       if (val > UINT_MAX)
+               state->period = UINT_MAX;
+       else if (val)
+               state->period = val;
+       else
                state->period = 1;
-       } else {
-               val -= state->duty_cycle;
-               if (val > UINT_MAX)
-                       state->period = UINT_MAX;
-               else if (val)
-                       state->period = val;
-               else
-                       state->period = 1;
-       }
 
        regmap_read(mvchip->regs, GPIO_BLINK_EN_OFF + mvchip->offset, &u);
        if (u)
index 12b679c..1a7b511 100644 (file)
@@ -1979,6 +1979,21 @@ struct gpio_chardev_data {
 #endif
 };
 
+static int chipinfo_get(struct gpio_chardev_data *cdev, void __user *ip)
+{
+       struct gpio_device *gdev = cdev->gdev;
+       struct gpiochip_info chipinfo;
+
+       memset(&chipinfo, 0, sizeof(chipinfo));
+
+       strscpy(chipinfo.name, dev_name(&gdev->dev), sizeof(chipinfo.name));
+       strscpy(chipinfo.label, gdev->label, sizeof(chipinfo.label));
+       chipinfo.lines = gdev->ngpio;
+       if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
+               return -EFAULT;
+       return 0;
+}
+
 #ifdef CONFIG_GPIO_CDEV_V1
 /*
  * returns 0 if the versions match, else the previously selected ABI version
@@ -1993,6 +2008,41 @@ static int lineinfo_ensure_abi_version(struct gpio_chardev_data *cdata,
 
        return abiv;
 }
+
+static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
+                          bool watch)
+{
+       struct gpio_desc *desc;
+       struct gpioline_info lineinfo;
+       struct gpio_v2_line_info lineinfo_v2;
+
+       if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
+               return -EFAULT;
+
+       /* this doubles as a range check on line_offset */
+       desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset);
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       if (watch) {
+               if (lineinfo_ensure_abi_version(cdev, 1))
+                       return -EPERM;
+
+               if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines))
+                       return -EBUSY;
+       }
+
+       gpio_desc_to_lineinfo(desc, &lineinfo_v2);
+       gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
+
+       if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
+               if (watch)
+                       clear_bit(lineinfo.line_offset, cdev->watched_lines);
+               return -EFAULT;
+       }
+
+       return 0;
+}
 #endif
 
 static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
@@ -2030,6 +2080,22 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
        return 0;
 }
 
+static int lineinfo_unwatch(struct gpio_chardev_data *cdev, void __user *ip)
+{
+       __u32 offset;
+
+       if (copy_from_user(&offset, ip, sizeof(offset)))
+               return -EFAULT;
+
+       if (offset >= cdev->gdev->ngpio)
+               return -EINVAL;
+
+       if (!test_and_clear_bit(offset, cdev->watched_lines))
+               return -EBUSY;
+
+       return 0;
+}
+
 /*
  * gpio_ioctl() - ioctl handler for the GPIO chardev
  */
@@ -2037,80 +2103,24 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct gpio_chardev_data *cdev = file->private_data;
        struct gpio_device *gdev = cdev->gdev;
-       struct gpio_chip *gc = gdev->chip;
        void __user *ip = (void __user *)arg;
-       __u32 offset;
 
        /* We fail any subsequent ioctl():s when the chip is gone */
-       if (!gc)
+       if (!gdev->chip)
                return -ENODEV;
 
        /* Fill in the struct and pass to userspace */
        if (cmd == GPIO_GET_CHIPINFO_IOCTL) {
-               struct gpiochip_info chipinfo;
-
-               memset(&chipinfo, 0, sizeof(chipinfo));
-
-               strscpy(chipinfo.name, dev_name(&gdev->dev),
-                       sizeof(chipinfo.name));
-               strscpy(chipinfo.label, gdev->label,
-                       sizeof(chipinfo.label));
-               chipinfo.lines = gdev->ngpio;
-               if (copy_to_user(ip, &chipinfo, sizeof(chipinfo)))
-                       return -EFAULT;
-               return 0;
+               return chipinfo_get(cdev, ip);
 #ifdef CONFIG_GPIO_CDEV_V1
-       } else if (cmd == GPIO_GET_LINEINFO_IOCTL) {
-               struct gpio_desc *desc;
-               struct gpioline_info lineinfo;
-               struct gpio_v2_line_info lineinfo_v2;
-
-               if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
-                       return -EFAULT;
-
-               /* this doubles as a range check on line_offset */
-               desc = gpiochip_get_desc(gc, lineinfo.line_offset);
-               if (IS_ERR(desc))
-                       return PTR_ERR(desc);
-
-               gpio_desc_to_lineinfo(desc, &lineinfo_v2);
-               gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
-
-               if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
-                       return -EFAULT;
-               return 0;
        } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) {
                return linehandle_create(gdev, ip);
        } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) {
                return lineevent_create(gdev, ip);
-       } else if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) {
-               struct gpio_desc *desc;
-               struct gpioline_info lineinfo;
-               struct gpio_v2_line_info lineinfo_v2;
-
-               if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
-                       return -EFAULT;
-
-               /* this doubles as a range check on line_offset */
-               desc = gpiochip_get_desc(gc, lineinfo.line_offset);
-               if (IS_ERR(desc))
-                       return PTR_ERR(desc);
-
-               if (lineinfo_ensure_abi_version(cdev, 1))
-                       return -EPERM;
-
-               if (test_and_set_bit(lineinfo.line_offset, cdev->watched_lines))
-                       return -EBUSY;
-
-               gpio_desc_to_lineinfo(desc, &lineinfo_v2);
-               gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
-
-               if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
-                       clear_bit(lineinfo.line_offset, cdev->watched_lines);
-                       return -EFAULT;
-               }
-
-               return 0;
+       } else if (cmd == GPIO_GET_LINEINFO_IOCTL ||
+                  cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) {
+               return lineinfo_get_v1(cdev, ip,
+                                      cmd == GPIO_GET_LINEINFO_WATCH_IOCTL);
 #endif /* CONFIG_GPIO_CDEV_V1 */
        } else if (cmd == GPIO_V2_GET_LINEINFO_IOCTL ||
                   cmd == GPIO_V2_GET_LINEINFO_WATCH_IOCTL) {
@@ -2119,16 +2129,7 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        } else if (cmd == GPIO_V2_GET_LINE_IOCTL) {
                return linereq_create(gdev, ip);
        } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) {
-               if (copy_from_user(&offset, ip, sizeof(offset)))
-                       return -EFAULT;
-
-               if (offset >= cdev->gdev->ngpio)
-                       return -EINVAL;
-
-               if (!test_and_clear_bit(offset, cdev->watched_lines))
-                       return -EBUSY;
-
-               return 0;
+               return lineinfo_unwatch(cdev, ip);
        }
        return -EINVAL;
 }
index b02cc2a..b78a634 100644 (file)
@@ -1489,6 +1489,9 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
                type = IRQ_TYPE_NONE;
        }
 
+       if (gc->to_irq)
+               chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
+
        gc->to_irq = gpiochip_to_irq;
        gc->irq.default_type = type;
        gc->irq.lock_key = lock_key;
index 3060778..6107ac9 100644 (file)
@@ -112,6 +112,7 @@ int amdgpu_atomfirmware_allocate_fb_scratch(struct amdgpu_device *adev)
 union igp_info {
        struct atom_integrated_system_info_v1_11 v11;
        struct atom_integrated_system_info_v1_12 v12;
+       struct atom_integrated_system_info_v2_1 v21;
 };
 
 union umc_info {
@@ -209,24 +210,42 @@ amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev,
                if (adev->flags & AMD_IS_APU) {
                        igp_info = (union igp_info *)
                                (mode_info->atom_context->bios + data_offset);
-                       switch (crev) {
-                       case 11:
-                               mem_channel_number = igp_info->v11.umachannelnumber;
-                               /* channel width is 64 */
-                               if (vram_width)
-                                       *vram_width = mem_channel_number * 64;
-                               mem_type = igp_info->v11.memorytype;
-                               if (vram_type)
-                                       *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type);
+                       switch (frev) {
+                       case 1:
+                               switch (crev) {
+                               case 11:
+                               case 12:
+                                       mem_channel_number = igp_info->v11.umachannelnumber;
+                                       if (!mem_channel_number)
+                                               mem_channel_number = 1;
+                                       /* channel width is 64 */
+                                       if (vram_width)
+                                               *vram_width = mem_channel_number * 64;
+                                       mem_type = igp_info->v11.memorytype;
+                                       if (vram_type)
+                                               *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type);
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
                                break;
-                       case 12:
-                               mem_channel_number = igp_info->v12.umachannelnumber;
-                               /* channel width is 64 */
-                               if (vram_width)
-                                       *vram_width = mem_channel_number * 64;
-                               mem_type = igp_info->v12.memorytype;
-                               if (vram_type)
-                                       *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type);
+                       case 2:
+                               switch (crev) {
+                               case 1:
+                               case 2:
+                                       mem_channel_number = igp_info->v21.umachannelnumber;
+                                       if (!mem_channel_number)
+                                               mem_channel_number = 1;
+                                       /* channel width is 64 */
+                                       if (vram_width)
+                                               *vram_width = mem_channel_number * 64;
+                                       mem_type = igp_info->v21.memorytype;
+                                       if (vram_type)
+                                               *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type);
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
                                break;
                        default:
                                return -EINVAL;
index 1cb7d73..cab1eba 100644 (file)
@@ -81,7 +81,6 @@ MODULE_FIRMWARE("amdgpu/navi10_gpu_info.bin");
 MODULE_FIRMWARE("amdgpu/navi14_gpu_info.bin");
 MODULE_FIRMWARE("amdgpu/navi12_gpu_info.bin");
 MODULE_FIRMWARE("amdgpu/vangogh_gpu_info.bin");
-MODULE_FIRMWARE("amdgpu/green_sardine_gpu_info.bin");
 
 #define AMDGPU_RESUME_MS               2000
 
@@ -2548,11 +2547,11 @@ static int amdgpu_device_ip_fini(struct amdgpu_device *adev)
        if (adev->gmc.xgmi.num_physical_nodes > 1)
                amdgpu_xgmi_remove_device(adev);
 
-       amdgpu_amdkfd_device_fini(adev);
-
        amdgpu_device_set_pg_state(adev, AMD_PG_STATE_UNGATE);
        amdgpu_device_set_cg_state(adev, AMD_CG_STATE_UNGATE);
 
+       amdgpu_amdkfd_device_fini(adev);
+
        /* need to disable SMC first */
        for (i = 0; i < adev->num_ip_blocks; i++) {
                if (!adev->ip_blocks[i].status.hw)
@@ -3034,7 +3033,7 @@ bool amdgpu_device_asic_has_dc_support(enum amd_asic_type asic_type)
 #endif
        default:
                if (amdgpu_dc > 0)
-                       DRM_INFO("Display Core has been requested via kernel parameter "
+                       DRM_INFO_ONCE("Display Core has been requested via kernel parameter "
                                         "but isn't supported by ASIC, ignoring\n");
                return false;
        }
index 72efd57..7169fb5 100644 (file)
@@ -1085,6 +1085,8 @@ static const struct pci_device_id pciidlist[] = {
 
        /* Renoir */
        {0x1002, 0x1636, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU},
+       {0x1002, 0x1638, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU},
+       {0x1002, 0x164C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RENOIR|AMD_IS_APU},
 
        /* Navi12 */
        {0x1002, 0x7360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_NAVI12},
index 523d22d..347fec6 100644 (file)
@@ -563,7 +563,7 @@ static int psp_asd_load(struct psp_context *psp)
         * add workaround to bypass it for sriov now.
         * TODO: add version check to make it common
         */
-       if (amdgpu_sriov_vf(psp->adev) || !psp->asd_fw)
+       if (amdgpu_sriov_vf(psp->adev) || !psp->asd_ucode_size)
                return 0;
 
        cmd = kzalloc(sizeof(struct psp_gfx_cmd_resp), GFP_KERNEL);
@@ -1315,8 +1315,12 @@ static int psp_hdcp_terminate(struct psp_context *psp)
        if (amdgpu_sriov_vf(psp->adev))
                return 0;
 
-       if (!psp->hdcp_context.hdcp_initialized)
-               return 0;
+       if (!psp->hdcp_context.hdcp_initialized) {
+               if (psp->hdcp_context.hdcp_shared_buf)
+                       goto out;
+               else
+                       return 0;
+       }
 
        ret = psp_hdcp_unload(psp);
        if (ret)
@@ -1324,6 +1328,7 @@ static int psp_hdcp_terminate(struct psp_context *psp)
 
        psp->hdcp_context.hdcp_initialized = false;
 
+out:
        /* free hdcp shared memory */
        amdgpu_bo_free_kernel(&psp->hdcp_context.hdcp_shared_bo,
                              &psp->hdcp_context.hdcp_shared_mc_addr,
@@ -1462,8 +1467,12 @@ static int psp_dtm_terminate(struct psp_context *psp)
        if (amdgpu_sriov_vf(psp->adev))
                return 0;
 
-       if (!psp->dtm_context.dtm_initialized)
-               return 0;
+       if (!psp->dtm_context.dtm_initialized) {
+               if (psp->dtm_context.dtm_shared_buf)
+                       goto out;
+               else
+                       return 0;
+       }
 
        ret = psp_dtm_unload(psp);
        if (ret)
@@ -1471,6 +1480,7 @@ static int psp_dtm_terminate(struct psp_context *psp)
 
        psp->dtm_context.dtm_initialized = false;
 
+out:
        /* free hdcp shared memory */
        amdgpu_bo_free_kernel(&psp->dtm_context.dtm_shared_bo,
                              &psp->dtm_context.dtm_shared_mc_addr,
@@ -2589,11 +2599,10 @@ static int parse_ta_bin_descriptor(struct psp_context *psp,
 
        switch (desc->fw_type) {
        case TA_FW_TYPE_PSP_ASD:
-               psp->asd_fw_version        = le32_to_cpu(desc->fw_version);
+               psp->asd_fw_version        = le32_to_cpu(desc->fw_version);
                psp->asd_feature_version   = le32_to_cpu(desc->fw_version);
-               psp->asd_ucode_size        = le32_to_cpu(desc->size_bytes);
+               psp->asd_ucode_size        = le32_to_cpu(desc->size_bytes);
                psp->asd_start_addr        = ucode_start_addr;
-               psp->asd_fw                = psp->ta_fw;
                break;
        case TA_FW_TYPE_PSP_XGMI:
                psp->ta_xgmi_ucode_version = le32_to_cpu(desc->fw_version);
index c136bd4..82e9526 100644 (file)
@@ -1518,7 +1518,7 @@ static int amdgpu_ras_badpages_read(struct amdgpu_device *adev,
        struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
        struct ras_err_handler_data *data;
        int i = 0;
-       int ret = 0;
+       int ret = 0, status;
 
        if (!con || !con->eh_data || !bps || !count)
                return -EINVAL;
@@ -1543,12 +1543,12 @@ static int amdgpu_ras_badpages_read(struct amdgpu_device *adev,
                        .size = AMDGPU_GPU_PAGE_SIZE,
                        .flags = AMDGPU_RAS_RETIRE_PAGE_RESERVED,
                };
-               ret = amdgpu_vram_mgr_query_page_status(
+               status = amdgpu_vram_mgr_query_page_status(
                                ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM),
                                data->bps[i].retired_page);
-               if (ret == -EBUSY)
+               if (status == -EBUSY)
                        (*bps)[i].flags = AMDGPU_RAS_RETIRE_PAGE_PENDING;
-               else if (ret == -ENOENT)
+               else if (status == -ENOENT)
                        (*bps)[i].flags = AMDGPU_RAS_RETIRE_PAGE_FAULT;
        }
 
index 1dd0401..19d9aa7 100644 (file)
@@ -30,6 +30,7 @@
 #define EEPROM_I2C_TARGET_ADDR_VEGA20          0xA0
 #define EEPROM_I2C_TARGET_ADDR_ARCTURUS                0xA8
 #define EEPROM_I2C_TARGET_ADDR_ARCTURUS_D342   0xA0
+#define EEPROM_I2C_TARGET_ADDR_SIENNA_CICHLID   0xA0
 
 /*
  * The 2 macros bellow represent the actual size in bytes that
@@ -62,7 +63,8 @@
 static bool __is_ras_eeprom_supported(struct amdgpu_device *adev)
 {
        if ((adev->asic_type == CHIP_VEGA20) ||
-           (adev->asic_type == CHIP_ARCTURUS))
+           (adev->asic_type == CHIP_ARCTURUS) ||
+           (adev->asic_type == CHIP_SIENNA_CICHLID))
                return true;
 
        return false;
@@ -100,6 +102,10 @@ static bool __get_eeprom_i2c_addr(struct amdgpu_device *adev,
        case CHIP_ARCTURUS:
                return __get_eeprom_i2c_addr_arct(adev, i2c_addr);
 
+       case CHIP_SIENNA_CICHLID:
+               *i2c_addr = EEPROM_I2C_TARGET_ADDR_SIENNA_CICHLID;
+               break;
+
        default:
                return false;
        }
index ba10867..346963e 100644 (file)
 #define mmGCR_GENERAL_CNTL_Sienna_Cichlid                      0x1580
 #define mmGCR_GENERAL_CNTL_Sienna_Cichlid_BASE_IDX     0
 
+#define mmGOLDEN_TSC_COUNT_UPPER_Vangogh                0x0025
+#define mmGOLDEN_TSC_COUNT_UPPER_Vangogh_BASE_IDX       1
+#define mmGOLDEN_TSC_COUNT_LOWER_Vangogh                0x0026
+#define mmGOLDEN_TSC_COUNT_LOWER_Vangogh_BASE_IDX       1
 #define mmSPI_CONFIG_CNTL_1_Vangogh             0x2441
 #define mmSPI_CONFIG_CNTL_1_Vangogh_BASE_IDX    1
 #define mmVGT_TF_MEMORY_BASE_HI_Vangogh          0x2261
 #define mmVGT_ESGS_RING_SIZE_Vangogh_BASE_IDX    1
 #define mmSPI_CONFIG_CNTL_Vangogh                0x2440
 #define mmSPI_CONFIG_CNTL_Vangogh_BASE_IDX       1
+#define mmGCR_GENERAL_CNTL_Vangogh               0x1580
+#define mmGCR_GENERAL_CNTL_Vangogh_BASE_IDX      0
 
 #define mmCP_HYP_PFP_UCODE_ADDR                        0x5814
 #define mmCP_HYP_PFP_UCODE_ADDR_BASE_IDX       1
 #define mmGCVM_L2_CGTT_CLK_CTRL_Sienna_Cichlid          0x15db
 #define mmGCVM_L2_CGTT_CLK_CTRL_Sienna_Cichlid_BASE_IDX        0
 
+#define mmGC_THROTTLE_CTRL_Sienna_Cichlid              0x2030
+#define mmGC_THROTTLE_CTRL_Sienna_Cichlid_BASE_IDX     0
+
 MODULE_FIRMWARE("amdgpu/navi10_ce.bin");
 MODULE_FIRMWARE("amdgpu/navi10_pfp.bin");
 MODULE_FIRMWARE("amdgpu/navi10_me.bin");
@@ -3237,7 +3246,7 @@ static const struct soc15_reg_golden golden_settings_gc_10_3_vangogh[] =
        SOC15_REG_GOLDEN_VALUE(GC, 0, mmDB_DEBUG4, 0xffffffff, 0x00800000),
        SOC15_REG_GOLDEN_VALUE(GC, 0, mmDB_EXCEPTION_CONTROL, 0x7fff0f1f, 0x00b80000),
        SOC15_REG_GOLDEN_VALUE(GC, 0, mmGB_ADDR_CONFIG, 0x0c1807ff, 0x00000142),
-       SOC15_REG_GOLDEN_VALUE(GC, 0, mmGCR_GENERAL_CNTL, 0x1ff1ffff, 0x00000500),
+       SOC15_REG_GOLDEN_VALUE(GC, 0, mmGCR_GENERAL_CNTL_Vangogh, 0x1ff1ffff, 0x00000500),
        SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL1_PIPE_STEER, 0x000000ff, 0x000000e4),
        SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2_PIPE_STEER_0, 0x77777777, 0x32103210),
        SOC15_REG_GOLDEN_VALUE(GC, 0, mmGL2_PIPE_STEER_1, 0x77777777, 0x32103210),
@@ -3324,6 +3333,7 @@ static void gfx_v10_0_ring_emit_de_meta(struct amdgpu_ring *ring, bool resume);
 static void gfx_v10_0_ring_emit_frame_cntl(struct amdgpu_ring *ring, bool start, bool secure);
 static u32 gfx_v10_3_get_disabled_sa(struct amdgpu_device *adev);
 static void gfx_v10_3_program_pbb_mode(struct amdgpu_device *adev);
+static void gfx_v10_3_set_power_brake_sequence(struct amdgpu_device *adev);
 
 static void gfx10_kiq_set_resources(struct amdgpu_ring *kiq_ring, uint64_t queue_mask)
 {
@@ -7192,6 +7202,9 @@ static int gfx_v10_0_hw_init(void *handle)
        if (adev->asic_type == CHIP_SIENNA_CICHLID)
                gfx_v10_3_program_pbb_mode(adev);
 
+       if (adev->asic_type >= CHIP_SIENNA_CICHLID)
+               gfx_v10_3_set_power_brake_sequence(adev);
+
        return r;
 }
 
@@ -7377,8 +7390,16 @@ static uint64_t gfx_v10_0_get_gpu_clock_counter(struct amdgpu_device *adev)
 
        amdgpu_gfx_off_ctrl(adev, false);
        mutex_lock(&adev->gfx.gpu_clock_mutex);
-       clock = (uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER) |
-               ((uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER) << 32ULL);
+       switch (adev->asic_type) {
+       case CHIP_VANGOGH:
+               clock = (uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER_Vangogh) |
+                       ((uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER_Vangogh) << 32ULL);
+               break;
+       default:
+               clock = (uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_LOWER) |
+                       ((uint64_t)RREG32_SOC15(SMUIO, 0, mmGOLDEN_TSC_COUNT_UPPER) << 32ULL);
+               break;
+       }
        mutex_unlock(&adev->gfx.gpu_clock_mutex);
        amdgpu_gfx_off_ctrl(adev, true);
        return clock;
@@ -9169,6 +9190,31 @@ static void gfx_v10_3_program_pbb_mode(struct amdgpu_device *adev)
        }
 }
 
+static void gfx_v10_3_set_power_brake_sequence(struct amdgpu_device *adev)
+{
+       WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX,
+                    (0x1 << GRBM_GFX_INDEX__SA_BROADCAST_WRITES__SHIFT) |
+                    (0x1 << GRBM_GFX_INDEX__INSTANCE_BROADCAST_WRITES__SHIFT) |
+                    (0x1 << GRBM_GFX_INDEX__SE_BROADCAST_WRITES__SHIFT));
+
+       WREG32_SOC15(GC, 0, mmGC_CAC_IND_INDEX, ixPWRBRK_STALL_PATTERN_CTRL);
+       WREG32_SOC15(GC, 0, mmGC_CAC_IND_DATA,
+                    (0x1 << PWRBRK_STALL_PATTERN_CTRL__PWRBRK_STEP_INTERVAL__SHIFT) |
+                    (0x12 << PWRBRK_STALL_PATTERN_CTRL__PWRBRK_BEGIN_STEP__SHIFT) |
+                    (0x13 << PWRBRK_STALL_PATTERN_CTRL__PWRBRK_END_STEP__SHIFT) |
+                    (0xf << PWRBRK_STALL_PATTERN_CTRL__PWRBRK_THROTTLE_PATTERN_BIT_NUMS__SHIFT));
+
+       WREG32_SOC15(GC, 0, mmGC_THROTTLE_CTRL_Sienna_Cichlid,
+                    (0x1 << GC_THROTTLE_CTRL__PWRBRK_STALL_EN__SHIFT) |
+                    (0x1 << GC_THROTTLE_CTRL__PATTERN_MODE__SHIFT) |
+                    (0x5 << GC_THROTTLE_CTRL__RELEASE_STEP_INTERVAL__SHIFT));
+
+       WREG32_SOC15(GC, 0, mmDIDT_IND_INDEX, ixDIDT_SQ_THROTTLE_CTRL);
+
+       WREG32_SOC15(GC, 0, mmDIDT_IND_DATA,
+                    (0x1 << DIDT_SQ_THROTTLE_CTRL__PWRBRK_STALL_EN__SHIFT));
+}
+
 const struct amdgpu_ip_block_version gfx_v10_0_ip_block =
 {
        .type = AMD_IP_BLOCK_TYPE_GFX,
index b72c8e4..1961745 100644 (file)
@@ -310,7 +310,7 @@ static void mmhub_v2_3_setup_vmid_config(struct amdgpu_device *adev)
                /* Send no-retry XNACK on fault to suppress VM fault storm. */
                tmp = REG_SET_FIELD(tmp, MMVM_CONTEXT1_CNTL,
                                    RETRY_PERMISSION_OR_INVALID_PAGE_FAULT,
-                                   !amdgpu_noretry);
+                                   !adev->gmc.noretry);
                WREG32_SOC15_OFFSET(MMHUB, 0, mmMMVM_CONTEXT1_CNTL,
                                    i * hub->ctx_distance, tmp);
                WREG32_SOC15_OFFSET(MMHUB, 0, mmMMVM_CONTEXT1_PAGE_TABLE_START_ADDR_LO32,
@@ -491,12 +491,11 @@ mmhub_v2_3_update_medium_grain_clock_gating(struct amdgpu_device *adev,
 {
        uint32_t def, data, def1, data1;
 
-       def  = data  = RREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_MISC_CG);
+       def  = data  = RREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_CGTT_CLK_CTRL);
        def1 = data1 = RREG32_SOC15(MMHUB, 0, mmDAGB0_CNTL_MISC2);
 
        if (enable && (adev->cg_flags & AMD_CG_SUPPORT_MC_MGCG)) {
-               data |= MM_ATC_L2_MISC_CG__ENABLE_MASK;
-
+               data &= ~MM_ATC_L2_CGTT_CLK_CTRL__SOFT_OVERRIDE_MASK;
                data1 &= ~(DAGB0_CNTL_MISC2__DISABLE_WRREQ_CG_MASK |
                           DAGB0_CNTL_MISC2__DISABLE_WRRET_CG_MASK |
                           DAGB0_CNTL_MISC2__DISABLE_RDREQ_CG_MASK |
@@ -505,8 +504,7 @@ mmhub_v2_3_update_medium_grain_clock_gating(struct amdgpu_device *adev,
                           DAGB0_CNTL_MISC2__DISABLE_TLBRD_CG_MASK);
 
        } else {
-               data &= ~MM_ATC_L2_MISC_CG__ENABLE_MASK;
-
+               data |= MM_ATC_L2_CGTT_CLK_CTRL__SOFT_OVERRIDE_MASK;
                data1 |= (DAGB0_CNTL_MISC2__DISABLE_WRREQ_CG_MASK |
                          DAGB0_CNTL_MISC2__DISABLE_WRRET_CG_MASK |
                          DAGB0_CNTL_MISC2__DISABLE_RDREQ_CG_MASK |
@@ -516,7 +514,7 @@ mmhub_v2_3_update_medium_grain_clock_gating(struct amdgpu_device *adev,
        }
 
        if (def != data)
-               WREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_MISC_CG, data);
+               WREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_CGTT_CLK_CTRL, data);
        if (def1 != data1)
                WREG32_SOC15(MMHUB, 0, mmDAGB0_CNTL_MISC2, data1);
 }
@@ -525,17 +523,44 @@ static void
 mmhub_v2_3_update_medium_grain_light_sleep(struct amdgpu_device *adev,
                                           bool enable)
 {
-       uint32_t def, data;
-
-       def  = data  = RREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_MISC_CG);
-
-       if (enable && (adev->cg_flags & AMD_CG_SUPPORT_MC_LS))
-               data |= MM_ATC_L2_MISC_CG__MEM_LS_ENABLE_MASK;
-       else
-               data &= ~MM_ATC_L2_MISC_CG__MEM_LS_ENABLE_MASK;
+       uint32_t def, data, def1, data1, def2, data2;
+
+       def  = data  = RREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_CGTT_CLK_CTRL);
+       def1 = data1 = RREG32_SOC15(MMHUB, 0, mmDAGB0_WR_CGTT_CLK_CTRL);
+       def2 = data2 = RREG32_SOC15(MMHUB, 0, mmDAGB0_RD_CGTT_CLK_CTRL);
+
+       if (enable && (adev->cg_flags & AMD_CG_SUPPORT_MC_LS)) {
+               data &= ~MM_ATC_L2_CGTT_CLK_CTRL__MGLS_OVERRIDE_MASK;
+               data1 &= !(DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_WRITE_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_READ_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_RETURN_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_REGISTER_MASK);
+               data2 &= !(DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_WRITE_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_READ_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_RETURN_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_REGISTER_MASK);
+       } else {
+               data |= MM_ATC_L2_CGTT_CLK_CTRL__MGLS_OVERRIDE_MASK;
+               data1 |= (DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_WRITE_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_READ_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_RETURN_MASK |
+                       DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_REGISTER_MASK);
+               data2 |= (DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_WRITE_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_READ_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_RETURN_MASK |
+                       DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_REGISTER_MASK);
+       }
 
        if (def != data)
-               WREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_MISC_CG, data);
+               WREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_CGTT_CLK_CTRL, data);
+       if (def1 != data1)
+               WREG32_SOC15(MMHUB, 0, mmDAGB0_WR_CGTT_CLK_CTRL, data1);
+       if (def2 != data2)
+               WREG32_SOC15(MMHUB, 0, mmDAGB0_RD_CGTT_CLK_CTRL, data2);
 }
 
 static int mmhub_v2_3_set_clockgating(struct amdgpu_device *adev,
@@ -554,26 +579,39 @@ static int mmhub_v2_3_set_clockgating(struct amdgpu_device *adev,
 
 static void mmhub_v2_3_get_clockgating(struct amdgpu_device *adev, u32 *flags)
 {
-       int data, data1;
+       int data, data1, data2, data3;
 
        if (amdgpu_sriov_vf(adev))
                *flags = 0;
 
-       data  = RREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_MISC_CG);
-       data1 = RREG32_SOC15(MMHUB, 0, mmDAGB0_CNTL_MISC2);
+       data = RREG32_SOC15(MMHUB, 0, mmDAGB0_CNTL_MISC2);
+       data1  = RREG32_SOC15(MMHUB, 0, mmMM_ATC_L2_CGTT_CLK_CTRL);
+       data2 = RREG32_SOC15(MMHUB, 0, mmDAGB0_WR_CGTT_CLK_CTRL);
+       data3 = RREG32_SOC15(MMHUB, 0, mmDAGB0_RD_CGTT_CLK_CTRL);
 
        /* AMD_CG_SUPPORT_MC_MGCG */
-       if ((data & MM_ATC_L2_MISC_CG__ENABLE_MASK) &&
-           !(data1 & (DAGB0_CNTL_MISC2__DISABLE_WRREQ_CG_MASK |
+       if (!(data & (DAGB0_CNTL_MISC2__DISABLE_WRREQ_CG_MASK |
                       DAGB0_CNTL_MISC2__DISABLE_WRRET_CG_MASK |
                       DAGB0_CNTL_MISC2__DISABLE_RDREQ_CG_MASK |
                       DAGB0_CNTL_MISC2__DISABLE_RDRET_CG_MASK |
                       DAGB0_CNTL_MISC2__DISABLE_TLBWR_CG_MASK |
-                      DAGB0_CNTL_MISC2__DISABLE_TLBRD_CG_MASK)))
-               *flags |= AMD_CG_SUPPORT_MC_MGCG;
+                      DAGB0_CNTL_MISC2__DISABLE_TLBRD_CG_MASK))
+               && !(data1 & MM_ATC_L2_CGTT_CLK_CTRL__SOFT_OVERRIDE_MASK)) {
+                       *flags |= AMD_CG_SUPPORT_MC_MGCG;
+       }
 
        /* AMD_CG_SUPPORT_MC_LS */
-       if (data & MM_ATC_L2_MISC_CG__MEM_LS_ENABLE_MASK)
+       if (!(data1 & MM_ATC_L2_CGTT_CLK_CTRL__MGLS_OVERRIDE_MASK)
+               && !(data2 & (DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_MASK |
+                               DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_WRITE_MASK |
+                               DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_READ_MASK |
+                               DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_RETURN_MASK |
+                               DAGB0_WR_CGTT_CLK_CTRL__LS_OVERRIDE_REGISTER_MASK))
+               && !(data3 & (DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_MASK |
+                               DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_WRITE_MASK |
+                               DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_READ_MASK |
+                               DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_RETURN_MASK |
+                               DAGB0_RD_CGTT_CLK_CTRL__LS_OVERRIDE_REGISTER_MASK)))
                *flags |= AMD_CG_SUPPORT_MC_LS;
 }
 
index d65a533..3ba7bdf 100644 (file)
@@ -47,7 +47,7 @@ enum psp_gfx_crtl_cmd_id
     GFX_CTRL_CMD_ID_DISABLE_INT     = 0x00060000,   /* disable PSP-to-Gfx interrupt */
     GFX_CTRL_CMD_ID_MODE1_RST       = 0x00070000,   /* trigger the Mode 1 reset */
     GFX_CTRL_CMD_ID_GBR_IH_SET      = 0x00080000,   /* set Gbr IH_RB_CNTL registers */
-    GFX_CTRL_CMD_ID_CONSUME_CMD     = 0x000A0000,   /* send interrupt to psp for updating write pointer of vf */
+    GFX_CTRL_CMD_ID_CONSUME_CMD     = 0x00090000,   /* send interrupt to psp for updating write pointer of vf */
     GFX_CTRL_CMD_ID_DESTROY_GPCOM_RING = 0x000C0000, /* destroy GPCOM ring */
 
     GFX_CTRL_CMD_ID_MAX             = 0x000F0000,   /* max command ID */
index 8a23636..0b3516c 100644 (file)
@@ -1239,7 +1239,8 @@ static int soc15_common_early_init(void *handle)
                break;
        case CHIP_RENOIR:
                adev->asic_funcs = &soc15_asic_funcs;
-               if (adev->pdev->device == 0x1636)
+               if ((adev->pdev->device == 0x1636) ||
+                   (adev->pdev->device == 0x164c))
                        adev->apu_flags |= AMD_APU_IS_RENOIR;
                else
                        adev->apu_flags |= AMD_APU_IS_GREEN_SARDINE;
index 8cac497..a5640a6 100644 (file)
@@ -1040,11 +1040,14 @@ static int kfd_create_vcrat_image_cpu(void *pcrat_image, size_t *size)
                                (struct crat_subtype_iolink *)sub_type_hdr);
                if (ret < 0)
                        return ret;
-               crat_table->length += (sub_type_hdr->length * entries);
-               crat_table->total_entries += entries;
 
-               sub_type_hdr = (typeof(sub_type_hdr))((char *)sub_type_hdr +
-                               sub_type_hdr->length * entries);
+               if (entries) {
+                       crat_table->length += (sub_type_hdr->length * entries);
+                       crat_table->total_entries += entries;
+
+                       sub_type_hdr = (typeof(sub_type_hdr))((char *)sub_type_hdr +
+                                       sub_type_hdr->length * entries);
+               }
 #else
                pr_info("IO link not available for non x86 platforms\n");
 #endif
index 797b5d4..e509a17 100644 (file)
@@ -6,7 +6,7 @@ config DRM_AMD_DC
        bool "AMD DC - Enable new display engine"
        default y
        select SND_HDA_COMPONENT if SND_HDA_CORE
-       select DRM_AMD_DC_DCN if (X86 || PPC64 || (ARM64 && KERNEL_MODE_NEON)) && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS)
+       select DRM_AMD_DC_DCN if (X86 || PPC64) && !(KCOV_INSTRUMENT_ALL && KCOV_ENABLE_COMPARISONS)
        help
          Choose this option if you want to use the new display engine
          support for AMDGPU. This adds required support for Vega and
index 519080e..c6da89d 100644 (file)
@@ -939,41 +939,6 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_
 }
 #endif
 
-#ifdef CONFIG_DEBUG_FS
-static int create_crtc_crc_properties(struct amdgpu_display_manager *dm)
-{
-       dm->crc_win_x_start_property =
-               drm_property_create_range(adev_to_drm(dm->adev),
-                                         DRM_MODE_PROP_ATOMIC,
-                                         "AMD_CRC_WIN_X_START", 0, U16_MAX);
-       if (!dm->crc_win_x_start_property)
-               return -ENOMEM;
-
-       dm->crc_win_y_start_property =
-               drm_property_create_range(adev_to_drm(dm->adev),
-                                         DRM_MODE_PROP_ATOMIC,
-                                         "AMD_CRC_WIN_Y_START", 0, U16_MAX);
-       if (!dm->crc_win_y_start_property)
-               return -ENOMEM;
-
-       dm->crc_win_x_end_property =
-               drm_property_create_range(adev_to_drm(dm->adev),
-                                         DRM_MODE_PROP_ATOMIC,
-                                         "AMD_CRC_WIN_X_END", 0, U16_MAX);
-       if (!dm->crc_win_x_end_property)
-               return -ENOMEM;
-
-       dm->crc_win_y_end_property =
-               drm_property_create_range(adev_to_drm(dm->adev),
-                                         DRM_MODE_PROP_ATOMIC,
-                                         "AMD_CRC_WIN_Y_END", 0, U16_MAX);
-       if (!dm->crc_win_y_end_property)
-               return -ENOMEM;
-
-       return 0;
-}
-#endif
-
 static int amdgpu_dm_init(struct amdgpu_device *adev)
 {
        struct dc_init_data init_data;
@@ -1120,10 +1085,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
 
                dc_init_callbacks(adev->dm.dc, &init_params);
        }
-#endif
-#ifdef CONFIG_DEBUG_FS
-       if (create_crtc_crc_properties(&adev->dm))
-               DRM_ERROR("amdgpu: failed to create crc property.\n");
 #endif
        if (amdgpu_dm_initialize_drm_device(adev)) {
                DRM_ERROR(
@@ -2386,8 +2347,7 @@ void amdgpu_dm_update_connector_after_detect(
 
                        drm_connector_update_edid_property(connector,
                                                           aconnector->edid);
-                       aconnector->num_modes = drm_add_edid_modes(connector, aconnector->edid);
-                       drm_connector_list_update(connector);
+                       drm_add_edid_modes(connector, aconnector->edid);
 
                        if (aconnector->dc_link->aux_mode)
                                drm_dp_cec_set_edid(&aconnector->dm_dp_aux.aux,
@@ -5334,64 +5294,12 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
        state->crc_src = cur->crc_src;
        state->cm_has_degamma = cur->cm_has_degamma;
        state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb;
-#ifdef CONFIG_DEBUG_FS
-       state->crc_window = cur->crc_window;
-#endif
+
        /* TODO Duplicate dc_stream after objects are stream object is flattened */
 
        return &state->base;
 }
 
-#ifdef CONFIG_DEBUG_FS
-static int amdgpu_dm_crtc_atomic_set_property(struct drm_crtc *crtc,
-                                           struct drm_crtc_state *crtc_state,
-                                           struct drm_property *property,
-                                           uint64_t val)
-{
-       struct drm_device *dev = crtc->dev;
-       struct amdgpu_device *adev = drm_to_adev(dev);
-       struct dm_crtc_state *dm_new_state =
-               to_dm_crtc_state(crtc_state);
-
-       if (property == adev->dm.crc_win_x_start_property)
-               dm_new_state->crc_window.x_start = val;
-       else if (property == adev->dm.crc_win_y_start_property)
-               dm_new_state->crc_window.y_start = val;
-       else if (property == adev->dm.crc_win_x_end_property)
-               dm_new_state->crc_window.x_end = val;
-       else if (property == adev->dm.crc_win_y_end_property)
-               dm_new_state->crc_window.y_end = val;
-       else
-               return -EINVAL;
-
-       return 0;
-}
-
-static int amdgpu_dm_crtc_atomic_get_property(struct drm_crtc *crtc,
-                                           const struct drm_crtc_state *state,
-                                           struct drm_property *property,
-                                           uint64_t *val)
-{
-       struct drm_device *dev = crtc->dev;
-       struct amdgpu_device *adev = drm_to_adev(dev);
-       struct dm_crtc_state *dm_state =
-               to_dm_crtc_state(state);
-
-       if (property == adev->dm.crc_win_x_start_property)
-               *val = dm_state->crc_window.x_start;
-       else if (property == adev->dm.crc_win_y_start_property)
-               *val = dm_state->crc_window.y_start;
-       else if (property == adev->dm.crc_win_x_end_property)
-               *val = dm_state->crc_window.x_end;
-       else if (property == adev->dm.crc_win_y_end_property)
-               *val = dm_state->crc_window.y_end;
-       else
-               return -EINVAL;
-
-       return 0;
-}
-#endif
-
 static inline int dm_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
 {
        enum dc_irq_source irq_source;
@@ -5458,10 +5366,6 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
        .enable_vblank = dm_enable_vblank,
        .disable_vblank = dm_disable_vblank,
        .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
-#ifdef CONFIG_DEBUG_FS
-       .atomic_set_property = amdgpu_dm_crtc_atomic_set_property,
-       .atomic_get_property = amdgpu_dm_crtc_atomic_get_property,
-#endif
 };
 
 static enum drm_connector_status
@@ -6663,25 +6567,6 @@ static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
        return 0;
 }
 
-#ifdef CONFIG_DEBUG_FS
-static void attach_crtc_crc_properties(struct amdgpu_display_manager *dm,
-                               struct amdgpu_crtc *acrtc)
-{
-       drm_object_attach_property(&acrtc->base.base,
-                                  dm->crc_win_x_start_property,
-                                  0);
-       drm_object_attach_property(&acrtc->base.base,
-                                  dm->crc_win_y_start_property,
-                                  0);
-       drm_object_attach_property(&acrtc->base.base,
-                                  dm->crc_win_x_end_property,
-                                  0);
-       drm_object_attach_property(&acrtc->base.base,
-                                  dm->crc_win_y_end_property,
-                                  0);
-}
-#endif
-
 static int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
                               struct drm_plane *plane,
                               uint32_t crtc_index)
@@ -6729,9 +6614,7 @@ static int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
        drm_crtc_enable_color_mgmt(&acrtc->base, MAX_COLOR_LUT_ENTRIES,
                                   true, MAX_COLOR_LUT_ENTRIES);
        drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LEGACY_LUT_ENTRIES);
-#ifdef CONFIG_DEBUG_FS
-       attach_crtc_crc_properties(dm, acrtc);
-#endif
+
        return 0;
 
 fail:
@@ -8368,7 +8251,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
         */
        for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
                struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
-               bool configure_crc = false;
 
                dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
 
@@ -8378,30 +8260,21 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
                        dc_stream_retain(dm_new_crtc_state->stream);
                        acrtc->dm_irq_params.stream = dm_new_crtc_state->stream;
                        manage_dm_interrupts(adev, acrtc, true);
-               }
+
 #ifdef CONFIG_DEBUG_FS
-               if (new_crtc_state->active &&
-                       amdgpu_dm_is_valid_crc_source(dm_new_crtc_state->crc_src)) {
                        /**
                         * Frontend may have changed so reapply the CRC capture
                         * settings for the stream.
                         */
                        dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-                       dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
-                       if (amdgpu_dm_crc_window_is_default(dm_new_crtc_state)) {
-                               if (!old_crtc_state->active || drm_atomic_crtc_needs_modeset(new_crtc_state))
-                                       configure_crc = true;
-                       } else {
-                               if (amdgpu_dm_crc_window_changed(dm_new_crtc_state, dm_old_crtc_state))
-                                       configure_crc = true;
-                       }
 
-                       if (configure_crc)
+                       if (amdgpu_dm_is_valid_crc_source(dm_new_crtc_state->crc_src)) {
                                amdgpu_dm_crtc_configure_crc_source(
-                                       crtc, dm_new_crtc_state, dm_new_crtc_state->crc_src);
-               }
+                                       crtc, dm_new_crtc_state,
+                                       dm_new_crtc_state->crc_src);
+                       }
 #endif
+               }
        }
 
        for_each_new_crtc_in_state(state, crtc, new_crtc_state, j)
index 2ee6edb..1182daf 100644 (file)
@@ -336,32 +336,6 @@ struct amdgpu_display_manager {
         */
        const struct gpu_info_soc_bounding_box_v1_0 *soc_bounding_box;
 
-#ifdef CONFIG_DEBUG_FS
-       /**
-        * @crc_win_x_start_property:
-        *
-        * X start of the crc calculation window
-        */
-       struct drm_property *crc_win_x_start_property;
-       /**
-        * @crc_win_y_start_property:
-        *
-        * Y start of the crc calculation window
-        */
-       struct drm_property *crc_win_y_start_property;
-       /**
-        * @crc_win_x_end_property:
-        *
-        * X end of the crc calculation window
-        */
-       struct drm_property *crc_win_x_end_property;
-       /**
-        * @crc_win_y_end_property:
-        *
-        * Y end of the crc calculation window
-        */
-       struct drm_property *crc_win_y_end_property;
-#endif
        /**
         * @mst_encoders:
         *
@@ -448,15 +422,6 @@ struct dm_plane_state {
        struct dc_plane_state *dc_state;
 };
 
-#ifdef CONFIG_DEBUG_FS
-struct crc_rec {
-       uint16_t x_start;
-       uint16_t y_start;
-       uint16_t x_end;
-       uint16_t y_end;
-       };
-#endif
-
 struct dm_crtc_state {
        struct drm_crtc_state base;
        struct dc_stream_state *stream;
@@ -479,9 +444,6 @@ struct dm_crtc_state {
        struct dc_info_packet vrr_infopacket;
 
        int abm_level;
-#ifdef CONFIG_DEBUG_FS
-       struct crc_rec crc_window;
-#endif
 };
 
 #define to_dm_crtc_state(x) container_of(x, struct dm_crtc_state, base)
index 7b886a7..66cb873 100644 (file)
@@ -81,41 +81,6 @@ const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc,
        return pipe_crc_sources;
 }
 
-static void amdgpu_dm_set_crc_window_default(struct dm_crtc_state *dm_crtc_state)
-{
-       dm_crtc_state->crc_window.x_start = 0;
-       dm_crtc_state->crc_window.y_start = 0;
-       dm_crtc_state->crc_window.x_end = 0;
-       dm_crtc_state->crc_window.y_end = 0;
-}
-
-bool amdgpu_dm_crc_window_is_default(struct dm_crtc_state *dm_crtc_state)
-{
-       bool ret = true;
-
-       if ((dm_crtc_state->crc_window.x_start != 0) ||
-               (dm_crtc_state->crc_window.y_start != 0) ||
-               (dm_crtc_state->crc_window.x_end != 0) ||
-               (dm_crtc_state->crc_window.y_end != 0))
-               ret = false;
-
-       return ret;
-}
-
-bool amdgpu_dm_crc_window_changed(struct dm_crtc_state *dm_new_crtc_state,
-                                       struct dm_crtc_state *dm_old_crtc_state)
-{
-       bool ret = false;
-
-       if ((dm_new_crtc_state->crc_window.x_start != dm_old_crtc_state->crc_window.x_start) ||
-               (dm_new_crtc_state->crc_window.y_start != dm_old_crtc_state->crc_window.y_start) ||
-               (dm_new_crtc_state->crc_window.x_end != dm_old_crtc_state->crc_window.x_end) ||
-               (dm_new_crtc_state->crc_window.y_end != dm_old_crtc_state->crc_window.y_end))
-               ret = true;
-
-       return ret;
-}
-
 int
 amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
                                 size_t *values_cnt)
@@ -140,7 +105,6 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
        struct dc_stream_state *stream_state = dm_crtc_state->stream;
        bool enable = amdgpu_dm_is_valid_crc_source(source);
        int ret = 0;
-       struct crc_params *crc_window = NULL, tmp_window;
 
        /* Configuration will be deferred to stream enable. */
        if (!stream_state)
@@ -150,24 +114,8 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
 
        /* Enable CRTC CRC generation if necessary. */
        if (dm_is_crc_source_crtc(source) || source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE) {
-               if (!enable)
-                       amdgpu_dm_set_crc_window_default(dm_crtc_state);
-
-               if (!amdgpu_dm_crc_window_is_default(dm_crtc_state)) {
-                       crc_window = &tmp_window;
-
-                       tmp_window.windowa_x_start = dm_crtc_state->crc_window.x_start;
-                       tmp_window.windowa_y_start = dm_crtc_state->crc_window.y_start;
-                       tmp_window.windowa_x_end = dm_crtc_state->crc_window.x_end;
-                       tmp_window.windowa_y_end = dm_crtc_state->crc_window.y_end;
-                       tmp_window.windowb_x_start = dm_crtc_state->crc_window.x_start;
-                       tmp_window.windowb_y_start = dm_crtc_state->crc_window.y_start;
-                       tmp_window.windowb_x_end = dm_crtc_state->crc_window.x_end;
-                       tmp_window.windowb_y_end = dm_crtc_state->crc_window.y_end;
-               }
-
                if (!dc_stream_configure_crc(stream_state->ctx->dc,
-                                            stream_state, crc_window, enable, enable)) {
+                                            stream_state, NULL, enable, enable)) {
                        ret = -EINVAL;
                        goto unlock;
                }
index 0235bfb..f7d7317 100644 (file)
@@ -47,9 +47,6 @@ static inline bool amdgpu_dm_is_valid_crc_source(enum amdgpu_dm_pipe_crc_source
 
 /* amdgpu_dm_crc.c */
 #ifdef CONFIG_DEBUG_FS
-bool amdgpu_dm_crc_window_is_default(struct dm_crtc_state *dm_crtc_state);
-bool amdgpu_dm_crc_window_changed(struct dm_crtc_state *dm_new_crtc_state,
-                                       struct dm_crtc_state *dm_old_crtc_state);
 int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
                                        struct dm_crtc_state *dm_crtc_state,
                                        enum amdgpu_dm_pipe_crc_source source);
index 64f515d..f3c00f4 100644 (file)
@@ -33,10 +33,6 @@ ifdef CONFIG_PPC64
 calcs_ccflags := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-calcs_rcflags := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index d59b380..ff96bee 100644 (file)
@@ -104,13 +104,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/clk_mgr/dcn21/rn_clk_mgr.o := $(call cc-option,-mno-gnu-attribute)
 endif
 
-# prevent build errors:
-# ...: '-mgeneral-regs-only' is incompatible with the use of floating-point types
-# this file is unused on arm64, just like on ppc64
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/clk_mgr/dcn21/rn_clk_mgr.o := -mgeneral-regs-only
-endif
-
 AMD_DAL_CLK_MGR_DCN21 = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn21/,$(CLK_MGR_DCN21))
 
 AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN21)
@@ -125,13 +118,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/clk_mgr/dcn30/dcn30_clk_mgr.o := $(call cc-option,-mno-gnu-attribute)
 endif
 
-# prevent build errors:
-# ...: '-mgeneral-regs-only' is incompatible with the use of floating-point types
-# this file is unused on arm64, just like on ppc64
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/clk_mgr/dcn30/dcn30_clk_mgr.o := -mgeneral-regs-only
-endif
-
 AMD_DAL_CLK_MGR_DCN30 = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn30/,$(CLK_MGR_DCN30))
 
 AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN30)
@@ -146,13 +132,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/clk_mgr/dcn301/vg_clk_mgr.o := $(call cc-option,-mno-gnu-attribute)
 endif
 
-# prevent build errors:
-# ...: '-mgeneral-regs-only' is incompatible with the use of floating-point types
-# this file is unused on arm64, just like on ppc64
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/clk_mgr/dcn301/vg_clk_mgr.o := -mgeneral-regs-only
-endif
-
 AMD_DAL_CLK_MGR_DCN301 = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn301/,$(CLK_MGR_DCN301))
 
 AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN301)
index 5b466f4..ab98c25 100644 (file)
@@ -251,6 +251,7 @@ static void dcn3_update_clocks(struct clk_mgr *clk_mgr_base,
        struct dmcu *dmcu = clk_mgr_base->ctx->dc->res_pool->dmcu;
        bool force_reset = false;
        bool update_uclk = false;
+       bool p_state_change_support;
 
        if (dc->work_arounds.skip_clock_update || !clk_mgr->smu_present)
                return;
@@ -291,8 +292,9 @@ static void dcn3_update_clocks(struct clk_mgr *clk_mgr_base,
                clk_mgr_base->clks.socclk_khz = new_clocks->socclk_khz;
 
        clk_mgr_base->clks.prev_p_state_change_support = clk_mgr_base->clks.p_state_change_support;
-       if (should_update_pstate_support(safe_to_lower, new_clocks->p_state_change_support, clk_mgr_base->clks.p_state_change_support)) {
-               clk_mgr_base->clks.p_state_change_support = new_clocks->p_state_change_support;
+       p_state_change_support = new_clocks->p_state_change_support || (display_count == 0);
+       if (should_update_pstate_support(safe_to_lower, p_state_change_support, clk_mgr_base->clks.p_state_change_support)) {
+               clk_mgr_base->clks.p_state_change_support = p_state_change_support;
 
                /* to disable P-State switching, set UCLK min = max */
                if (!clk_mgr_base->clks.p_state_change_support)
index 9e1071b..f4a2088 100644 (file)
@@ -2487,9 +2487,14 @@ enum dc_status dc_link_validate_mode_timing(
 static struct abm *get_abm_from_stream_res(const struct dc_link *link)
 {
        int i;
-       struct dc *dc = link->ctx->dc;
+       struct dc *dc = NULL;
        struct abm *abm = NULL;
 
+       if (!link || !link->ctx)
+               return NULL;
+
+       dc = link->ctx->dc;
+
        for (i = 0; i < MAX_PIPES; i++) {
                struct pipe_ctx pipe_ctx = dc->current_state->res_ctx.pipe_ctx[i];
                struct dc_stream_state *stream = pipe_ctx.stream;
index 2fc1223..f95bade 100644 (file)
@@ -2399,6 +2399,9 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
                        initial_link_setting;
        uint32_t link_bw;
 
+       if (req_bw > dc_link_bandwidth_kbps(link, &link->verified_link_cap))
+               return false;
+
        /* search for the minimum link setting that:
         * 1. is supported according to the link training result
         * 2. could support the b/w requested by the timing
@@ -3045,14 +3048,14 @@ bool dc_link_handle_hpd_rx_irq(struct dc_link *link, union hpd_irq_data *out_hpd
                for (i = 0; i < MAX_PIPES; i++) {
                        pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
                        if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off &&
-                                       pipe_ctx->stream->link == link)
+                                       pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe)
                                core_link_disable_stream(pipe_ctx);
                }
 
                for (i = 0; i < MAX_PIPES; i++) {
                        pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
                        if (pipe_ctx && pipe_ctx->stream && !pipe_ctx->stream->dpms_off &&
-                                       pipe_ctx->stream->link == link)
+                                       pipe_ctx->stream->link == link && !pipe_ctx->prev_odm_pipe)
                                core_link_enable_stream(link->dc->current_state, pipe_ctx);
                }
 
@@ -3992,7 +3995,7 @@ bool dc_link_dp_set_test_pattern(
        unsigned int cust_pattern_size)
 {
        struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx;
-       struct pipe_ctx *pipe_ctx = &pipes[0];
+       struct pipe_ctx *pipe_ctx = NULL;
        unsigned int lane;
        unsigned int i;
        unsigned char link_qual_pattern[LANE_COUNT_DP_MAX] = {0};
@@ -4002,12 +4005,18 @@ bool dc_link_dp_set_test_pattern(
        memset(&training_pattern, 0, sizeof(training_pattern));
 
        for (i = 0; i < MAX_PIPES; i++) {
+               if (pipes[i].stream == NULL)
+                       continue;
+
                if (pipes[i].stream->link == link && !pipes[i].top_pipe && !pipes[i].prev_odm_pipe) {
                        pipe_ctx = &pipes[i];
                        break;
                }
        }
 
+       if (pipe_ctx == NULL)
+               return false;
+
        /* Reset CRTC Test Pattern if it is currently running and request is VideoMode */
        if (link->test_pattern_enabled && test_pattern ==
                        DP_TEST_PATTERN_VIDEO_MODE) {
index 733e6e6..62ad1a1 100644 (file)
@@ -31,11 +31,4 @@ DCN10 = dcn10_init.o dcn10_resource.o dcn10_ipp.o dcn10_hw_sequencer.o \
 
 AMD_DAL_DCN10 = $(addprefix $(AMDDALPATH)/dc/dcn10/,$(DCN10))
 
-# fix:
-# ...: '-mgeneral-regs-only' is incompatible with the use of floating-point types
-# aarch64 does not support soft-float, so use hard-float and handle this in code
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn10/dcn10_resource.o := -mgeneral-regs-only
-endif
-
 AMD_DISPLAY_FILES += $(AMD_DAL_DCN10)
index cfc130e..017b67b 100644 (file)
@@ -647,8 +647,13 @@ static void power_on_plane(
        if (REG(DC_IP_REQUEST_CNTL)) {
                REG_SET(DC_IP_REQUEST_CNTL, 0,
                                IP_REQUEST_EN, 1);
-               hws->funcs.dpp_pg_control(hws, plane_id, true);
-               hws->funcs.hubp_pg_control(hws, plane_id, true);
+
+               if (hws->funcs.dpp_pg_control)
+                       hws->funcs.dpp_pg_control(hws, plane_id, true);
+
+               if (hws->funcs.hubp_pg_control)
+                       hws->funcs.hubp_pg_control(hws, plane_id, true);
+
                REG_SET(DC_IP_REQUEST_CNTL, 0,
                                IP_REQUEST_EN, 0);
                DC_LOG_DEBUG(
@@ -1082,8 +1087,13 @@ void dcn10_plane_atomic_power_down(struct dc *dc,
        if (REG(DC_IP_REQUEST_CNTL)) {
                REG_SET(DC_IP_REQUEST_CNTL, 0,
                                IP_REQUEST_EN, 1);
-               hws->funcs.dpp_pg_control(hws, dpp->inst, false);
-               hws->funcs.hubp_pg_control(hws, hubp->inst, false);
+
+               if (hws->funcs.dpp_pg_control)
+                       hws->funcs.dpp_pg_control(hws, dpp->inst, false);
+
+               if (hws->funcs.hubp_pg_control)
+                       hws->funcs.hubp_pg_control(hws, hubp->inst, false);
+
                dpp->funcs->dpp_reset(dpp);
                REG_SET(DC_IP_REQUEST_CNTL, 0,
                                IP_REQUEST_EN, 0);
index 100ce0e..b096011 100644 (file)
@@ -470,7 +470,7 @@ void mpc1_cursor_lock(struct mpc *mpc, int opp_id, bool lock)
 unsigned int mpc1_get_mpc_out_mux(struct mpc *mpc, int opp_id)
 {
        struct dcn10_mpc *mpc10 = TO_DCN10_MPC(mpc);
-       uint32_t val = 0;
+       uint32_t val = 0xf;
 
        if (opp_id < MAX_OPP && REG(MUX[opp_id]))
                REG_GET(MUX[opp_id], MPC_OUT_MUX, &val);
index bdc3783..90e912f 100644 (file)
@@ -608,8 +608,8 @@ static const struct dc_debug_options debug_defaults_drv = {
                .disable_pplib_clock_request = false,
                .disable_pplib_wm_range = false,
                .pplib_wm_report_mode = WM_REPORT_DEFAULT,
-               .pipe_split_policy = MPC_SPLIT_DYNAMIC,
-               .force_single_disp_pipe_split = true,
+               .pipe_split_policy = MPC_SPLIT_AVOID,
+               .force_single_disp_pipe_split = false,
                .disable_dcc = DCC_ENABLE,
                .voltage_align_fclk = true,
                .disable_stereo_support = true,
@@ -1534,15 +1534,8 @@ static bool dcn10_resource_construct(
        memcpy(dc->dcn_ip, &dcn10_ip_defaults, sizeof(dcn10_ip_defaults));
        memcpy(dc->dcn_soc, &dcn10_soc_defaults, sizeof(dcn10_soc_defaults));
 
-#if defined(CONFIG_ARM64)
-       /* Aarch64 does not support -msoft-float/-mfloat-abi=soft */
-       DC_FP_START();
-       dcn10_resource_construct_fp(dc);
-       DC_FP_END();
-#else
        /* Other architectures we build for build this with soft-float */
        dcn10_resource_construct_fp(dc);
-#endif
 
        pool->base.pp_smu = dcn10_pp_smu_create(ctx);
 
index 624cb13..5fcaf78 100644 (file)
@@ -17,10 +17,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/dcn20/dcn20_resource.o := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn20/dcn20_resource.o := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index cb822df..480d928 100644 (file)
@@ -1062,8 +1062,13 @@ static void dcn20_power_on_plane(
        if (REG(DC_IP_REQUEST_CNTL)) {
                REG_SET(DC_IP_REQUEST_CNTL, 0,
                                IP_REQUEST_EN, 1);
-               dcn20_dpp_pg_control(hws, pipe_ctx->plane_res.dpp->inst, true);
-               dcn20_hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, true);
+
+               if (hws->funcs.dpp_pg_control)
+                       hws->funcs.dpp_pg_control(hws, pipe_ctx->plane_res.dpp->inst, true);
+
+               if (hws->funcs.hubp_pg_control)
+                       hws->funcs.hubp_pg_control(hws, pipe_ctx->plane_res.hubp->inst, true);
+
                REG_SET(DC_IP_REQUEST_CNTL, 0,
                                IP_REQUEST_EN, 0);
                DC_LOG_DEBUG(
index e04ecf0..5ed18ca 100644 (file)
@@ -2517,8 +2517,7 @@ struct pipe_ctx *dcn20_find_secondary_pipe(struct dc *dc,
                 * if this primary pipe has a bottom pipe in prev. state
                 * and if the bottom pipe is still available (which it should be),
                 * pick that pipe as secondary
-                * Same logic applies for ODM pipes. Since mpo is not allowed with odm
-                * check in else case.
+                * Same logic applies for ODM pipes
                 */
                if (dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].bottom_pipe) {
                        preferred_pipe_idx = dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].bottom_pipe->pipe_idx;
@@ -2526,7 +2525,9 @@ struct pipe_ctx *dcn20_find_secondary_pipe(struct dc *dc,
                                secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx];
                                secondary_pipe->pipe_idx = preferred_pipe_idx;
                        }
-               } else if (dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].next_odm_pipe) {
+               }
+               if (secondary_pipe == NULL &&
+                               dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].next_odm_pipe) {
                        preferred_pipe_idx = dc->current_state->res_ctx.pipe_ctx[primary_pipe->pipe_idx].next_odm_pipe->pipe_idx;
                        if (res_ctx->pipe_ctx[preferred_pipe_idx].stream == NULL) {
                                secondary_pipe = &res_ctx->pipe_ctx[preferred_pipe_idx];
index 1ee5fc0..bb8c951 100644 (file)
@@ -13,10 +13,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/dcn21/dcn21_resource.o := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn21/dcn21_resource.o := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index 1c88d2e..b000b43 100644 (file)
@@ -296,7 +296,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_1_soc = {
        .num_banks = 8,
        .num_chans = 4,
        .vmm_page_size_bytes = 4096,
-       .dram_clock_change_latency_us = 23.84,
+       .dram_clock_change_latency_us = 11.72,
        .return_bus_width_bytes = 64,
        .dispclk_dppclk_vco_speed_mhz = 3600,
        .xfc_bus_transport_time_us = 4,
index 248c271..c20331e 100644 (file)
@@ -41,11 +41,6 @@ CFLAGS_$(AMDDALPATH)/dc/dcn30/dcn30_resource.o := -mhard-float -maltivec
 CFLAGS_$(AMDDALPATH)/dc/dcn30/dcn30_optc.o := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn30/dcn30_resource.o := -mgeneral-regs-only
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn30/dcn30_optc.o := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index 2fd5d34..3ca7d91 100644 (file)
@@ -21,10 +21,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/dcn301/dcn301_resource.o := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn301/dcn301_resource.o := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index 4825c5c..35f5bf0 100644 (file)
@@ -1731,6 +1731,7 @@ static struct resource_funcs dcn301_res_pool_funcs = {
        .populate_dml_pipes = dcn30_populate_dml_pipes_from_context,
        .acquire_idle_pipe_for_layer = dcn20_acquire_idle_pipe_for_layer,
        .add_stream_to_ctx = dcn30_add_stream_to_ctx,
+       .add_dsc_to_stream_resource = dcn20_add_dsc_to_stream_resource,
        .remove_stream_from_ctx = dcn20_remove_stream_from_ctx,
        .populate_dml_writeback_from_context = dcn30_populate_dml_writeback_from_context,
        .set_mcif_arb_params = dcn30_set_mcif_arb_params,
index 36e44e1..8d4924b 100644 (file)
@@ -20,10 +20,6 @@ ifdef CONFIG_PPC64
 CFLAGS_$(AMDDALPATH)/dc/dcn302/dcn302_resource.o := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dcn302/dcn302_resource.o := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index a02a33d..6bb7f29 100644 (file)
@@ -33,10 +33,6 @@ ifdef CONFIG_PPC64
 dml_ccflags := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-dml_rcflags := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index 860e72a..80170f9 100644 (file)
@@ -2635,14 +2635,15 @@ static void dml20v2_DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndP
        }
 
        if (mode_lib->vba.DRAMClockChangeSupportsVActive &&
-                       mode_lib->vba.MinActiveDRAMClockChangeMargin > 60 &&
-                       mode_lib->vba.PrefetchMode[mode_lib->vba.VoltageLevel][mode_lib->vba.maxMpcComb] == 0) {
+                       mode_lib->vba.MinActiveDRAMClockChangeMargin > 60) {
                mode_lib->vba.DRAMClockChangeWatermark += 25;
 
                for (k = 0; k < mode_lib->vba.NumberOfActivePlanes; ++k) {
-                       if (mode_lib->vba.DRAMClockChangeWatermark >
-                       dml_max(mode_lib->vba.StutterEnterPlusExitWatermark, mode_lib->vba.UrgentWatermark))
-                               mode_lib->vba.MinTTUVBlank[k] += 25;
+                       if (mode_lib->vba.PrefetchMode[mode_lib->vba.VoltageLevel][mode_lib->vba.maxMpcComb] == 0) {
+                               if (mode_lib->vba.DRAMClockChangeWatermark >
+                               dml_max(mode_lib->vba.StutterEnterPlusExitWatermark, mode_lib->vba.UrgentWatermark))
+                                       mode_lib->vba.MinTTUVBlank[k] += 25;
+                       }
                }
 
                mode_lib->vba.DRAMClockChangeSupport[0][0] = dm_dram_clock_change_vactive;
index f2624a1..8d31eb7 100644 (file)
@@ -10,10 +10,6 @@ ifdef CONFIG_PPC64
 dsc_ccflags := -mhard-float -maltivec
 endif
 
-ifdef CONFIG_ARM64
-dsc_rcflags := -mgeneral-regs-only
-endif
-
 ifdef CONFIG_CC_IS_GCC
 ifeq ($(call cc-ifversion, -lt, 0701, y), y)
 IS_OLD_GCC = 1
index 95cb569..126c2f3 100644 (file)
 #include <asm/fpu/api.h>
 #define DC_FP_START() kernel_fpu_begin()
 #define DC_FP_END() kernel_fpu_end()
-#elif defined(CONFIG_ARM64)
-#include <asm/neon.h>
-#define DC_FP_START() kernel_neon_begin()
-#define DC_FP_END() kernel_neon_end()
 #elif defined(CONFIG_PPC64)
 #include <asm/switch_to.h>
 #include <asm/cputable.h>
index e57e64b..8832278 100644 (file)
@@ -251,7 +251,7 @@ static int smu10_set_hard_min_gfxclk_by_freq(struct pp_hwmgr *hwmgr, uint32_t cl
                smu10_data->gfx_actual_soft_min_freq = clock;
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                        PPSMC_MSG_SetHardMinGfxClk,
-                                       smu10_data->gfx_actual_soft_min_freq,
+                                       clock,
                                        NULL);
        }
        return 0;
@@ -558,7 +558,8 @@ static int smu10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
 
        /* enable the pp_od_clk_voltage sysfs file */
        hwmgr->od_enabled = 1;
-
+       /* disabled fine grain tuning function by default */
+       data->fine_grain_enabled = 0;
        return result;
 }
 
@@ -597,6 +598,7 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
        uint32_t min_mclk = hwmgr->display_config->min_mem_set_clock/100;
        uint32_t index_fclk = data->clock_vol_info.vdd_dep_on_fclk->count - 1;
        uint32_t index_socclk = data->clock_vol_info.vdd_dep_on_socclk->count - 1;
+       uint32_t fine_grain_min_freq = 0, fine_grain_max_freq = 0;
 
        if (hwmgr->smu_version < 0x1E3700) {
                pr_info("smu firmware version too old, can not set dpm level\n");
@@ -613,6 +615,14 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
        switch (level) {
        case AMD_DPM_FORCED_LEVEL_HIGH:
        case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+               data->fine_grain_enabled = 0;
+
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &fine_grain_min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &fine_grain_max_freq);
+
+               data->gfx_actual_soft_min_freq = fine_grain_min_freq;
+               data->gfx_actual_soft_max_freq = fine_grain_max_freq;
+
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                                PPSMC_MSG_SetHardMinGfxClk,
                                                data->gfx_max_freq_limit/100,
@@ -648,6 +658,14 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
                                                NULL);
                break;
        case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+               data->fine_grain_enabled = 0;
+
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &fine_grain_min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &fine_grain_max_freq);
+
+               data->gfx_actual_soft_min_freq = fine_grain_min_freq;
+               data->gfx_actual_soft_max_freq = fine_grain_max_freq;
+
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                                PPSMC_MSG_SetHardMinGfxClk,
                                                min_sclk,
@@ -658,6 +676,14 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
                                                NULL);
                break;
        case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+               data->fine_grain_enabled = 0;
+
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &fine_grain_min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &fine_grain_max_freq);
+
+               data->gfx_actual_soft_min_freq = fine_grain_min_freq;
+               data->gfx_actual_soft_max_freq = fine_grain_max_freq;
+
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                                PPSMC_MSG_SetHardMinFclkByFreq,
                                                min_mclk,
@@ -668,6 +694,14 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
                                                NULL);
                break;
        case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+               data->fine_grain_enabled = 0;
+
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &fine_grain_min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &fine_grain_max_freq);
+
+               data->gfx_actual_soft_min_freq = fine_grain_min_freq;
+               data->gfx_actual_soft_max_freq = fine_grain_max_freq;
+
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                                PPSMC_MSG_SetHardMinGfxClk,
                                                SMU10_UMD_PSTATE_GFXCLK,
@@ -703,6 +737,14 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
                                                NULL);
                break;
        case AMD_DPM_FORCED_LEVEL_AUTO:
+               data->fine_grain_enabled = 0;
+
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &fine_grain_min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &fine_grain_max_freq);
+
+               data->gfx_actual_soft_min_freq = fine_grain_min_freq;
+               data->gfx_actual_soft_max_freq = fine_grain_max_freq;
+
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                                PPSMC_MSG_SetHardMinGfxClk,
                                                min_sclk,
@@ -741,6 +783,14 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
                                                NULL);
                break;
        case AMD_DPM_FORCED_LEVEL_LOW:
+               data->fine_grain_enabled = 0;
+
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &fine_grain_min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &fine_grain_max_freq);
+
+               data->gfx_actual_soft_min_freq = fine_grain_min_freq;
+               data->gfx_actual_soft_max_freq = fine_grain_max_freq;
+
                smum_send_msg_to_smc_with_parameter(hwmgr,
                                                PPSMC_MSG_SetHardMinGfxClk,
                                                data->gfx_min_freq_limit/100,
@@ -759,6 +809,7 @@ static int smu10_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
                                                NULL);
                break;
        case AMD_DPM_FORCED_LEVEL_MANUAL:
+               data->fine_grain_enabled = 1;
        case AMD_DPM_FORCED_LEVEL_PROFILE_EXIT:
        default:
                break;
@@ -948,6 +999,8 @@ static int smu10_print_clock_levels(struct pp_hwmgr *hwmgr,
        struct smu10_voltage_dependency_table *mclk_table =
                        data->clock_vol_info.vdd_dep_on_fclk;
        uint32_t i, now, size = 0;
+       uint32_t min_freq, max_freq = 0;
+       uint32_t ret = 0;
 
        switch (type) {
        case PP_SCLK:
@@ -983,18 +1036,28 @@ static int smu10_print_clock_levels(struct pp_hwmgr *hwmgr,
                break;
        case OD_SCLK:
                if (hwmgr->od_enabled) {
-                       size = sprintf(buf, "%s:\n", "OD_SCLK");
+                       ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &min_freq);
+                       if (ret)
+                               return ret;
+                       ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &max_freq);
+                       if (ret)
+                               return ret;
 
+                       size = sprintf(buf, "%s:\n", "OD_SCLK");
                        size += sprintf(buf + size, "0: %10uMhz\n",
-                       (data->gfx_actual_soft_min_freq > 0) ? data->gfx_actual_soft_min_freq : data->gfx_min_freq_limit/100);
-                       size += sprintf(buf + size, "1: %10uMhz\n", data->gfx_max_freq_limit/100);
+                       (data->gfx_actual_soft_min_freq > 0) ? data->gfx_actual_soft_min_freq : min_freq);
+                       size += sprintf(buf + size, "1: %10uMhz\n",
+                       (data->gfx_actual_soft_max_freq > 0) ? data->gfx_actual_soft_max_freq : max_freq);
                }
                break;
        case OD_RANGE:
                if (hwmgr->od_enabled) {
-                       uint32_t min_freq, max_freq = 0;
-                       smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &min_freq);
-                       smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &max_freq);
+                       ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &min_freq);
+                       if (ret)
+                               return ret;
+                       ret = smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &max_freq);
+                       if (ret)
+                               return ret;
 
                        size = sprintf(buf, "%s:\n", "OD_RANGE");
                        size += sprintf(buf + size, "SCLK: %7uMHz %10uMHz\n",
@@ -1414,23 +1477,96 @@ static int smu10_set_fine_grain_clk_vol(struct pp_hwmgr *hwmgr,
                                        enum PP_OD_DPM_TABLE_COMMAND type,
                                        long *input, uint32_t size)
 {
+       uint32_t min_freq, max_freq = 0;
+       struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)(hwmgr->backend);
+       int ret = 0;
+
        if (!hwmgr->od_enabled) {
                pr_err("Fine grain not support\n");
                return -EINVAL;
        }
 
-       if (size != 2) {
-               pr_err("Input parameter number not correct\n");
+       if (!smu10_data->fine_grain_enabled) {
+               pr_err("Fine grain not started\n");
                return -EINVAL;
        }
 
        if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) {
-               if (input[0] == 0)
-                       smu10_set_hard_min_gfxclk_by_freq(hwmgr, input[1]);
-               else if (input[0] == 1)
-                       smu10_set_soft_max_gfxclk_by_freq(hwmgr, input[1]);
-               else
+               if (size != 2) {
+                       pr_err("Input parameter number not correct\n");
                        return -EINVAL;
+               }
+
+               if (input[0] == 0) {
+                       smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &min_freq);
+                       if (input[1] < min_freq) {
+                               pr_err("Fine grain setting minimum sclk (%ld) MHz is less than the minimum allowed (%d) MHz\n",
+                                       input[1], min_freq);
+                               return -EINVAL;
+                       }
+                       smu10_data->gfx_actual_soft_min_freq = input[1];
+               } else if (input[0] == 1) {
+                       smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &max_freq);
+                       if (input[1] > max_freq) {
+                               pr_err("Fine grain setting maximum sclk (%ld) MHz is greater than the maximum allowed (%d) MHz\n",
+                                       input[1], max_freq);
+                               return -EINVAL;
+                       }
+                       smu10_data->gfx_actual_soft_max_freq = input[1];
+               } else {
+                       return -EINVAL;
+               }
+       } else if (type == PP_OD_RESTORE_DEFAULT_TABLE) {
+               if (size != 0) {
+                       pr_err("Input parameter number not correct\n");
+                       return -EINVAL;
+               }
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMinGfxclkFrequency, &min_freq);
+               smum_send_msg_to_smc(hwmgr, PPSMC_MSG_GetMaxGfxclkFrequency, &max_freq);
+
+               smu10_data->gfx_actual_soft_min_freq = min_freq;
+               smu10_data->gfx_actual_soft_max_freq = max_freq;
+
+               ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+                                       PPSMC_MSG_SetHardMinGfxClk,
+                                       min_freq,
+                                       NULL);
+               if (ret)
+                       return ret;
+
+               ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+                                       PPSMC_MSG_SetSoftMaxGfxClk,
+                                       max_freq,
+                                       NULL);
+               if (ret)
+                       return ret;
+       } else if (type == PP_OD_COMMIT_DPM_TABLE) {
+               if (size != 0) {
+                       pr_err("Input parameter number not correct\n");
+                       return -EINVAL;
+               }
+
+               if (smu10_data->gfx_actual_soft_min_freq > smu10_data->gfx_actual_soft_max_freq) {
+                       pr_err("The setting minimun sclk (%d) MHz is greater than the setting maximum sclk (%d) MHz\n",
+                                       smu10_data->gfx_actual_soft_min_freq, smu10_data->gfx_actual_soft_max_freq);
+                       return -EINVAL;
+               }
+
+               ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+                                       PPSMC_MSG_SetHardMinGfxClk,
+                                       smu10_data->gfx_actual_soft_min_freq,
+                                       NULL);
+               if (ret)
+                       return ret;
+
+               ret = smum_send_msg_to_smc_with_parameter(hwmgr,
+                                       PPSMC_MSG_SetSoftMaxGfxClk,
+                                       smu10_data->gfx_actual_soft_max_freq,
+                                       NULL);
+               if (ret)
+                       return ret;
+       } else {
+               return -EINVAL;
        }
 
        return 0;
index 6c9b5f0..808e0ec 100644 (file)
@@ -283,6 +283,7 @@ struct smu10_hwmgr {
        uint32_t                        vclk_soft_min;
        uint32_t                        dclk_soft_min;
        uint32_t                        gfx_actual_soft_min_freq;
+       uint32_t                        gfx_actual_soft_max_freq;
        uint32_t                        gfx_min_freq_limit;
        uint32_t                        gfx_max_freq_limit; /* in 10Khz*/
 
@@ -299,6 +300,8 @@ struct smu10_hwmgr {
        bool need_min_deep_sleep_dcefclk;
        uint32_t                             deep_sleep_dcefclk;
        uint32_t                             num_active_display;
+
+       bool                                                    fine_grain_enabled;
 };
 
 struct pp_hwmgr;
index 9608745..12b36eb 100644 (file)
@@ -2372,7 +2372,7 @@ static void sienna_cichlid_fill_i2c_req(SwI2cRequest_t  *req, bool write,
 {
        int i;
 
-       req->I2CcontrollerPort = 0;
+       req->I2CcontrollerPort = 1;
        req->I2CSpeed = 2;
        req->SlaveAddress = address;
        req->NumCmds = numbytes;
index 8cb4fce..5c1482d 100644 (file)
@@ -252,7 +252,8 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu,
                *value = metrics->UvdActivity;
                break;
        case METRICS_AVERAGE_SOCKETPOWER:
-               *value = metrics->CurrentSocketPower;
+               *value = (metrics->CurrentSocketPower << 8) /
+               1000 ;
                break;
        case METRICS_TEMPERATURE_EDGE:
                *value = metrics->GfxTemperature / 100 *
index dc75db8..9a96970 100644 (file)
@@ -188,6 +188,7 @@ static int renoir_get_dpm_clk_limited(struct smu_context *smu, enum smu_clk_type
                        return -EINVAL;
                *freq = clk_table->SocClocks[dpm_level].Freq;
                break;
+       case SMU_UCLK:
        case SMU_MCLK:
                if (dpm_level >= NUM_FCLK_DPM_LEVELS)
                        return -EINVAL;
@@ -1120,7 +1121,7 @@ static ssize_t renoir_get_gpu_metrics(struct smu_context *smu,
 static int renoir_gfx_state_change_set(struct smu_context *smu, uint32_t state)
 {
 
-       return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GpuChangeState, state, NULL);
+       return 0;
 }
 
 static const struct pptable_funcs renoir_ppt_funcs = {
index 522d550..06abf2a 100644 (file)
@@ -225,6 +225,7 @@ int smu_v12_0_set_soft_freq_limited_range(struct smu_context *smu, enum smu_clk_
        break;
        case SMU_FCLK:
        case SMU_MCLK:
+       case SMU_UCLK:
                ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetHardMinFclkByFreq, min, NULL);
                if (ret)
                        return ret;
index ba15070..4a8cbec 100644 (file)
@@ -3021,7 +3021,7 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set,
 
        ret = handle_conflicting_encoders(state, true);
        if (ret)
-               return ret;
+               goto fail;
 
        ret = drm_atomic_commit(state);
 
index 02ca22e..0b232a7 100644 (file)
@@ -387,9 +387,16 @@ static int drm_gem_vram_kmap_locked(struct drm_gem_vram_object *gbo,
        if (gbo->vmap_use_count > 0)
                goto out;
 
-       ret = ttm_bo_vmap(&gbo->bo, &gbo->map);
-       if (ret)
-               return ret;
+       /*
+        * VRAM helpers unmap the BO only on demand. So the previous
+        * page mapping might still be around. Only vmap if the there's
+        * no mapping present.
+        */
+       if (dma_buf_map_is_null(&gbo->map)) {
+               ret = ttm_bo_vmap(&gbo->bo, &gbo->map);
+               if (ret)
+                       return ret;
+       }
 
 out:
        ++gbo->vmap_use_count;
@@ -577,6 +584,7 @@ static void drm_gem_vram_bo_driver_move_notify(struct drm_gem_vram_object *gbo,
                return;
 
        ttm_bo_vunmap(bo, &gbo->map);
+       dma_buf_map_clear(&gbo->map); /* explicitly clear mapping for next vmap call */
 }
 
 static int drm_gem_vram_bo_driver_move(struct drm_gem_vram_object *gbo,
index e623194..a0cb746 100644 (file)
@@ -1163,7 +1163,14 @@ retry:
        if (ret)
                goto out;
 
-       if (old_fb->format != fb->format) {
+       /*
+        * Only check the FOURCC format code, excluding modifiers. This is
+        * enough for all legacy drivers. Atomic drivers have their own
+        * checks in their ->atomic_check implementation, which will
+        * return -EINVAL if any hw or driver constraint is violated due
+        * to modifier changes.
+        */
+       if (old_fb->format->format != fb->format->format) {
                DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
                ret = -EINVAL;
                goto out;
index 6e74e67..3491460 100644 (file)
@@ -388,19 +388,18 @@ int drm_syncobj_find_fence(struct drm_file *file_private,
                return -ENOENT;
 
        *fence = drm_syncobj_fence_get(syncobj);
-       drm_syncobj_put(syncobj);
 
        if (*fence) {
                ret = dma_fence_chain_find_seqno(fence, point);
                if (!ret)
-                       return 0;
+                       goto out;
                dma_fence_put(*fence);
        } else {
                ret = -EINVAL;
        }
 
        if (!(flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT))
-               return ret;
+               goto out;
 
        memset(&wait, 0, sizeof(wait));
        wait.task = current;
@@ -432,6 +431,9 @@ int drm_syncobj_find_fence(struct drm_file *file_private,
        if (wait.node.next)
                drm_syncobj_remove_wait(syncobj, &wait);
 
+out:
+       drm_syncobj_put(syncobj);
+
        return ret;
 }
 EXPORT_SYMBOL(drm_syncobj_find_fence);
index e5574e5..6d9e81e 100644 (file)
@@ -38,6 +38,7 @@ i915-y += i915_drv.o \
          i915_config.o \
          i915_irq.o \
          i915_getparam.o \
+         i915_mitigations.o \
          i915_params.o \
          i915_pci.o \
          i915_scatterlist.o \
index a9439b4..b3533a3 100644 (file)
@@ -1616,10 +1616,6 @@ static void gen11_dsi_get_power_domains(struct intel_encoder *encoder,
 
        get_dsi_io_power_domains(i915,
                                 enc_to_intel_dsi(encoder));
-
-       if (crtc_state->dsc.compression_enable)
-               intel_display_power_get(i915,
-                                       intel_dsc_power_domain(crtc_state));
 }
 
 static bool gen11_dsi_get_hw_state(struct intel_encoder *encoder,
index 92940a0..d5ace48 100644 (file)
@@ -3725,7 +3725,7 @@ static void hsw_ddi_pre_enable_dp(struct intel_atomic_state *state,
        intel_ddi_init_dp_buf_reg(encoder, crtc_state);
        if (!is_mst)
                intel_dp_set_power(intel_dp, DP_SET_POWER_D0);
-       intel_dp_configure_protocol_converter(intel_dp);
+       intel_dp_configure_protocol_converter(intel_dp, crtc_state);
        intel_dp_sink_set_decompression_state(intel_dp, crtc_state,
                                              true);
        intel_dp_sink_set_fec_ready(intel_dp, crtc_state);
index ce82d65..34d78c6 100644 (file)
@@ -1436,6 +1436,9 @@ struct intel_dp {
                bool ycbcr_444_to_420;
        } dfp;
 
+       /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
+       struct pm_qos_request pm_qos;
+
        /* Display stream compression testing */
        bool force_dsc_en;
 
index 2165398..09123e8 100644 (file)
@@ -1489,7 +1489,7 @@ intel_dp_aux_xfer(struct intel_dp *intel_dp,
         * lowest possible wakeup latency and so prevent the cpu from going into
         * deep sleep states.
         */
-       cpu_latency_qos_update_request(&i915->pm_qos, 0);
+       cpu_latency_qos_update_request(&intel_dp->pm_qos, 0);
 
        intel_dp_check_edp(intel_dp);
 
@@ -1622,7 +1622,7 @@ done:
 
        ret = recv_bytes;
 out:
-       cpu_latency_qos_update_request(&i915->pm_qos, PM_QOS_DEFAULT_VALUE);
+       cpu_latency_qos_update_request(&intel_dp->pm_qos, PM_QOS_DEFAULT_VALUE);
 
        if (vdd)
                edp_panel_vdd_off(intel_dp, false);
@@ -1898,6 +1898,9 @@ static i915_reg_t tgl_aux_data_reg(struct intel_dp *intel_dp, int index)
 static void
 intel_dp_aux_fini(struct intel_dp *intel_dp)
 {
+       if (cpu_latency_qos_request_active(&intel_dp->pm_qos))
+               cpu_latency_qos_remove_request(&intel_dp->pm_qos);
+
        kfree(intel_dp->aux.name);
 }
 
@@ -1950,6 +1953,7 @@ intel_dp_aux_init(struct intel_dp *intel_dp)
                                               encoder->base.name);
 
        intel_dp->aux.transfer = intel_dp_aux_transfer;
+       cpu_latency_qos_add_request(&intel_dp->pm_qos, PM_QOS_DEFAULT_VALUE);
 }
 
 bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
@@ -4010,7 +4014,8 @@ static void intel_dp_enable_port(struct intel_dp *intel_dp,
        intel_de_posting_read(dev_priv, intel_dp->output_reg);
 }
 
-void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp)
+void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
+                                          const struct intel_crtc_state *crtc_state)
 {
        struct drm_i915_private *i915 = dp_to_i915(intel_dp);
        u8 tmp;
@@ -4029,8 +4034,8 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp)
                drm_dbg_kms(&i915->drm, "Failed to set protocol converter HDMI mode to %s\n",
                            enableddisabled(intel_dp->has_hdmi_sink));
 
-       tmp = intel_dp->dfp.ycbcr_444_to_420 ?
-               DP_CONVERSION_TO_YCBCR420_ENABLE : 0;
+       tmp = crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444 &&
+               intel_dp->dfp.ycbcr_444_to_420 ? DP_CONVERSION_TO_YCBCR420_ENABLE : 0;
 
        if (drm_dp_dpcd_writeb(&intel_dp->aux,
                               DP_PROTOCOL_CONVERTER_CONTROL_1, tmp) != 1)
@@ -4084,7 +4089,7 @@ static void intel_enable_dp(struct intel_atomic_state *state,
        }
 
        intel_dp_set_power(intel_dp, DP_SET_POWER_D0);
-       intel_dp_configure_protocol_converter(intel_dp);
+       intel_dp_configure_protocol_converter(intel_dp, pipe_config);
        intel_dp_start_link_train(intel_dp, pipe_config);
        intel_dp_stop_link_train(intel_dp, pipe_config);
 
index b871a09..05f7ddf 100644 (file)
@@ -51,7 +51,8 @@ int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
 int intel_dp_retrain_link(struct intel_encoder *encoder,
                          struct drm_modeset_acquire_ctx *ctx);
 void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode);
-void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp);
+void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp,
+                                          const struct intel_crtc_state *crtc_state);
 void intel_dp_sink_set_decompression_state(struct intel_dp *intel_dp,
                                           const struct intel_crtc_state *crtc_state,
                                           bool enable);
index b2a4bbc..b9d8825 100644 (file)
@@ -2210,6 +2210,7 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state,
        if (content_protection_type_changed) {
                mutex_lock(&hdcp->mutex);
                hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+               drm_connector_get(&connector->base);
                schedule_work(&hdcp->prop_work);
                mutex_unlock(&hdcp->mutex);
        }
@@ -2221,6 +2222,14 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state,
                desired_and_not_enabled =
                        hdcp->value != DRM_MODE_CONTENT_PROTECTION_ENABLED;
                mutex_unlock(&hdcp->mutex);
+               /*
+                * If HDCP already ENABLED and CP property is DESIRED, schedule
+                * prop_work to update correct CP property to user space.
+                */
+               if (!desired_and_not_enabled && !content_protection_type_changed) {
+                       drm_connector_get(&connector->base);
+                       schedule_work(&hdcp->prop_work);
+               }
        }
 
        if (desired_and_not_enabled || content_protection_type_changed)
index 9f23bac..d64fce1 100644 (file)
@@ -1650,16 +1650,13 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus
                val = pch_get_backlight(connector);
        else
                val = lpt_get_backlight(connector);
-       val = intel_panel_compute_brightness(connector, val);
-       panel->backlight.level = clamp(val, panel->backlight.min,
-                                      panel->backlight.max);
 
        if (cpu_mode) {
                drm_dbg_kms(&dev_priv->drm,
                            "CPU backlight register was enabled, switching to PCH override\n");
 
                /* Write converted CPU PWM value to PCH override register */
-               lpt_set_backlight(connector->base.state, panel->backlight.level);
+               lpt_set_backlight(connector->base.state, val);
                intel_de_write(dev_priv, BLC_PWM_PCH_CTL1,
                               pch_ctl1 | BLM_PCH_OVERRIDE_ENABLE);
 
@@ -1667,6 +1664,10 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus
                               cpu_ctl2 & ~BLM_PWM_ENABLE);
        }
 
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
+
        return 0;
 }
 
index d52f9c1..f94025e 100644 (file)
@@ -812,10 +812,20 @@ static void intel_dsi_pre_enable(struct intel_atomic_state *state,
                intel_dsi_prepare(encoder, pipe_config);
 
        intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_POWER_ON);
-       intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay);
 
-       /* Deassert reset */
-       intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
+       /*
+        * Give the panel time to power-on and then deassert its reset.
+        * Depending on the VBT MIPI sequences version the deassert-seq
+        * may contain the necessary delay, intel_dsi_msleep() will skip
+        * the delay in that case. If there is no deassert-seq, then an
+        * unconditional msleep is used to give the panel time to power-on.
+        */
+       if (dev_priv->vbt.dsi.sequence[MIPI_SEQ_DEASSERT_RESET]) {
+               intel_dsi_msleep(intel_dsi, intel_dsi->panel_on_delay);
+               intel_dsi_vbt_exec_sequence(intel_dsi, MIPI_SEQ_DEASSERT_RESET);
+       } else {
+               msleep(intel_dsi->panel_on_delay);
+       }
 
        if (IS_GEMINILAKE(dev_priv)) {
                glk_cold_boot = glk_dsi_enable_io(encoder);
index bcc80f4..bd3046e 100644 (file)
@@ -1046,7 +1046,7 @@ static void reloc_gpu_flush(struct i915_execbuffer *eb, struct reloc_cache *cach
        GEM_BUG_ON(cache->rq_size >= obj->base.size / sizeof(u32));
        cache->rq_cmd[cache->rq_size] = MI_BATCH_BUFFER_END;
 
-       __i915_gem_object_flush_map(obj, 0, sizeof(u32) * (cache->rq_size + 1));
+       i915_gem_object_flush_map(obj);
        i915_gem_object_unpin_map(obj);
 
        intel_gt_chipset_flush(cache->rq->engine->gt);
@@ -1296,6 +1296,8 @@ static int __reloc_gpu_alloc(struct i915_execbuffer *eb,
                goto err_pool;
        }
 
+       memset32(cmd, 0, pool->obj->base.size / sizeof(u32));
+
        batch = i915_vma_instance(pool->obj, vma->vm, NULL);
        if (IS_ERR(batch)) {
                err = PTR_ERR(batch);
index d93d85c..9446537 100644 (file)
@@ -7,8 +7,6 @@
 #include "i915_drv.h"
 #include "intel_gpu_commands.h"
 
-#define MAX_URB_ENTRIES 64
-#define STATE_SIZE (4 * 1024)
 #define GT3_INLINE_DATA_DELAYS 0x1E00
 #define batch_advance(Y, CS) GEM_BUG_ON((Y)->end != (CS))
 
@@ -34,38 +32,59 @@ struct batch_chunk {
 };
 
 struct batch_vals {
-       u32 max_primitives;
-       u32 max_urb_entries;
-       u32 cmd_size;
-       u32 state_size;
+       u32 max_threads;
        u32 state_start;
-       u32 batch_size;
+       u32 surface_start;
        u32 surface_height;
        u32 surface_width;
-       u32 scratch_size;
-       u32 max_size;
+       u32 size;
 };
 
+static inline int num_primitives(const struct batch_vals *bv)
+{
+       /*
+        * We need to saturate the GPU with work in order to dispatch
+        * a shader on every HW thread, and clear the thread-local registers.
+        * In short, we have to dispatch work faster than the shaders can
+        * run in order to fill the EU and occupy each HW thread.
+        */
+       return bv->max_threads;
+}
+
 static void
 batch_get_defaults(struct drm_i915_private *i915, struct batch_vals *bv)
 {
        if (IS_HASWELL(i915)) {
-               bv->max_primitives = 280;
-               bv->max_urb_entries = MAX_URB_ENTRIES;
+               switch (INTEL_INFO(i915)->gt) {
+               default:
+               case 1:
+                       bv->max_threads = 70;
+                       break;
+               case 2:
+                       bv->max_threads = 140;
+                       break;
+               case 3:
+                       bv->max_threads = 280;
+                       break;
+               }
                bv->surface_height = 16 * 16;
                bv->surface_width = 32 * 2 * 16;
        } else {
-               bv->max_primitives = 128;
-               bv->max_urb_entries = MAX_URB_ENTRIES / 2;
+               switch (INTEL_INFO(i915)->gt) {
+               default:
+               case 1: /* including vlv */
+                       bv->max_threads = 36;
+                       break;
+               case 2:
+                       bv->max_threads = 128;
+                       break;
+               }
                bv->surface_height = 16 * 8;
                bv->surface_width = 32 * 16;
        }
-       bv->cmd_size = bv->max_primitives * 4096;
-       bv->state_size = STATE_SIZE;
-       bv->state_start = bv->cmd_size;
-       bv->batch_size = bv->cmd_size + bv->state_size;
-       bv->scratch_size = bv->surface_height * bv->surface_width;
-       bv->max_size = bv->batch_size + bv->scratch_size;
+       bv->state_start = round_up(SZ_1K + num_primitives(bv) * 64, SZ_4K);
+       bv->surface_start = bv->state_start + SZ_4K;
+       bv->size = bv->surface_start + bv->surface_height * bv->surface_width;
 }
 
 static void batch_init(struct batch_chunk *bc,
@@ -155,7 +174,8 @@ static u32
 gen7_fill_binding_table(struct batch_chunk *state,
                        const struct batch_vals *bv)
 {
-       u32 surface_start = gen7_fill_surface_state(state, bv->batch_size, bv);
+       u32 surface_start =
+               gen7_fill_surface_state(state, bv->surface_start, bv);
        u32 *cs = batch_alloc_items(state, 32, 8);
        u32 offset = batch_offset(state, cs);
 
@@ -214,9 +234,9 @@ static void
 gen7_emit_state_base_address(struct batch_chunk *batch,
                             u32 surface_state_base)
 {
-       u32 *cs = batch_alloc_items(batch, 0, 12);
+       u32 *cs = batch_alloc_items(batch, 0, 10);
 
-       *cs++ = STATE_BASE_ADDRESS | (12 - 2);
+       *cs++ = STATE_BASE_ADDRESS | (10 - 2);
        /* general */
        *cs++ = batch_addr(batch) | BASE_ADDRESS_MODIFY;
        /* surface */
@@ -233,8 +253,6 @@ gen7_emit_state_base_address(struct batch_chunk *batch,
        *cs++ = BASE_ADDRESS_MODIFY;
        *cs++ = 0;
        *cs++ = BASE_ADDRESS_MODIFY;
-       *cs++ = 0;
-       *cs++ = 0;
        batch_advance(batch, cs);
 }
 
@@ -244,8 +262,7 @@ gen7_emit_vfe_state(struct batch_chunk *batch,
                    u32 urb_size, u32 curbe_size,
                    u32 mode)
 {
-       u32 urb_entries = bv->max_urb_entries;
-       u32 threads = bv->max_primitives - 1;
+       u32 threads = bv->max_threads - 1;
        u32 *cs = batch_alloc_items(batch, 32, 8);
 
        *cs++ = MEDIA_VFE_STATE | (8 - 2);
@@ -254,7 +271,7 @@ gen7_emit_vfe_state(struct batch_chunk *batch,
        *cs++ = 0;
 
        /* number of threads & urb entries for GPGPU vs Media Mode */
-       *cs++ = threads << 16 | urb_entries << 8 | mode << 2;
+       *cs++ = threads << 16 | 1 << 8 | mode << 2;
 
        *cs++ = 0;
 
@@ -293,17 +310,12 @@ gen7_emit_media_object(struct batch_chunk *batch,
 {
        unsigned int x_offset = (media_object_index % 16) * 64;
        unsigned int y_offset = (media_object_index / 16) * 16;
-       unsigned int inline_data_size;
-       unsigned int media_batch_size;
-       unsigned int i;
+       unsigned int pkt = 6 + 3;
        u32 *cs;
 
-       inline_data_size = 112 * 8;
-       media_batch_size = inline_data_size + 6;
-
-       cs = batch_alloc_items(batch, 8, media_batch_size);
+       cs = batch_alloc_items(batch, 8, pkt);
 
-       *cs++ = MEDIA_OBJECT | (media_batch_size - 2);
+       *cs++ = MEDIA_OBJECT | (pkt - 2);
 
        /* interface descriptor offset */
        *cs++ = 0;
@@ -317,25 +329,44 @@ gen7_emit_media_object(struct batch_chunk *batch,
        *cs++ = 0;
 
        /* inline */
-       *cs++ = (y_offset << 16) | (x_offset);
+       *cs++ = y_offset << 16 | x_offset;
        *cs++ = 0;
        *cs++ = GT3_INLINE_DATA_DELAYS;
-       for (i = 3; i < inline_data_size; i++)
-               *cs++ = 0;
 
        batch_advance(batch, cs);
 }
 
 static void gen7_emit_pipeline_flush(struct batch_chunk *batch)
 {
-       u32 *cs = batch_alloc_items(batch, 0, 5);
+       u32 *cs = batch_alloc_items(batch, 0, 4);
 
-       *cs++ = GFX_OP_PIPE_CONTROL(5);
-       *cs++ = PIPE_CONTROL_STATE_CACHE_INVALIDATE |
-               PIPE_CONTROL_GLOBAL_GTT_IVB;
+       *cs++ = GFX_OP_PIPE_CONTROL(4);
+       *cs++ = PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH |
+               PIPE_CONTROL_DEPTH_CACHE_FLUSH |
+               PIPE_CONTROL_DC_FLUSH_ENABLE |
+               PIPE_CONTROL_CS_STALL;
        *cs++ = 0;
        *cs++ = 0;
+
+       batch_advance(batch, cs);
+}
+
+static void gen7_emit_pipeline_invalidate(struct batch_chunk *batch)
+{
+       u32 *cs = batch_alloc_items(batch, 0, 8);
+
+       /* ivb: Stall before STATE_CACHE_INVALIDATE */
+       *cs++ = GFX_OP_PIPE_CONTROL(4);
+       *cs++ = PIPE_CONTROL_STALL_AT_SCOREBOARD |
+               PIPE_CONTROL_CS_STALL;
+       *cs++ = 0;
+       *cs++ = 0;
+
+       *cs++ = GFX_OP_PIPE_CONTROL(4);
+       *cs++ = PIPE_CONTROL_STATE_CACHE_INVALIDATE;
        *cs++ = 0;
+       *cs++ = 0;
+
        batch_advance(batch, cs);
 }
 
@@ -344,34 +375,34 @@ static void emit_batch(struct i915_vma * const vma,
                       const struct batch_vals *bv)
 {
        struct drm_i915_private *i915 = vma->vm->i915;
-       unsigned int desc_count = 64;
-       const u32 urb_size = 112;
+       const unsigned int desc_count = 1;
+       const unsigned int urb_size = 1;
        struct batch_chunk cmds, state;
-       u32 interface_descriptor;
+       u32 descriptors;
        unsigned int i;
 
-       batch_init(&cmds, vma, start, 0, bv->cmd_size);
-       batch_init(&state, vma, start, bv->state_start, bv->state_size);
+       batch_init(&cmds, vma, start, 0, bv->state_start);
+       batch_init(&state, vma, start, bv->state_start, SZ_4K);
 
-       interface_descriptor =
-               gen7_fill_interface_descriptor(&state, bv,
-                                              IS_HASWELL(i915) ?
-                                              &cb_kernel_hsw :
-                                              &cb_kernel_ivb,
-                                              desc_count);
-       gen7_emit_pipeline_flush(&cmds);
+       descriptors = gen7_fill_interface_descriptor(&state, bv,
+                                                    IS_HASWELL(i915) ?
+                                                    &cb_kernel_hsw :
+                                                    &cb_kernel_ivb,
+                                                    desc_count);
+
+       gen7_emit_pipeline_invalidate(&cmds);
        batch_add(&cmds, PIPELINE_SELECT | PIPELINE_SELECT_MEDIA);
        batch_add(&cmds, MI_NOOP);
-       gen7_emit_state_base_address(&cmds, interface_descriptor);
+       gen7_emit_pipeline_invalidate(&cmds);
+
        gen7_emit_pipeline_flush(&cmds);
+       gen7_emit_state_base_address(&cmds, descriptors);
+       gen7_emit_pipeline_invalidate(&cmds);
 
        gen7_emit_vfe_state(&cmds, bv, urb_size - 1, 0, 0);
+       gen7_emit_interface_descriptor_load(&cmds, descriptors, desc_count);
 
-       gen7_emit_interface_descriptor_load(&cmds,
-                                           interface_descriptor,
-                                           desc_count);
-
-       for (i = 0; i < bv->max_primitives; i++)
+       for (i = 0; i < num_primitives(bv); i++)
                gen7_emit_media_object(&cmds, i);
 
        batch_add(&cmds, MI_BATCH_BUFFER_END);
@@ -385,15 +416,15 @@ int gen7_setup_clear_gpr_bb(struct intel_engine_cs * const engine,
 
        batch_get_defaults(engine->i915, &bv);
        if (!vma)
-               return bv.max_size;
+               return bv.size;
 
-       GEM_BUG_ON(vma->obj->base.size < bv.max_size);
+       GEM_BUG_ON(vma->obj->base.size < bv.size);
 
        batch = i915_gem_object_pin_map(vma->obj, I915_MAP_WC);
        if (IS_ERR(batch))
                return PTR_ERR(batch);
 
-       emit_batch(vma, memset(batch, 0, bv.max_size), &bv);
+       emit_batch(vma, memset(batch, 0, bv.size), &bv);
 
        i915_gem_object_flush_map(vma->obj);
        __i915_gem_object_release_map(vma->obj);
index a24cc1f..0625cbb 100644 (file)
@@ -134,11 +134,6 @@ static bool remove_signaling_context(struct intel_breadcrumbs *b,
        return true;
 }
 
-static inline bool __request_completed(const struct i915_request *rq)
-{
-       return i915_seqno_passed(__hwsp_seqno(rq), rq->fence.seqno);
-}
-
 __maybe_unused static bool
 check_signal_order(struct intel_context *ce, struct i915_request *rq)
 {
@@ -257,7 +252,7 @@ static void signal_irq_work(struct irq_work *work)
                list_for_each_entry_rcu(rq, &ce->signals, signal_link) {
                        bool release;
 
-                       if (!__request_completed(rq))
+                       if (!__i915_request_is_complete(rq))
                                break;
 
                        if (!test_and_clear_bit(I915_FENCE_FLAG_SIGNAL,
@@ -379,7 +374,7 @@ static void insert_breadcrumb(struct i915_request *rq)
         * straight onto a signaled list, and queue the irq worker for
         * its signal completion.
         */
-       if (__request_completed(rq)) {
+       if (__i915_request_is_complete(rq)) {
                if (__signal_request(rq) &&
                    llist_add(&rq->signal_node, &b->signaled_requests))
                        irq_work_queue(&b->irq_work);
index 7614a3d..26c7d0a 100644 (file)
@@ -3988,6 +3988,9 @@ err:
 static void lrc_destroy_wa_ctx(struct intel_engine_cs *engine)
 {
        i915_vma_unpin_and_release(&engine->wa_ctx.vma, 0);
+
+       /* Called on error unwind, clear all flags to prevent further use */
+       memset(&engine->wa_ctx, 0, sizeof(engine->wa_ctx));
 }
 
 typedef u32 *(*wa_bb_func_t)(struct intel_engine_cs *engine, u32 *batch);
index a41b43f..ecf3a61 100644 (file)
@@ -32,6 +32,7 @@
 #include "gen6_ppgtt.h"
 #include "gen7_renderclear.h"
 #include "i915_drv.h"
+#include "i915_mitigations.h"
 #include "intel_breadcrumbs.h"
 #include "intel_context.h"
 #include "intel_gt.h"
@@ -886,7 +887,8 @@ static int switch_context(struct i915_request *rq)
        GEM_BUG_ON(HAS_EXECLISTS(engine->i915));
 
        if (engine->wa_ctx.vma && ce != engine->kernel_context) {
-               if (engine->wa_ctx.vma->private != ce) {
+               if (engine->wa_ctx.vma->private != ce &&
+                   i915_mitigate_clear_residuals()) {
                        ret = clear_residuals(rq);
                        if (ret)
                                return ret;
@@ -1290,7 +1292,7 @@ int intel_ring_submission_setup(struct intel_engine_cs *engine)
 
        GEM_BUG_ON(timeline->hwsp_ggtt != engine->status_page.vma);
 
-       if (IS_HASWELL(engine->i915) && engine->class == RENDER_CLASS) {
+       if (IS_GEN(engine->i915, 7) && engine->class == RENDER_CLASS) {
                err = gen7_ctx_switch_bb_init(engine);
                if (err)
                        goto err_ring_unpin;
index 7ea94d2..8015964 100644 (file)
@@ -126,6 +126,10 @@ static void __rcu_cacheline_free(struct rcu_head *rcu)
        struct intel_timeline_cacheline *cl =
                container_of(rcu, typeof(*cl), rcu);
 
+       /* Must wait until after all *rq->hwsp are complete before removing */
+       i915_gem_object_unpin_map(cl->hwsp->vma->obj);
+       __idle_hwsp_free(cl->hwsp, ptr_unmask_bits(cl->vaddr, CACHELINE_BITS));
+
        i915_active_fini(&cl->active);
        kfree(cl);
 }
@@ -133,11 +137,6 @@ static void __rcu_cacheline_free(struct rcu_head *rcu)
 static void __idle_cacheline_free(struct intel_timeline_cacheline *cl)
 {
        GEM_BUG_ON(!i915_active_is_idle(&cl->active));
-
-       i915_gem_object_unpin_map(cl->hwsp->vma->obj);
-       i915_vma_put(cl->hwsp->vma);
-       __idle_hwsp_free(cl->hwsp, ptr_unmask_bits(cl->vaddr, CACHELINE_BITS));
-
        call_rcu(&cl->rcu, __rcu_cacheline_free);
 }
 
@@ -179,7 +178,6 @@ cacheline_alloc(struct intel_timeline_hwsp *hwsp, unsigned int cacheline)
                return ERR_CAST(vaddr);
        }
 
-       i915_vma_get(hwsp->vma);
        cl->hwsp = hwsp;
        cl->vaddr = page_pack_bits(vaddr, cacheline);
 
index 180c23e..602f1a0 100644 (file)
@@ -53,6 +53,7 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw,
        fw_def(ELKHARTLAKE, 0, guc_def(ehl, 49, 0, 1), huc_def(ehl,  9, 0, 0)) \
        fw_def(ICELAKE,     0, guc_def(icl, 49, 0, 1), huc_def(icl,  9, 0, 0)) \
        fw_def(COMETLAKE,   5, guc_def(cml, 49, 0, 1), huc_def(cml,  4, 0, 0)) \
+       fw_def(COMETLAKE,   0, guc_def(kbl, 49, 0, 1), huc_def(kbl,  4, 0, 0)) \
        fw_def(COFFEELAKE,  0, guc_def(kbl, 49, 0, 1), huc_def(kbl,  4, 0, 0)) \
        fw_def(GEMINILAKE,  0, guc_def(glk, 49, 0, 1), huc_def(glk,  4, 0, 0)) \
        fw_def(KABYLAKE,    0, guc_def(kbl, 49, 0, 1), huc_def(kbl,  4, 0, 0)) \
index a15f875..62a5b0d 100644 (file)
@@ -217,6 +217,15 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                                  DDI_BUF_CTL_ENABLE);
                        vgpu_vreg_t(vgpu, DDI_BUF_CTL(port)) |= DDI_BUF_IS_IDLE;
                }
+               vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &=
+                       ~(PORTA_HOTPLUG_ENABLE | PORTA_HOTPLUG_STATUS_MASK);
+               vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &=
+                       ~(PORTB_HOTPLUG_ENABLE | PORTB_HOTPLUG_STATUS_MASK);
+               vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &=
+                       ~(PORTC_HOTPLUG_ENABLE | PORTC_HOTPLUG_STATUS_MASK);
+               /* No hpd_invert set in vgpu vbt, need to clear invert mask */
+               vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &= ~BXT_DDI_HPD_INVERT_MASK;
+               vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) &= ~BXT_DE_PORT_HOTPLUG_MASK;
 
                vgpu_vreg_t(vgpu, BXT_P_CR_GT_DISP_PWRON) &= ~(BIT(0) | BIT(1));
                vgpu_vreg_t(vgpu, BXT_PORT_CL1CM_DW0(DPIO_PHY0)) &=
@@ -273,6 +282,8 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                        vgpu_vreg_t(vgpu, TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)) |=
                                (TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
                                 TRANS_DDI_FUNC_ENABLE);
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
+                               PORTA_HOTPLUG_ENABLE;
                        vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
                                GEN8_DE_PORT_HOTPLUG(HPD_PORT_A);
                }
@@ -301,6 +312,8 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                                (TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
                                 (PORT_B << TRANS_DDI_PORT_SHIFT) |
                                 TRANS_DDI_FUNC_ENABLE);
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
+                               PORTB_HOTPLUG_ENABLE;
                        vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
                                GEN8_DE_PORT_HOTPLUG(HPD_PORT_B);
                }
@@ -329,6 +342,8 @@ static void emulate_monitor_status_change(struct intel_vgpu *vgpu)
                                (TRANS_DDI_BPC_8 | TRANS_DDI_MODE_SELECT_DP_SST |
                                 (PORT_B << TRANS_DDI_PORT_SHIFT) |
                                 TRANS_DDI_FUNC_ENABLE);
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
+                               PORTC_HOTPLUG_ENABLE;
                        vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
                                GEN8_DE_PORT_HOTPLUG(HPD_PORT_C);
                }
@@ -661,44 +676,62 @@ void intel_vgpu_emulate_hotplug(struct intel_vgpu *vgpu, bool connected)
                                PORTD_HOTPLUG_STATUS_MASK;
                intel_vgpu_trigger_virtual_event(vgpu, DP_D_HOTPLUG);
        } else if (IS_BROXTON(i915)) {
-               if (connected) {
-                       if (intel_vgpu_has_monitor_on_port(vgpu, PORT_A)) {
+               if (intel_vgpu_has_monitor_on_port(vgpu, PORT_A)) {
+                       if (connected) {
                                vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
                                        GEN8_DE_PORT_HOTPLUG(HPD_PORT_A);
+                       } else {
+                               vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) &=
+                                       ~GEN8_DE_PORT_HOTPLUG(HPD_PORT_A);
                        }
-                       if (intel_vgpu_has_monitor_on_port(vgpu, PORT_B)) {
-                               vgpu_vreg_t(vgpu, SFUSE_STRAP) |=
-                                       SFUSE_STRAP_DDIB_DETECTED;
+                       vgpu_vreg_t(vgpu, GEN8_DE_PORT_IIR) |=
+                               GEN8_DE_PORT_HOTPLUG(HPD_PORT_A);
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &=
+                               ~PORTA_HOTPLUG_STATUS_MASK;
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
+                               PORTA_HOTPLUG_LONG_DETECT;
+                       intel_vgpu_trigger_virtual_event(vgpu, DP_A_HOTPLUG);
+               }
+               if (intel_vgpu_has_monitor_on_port(vgpu, PORT_B)) {
+                       if (connected) {
                                vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
                                        GEN8_DE_PORT_HOTPLUG(HPD_PORT_B);
-                       }
-                       if (intel_vgpu_has_monitor_on_port(vgpu, PORT_C)) {
                                vgpu_vreg_t(vgpu, SFUSE_STRAP) |=
-                                       SFUSE_STRAP_DDIC_DETECTED;
-                               vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
-                                       GEN8_DE_PORT_HOTPLUG(HPD_PORT_C);
-                       }
-               } else {
-                       if (intel_vgpu_has_monitor_on_port(vgpu, PORT_A)) {
+                                       SFUSE_STRAP_DDIB_DETECTED;
+                       } else {
                                vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) &=
-                                       ~GEN8_DE_PORT_HOTPLUG(HPD_PORT_A);
-                       }
-                       if (intel_vgpu_has_monitor_on_port(vgpu, PORT_B)) {
+                                       ~GEN8_DE_PORT_HOTPLUG(HPD_PORT_B);
                                vgpu_vreg_t(vgpu, SFUSE_STRAP) &=
                                        ~SFUSE_STRAP_DDIB_DETECTED;
-                               vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) &=
-                                       ~GEN8_DE_PORT_HOTPLUG(HPD_PORT_B);
                        }
-                       if (intel_vgpu_has_monitor_on_port(vgpu, PORT_C)) {
-                               vgpu_vreg_t(vgpu, SFUSE_STRAP) &=
-                                       ~SFUSE_STRAP_DDIC_DETECTED;
+                       vgpu_vreg_t(vgpu, GEN8_DE_PORT_IIR) |=
+                               GEN8_DE_PORT_HOTPLUG(HPD_PORT_B);
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &=
+                               ~PORTB_HOTPLUG_STATUS_MASK;
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
+                               PORTB_HOTPLUG_LONG_DETECT;
+                       intel_vgpu_trigger_virtual_event(vgpu, DP_B_HOTPLUG);
+               }
+               if (intel_vgpu_has_monitor_on_port(vgpu, PORT_C)) {
+                       if (connected) {
+                               vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) |=
+                                       GEN8_DE_PORT_HOTPLUG(HPD_PORT_C);
+                               vgpu_vreg_t(vgpu, SFUSE_STRAP) |=
+                                       SFUSE_STRAP_DDIC_DETECTED;
+                       } else {
                                vgpu_vreg_t(vgpu, GEN8_DE_PORT_ISR) &=
                                        ~GEN8_DE_PORT_HOTPLUG(HPD_PORT_C);
+                               vgpu_vreg_t(vgpu, SFUSE_STRAP) &=
+                                       ~SFUSE_STRAP_DDIC_DETECTED;
                        }
+                       vgpu_vreg_t(vgpu, GEN8_DE_PORT_IIR) |=
+                               GEN8_DE_PORT_HOTPLUG(HPD_PORT_C);
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) &=
+                               ~PORTC_HOTPLUG_STATUS_MASK;
+                       vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
+                               PORTC_HOTPLUG_LONG_DETECT;
+                       intel_vgpu_trigger_virtual_event(vgpu, DP_C_HOTPLUG);
                }
-               vgpu_vreg_t(vgpu, PCH_PORT_HOTPLUG) |=
-                       PORTB_HOTPLUG_STATUS_MASK;
-               intel_vgpu_trigger_virtual_event(vgpu, DP_B_HOTPLUG);
        }
 }
 
index e49944f..cbe5931 100644 (file)
@@ -437,10 +437,9 @@ static struct intel_vgpu *__intel_gvt_create_vgpu(struct intel_gvt *gvt,
        if (ret)
                goto out_clean_sched_policy;
 
-       if (IS_BROADWELL(dev_priv))
+       if (IS_BROADWELL(dev_priv) || IS_BROXTON(dev_priv))
                ret = intel_gvt_hypervisor_set_edid(vgpu, PORT_B);
-       /* FixMe: Re-enable APL/BXT once vfio_edid enabled */
-       else if (!IS_BROXTON(dev_priv))
+       else
                ret = intel_gvt_hypervisor_set_edid(vgpu, PORT_D);
        if (ret)
                goto out_clean_sched_policy;
index 9326595..b0899b6 100644 (file)
@@ -1166,7 +1166,7 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
                }
        }
        if (IS_ERR(src)) {
-               unsigned long x, n;
+               unsigned long x, n, remain;
                void *ptr;
 
                /*
@@ -1177,14 +1177,15 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
                 * We don't care about copying too much here as we only
                 * validate up to the end of the batch.
                 */
+               remain = length;
                if (!(dst_obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ))
-                       length = round_up(length,
+                       remain = round_up(remain,
                                          boot_cpu_data.x86_clflush_size);
 
                ptr = dst;
                x = offset_in_page(offset);
-               for (n = offset >> PAGE_SHIFT; length; n++) {
-                       int len = min(length, PAGE_SIZE - x);
+               for (n = offset >> PAGE_SHIFT; remain; n++) {
+                       int len = min(remain, PAGE_SIZE - x);
 
                        src = kmap_atomic(i915_gem_object_get_page(src_obj, n));
                        if (needs_clflush)
@@ -1193,13 +1194,15 @@ static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
                        kunmap_atomic(src);
 
                        ptr += len;
-                       length -= len;
+                       remain -= len;
                        x = 0;
                }
        }
 
        i915_gem_object_unpin_pages(src_obj);
 
+       memset32(dst + length, 0, (dst_obj->base.size - length) / sizeof(u32));
+
        /* dst_obj is returned with vmap pinned */
        return dst;
 }
@@ -1392,11 +1395,6 @@ static unsigned long *alloc_whitelist(u32 batch_length)
 
 #define LENGTH_BIAS 2
 
-static bool shadow_needs_clflush(struct drm_i915_gem_object *obj)
-{
-       return !(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_WRITE);
-}
-
 /**
  * intel_engine_cmd_parser() - parse a batch buffer for privilege violations
  * @engine: the engine on which the batch is to execute
@@ -1538,16 +1536,9 @@ int intel_engine_cmd_parser(struct intel_engine_cs *engine,
                                ret = 0; /* allow execution */
                        }
                }
-
-               if (shadow_needs_clflush(shadow->obj))
-                       drm_clflush_virt_range(batch_end, 8);
        }
 
-       if (shadow_needs_clflush(shadow->obj)) {
-               void *ptr = page_mask_bits(shadow->obj->mm.mapping);
-
-               drm_clflush_virt_range(ptr, (void *)(cmd + 1) - ptr);
-       }
+       i915_gem_object_flush_map(shadow->obj);
 
        if (!IS_ERR_OR_NULL(jump_whitelist))
                kfree(jump_whitelist);
index 320856b..99eb0d7 100644 (file)
@@ -578,8 +578,6 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv)
 
        pci_set_master(pdev);
 
-       cpu_latency_qos_add_request(&dev_priv->pm_qos, PM_QOS_DEFAULT_VALUE);
-
        intel_gt_init_workarounds(dev_priv);
 
        /* On the 945G/GM, the chipset reports the MSI capability on the
@@ -626,7 +624,6 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv)
 err_msi:
        if (pdev->msi_enabled)
                pci_disable_msi(pdev);
-       cpu_latency_qos_remove_request(&dev_priv->pm_qos);
 err_mem_regions:
        intel_memory_regions_driver_release(dev_priv);
 err_ggtt:
@@ -648,8 +645,6 @@ static void i915_driver_hw_remove(struct drm_i915_private *dev_priv)
 
        if (pdev->msi_enabled)
                pci_disable_msi(pdev);
-
-       cpu_latency_qos_remove_request(&dev_priv->pm_qos);
 }
 
 /**
@@ -1052,6 +1047,8 @@ static void intel_shutdown_encoders(struct drm_i915_private *dev_priv)
 
 void i915_driver_shutdown(struct drm_i915_private *i915)
 {
+       disable_rpm_wakeref_asserts(&i915->runtime_pm);
+
        i915_gem_suspend(i915);
 
        drm_kms_helper_poll_disable(&i915->drm);
@@ -1065,6 +1062,8 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
 
        intel_suspend_encoders(i915);
        intel_shutdown_encoders(i915);
+
+       enable_rpm_wakeref_asserts(&i915->runtime_pm);
 }
 
 static bool suspend_to_idle(struct drm_i915_private *dev_priv)
index 0a3ee4f..632c713 100644 (file)
@@ -891,9 +891,6 @@ struct drm_i915_private {
 
        bool display_irqs_enabled;
 
-       /* To control wakeup latency, e.g. for irq-driven dp aux transfers. */
-       struct pm_qos_request pm_qos;
-
        /* Sideband mailbox protection */
        struct mutex sb_lock;
        struct pm_qos_request sb_qos;
diff --git a/drivers/gpu/drm/i915/i915_mitigations.c b/drivers/gpu/drm/i915/i915_mitigations.c
new file mode 100644 (file)
index 0000000..84f1259
--- /dev/null
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "i915_drv.h"
+#include "i915_mitigations.h"
+
+static unsigned long mitigations __read_mostly = ~0UL;
+
+enum {
+       CLEAR_RESIDUALS = 0,
+};
+
+static const char * const names[] = {
+       [CLEAR_RESIDUALS] = "residuals",
+};
+
+bool i915_mitigate_clear_residuals(void)
+{
+       return READ_ONCE(mitigations) & BIT(CLEAR_RESIDUALS);
+}
+
+static int mitigations_set(const char *val, const struct kernel_param *kp)
+{
+       unsigned long new = ~0UL;
+       char *str, *sep, *tok;
+       bool first = true;
+       int err = 0;
+
+       BUILD_BUG_ON(ARRAY_SIZE(names) >= BITS_PER_TYPE(mitigations));
+
+       str = kstrdup(val, GFP_KERNEL);
+       if (!str)
+               return -ENOMEM;
+
+       for (sep = str; (tok = strsep(&sep, ","));) {
+               bool enable = true;
+               int i;
+
+               /* Be tolerant of leading/trailing whitespace */
+               tok = strim(tok);
+
+               if (first) {
+                       first = false;
+
+                       if (!strcmp(tok, "auto"))
+                               continue;
+
+                       new = 0;
+                       if (!strcmp(tok, "off"))
+                               continue;
+               }
+
+               if (*tok == '!') {
+                       enable = !enable;
+                       tok++;
+               }
+
+               if (!strncmp(tok, "no", 2)) {
+                       enable = !enable;
+                       tok += 2;
+               }
+
+               if (*tok == '\0')
+                       continue;
+
+               for (i = 0; i < ARRAY_SIZE(names); i++) {
+                       if (!strcmp(tok, names[i])) {
+                               if (enable)
+                                       new |= BIT(i);
+                               else
+                                       new &= ~BIT(i);
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(names)) {
+                       pr_err("Bad \"%s.mitigations=%s\", '%s' is unknown\n",
+                              DRIVER_NAME, val, tok);
+                       err = -EINVAL;
+                       break;
+               }
+       }
+       kfree(str);
+       if (err)
+               return err;
+
+       WRITE_ONCE(mitigations, new);
+       return 0;
+}
+
+static int mitigations_get(char *buffer, const struct kernel_param *kp)
+{
+       unsigned long local = READ_ONCE(mitigations);
+       int count, i;
+       bool enable;
+
+       if (!local)
+               return scnprintf(buffer, PAGE_SIZE, "%s\n", "off");
+
+       if (local & BIT(BITS_PER_LONG - 1)) {
+               count = scnprintf(buffer, PAGE_SIZE, "%s,", "auto");
+               enable = false;
+       } else {
+               enable = true;
+               count = 0;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(names); i++) {
+               if ((local & BIT(i)) != enable)
+                       continue;
+
+               count += scnprintf(buffer + count, PAGE_SIZE - count,
+                                  "%s%s,", enable ? "" : "!", names[i]);
+       }
+
+       buffer[count - 1] = '\n';
+       return count;
+}
+
+static const struct kernel_param_ops ops = {
+       .set = mitigations_set,
+       .get = mitigations_get,
+};
+
+module_param_cb_unsafe(mitigations, &ops, NULL, 0600);
+MODULE_PARM_DESC(mitigations,
+"Selectively enable security mitigations for all Intel® GPUs in the system.\n"
+"\n"
+"  auto -- enables all mitigations required for the platform [default]\n"
+"  off  -- disables all mitigations\n"
+"\n"
+"Individual mitigations can be enabled by passing a comma-separated string,\n"
+"e.g. mitigations=residuals to enable only clearing residuals or\n"
+"mitigations=auto,noresiduals to disable only the clear residual mitigation.\n"
+"Either '!' or 'no' may be used to switch from enabling the mitigation to\n"
+"disabling it.\n"
+"\n"
+"Active mitigations for Ivybridge, Baytrail, Haswell:\n"
+"  residuals -- clear all thread-local registers between contexts"
+);
diff --git a/drivers/gpu/drm/i915/i915_mitigations.h b/drivers/gpu/drm/i915/i915_mitigations.h
new file mode 100644 (file)
index 0000000..1359d81
--- /dev/null
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2021 Intel Corporation
+ */
+
+#ifndef __I915_MITIGATIONS_H__
+#define __I915_MITIGATIONS_H__
+
+#include <linux/types.h>
+
+bool i915_mitigate_clear_residuals(void);
+
+#endif /* __I915_MITIGATIONS_H__ */
index d76685c..9856479 100644 (file)
@@ -184,13 +184,24 @@ static u64 get_rc6(struct intel_gt *gt)
        return val;
 }
 
-static void park_rc6(struct drm_i915_private *i915)
+static void init_rc6(struct i915_pmu *pmu)
 {
-       struct i915_pmu *pmu = &i915->pmu;
+       struct drm_i915_private *i915 = container_of(pmu, typeof(*i915), pmu);
+       intel_wakeref_t wakeref;
 
-       if (pmu->enable & config_enabled_mask(I915_PMU_RC6_RESIDENCY))
+       with_intel_runtime_pm(i915->gt.uncore->rpm, wakeref) {
                pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt);
+               pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur =
+                                       pmu->sample[__I915_SAMPLE_RC6].cur;
+               pmu->sleep_last = ktime_get();
+       }
+}
 
+static void park_rc6(struct drm_i915_private *i915)
+{
+       struct i915_pmu *pmu = &i915->pmu;
+
+       pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt);
        pmu->sleep_last = ktime_get();
 }
 
@@ -201,6 +212,7 @@ static u64 get_rc6(struct intel_gt *gt)
        return __get_rc6(gt);
 }
 
+static void init_rc6(struct i915_pmu *pmu) { }
 static void park_rc6(struct drm_i915_private *i915) {}
 
 #endif
@@ -612,10 +624,8 @@ static void i915_pmu_enable(struct perf_event *event)
                container_of(event->pmu, typeof(*i915), pmu.base);
        unsigned int bit = event_enabled_bit(event);
        struct i915_pmu *pmu = &i915->pmu;
-       intel_wakeref_t wakeref;
        unsigned long flags;
 
-       wakeref = intel_runtime_pm_get(&i915->runtime_pm);
        spin_lock_irqsave(&pmu->lock, flags);
 
        /*
@@ -626,13 +636,6 @@ static void i915_pmu_enable(struct perf_event *event)
        GEM_BUG_ON(bit >= ARRAY_SIZE(pmu->enable_count));
        GEM_BUG_ON(pmu->enable_count[bit] == ~0);
 
-       if (pmu->enable_count[bit] == 0 &&
-           config_enabled_mask(I915_PMU_RC6_RESIDENCY) & BIT_ULL(bit)) {
-               pmu->sample[__I915_SAMPLE_RC6_LAST_REPORTED].cur = 0;
-               pmu->sample[__I915_SAMPLE_RC6].cur = __get_rc6(&i915->gt);
-               pmu->sleep_last = ktime_get();
-       }
-
        pmu->enable |= BIT_ULL(bit);
        pmu->enable_count[bit]++;
 
@@ -673,8 +676,6 @@ static void i915_pmu_enable(struct perf_event *event)
         * an existing non-zero value.
         */
        local64_set(&event->hw.prev_count, __i915_pmu_event_read(event));
-
-       intel_runtime_pm_put(&i915->runtime_pm, wakeref);
 }
 
 static void i915_pmu_disable(struct perf_event *event)
@@ -1130,6 +1131,7 @@ void i915_pmu_register(struct drm_i915_private *i915)
        hrtimer_init(&pmu->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        pmu->timer.function = i915_sample;
        pmu->cpuhp.cpu = -1;
+       init_rc6(pmu);
 
        if (!is_igp(i915)) {
                pmu->name = kasprintf(GFP_KERNEL,
index 620b6fa..92adfee 100644 (file)
@@ -434,7 +434,7 @@ static inline u32 hwsp_seqno(const struct i915_request *rq)
 
 static inline bool __i915_request_has_started(const struct i915_request *rq)
 {
-       return i915_seqno_passed(hwsp_seqno(rq), rq->fence.seqno - 1);
+       return i915_seqno_passed(__hwsp_seqno(rq), rq->fence.seqno - 1);
 }
 
 /**
@@ -465,11 +465,19 @@ static inline bool __i915_request_has_started(const struct i915_request *rq)
  */
 static inline bool i915_request_started(const struct i915_request *rq)
 {
+       bool result;
+
        if (i915_request_signaled(rq))
                return true;
 
-       /* Remember: started but may have since been preempted! */
-       return __i915_request_has_started(rq);
+       result = true;
+       rcu_read_lock(); /* the HWSP may be freed at runtime */
+       if (likely(!i915_request_signaled(rq)))
+               /* Remember: started but may have since been preempted! */
+               result = __i915_request_has_started(rq);
+       rcu_read_unlock();
+
+       return result;
 }
 
 /**
@@ -482,10 +490,16 @@ static inline bool i915_request_started(const struct i915_request *rq)
  */
 static inline bool i915_request_is_running(const struct i915_request *rq)
 {
+       bool result;
+
        if (!i915_request_is_active(rq))
                return false;
 
-       return __i915_request_has_started(rq);
+       rcu_read_lock();
+       result = __i915_request_has_started(rq) && i915_request_is_active(rq);
+       rcu_read_unlock();
+
+       return result;
 }
 
 /**
@@ -509,12 +523,25 @@ static inline bool i915_request_is_ready(const struct i915_request *rq)
        return !list_empty(&rq->sched.link);
 }
 
+static inline bool __i915_request_is_complete(const struct i915_request *rq)
+{
+       return i915_seqno_passed(__hwsp_seqno(rq), rq->fence.seqno);
+}
+
 static inline bool i915_request_completed(const struct i915_request *rq)
 {
+       bool result;
+
        if (i915_request_signaled(rq))
                return true;
 
-       return i915_seqno_passed(hwsp_seqno(rq), rq->fence.seqno);
+       result = true;
+       rcu_read_lock(); /* the HWSP may be freed at runtime */
+       if (likely(!i915_request_signaled(rq)))
+               result = __i915_request_is_complete(rq);
+       rcu_read_unlock();
+
+       return result;
 }
 
 static inline void i915_request_mark_complete(struct i915_request *rq)
index 7e82c41..bdc9891 100644 (file)
@@ -534,8 +534,10 @@ struct msm_gpu *a2xx_gpu_init(struct drm_device *dev)
 
        if (!gpu->aspace) {
                dev_err(dev->dev, "No memory protection without MMU\n");
-               ret = -ENXIO;
-               goto fail;
+               if (!allow_vram_carveout) {
+                       ret = -ENXIO;
+                       goto fail;
+               }
        }
 
        return gpu;
index 93da668..4534633 100644 (file)
@@ -564,8 +564,10 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
                 * implement a cmdstream validator.
                 */
                DRM_DEV_ERROR(dev->dev, "No memory protection without IOMMU\n");
-               ret = -ENXIO;
-               goto fail;
+               if (!allow_vram_carveout) {
+                       ret = -ENXIO;
+                       goto fail;
+               }
        }
 
        icc_path = devm_of_icc_get(&pdev->dev, "gfx-mem");
index c0be3a0..82bebb4 100644 (file)
@@ -692,8 +692,10 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev)
                 * implement a cmdstream validator.
                 */
                DRM_DEV_ERROR(dev->dev, "No memory protection without IOMMU\n");
-               ret = -ENXIO;
-               goto fail;
+               if (!allow_vram_carveout) {
+                       ret = -ENXIO;
+                       goto fail;
+               }
        }
 
        icc_path = devm_of_icc_get(&pdev->dev, "gfx-mem");
index 87c8b03..12e75ba 100644 (file)
@@ -18,6 +18,10 @@ bool snapshot_debugbus = false;
 MODULE_PARM_DESC(snapshot_debugbus, "Include debugbus sections in GPU devcoredump (if not fused off)");
 module_param_named(snapshot_debugbus, snapshot_debugbus, bool, 0600);
 
+bool allow_vram_carveout = false;
+MODULE_PARM_DESC(allow_vram_carveout, "Allow using VRAM Carveout, in place of IOMMU");
+module_param_named(allow_vram_carveout, allow_vram_carveout, bool, 0600);
+
 static const struct adreno_info gpulist[] = {
        {
                .rev   = ADRENO_REV(2, 0, 0, 0),
index 6cf9975..f091756 100644 (file)
@@ -191,8 +191,6 @@ adreno_iommu_create_address_space(struct msm_gpu *gpu,
                struct platform_device *pdev)
 {
        struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
-       struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
-       struct io_pgtable_domain_attr pgtbl_cfg;
        struct iommu_domain *iommu;
        struct msm_mmu *mmu;
        struct msm_gem_address_space *aspace;
@@ -202,13 +200,18 @@ adreno_iommu_create_address_space(struct msm_gpu *gpu,
        if (!iommu)
                return NULL;
 
-       /*
-        * This allows GPU to set the bus attributes required to use system
-        * cache on behalf of the iommu page table walker.
-        */
-       if (!IS_ERR(a6xx_gpu->htw_llc_slice)) {
-               pgtbl_cfg.quirks = IO_PGTABLE_QUIRK_ARM_OUTER_WBWA;
-               iommu_domain_set_attr(iommu, DOMAIN_ATTR_IO_PGTABLE_CFG, &pgtbl_cfg);
+
+       if (adreno_is_a6xx(adreno_gpu)) {
+               struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+               struct io_pgtable_domain_attr pgtbl_cfg;
+               /*
+               * This allows GPU to set the bus attributes required to use system
+               * cache on behalf of the iommu page table walker.
+               */
+               if (!IS_ERR(a6xx_gpu->htw_llc_slice)) {
+                       pgtbl_cfg.quirks = IO_PGTABLE_QUIRK_ARM_OUTER_WBWA;
+                       iommu_domain_set_attr(iommu, DOMAIN_ATTR_IO_PGTABLE_CFG, &pgtbl_cfg);
+               }
        }
 
        mmu = msm_iommu_new(&pdev->dev, iommu);
index c3775f7..b3d9a33 100644 (file)
@@ -18,6 +18,7 @@
 #include "adreno_pm4.xml.h"
 
 extern bool snapshot_debugbus;
+extern bool allow_vram_carveout;
 
 enum {
        ADRENO_FW_PM4 = 0,
@@ -211,6 +212,11 @@ static inline int adreno_is_a540(struct adreno_gpu *gpu)
        return gpu->revn == 540;
 }
 
+static inline bool adreno_is_a6xx(struct adreno_gpu *gpu)
+{
+       return ((gpu->revn < 700 && gpu->revn > 599));
+}
+
 static inline int adreno_is_a618(struct adreno_gpu *gpu)
 {
        return gpu->revn == 618;
index 6e971d5..3bc7ed2 100644 (file)
@@ -693,6 +693,13 @@ static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)
                return 0;
        }
 
+       if (state == ST_CONNECT_PENDING) {
+               /* wait until ST_CONNECTED */
+               dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */
+               mutex_unlock(&dp->event_mutex);
+               return 0;
+       }
+
        ret = dp_display_usbpd_attention_cb(&dp->pdev->dev);
        if (ret == -ECONNRESET) { /* cable unplugged */
                dp->core_initialized = false;
index 97dca3e..d1780bc 100644 (file)
@@ -167,12 +167,18 @@ int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
        panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 
        rc = dp_panel_read_dpcd(dp_panel);
+       if (rc) {
+               DRM_ERROR("read dpcd failed %d\n", rc);
+               return rc;
+       }
+
        bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate);
-       if (rc || !is_link_rate_valid(bw_code) ||
+       if (!is_link_rate_valid(bw_code) ||
                        !is_lane_count_valid(dp_panel->link_info.num_lanes) ||
                        (bw_code > dp_panel->max_bw_code)) {
-               DRM_ERROR("read dpcd failed %d\n", rc);
-               return rc;
+               DRM_ERROR("Illegal link rate=%d lane=%d\n", dp_panel->link_info.rate,
+                               dp_panel->link_info.num_lanes);
+               return -EINVAL;
        }
 
        if (dp_panel->dfp_present) {
index 535a026..108c405 100644 (file)
@@ -457,14 +457,14 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv)
 
        drm_mode_config_init(ddev);
 
-       /* Bind all our sub-components: */
-       ret = component_bind_all(dev, ddev);
+       ret = msm_init_vram(ddev);
        if (ret)
                goto err_destroy_mdss;
 
-       ret = msm_init_vram(ddev);
+       /* Bind all our sub-components: */
+       ret = component_bind_all(dev, ddev);
        if (ret)
-               goto err_msm_uninit;
+               goto err_destroy_mdss;
 
        dma_set_max_seg_size(dev, UINT_MAX);
 
index 9a7c49b..9d10739 100644 (file)
@@ -96,6 +96,8 @@ static struct page **get_pages(struct drm_gem_object *obj)
 {
        struct msm_gem_object *msm_obj = to_msm_bo(obj);
 
+       WARN_ON(!msm_gem_is_locked(obj));
+
        if (!msm_obj->pages) {
                struct drm_device *dev = obj->dev;
                struct page **p;
@@ -988,6 +990,8 @@ void msm_gem_free_object(struct drm_gem_object *obj)
                if (msm_obj->pages)
                        kvfree(msm_obj->pages);
 
+               put_iova_vmas(obj);
+
                /* dma_buf_detach() grabs resv lock, so we need to unlock
                 * prior to drm_prime_gem_destroy
                 */
@@ -997,11 +1001,10 @@ void msm_gem_free_object(struct drm_gem_object *obj)
        } else {
                msm_gem_vunmap(obj);
                put_pages(obj);
+               put_iova_vmas(obj);
                msm_gem_unlock(obj);
        }
 
-       put_iova_vmas(obj);
-
        drm_gem_object_release(obj);
 
        kfree(msm_obj);
@@ -1115,6 +1118,8 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev,
                struct msm_gem_vma *vma;
                struct page **pages;
 
+               drm_gem_private_object_init(dev, obj, size);
+
                msm_gem_lock(obj);
 
                vma = add_vma(obj, NULL);
@@ -1126,9 +1131,9 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev,
 
                to_msm_bo(obj)->vram_node = &vma->node;
 
-               drm_gem_private_object_init(dev, obj, size);
-
+               msm_gem_lock(obj);
                pages = get_pages(obj);
+               msm_gem_unlock(obj);
                if (IS_ERR(pages)) {
                        ret = PTR_ERR(pages);
                        goto fail;
index 6fdddb2..4488e1c 100644 (file)
@@ -37,6 +37,7 @@ nouveau-y += dispnv50/wimmc37b.o
 nouveau-y += dispnv50/wndw.o
 nouveau-y += dispnv50/wndwc37e.o
 nouveau-y += dispnv50/wndwc57e.o
+nouveau-y += dispnv50/wndwc67e.o
 
 nouveau-y += dispnv50/base.o
 nouveau-y += dispnv50/base507c.o
index 27ea3f3..abefc23 100644 (file)
@@ -42,6 +42,7 @@ nv50_core_new(struct nouveau_drm *drm, struct nv50_core **pcore)
                int version;
                int (*new)(struct nouveau_drm *, s32, struct nv50_core **);
        } cores[] = {
+               { GA102_DISP_CORE_CHANNEL_DMA, 0, corec57d_new },
                { TU102_DISP_CORE_CHANNEL_DMA, 0, corec57d_new },
                { GV100_DISP_CORE_CHANNEL_DMA, 0, corec37d_new },
                { GP102_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
index 121c24a..31d8b2e 100644 (file)
@@ -31,6 +31,7 @@ nv50_curs_new(struct nouveau_drm *drm, int head, struct nv50_wndw **pwndw)
                int version;
                int (*new)(struct nouveau_drm *, int, s32, struct nv50_wndw **);
        } curses[] = {
+               { GA102_DISP_CURSOR, 0, cursc37a_new },
                { TU102_DISP_CURSOR, 0, cursc37a_new },
                { GV100_DISP_CURSOR, 0, cursc37a_new },
                { GK104_DISP_CURSOR, 0, curs907a_new },
index 33fff38..c636703 100644 (file)
@@ -222,7 +222,7 @@ nv50_dmac_wait(struct nvif_push *push, u32 size)
 
 int
 nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
-                const s32 *oclass, u8 head, void *data, u32 size, u64 syncbuf,
+                const s32 *oclass, u8 head, void *data, u32 size, s64 syncbuf,
                 struct nv50_dmac *dmac)
 {
        struct nouveau_cli *cli = (void *)device->object.client;
@@ -271,7 +271,7 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
        if (ret)
                return ret;
 
-       if (!syncbuf)
+       if (syncbuf < 0)
                return 0;
 
        ret = nvif_object_ctor(&dmac->base.user, "kmsSyncCtxDma", NV50_DISP_HANDLE_SYNCBUF,
index 92bddc0..38dec11 100644 (file)
@@ -95,7 +95,7 @@ struct nv50_outp_atom {
 
 int nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp,
                     const s32 *oclass, u8 head, void *data, u32 size,
-                    u64 syncbuf, struct nv50_dmac *dmac);
+                    s64 syncbuf, struct nv50_dmac *dmac);
 void nv50_dmac_destroy(struct nv50_dmac *);
 
 /*
index a1ac153..566fbdd 100644 (file)
@@ -31,6 +31,7 @@ nv50_wimm_init(struct nouveau_drm *drm, struct nv50_wndw *wndw)
                int version;
                int (*init)(struct nouveau_drm *, s32, struct nv50_wndw *);
        } wimms[] = {
+               { GA102_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
                { TU102_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
                { GV100_DISP_WINDOW_IMM_CHANNEL_DMA, 0, wimmc37b_init },
                {}
index 685b708..b390029 100644 (file)
@@ -76,7 +76,7 @@ wimmc37b_init_(const struct nv50_wimm_func *func, struct nouveau_drm *drm,
        int ret;
 
        ret = nv50_dmac_create(&drm->client.device, &disp->disp->object,
-                              &oclass, 0, &args, sizeof(args), 0,
+                              &oclass, 0, &args, sizeof(args), -1,
                               &wndw->wimm);
        if (ret) {
                NV_ERROR(drm, "wimm%04x allocation failed: %d\n", oclass, ret);
index 0356474..ce45124 100644 (file)
@@ -784,6 +784,7 @@ nv50_wndw_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
                int (*new)(struct nouveau_drm *, enum drm_plane_type,
                           int, s32, struct nv50_wndw **);
        } wndws[] = {
+               { GA102_DISP_WINDOW_CHANNEL_DMA, 0, wndwc67e_new },
                { TU102_DISP_WINDOW_CHANNEL_DMA, 0, wndwc57e_new },
                { GV100_DISP_WINDOW_CHANNEL_DMA, 0, wndwc37e_new },
                {}
index 3278e28..f4e0c50 100644 (file)
@@ -129,6 +129,14 @@ int wndwc37e_update(struct nv50_wndw *, u32 *);
 
 int wndwc57e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
                 struct nv50_wndw **);
+bool wndwc57e_ilut(struct nv50_wndw *, struct nv50_wndw_atom *, int);
+int wndwc57e_ilut_set(struct nv50_wndw *, struct nv50_wndw_atom *);
+int wndwc57e_ilut_clr(struct nv50_wndw *);
+int wndwc57e_csc_set(struct nv50_wndw *, struct nv50_wndw_atom *);
+int wndwc57e_csc_clr(struct nv50_wndw *);
+
+int wndwc67e_new(struct nouveau_drm *, enum drm_plane_type, int, s32,
+                struct nv50_wndw **);
 
 int nv50_wndw_new(struct nouveau_drm *, enum drm_plane_type, int index,
                  struct nv50_wndw **);
index 429be0b..abdd3bb 100644 (file)
@@ -80,7 +80,7 @@ wndwc57e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
        return 0;
 }
 
-static int
+int
 wndwc57e_csc_clr(struct nv50_wndw *wndw)
 {
        struct nvif_push *push = wndw->wndw.push;
@@ -98,7 +98,7 @@ wndwc57e_csc_clr(struct nv50_wndw *wndw)
        return 0;
 }
 
-static int
+int
 wndwc57e_csc_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 {
        struct nvif_push *push = wndw->wndw.push;
@@ -111,7 +111,7 @@ wndwc57e_csc_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
        return 0;
 }
 
-static int
+int
 wndwc57e_ilut_clr(struct nv50_wndw *wndw)
 {
        struct nvif_push *push = wndw->wndw.push;
@@ -124,7 +124,7 @@ wndwc57e_ilut_clr(struct nv50_wndw *wndw)
        return 0;
 }
 
-static int
+int
 wndwc57e_ilut_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
 {
        struct nvif_push *push = wndw->wndw.push;
@@ -179,7 +179,7 @@ wndwc57e_ilut_load(struct drm_color_lut *in, int size, void __iomem *mem)
        writew(readw(mem - 4), mem + 4);
 }
 
-static bool
+bool
 wndwc57e_ilut(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw, int size)
 {
        if (size = size ? size : 1024, size != 256 && size != 1024)
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwc67e.c
new file mode 100644 (file)
index 0000000..7a370fa
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wndw.h"
+#include "atom.h"
+
+#include <nvif/pushc37b.h>
+
+#include <nvhw/class/clc57e.h>
+
+static int
+wndwc67e_image_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
+{
+       struct nvif_push *push = wndw->wndw.push;
+       int ret;
+
+       if ((ret = PUSH_WAIT(push, 17)))
+               return ret;
+
+       PUSH_MTHD(push, NVC57E, SET_PRESENT_CONTROL,
+                 NVVAL(NVC57E, SET_PRESENT_CONTROL, MIN_PRESENT_INTERVAL, asyw->image.interval) |
+                 NVVAL(NVC57E, SET_PRESENT_CONTROL, BEGIN_MODE, asyw->image.mode) |
+                 NVDEF(NVC57E, SET_PRESENT_CONTROL, TIMESTAMP_MODE, DISABLE));
+
+       PUSH_MTHD(push, NVC57E, SET_SIZE,
+                 NVVAL(NVC57E, SET_SIZE, WIDTH, asyw->image.w) |
+                 NVVAL(NVC57E, SET_SIZE, HEIGHT, asyw->image.h),
+
+                               SET_STORAGE,
+                 NVVAL(NVC57E, SET_STORAGE, BLOCK_HEIGHT, asyw->image.blockh),
+
+                               SET_PARAMS,
+                 NVVAL(NVC57E, SET_PARAMS, FORMAT, asyw->image.format) |
+                 NVDEF(NVC57E, SET_PARAMS, CLAMP_BEFORE_BLEND, DISABLE) |
+                 NVDEF(NVC57E, SET_PARAMS, SWAP_UV, DISABLE) |
+                 NVDEF(NVC57E, SET_PARAMS, FMT_ROUNDING_MODE, ROUND_TO_NEAREST),
+
+                               SET_PLANAR_STORAGE(0),
+                 NVVAL(NVC57E, SET_PLANAR_STORAGE, PITCH, asyw->image.blocks[0]) |
+                 NVVAL(NVC57E, SET_PLANAR_STORAGE, PITCH, asyw->image.pitch[0] >> 6));
+
+       PUSH_MTHD(push, NVC57E, SET_CONTEXT_DMA_ISO(0), asyw->image.handle, 1);
+       PUSH_MTHD(push, NVC57E, SET_OFFSET(0), asyw->image.offset[0] >> 8);
+
+       PUSH_MTHD(push, NVC57E, SET_POINT_IN(0),
+                 NVVAL(NVC57E, SET_POINT_IN, X, asyw->state.src_x >> 16) |
+                 NVVAL(NVC57E, SET_POINT_IN, Y, asyw->state.src_y >> 16));
+
+       PUSH_MTHD(push, NVC57E, SET_SIZE_IN,
+                 NVVAL(NVC57E, SET_SIZE_IN, WIDTH, asyw->state.src_w >> 16) |
+                 NVVAL(NVC57E, SET_SIZE_IN, HEIGHT, asyw->state.src_h >> 16));
+
+       PUSH_MTHD(push, NVC57E, SET_SIZE_OUT,
+                 NVVAL(NVC57E, SET_SIZE_OUT, WIDTH, asyw->state.crtc_w) |
+                 NVVAL(NVC57E, SET_SIZE_OUT, HEIGHT, asyw->state.crtc_h));
+       return 0;
+}
+
+static const struct nv50_wndw_func
+wndwc67e = {
+       .acquire = wndwc37e_acquire,
+       .release = wndwc37e_release,
+       .sema_set = wndwc37e_sema_set,
+       .sema_clr = wndwc37e_sema_clr,
+       .ntfy_set = wndwc37e_ntfy_set,
+       .ntfy_clr = wndwc37e_ntfy_clr,
+       .ntfy_reset = corec37d_ntfy_init,
+       .ntfy_wait_begun = base507c_ntfy_wait_begun,
+       .ilut = wndwc57e_ilut,
+       .ilut_identity = true,
+       .ilut_size = 1024,
+       .xlut_set = wndwc57e_ilut_set,
+       .xlut_clr = wndwc57e_ilut_clr,
+       .csc = base907c_csc,
+       .csc_set = wndwc57e_csc_set,
+       .csc_clr = wndwc57e_csc_clr,
+       .image_set = wndwc67e_image_set,
+       .image_clr = wndwc37e_image_clr,
+       .blend_set = wndwc37e_blend_set,
+       .update = wndwc37e_update,
+};
+
+int
+wndwc67e_new(struct nouveau_drm *drm, enum drm_plane_type type, int index,
+            s32 oclass, struct nv50_wndw **pwndw)
+{
+       return wndwc37e_new_(&wndwc67e, drm, type, index, oclass, BIT(index >> 1), pwndw);
+}
index cd9a2e6..57d4f45 100644 (file)
@@ -33,6 +33,7 @@ struct nv_device_info_v0 {
 #define NV_DEVICE_INFO_V0_PASCAL                                           0x0a
 #define NV_DEVICE_INFO_V0_VOLTA                                            0x0b
 #define NV_DEVICE_INFO_V0_TURING                                           0x0c
+#define NV_DEVICE_INFO_V0_AMPERE                                           0x0d
        __u8  family;
        __u8  pad06[2];
        __u64 ram_size;
index 2c79beb..ba2c28e 100644 (file)
@@ -88,6 +88,7 @@
 #define GP102_DISP                                    /* cl5070.h */ 0x00009870
 #define GV100_DISP                                    /* cl5070.h */ 0x0000c370
 #define TU102_DISP                                    /* cl5070.h */ 0x0000c570
+#define GA102_DISP                                    /* cl5070.h */ 0x0000c670
 
 #define GV100_DISP_CAPS                                              0x0000c373
 
 #define GK104_DISP_CURSOR                             /* cl507a.h */ 0x0000917a
 #define GV100_DISP_CURSOR                             /* cl507a.h */ 0x0000c37a
 #define TU102_DISP_CURSOR                             /* cl507a.h */ 0x0000c57a
+#define GA102_DISP_CURSOR                             /* cl507a.h */ 0x0000c67a
 
 #define NV50_DISP_OVERLAY                             /* cl507b.h */ 0x0000507b
 #define G82_DISP_OVERLAY                              /* cl507b.h */ 0x0000827b
 
 #define GV100_DISP_WINDOW_IMM_CHANNEL_DMA             /* clc37b.h */ 0x0000c37b
 #define TU102_DISP_WINDOW_IMM_CHANNEL_DMA             /* clc37b.h */ 0x0000c57b
+#define GA102_DISP_WINDOW_IMM_CHANNEL_DMA             /* clc37b.h */ 0x0000c67b
 
 #define NV50_DISP_BASE_CHANNEL_DMA                    /* cl507c.h */ 0x0000507c
 #define G82_DISP_BASE_CHANNEL_DMA                     /* cl507c.h */ 0x0000827c
 #define GP102_DISP_CORE_CHANNEL_DMA                   /* cl507d.h */ 0x0000987d
 #define GV100_DISP_CORE_CHANNEL_DMA                   /* cl507d.h */ 0x0000c37d
 #define TU102_DISP_CORE_CHANNEL_DMA                   /* cl507d.h */ 0x0000c57d
+#define GA102_DISP_CORE_CHANNEL_DMA                   /* cl507d.h */ 0x0000c67d
 
 #define NV50_DISP_OVERLAY_CHANNEL_DMA                 /* cl507e.h */ 0x0000507e
 #define G82_DISP_OVERLAY_CHANNEL_DMA                  /* cl507e.h */ 0x0000827e
 
 #define GV100_DISP_WINDOW_CHANNEL_DMA                 /* clc37e.h */ 0x0000c37e
 #define TU102_DISP_WINDOW_CHANNEL_DMA                 /* clc37e.h */ 0x0000c57e
+#define GA102_DISP_WINDOW_CHANNEL_DMA                 /* clc37e.h */ 0x0000c67e
 
 #define NV50_TESLA                                                   0x00005097
 #define G82_TESLA                                                    0x00008297
index 5c007ce..c920939 100644 (file)
@@ -120,6 +120,7 @@ struct nvkm_device {
                GP100    = 0x130,
                GV100    = 0x140,
                TU100    = 0x160,
+               GA100    = 0x170,
        } card_type;
        u32 chipset;
        u8  chiprev;
index 5a96c94..0f6fa66 100644 (file)
@@ -37,4 +37,5 @@ int gp100_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
 int gp102_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
 int gv100_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
 int tu102_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
+int ga102_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
 #endif
index 1a39e52..50cc7c0 100644 (file)
@@ -32,4 +32,5 @@ int gm107_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
 int gm200_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
 int gv100_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
 int tu102_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
+int ga100_devinit_new(struct nvkm_device *, int, struct nvkm_devinit **);
 #endif
index 34b56b1..2ecd52a 100644 (file)
@@ -86,6 +86,8 @@ int gp100_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gp102_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gp10b_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 int gv100_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
+int ga100_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
+int ga102_fb_new(struct nvkm_device *, int, struct nvkm_fb **);
 
 #include <subdev/bios.h>
 #include <subdev/bios/ramcfg.h>
index eaacf8d..cdcce5e 100644 (file)
@@ -37,4 +37,5 @@ int nv50_gpio_new(struct nvkm_device *, int, struct nvkm_gpio **);
 int g94_gpio_new(struct nvkm_device *, int, struct nvkm_gpio **);
 int gf119_gpio_new(struct nvkm_device *, int, struct nvkm_gpio **);
 int gk104_gpio_new(struct nvkm_device *, int, struct nvkm_gpio **);
+int ga102_gpio_new(struct nvkm_device *, int, struct nvkm_gpio **);
 #endif
index 81b9773..640f649 100644 (file)
@@ -92,6 +92,7 @@ int g94_i2c_new(struct nvkm_device *, int, struct nvkm_i2c **);
 int gf117_i2c_new(struct nvkm_device *, int, struct nvkm_i2c **);
 int gf119_i2c_new(struct nvkm_device *, int, struct nvkm_i2c **);
 int gk104_i2c_new(struct nvkm_device *, int, struct nvkm_i2c **);
+int gk110_i2c_new(struct nvkm_device *, int, struct nvkm_i2c **);
 int gm200_i2c_new(struct nvkm_device *, int, struct nvkm_i2c **);
 
 static inline int
index 6641fe4..e45ca45 100644 (file)
@@ -32,4 +32,5 @@ int gk20a_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
 int gp100_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
 int gp10b_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
 int tu102_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
+int ga100_mc_new(struct nvkm_device *, int, struct nvkm_mc **);
 #endif
index c7a94c9..72f35a2 100644 (file)
@@ -256,6 +256,7 @@ nouveau_backlight_init(struct drm_connector *connector)
        case NV_DEVICE_INFO_V0_PASCAL:
        case NV_DEVICE_INFO_V0_VOLTA:
        case NV_DEVICE_INFO_V0_TURING:
+       case NV_DEVICE_INFO_V0_AMPERE: //XXX: not confirmed
                ret = nv50_backlight_init(nv_encoder, &props, &ops);
                break;
        default:
index 8d0d30e..529cb60 100644 (file)
@@ -35,6 +35,7 @@ nvif_disp_ctor(struct nvif_device *device, const char *name, s32 oclass,
               struct nvif_disp *disp)
 {
        static const struct nvif_mclass disps[] = {
+               { GA102_DISP, -1 },
                { TU102_DISP, -1 },
                { GV100_DISP, -1 },
                { GP102_DISP, -1 },
index 7851bec..cdcc851 100644 (file)
@@ -1815,7 +1815,7 @@ nvf0_chipset = {
        .fb = gk110_fb_new,
        .fuse = gf100_fuse_new,
        .gpio = gk104_gpio_new,
-       .i2c = gk104_i2c_new,
+       .i2c = gk110_i2c_new,
        .ibus = gk104_ibus_new,
        .iccsense = gf100_iccsense_new,
        .imem = nv50_instmem_new,
@@ -1853,7 +1853,7 @@ nvf1_chipset = {
        .fb = gk110_fb_new,
        .fuse = gf100_fuse_new,
        .gpio = gk104_gpio_new,
-       .i2c = gk104_i2c_new,
+       .i2c = gk110_i2c_new,
        .ibus = gk104_ibus_new,
        .iccsense = gf100_iccsense_new,
        .imem = nv50_instmem_new,
@@ -1891,7 +1891,7 @@ nv106_chipset = {
        .fb = gk110_fb_new,
        .fuse = gf100_fuse_new,
        .gpio = gk104_gpio_new,
-       .i2c = gk104_i2c_new,
+       .i2c = gk110_i2c_new,
        .ibus = gk104_ibus_new,
        .iccsense = gf100_iccsense_new,
        .imem = nv50_instmem_new,
@@ -1929,7 +1929,7 @@ nv108_chipset = {
        .fb = gk110_fb_new,
        .fuse = gf100_fuse_new,
        .gpio = gk104_gpio_new,
-       .i2c = gk104_i2c_new,
+       .i2c = gk110_i2c_new,
        .ibus = gk104_ibus_new,
        .iccsense = gf100_iccsense_new,
        .imem = nv50_instmem_new,
@@ -1967,7 +1967,7 @@ nv117_chipset = {
        .fb = gm107_fb_new,
        .fuse = gm107_fuse_new,
        .gpio = gk104_gpio_new,
-       .i2c = gk104_i2c_new,
+       .i2c = gk110_i2c_new,
        .ibus = gk104_ibus_new,
        .iccsense = gf100_iccsense_new,
        .imem = nv50_instmem_new,
@@ -2003,7 +2003,7 @@ nv118_chipset = {
        .fb = gm107_fb_new,
        .fuse = gm107_fuse_new,
        .gpio = gk104_gpio_new,
-       .i2c = gk104_i2c_new,
+       .i2c = gk110_i2c_new,
        .ibus = gk104_ibus_new,
        .iccsense = gf100_iccsense_new,
        .imem = nv50_instmem_new,
@@ -2652,6 +2652,61 @@ nv168_chipset = {
        .sec2 = tu102_sec2_new,
 };
 
+static const struct nvkm_device_chip
+nv170_chipset = {
+       .name = "GA100",
+       .bar = tu102_bar_new,
+       .bios = nvkm_bios_new,
+       .devinit = ga100_devinit_new,
+       .fb = ga100_fb_new,
+       .gpio = gk104_gpio_new,
+       .i2c = gm200_i2c_new,
+       .ibus = gm200_ibus_new,
+       .imem = nv50_instmem_new,
+       .mc = ga100_mc_new,
+       .mmu = tu102_mmu_new,
+       .pci = gp100_pci_new,
+       .timer = gk20a_timer_new,
+};
+
+static const struct nvkm_device_chip
+nv172_chipset = {
+       .name = "GA102",
+       .bar = tu102_bar_new,
+       .bios = nvkm_bios_new,
+       .devinit = ga100_devinit_new,
+       .fb = ga102_fb_new,
+       .gpio = ga102_gpio_new,
+       .i2c = gm200_i2c_new,
+       .ibus = gm200_ibus_new,
+       .imem = nv50_instmem_new,
+       .mc = ga100_mc_new,
+       .mmu = tu102_mmu_new,
+       .pci = gp100_pci_new,
+       .timer = gk20a_timer_new,
+       .disp = ga102_disp_new,
+       .dma = gv100_dma_new,
+};
+
+static const struct nvkm_device_chip
+nv174_chipset = {
+       .name = "GA104",
+       .bar = tu102_bar_new,
+       .bios = nvkm_bios_new,
+       .devinit = ga100_devinit_new,
+       .fb = ga102_fb_new,
+       .gpio = ga102_gpio_new,
+       .i2c = gm200_i2c_new,
+       .ibus = gm200_ibus_new,
+       .imem = nv50_instmem_new,
+       .mc = ga100_mc_new,
+       .mmu = tu102_mmu_new,
+       .pci = gp100_pci_new,
+       .timer = gk20a_timer_new,
+       .disp = ga102_disp_new,
+       .dma = gv100_dma_new,
+};
+
 static int
 nvkm_device_event_ctor(struct nvkm_object *object, void *data, u32 size,
                       struct nvkm_notify *notify)
@@ -3063,6 +3118,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
                        case 0x130: device->card_type = GP100; break;
                        case 0x140: device->card_type = GV100; break;
                        case 0x160: device->card_type = TU100; break;
+                       case 0x170: device->card_type = GA100; break;
                        default:
                                break;
                        }
@@ -3160,10 +3216,23 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
                case 0x166: device->chip = &nv166_chipset; break;
                case 0x167: device->chip = &nv167_chipset; break;
                case 0x168: device->chip = &nv168_chipset; break;
+               case 0x172: device->chip = &nv172_chipset; break;
+               case 0x174: device->chip = &nv174_chipset; break;
                default:
-                       nvdev_error(device, "unknown chipset (%08x)\n", boot0);
-                       ret = -ENODEV;
-                       goto done;
+                       if (nvkm_boolopt(device->cfgopt, "NvEnableUnsupportedChipsets", false)) {
+                               switch (device->chipset) {
+                               case 0x170: device->chip = &nv170_chipset; break;
+                               default:
+                                       break;
+                               }
+                       }
+
+                       if (!device->chip) {
+                               nvdev_error(device, "unknown chipset (%08x)\n", boot0);
+                               ret = -ENODEV;
+                               goto done;
+                       }
+                       break;
                }
 
                nvdev_info(device, "NVIDIA %s (%08x)\n",
index 03c6d9a..1478947 100644 (file)
@@ -176,6 +176,7 @@ nvkm_udevice_info(struct nvkm_udevice *udev, void *data, u32 size)
        case GP100: args->v0.family = NV_DEVICE_INFO_V0_PASCAL; break;
        case GV100: args->v0.family = NV_DEVICE_INFO_V0_VOLTA; break;
        case TU100: args->v0.family = NV_DEVICE_INFO_V0_TURING; break;
+       case GA100: args->v0.family = NV_DEVICE_INFO_V0_AMPERE; break;
        default:
                args->v0.family = 0;
                break;
index cf07531..b03f043 100644 (file)
@@ -17,6 +17,7 @@ nvkm-y += nvkm/engine/disp/gp100.o
 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/vga.o
 
 nvkm-y += nvkm/engine/disp/head.o
@@ -42,6 +43,7 @@ nvkm-y += nvkm/engine/disp/sorgm200.o
 nvkm-y += nvkm/engine/disp/sorgp100.o
 nvkm-y += nvkm/engine/disp/sorgv100.o
 nvkm-y += nvkm/engine/disp/sortu102.o
+nvkm-y += nvkm/engine/disp/sorga102.o
 
 nvkm-y += nvkm/engine/disp/outp.o
 nvkm-y += nvkm/engine/disp/dp.o
@@ -75,6 +77,7 @@ nvkm-y += nvkm/engine/disp/rootgp100.o
 nvkm-y += nvkm/engine/disp/rootgp102.o
 nvkm-y += nvkm/engine/disp/rootgv100.o
 nvkm-y += nvkm/engine/disp/roottu102.o
+nvkm-y += nvkm/engine/disp/rootga102.o
 
 nvkm-y += nvkm/engine/disp/capsgv100.o
 
index 3800aeb..55fbfe2 100644 (file)
 
 #include <nvif/event.h>
 
+/* IED scripts are no longer used by UEFI/RM from Ampere, but have been updated for
+ * the x86 option ROM.  However, the relevant VBIOS table versions weren't modified,
+ * so we're unable to detect this in a nice way.
+ */
+#define AMPERE_IED_HACK(disp) ((disp)->engine.subdev.device->card_type >= GA100)
+
 struct lt_state {
        struct nvkm_dp *dp;
        u8  stat[6];
@@ -238,6 +244,19 @@ nvkm_dp_train_links(struct nvkm_dp *dp)
                dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED;
        lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED;
 
+       if (AMPERE_IED_HACK(disp) && (lnkcmp = lt.dp->info.script[0])) {
+               /* Execute BeforeLinkTraining script from DP Info table. */
+               while (ior->dp.bw < nvbios_rd08(bios, lnkcmp))
+                       lnkcmp += 3;
+               lnkcmp = nvbios_rd16(bios, lnkcmp + 1);
+
+               nvbios_init(&dp->outp.disp->engine.subdev, lnkcmp,
+                       init.outp = &dp->outp.info;
+                       init.or   = ior->id;
+                       init.link = ior->asy.link;
+               );
+       }
+
        /* Set desired link configuration on the source. */
        if ((lnkcmp = lt.dp->info.lnkcmp)) {
                if (dp->version < 0x30) {
@@ -316,12 +335,14 @@ nvkm_dp_train_init(struct nvkm_dp *dp)
                );
        }
 
-       /* Execute BeforeLinkTraining script from DP Info table. */
-       nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0],
-               init.outp = &dp->outp.info;
-               init.or   = dp->outp.ior->id;
-               init.link = dp->outp.ior->asy.link;
-       );
+       if (!AMPERE_IED_HACK(dp->outp.disp)) {
+               /* Execute BeforeLinkTraining script from DP Info table. */
+               nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0],
+                       init.outp = &dp->outp.info;
+                       init.or   = dp->outp.ior->id;
+                       init.link = dp->outp.ior->asy.link;
+               );
+       }
 }
 
 static const struct dp_rates {
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c
new file mode 100644 (file)
index 0000000..aa2e564
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nv50.h"
+#include "head.h"
+#include "ior.h"
+#include "channv50.h"
+#include "rootnv50.h"
+
+static const struct nv50_disp_func
+ga102_disp = {
+       .init = tu102_disp_init,
+       .fini = gv100_disp_fini,
+       .intr = gv100_disp_intr,
+       .uevent = &gv100_disp_chan_uevent,
+       .super = gv100_disp_super,
+       .root = &ga102_disp_root_oclass,
+       .wndw = { .cnt = gv100_disp_wndw_cnt },
+       .head = { .cnt = gv100_head_cnt, .new = gv100_head_new },
+       .sor = { .cnt = gv100_sor_cnt, .new = ga102_sor_new },
+       .ramht_size = 0x2000,
+};
+
+int
+ga102_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
+{
+       return nv50_disp_new_(&ga102_disp, device, index, pdisp);
+}
index 09f3038..9f0bb7c 100644 (file)
@@ -150,6 +150,8 @@ void gv100_sor_dp_audio(struct nvkm_ior *, int, bool);
 void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
 void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8);
 
+void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
+
 void g84_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
 void gt215_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
 void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
@@ -207,4 +209,6 @@ int gv100_sor_cnt(struct nvkm_disp *, unsigned long *);
 int gv100_sor_new(struct nvkm_disp *, int);
 
 int tu102_sor_new(struct nvkm_disp *, int);
+
+int ga102_sor_new(struct nvkm_disp *, int);
 #endif
index a677161..db31b37 100644 (file)
@@ -86,6 +86,8 @@ void gv100_disp_intr(struct nv50_disp *);
 void gv100_disp_super(struct work_struct *);
 int gv100_disp_wndw_cnt(struct nvkm_disp *, unsigned long *);
 
+int tu102_disp_init(struct nv50_disp *);
+
 void nv50_disp_dptmds_war_2(struct nv50_disp *, struct dcb_output *);
 void nv50_disp_dptmds_war_3(struct nv50_disp *, struct dcb_output *);
 void nv50_disp_update_sppll1(struct nv50_disp *);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootga102.c
new file mode 100644 (file)
index 0000000..9af07c3
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "rootnv50.h"
+#include "channv50.h"
+
+#include <nvif/class.h>
+
+static const struct nv50_disp_root_func
+ga102_disp_root = {
+       .user = {
+               {{-1,-1,GV100_DISP_CAPS                }, gv100_disp_caps_new },
+               {{0,0,GA102_DISP_CURSOR                }, gv100_disp_curs_new },
+               {{0,0,GA102_DISP_WINDOW_IMM_CHANNEL_DMA}, gv100_disp_wimm_new },
+               {{0,0,GA102_DISP_CORE_CHANNEL_DMA      }, gv100_disp_core_new },
+               {{0,0,GA102_DISP_WINDOW_CHANNEL_DMA    }, gv100_disp_wndw_new },
+               {}
+       },
+};
+
+static int
+ga102_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
+                   void *data, u32 size, struct nvkm_object **pobject)
+{
+       return nv50_disp_root_new_(&ga102_disp_root, disp, oclass, data, size, pobject);
+}
+
+const struct nvkm_disp_oclass
+ga102_disp_root_oclass = {
+       .base.oclass = GA102_DISP,
+       .base.minver = -1,
+       .base.maxver = -1,
+       .ctor = ga102_disp_root_new,
+};
index 7070f54..27bb170 100644 (file)
@@ -41,4 +41,5 @@ extern const struct nvkm_disp_oclass gp100_disp_root_oclass;
 extern const struct nvkm_disp_oclass gp102_disp_root_oclass;
 extern const struct nvkm_disp_oclass gv100_disp_root_oclass;
 extern const struct nvkm_disp_oclass tu102_disp_root_oclass;
+extern const struct nvkm_disp_oclass ga102_disp_root_oclass;
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorga102.c
new file mode 100644 (file)
index 0000000..033827d
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ior.h"
+
+#include <subdev/timer.h>
+
+static int
+ga102_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       const u32 soff = nv50_ior_base(sor);
+       const u32 loff = nv50_sor_link(sor);
+       u32 dpctrl = 0x00000000;
+       u32 clksor = 0x00000000;
+
+       switch (sor->dp.bw) {
+       case 0x06: clksor |= 0x00000000; break;
+       case 0x0a: clksor |= 0x00040000; break;
+       case 0x14: clksor |= 0x00080000; break;
+       case 0x1e: clksor |= 0x000c0000; break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
+       }
+
+       dpctrl |= ((1 << sor->dp.nr) - 1) << 16;
+       if (sor->dp.mst)
+               dpctrl |= 0x40000000;
+       if (sor->dp.ef)
+               dpctrl |= 0x00004000;
+
+       nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor);
+
+       /*XXX*/
+       nvkm_msec(device, 40, NVKM_DELAY);
+       nvkm_mask(device, 0x612300 + soff, 0x00030000, 0x00010000);
+       nvkm_mask(device, 0x61c10c + loff, 0x00000003, 0x00000001);
+
+       nvkm_mask(device, 0x61c10c + loff, 0x401f4000, dpctrl);
+       return 0;
+}
+
+static void
+ga102_sor_clock(struct nvkm_ior *sor)
+{
+       struct nvkm_device *device = sor->disp->engine.subdev.device;
+       u32 div2 = 0;
+       if (sor->asy.proto == TMDS) {
+               if (sor->tmds.high_speed)
+                       div2 = 1;
+       }
+       nvkm_wr32(device, 0x00ec08 + (sor->id * 0x10), 0x00000000);
+       nvkm_wr32(device, 0x00ec04 + (sor->id * 0x10), div2);
+}
+
+static const struct nvkm_ior_func
+ga102_sor_hda = {
+       .route = {
+               .get = gm200_sor_route_get,
+               .set = gm200_sor_route_set,
+       },
+       .state = gv100_sor_state,
+       .power = nv50_sor_power,
+       .clock = ga102_sor_clock,
+       .hdmi = {
+               .ctrl = gv100_hdmi_ctrl,
+               .scdc = gm200_hdmi_scdc,
+       },
+       .dp = {
+               .lanes = { 0, 1, 2, 3 },
+               .links = ga102_sor_dp_links,
+               .power = g94_sor_dp_power,
+               .pattern = gm107_sor_dp_pattern,
+               .drive = gm200_sor_dp_drive,
+               .vcpi = tu102_sor_dp_vcpi,
+               .audio = gv100_sor_dp_audio,
+               .audio_sym = gv100_sor_dp_audio_sym,
+               .watermark = gv100_sor_dp_watermark,
+       },
+       .hda = {
+               .hpd = gf119_hda_hpd,
+               .eld = gf119_hda_eld,
+               .device_entry = gv100_hda_device_entry,
+       },
+};
+
+static const struct nvkm_ior_func
+ga102_sor = {
+       .route = {
+               .get = gm200_sor_route_get,
+               .set = gm200_sor_route_set,
+       },
+       .state = gv100_sor_state,
+       .power = nv50_sor_power,
+       .clock = ga102_sor_clock,
+       .hdmi = {
+               .ctrl = gv100_hdmi_ctrl,
+               .scdc = gm200_hdmi_scdc,
+       },
+       .dp = {
+               .lanes = { 0, 1, 2, 3 },
+               .links = ga102_sor_dp_links,
+               .power = g94_sor_dp_power,
+               .pattern = gm107_sor_dp_pattern,
+               .drive = gm200_sor_dp_drive,
+               .vcpi = tu102_sor_dp_vcpi,
+               .audio = gv100_sor_dp_audio,
+               .audio_sym = gv100_sor_dp_audio_sym,
+               .watermark = gv100_sor_dp_watermark,
+       },
+};
+
+int
+ga102_sor_new(struct nvkm_disp *disp, int id)
+{
+       struct nvkm_device *device = disp->engine.subdev.device;
+       u32 hda = nvkm_rd32(device, 0x08a15c);
+       if (hda & BIT(id))
+               return nvkm_ior_new_(&ga102_sor_hda, disp, SOR, id);
+       return nvkm_ior_new_(&ga102_sor, disp, SOR, id);
+}
index 59865a9..0cf9e87 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <subdev/timer.h>
 
-static void
+void
 tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head,
                  u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
 {
index 883ae41..4c85d1d 100644 (file)
@@ -28,7 +28,7 @@
 #include <core/gpuobj.h>
 #include <subdev/timer.h>
 
-static int
+int
 tu102_disp_init(struct nv50_disp *disp)
 {
        struct nvkm_device *device = disp->base.engine.subdev.device;
index 7deb81b..4b571cc 100644 (file)
@@ -75,7 +75,7 @@ shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd)
        nvkm_debug(subdev, "%08x: type %02x, %d bytes\n",
                   image.base, image.type, image.size);
 
-       if (!shadow_fetch(bios, mthd, image.size)) {
+       if (!shadow_fetch(bios, mthd, image.base + image.size)) {
                nvkm_debug(subdev, "%08x: fetch failed\n", image.base);
                return 0;
        }
index 3634cd0..023ddc7 100644 (file)
@@ -64,6 +64,9 @@ pramin_init(struct nvkm_bios *bios, const char *name)
                return NULL;
 
        /* we can't get the bios image pointer without PDISP */
+       if (device->card_type >= GA100)
+               addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */
+       else
        if (device->card_type >= GM100)
                addr = nvkm_rd32(device, 0x021c04);
        else
index b342937..d1abb64 100644 (file)
@@ -15,3 +15,4 @@ nvkm-y += nvkm/subdev/devinit/gm107.o
 nvkm-y += nvkm/subdev/devinit/gm200.o
 nvkm-y += nvkm/subdev/devinit/gv100.o
 nvkm-y += nvkm/subdev/devinit/tu102.o
+nvkm-y += nvkm/subdev/devinit/ga100.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/ga100.c
new file mode 100644 (file)
index 0000000..636a921
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "nv50.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/pll.h>
+#include <subdev/clk/pll.h>
+
+static int
+ga100_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq)
+{
+       struct nvkm_subdev *subdev = &init->subdev;
+       struct nvkm_device *device = subdev->device;
+       struct nvbios_pll info;
+       int head = type - PLL_VPLL0;
+       int N, fN, M, P;
+       int ret;
+
+       ret = nvbios_pll_parse(device->bios, type, &info);
+       if (ret)
+               return ret;
+
+       ret = gt215_pll_calc(subdev, &info, freq, &N, &fN, &M, &P);
+       if (ret < 0)
+               return ret;
+
+       switch (info.type) {
+       case PLL_VPLL0:
+       case PLL_VPLL1:
+       case PLL_VPLL2:
+       case PLL_VPLL3:
+               nvkm_wr32(device, 0x00ef00 + (head * 0x40), 0x02080004);
+               nvkm_wr32(device, 0x00ef18 + (head * 0x40), (N << 16) | fN);
+               nvkm_wr32(device, 0x00ef04 + (head * 0x40), (P << 16) | M);
+               nvkm_wr32(device, 0x00e9c0 + (head * 0x04), 0x00000001);
+               break;
+       default:
+               nvkm_warn(subdev, "%08x/%dKhz unimplemented\n", type, freq);
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static const struct nvkm_devinit_func
+ga100_devinit = {
+       .init = nv50_devinit_init,
+       .post = tu102_devinit_post,
+       .pll_set = ga100_devinit_pll_set,
+};
+
+int
+ga100_devinit_new(struct nvkm_device *device, int index, struct nvkm_devinit **pinit)
+{
+       return nv50_devinit_new_(&ga100_devinit, device, index, pinit);
+}
index 9472335..05961e6 100644 (file)
@@ -19,4 +19,5 @@ void nvkm_devinit_ctor(const struct nvkm_devinit_func *, struct nvkm_device *,
                       int index, struct nvkm_devinit *);
 
 int nv04_devinit_post(struct nvkm_devinit *, bool);
+int tu102_devinit_post(struct nvkm_devinit *, bool);
 #endif
index 397670e..9a469bf 100644 (file)
@@ -65,7 +65,7 @@ tu102_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq)
        return ret;
 }
 
-static int
+int
 tu102_devinit_post(struct nvkm_devinit *base, bool post)
 {
        struct nv50_devinit *init = nv50_devinit(base);
index 43a4215..5d0bab8 100644 (file)
@@ -32,6 +32,8 @@ nvkm-y += nvkm/subdev/fb/gp100.o
 nvkm-y += nvkm/subdev/fb/gp102.o
 nvkm-y += nvkm/subdev/fb/gp10b.o
 nvkm-y += nvkm/subdev/fb/gv100.o
+nvkm-y += nvkm/subdev/fb/ga100.o
+nvkm-y += nvkm/subdev/fb/ga102.o
 
 nvkm-y += nvkm/subdev/fb/ram.o
 nvkm-y += nvkm/subdev/fb/ramnv04.o
@@ -52,6 +54,7 @@ nvkm-y += nvkm/subdev/fb/ramgk104.o
 nvkm-y += nvkm/subdev/fb/ramgm107.o
 nvkm-y += nvkm/subdev/fb/ramgm200.o
 nvkm-y += nvkm/subdev/fb/ramgp100.o
+nvkm-y += nvkm/subdev/fb/ramga102.o
 nvkm-y += nvkm/subdev/fb/sddr2.o
 nvkm-y += nvkm/subdev/fb/sddr3.o
 nvkm-y += nvkm/subdev/fb/gddr3.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga100.c
new file mode 100644 (file)
index 0000000..bf82686
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "gf100.h"
+#include "ram.h"
+
+static const struct nvkm_fb_func
+ga100_fb = {
+       .dtor = gf100_fb_dtor,
+       .oneinit = gf100_fb_oneinit,
+       .init = gp100_fb_init,
+       .init_page = gv100_fb_init_page,
+       .init_unkn = gp100_fb_init_unkn,
+       .ram_new = gp100_ram_new,
+       .default_bigpage = 16,
+};
+
+int
+ga100_fb_new(struct nvkm_device *device, int index, struct nvkm_fb **pfb)
+{
+       return gp102_fb_new_(&ga100_fb, device, index, pfb);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c
new file mode 100644 (file)
index 0000000..bcecf84
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "gf100.h"
+#include "ram.h"
+
+static const struct nvkm_fb_func
+ga102_fb = {
+       .dtor = gf100_fb_dtor,
+       .oneinit = gf100_fb_oneinit,
+       .init = gp100_fb_init,
+       .init_page = gv100_fb_init_page,
+       .init_unkn = gp100_fb_init_unkn,
+       .ram_new = ga102_ram_new,
+       .default_bigpage = 16,
+};
+
+int
+ga102_fb_new(struct nvkm_device *device, int index, struct nvkm_fb **pfb)
+{
+       return gp102_fb_new_(&ga102_fb, device, index, pfb);
+}
index 10ff5d0..feda86a 100644 (file)
@@ -22,7 +22,7 @@
 #include "gf100.h"
 #include "ram.h"
 
-static int
+int
 gv100_fb_init_page(struct nvkm_fb *fb)
 {
        return (fb->page == 16) ? 0 : -EINVAL;
index 5be9c56..66932ac 100644 (file)
@@ -82,4 +82,6 @@ int gp102_fb_new_(const struct nvkm_fb_func *, struct nvkm_device *, int,
                  struct nvkm_fb **);
 bool gp102_fb_vpr_scrub_required(struct nvkm_fb *);
 int gp102_fb_vpr_scrub(struct nvkm_fb *);
+
+int gv100_fb_init_page(struct nvkm_fb *);
 #endif
index d723a9b..ea7d66f 100644 (file)
@@ -70,4 +70,5 @@ int gk104_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gm107_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gm200_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 int gp100_ram_new(struct nvkm_fb *, struct nvkm_ram **);
+int ga102_ram_new(struct nvkm_fb *, struct nvkm_ram **);
 #endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ramga102.c
new file mode 100644 (file)
index 0000000..298c136
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ram.h"
+
+#include <subdev/bios.h>
+#include <subdev/bios/init.h>
+#include <subdev/bios/rammap.h>
+
+static const struct nvkm_ram_func
+ga102_ram = {
+};
+
+int
+ga102_ram_new(struct nvkm_fb *fb, struct nvkm_ram **pram)
+{
+       struct nvkm_device *device = fb->subdev.device;
+       enum nvkm_ram_type type = nvkm_fb_bios_memtype(device->bios);
+       u32 size = nvkm_rd32(device, 0x1183a4);
+
+       return nvkm_ram_new_(&ga102_ram, fb, type, (u64)size << 20, pram);
+}
index b2ad592..efbbaa0 100644 (file)
@@ -5,3 +5,4 @@ nvkm-y += nvkm/subdev/gpio/nv50.o
 nvkm-y += nvkm/subdev/gpio/g94.o
 nvkm-y += nvkm/subdev/gpio/gf119.o
 nvkm-y += nvkm/subdev/gpio/gk104.o
+nvkm-y += nvkm/subdev/gpio/ga102.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/ga102.c
new file mode 100644 (file)
index 0000000..62c791b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+static void
+ga102_gpio_reset(struct nvkm_gpio *gpio, u8 match)
+{
+       struct nvkm_device *device = gpio->subdev.device;
+       struct nvkm_bios *bios = device->bios;
+       u8 ver, len;
+       u16 entry;
+       int ent = -1;
+
+       while ((entry = dcb_gpio_entry(bios, 0, ++ent, &ver, &len))) {
+               u32 data = nvbios_rd32(bios, entry);
+               u8  line =   (data & 0x0000003f);
+               u8  defs = !!(data & 0x00000080);
+               u8  func =   (data & 0x0000ff00) >> 8;
+               u8  unk0 =   (data & 0x00ff0000) >> 16;
+               u8  unk1 =   (data & 0x1f000000) >> 24;
+
+               if ( func  == DCB_GPIO_UNUSED ||
+                   (match != DCB_GPIO_UNUSED && match != func))
+                       continue;
+
+               nvkm_gpio_set(gpio, 0, func, line, defs);
+
+               nvkm_mask(device, 0x021200 + (line * 4), 0xff, unk0);
+               if (unk1--)
+                       nvkm_mask(device, 0x00d740 + (unk1 * 4), 0xff, line);
+       }
+}
+
+static int
+ga102_gpio_drive(struct nvkm_gpio *gpio, int line, int dir, int out)
+{
+       struct nvkm_device *device = gpio->subdev.device;
+       u32 data = ((dir ^ 1) << 13) | (out << 12);
+       nvkm_mask(device, 0x021200 + (line * 4), 0x00003000, data);
+       nvkm_mask(device, 0x00d604, 0x00000001, 0x00000001); /* update? */
+       return 0;
+}
+
+static int
+ga102_gpio_sense(struct nvkm_gpio *gpio, int line)
+{
+       struct nvkm_device *device = gpio->subdev.device;
+       return !!(nvkm_rd32(device, 0x021200 + (line * 4)) & 0x00004000);
+}
+
+static void
+ga102_gpio_intr_stat(struct nvkm_gpio *gpio, u32 *hi, u32 *lo)
+{
+       struct nvkm_device *device = gpio->subdev.device;
+       u32 intr0 = nvkm_rd32(device, 0x021640);
+       u32 intr1 = nvkm_rd32(device, 0x02164c);
+       u32 stat0 = nvkm_rd32(device, 0x021648) & intr0;
+       u32 stat1 = nvkm_rd32(device, 0x021654) & intr1;
+       *lo = (stat1 & 0xffff0000) | (stat0 >> 16);
+       *hi = (stat1 << 16) | (stat0 & 0x0000ffff);
+       nvkm_wr32(device, 0x021640, intr0);
+       nvkm_wr32(device, 0x02164c, intr1);
+}
+
+static void
+ga102_gpio_intr_mask(struct nvkm_gpio *gpio, u32 type, u32 mask, u32 data)
+{
+       struct nvkm_device *device = gpio->subdev.device;
+       u32 inte0 = nvkm_rd32(device, 0x021648);
+       u32 inte1 = nvkm_rd32(device, 0x021654);
+       if (type & NVKM_GPIO_LO)
+               inte0 = (inte0 & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte0 = (inte0 & ~(mask & 0xffff)) | (data & 0xffff);
+       mask >>= 16;
+       data >>= 16;
+       if (type & NVKM_GPIO_LO)
+               inte1 = (inte1 & ~(mask << 16)) | (data << 16);
+       if (type & NVKM_GPIO_HI)
+               inte1 = (inte1 & ~mask) | data;
+       nvkm_wr32(device, 0x021648, inte0);
+       nvkm_wr32(device, 0x021654, inte1);
+}
+
+static const struct nvkm_gpio_func
+ga102_gpio = {
+       .lines = 32,
+       .intr_stat = ga102_gpio_intr_stat,
+       .intr_mask = ga102_gpio_intr_mask,
+       .drive = ga102_gpio_drive,
+       .sense = ga102_gpio_sense,
+       .reset = ga102_gpio_reset,
+};
+
+int
+ga102_gpio_new(struct nvkm_device *device, int index, struct nvkm_gpio **pgpio)
+{
+       return nvkm_gpio_new_(&ga102_gpio, device, index, pgpio);
+}
index 723d028..8197039 100644 (file)
@@ -7,6 +7,7 @@ nvkm-y += nvkm/subdev/i2c/g94.o
 nvkm-y += nvkm/subdev/i2c/gf117.o
 nvkm-y += nvkm/subdev/i2c/gf119.o
 nvkm-y += nvkm/subdev/i2c/gk104.o
+nvkm-y += nvkm/subdev/i2c/gk110.o
 nvkm-y += nvkm/subdev/i2c/gm200.o
 
 nvkm-y += nvkm/subdev/i2c/pad.o
index 30b4889..f920eab 100644 (file)
@@ -3,6 +3,13 @@
 #define __NVKM_I2C_AUX_H__
 #include "pad.h"
 
+static inline void
+nvkm_i2c_aux_autodpcd(struct nvkm_i2c *i2c, int aux, bool enable)
+{
+       if (i2c->func->aux_autodpcd)
+               i2c->func->aux_autodpcd(i2c, aux, false);
+}
+
 struct nvkm_i2c_aux_func {
        bool address_only;
        int  (*xfer)(struct nvkm_i2c_aux *, bool retry, u8 type,
index db7769c..47068f6 100644 (file)
@@ -77,7 +77,8 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                 u8 type, u32 addr, u8 *data, u8 *size)
 {
        struct g94_i2c_aux *aux = g94_i2c_aux(obj);
-       struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
+       struct nvkm_i2c *i2c = aux->base.pad->i2c;
+       struct nvkm_device *device = i2c->subdev.device;
        const u32 base = aux->ch * 0x50;
        u32 ctrl, stat, timeout, retries = 0;
        u32 xbuf[4] = {};
@@ -96,6 +97,8 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                goto out;
        }
 
+       nvkm_i2c_aux_autodpcd(i2c, aux->ch, false);
+
        if (!(type & 1)) {
                memcpy(xbuf, data, *size);
                for (i = 0; i < 16; i += 4) {
@@ -128,7 +131,7 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                        if (!timeout--) {
                                AUX_ERR(&aux->base, "timeout %08x", ctrl);
                                ret = -EIO;
-                               goto out;
+                               goto out_err;
                        }
                } while (ctrl & 0x00010000);
                ret = 0;
@@ -154,7 +157,8 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                memcpy(data, xbuf, *size);
                *size = stat & 0x0000001f;
        }
-
+out_err:
+       nvkm_i2c_aux_autodpcd(i2c, aux->ch, true);
 out:
        g94_i2c_aux_fini(aux);
        return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
index edb6148..8bd1d44 100644 (file)
@@ -33,7 +33,7 @@ static void
 gm200_i2c_aux_fini(struct gm200_i2c_aux *aux)
 {
        struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
-       nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00310000, 0x00000000);
+       nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00710000, 0x00000000);
 }
 
 static int
@@ -54,10 +54,10 @@ gm200_i2c_aux_init(struct gm200_i2c_aux *aux)
                        AUX_ERR(&aux->base, "begin idle timeout %08x", ctrl);
                        return -EBUSY;
                }
-       } while (ctrl & 0x03010000);
+       } while (ctrl & 0x07010000);
 
        /* set some magic, and wait up to 1ms for it to appear */
-       nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00300000, ureq);
+       nvkm_mask(device, 0x00d954 + (aux->ch * 0x50), 0x00700000, ureq);
        timeout = 1000;
        do {
                ctrl = nvkm_rd32(device, 0x00d954 + (aux->ch * 0x50));
@@ -67,7 +67,7 @@ gm200_i2c_aux_init(struct gm200_i2c_aux *aux)
                        gm200_i2c_aux_fini(aux);
                        return -EBUSY;
                }
-       } while ((ctrl & 0x03000000) != urep);
+       } while ((ctrl & 0x07000000) != urep);
 
        return 0;
 }
@@ -77,7 +77,8 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                   u8 type, u32 addr, u8 *data, u8 *size)
 {
        struct gm200_i2c_aux *aux = gm200_i2c_aux(obj);
-       struct nvkm_device *device = aux->base.pad->i2c->subdev.device;
+       struct nvkm_i2c *i2c = aux->base.pad->i2c;
+       struct nvkm_device *device = i2c->subdev.device;
        const u32 base = aux->ch * 0x50;
        u32 ctrl, stat, timeout, retries = 0;
        u32 xbuf[4] = {};
@@ -96,6 +97,8 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                goto out;
        }
 
+       nvkm_i2c_aux_autodpcd(i2c, aux->ch, false);
+
        if (!(type & 1)) {
                memcpy(xbuf, data, *size);
                for (i = 0; i < 16; i += 4) {
@@ -128,7 +131,7 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                        if (!timeout--) {
                                AUX_ERR(&aux->base, "timeout %08x", ctrl);
                                ret = -EIO;
-                               goto out;
+                               goto out_err;
                        }
                } while (ctrl & 0x00010000);
                ret = 0;
@@ -155,6 +158,8 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry,
                *size = stat & 0x0000001f;
        }
 
+out_err:
+       nvkm_i2c_aux_autodpcd(i2c, aux->ch, true);
 out:
        gm200_i2c_aux_fini(aux);
        return ret < 0 ? ret : (stat & 0x000f0000) >> 16;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/gk110.c
new file mode 100644 (file)
index 0000000..8e3bfa1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+#include "pad.h"
+
+static void
+gk110_aux_autodpcd(struct nvkm_i2c *i2c, int aux, bool enable)
+{
+       nvkm_mask(i2c->subdev.device, 0x00e4f8 + (aux * 0x50), 0x00010000, enable << 16);
+}
+
+static const struct nvkm_i2c_func
+gk110_i2c = {
+       .pad_x_new = gf119_i2c_pad_x_new,
+       .pad_s_new = gf119_i2c_pad_s_new,
+       .aux = 4,
+       .aux_stat = gk104_aux_stat,
+       .aux_mask = gk104_aux_mask,
+       .aux_autodpcd = gk110_aux_autodpcd,
+};
+
+int
+gk110_i2c_new(struct nvkm_device *device, int index, struct nvkm_i2c **pi2c)
+{
+       return nvkm_i2c_new_(&gk110_i2c, device, index, pi2c);
+}
index a23c5f3..7b2375b 100644 (file)
 #include "priv.h"
 #include "pad.h"
 
+static void
+gm200_aux_autodpcd(struct nvkm_i2c *i2c, int aux, bool enable)
+{
+       nvkm_mask(i2c->subdev.device, 0x00d968 + (aux * 0x50), 0x00010000, enable << 16);
+}
+
 static const struct nvkm_i2c_func
 gm200_i2c = {
        .pad_x_new = gf119_i2c_pad_x_new,
@@ -31,6 +37,7 @@ gm200_i2c = {
        .aux = 8,
        .aux_stat = gk104_aux_stat,
        .aux_mask = gk104_aux_mask,
+       .aux_autodpcd = gm200_aux_autodpcd,
 };
 
 int
index 4610168..44b7bb7 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: MIT */
 #ifndef __NVKM_I2C_PAD_H__
 #define __NVKM_I2C_PAD_H__
-#include <subdev/i2c.h>
+#include "priv.h"
 
 struct nvkm_i2c_pad {
        const struct nvkm_i2c_pad_func *func;
index bd86bc2..e35f603 100644 (file)
@@ -23,6 +23,10 @@ struct nvkm_i2c_func {
        /* mask on/off interrupt types for a given set of auxch
         */
        void (*aux_mask)(struct nvkm_i2c *, u32, u32, u32);
+
+       /* enable/disable HW-initiated DPCD reads
+        */
+       void (*aux_autodpcd)(struct nvkm_i2c *, int aux, bool enable);
 };
 
 void g94_aux_stat(struct nvkm_i2c *, u32 *, u32 *, u32 *, u32 *);
index 2340040..1115376 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 #include "priv.h"
+#include <subdev/timer.h>
 
 static void
 gf100_ibus_intr_hub(struct nvkm_subdev *ibus, int i)
@@ -31,7 +32,6 @@ gf100_ibus_intr_hub(struct nvkm_subdev *ibus, int i)
        u32 data = nvkm_rd32(device, 0x122124 + (i * 0x0400));
        u32 stat = nvkm_rd32(device, 0x122128 + (i * 0x0400));
        nvkm_debug(ibus, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat);
-       nvkm_mask(device, 0x122128 + (i * 0x0400), 0x00000200, 0x00000000);
 }
 
 static void
@@ -42,7 +42,6 @@ gf100_ibus_intr_rop(struct nvkm_subdev *ibus, int i)
        u32 data = nvkm_rd32(device, 0x124124 + (i * 0x0400));
        u32 stat = nvkm_rd32(device, 0x124128 + (i * 0x0400));
        nvkm_debug(ibus, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat);
-       nvkm_mask(device, 0x124128 + (i * 0x0400), 0x00000200, 0x00000000);
 }
 
 static void
@@ -53,7 +52,6 @@ gf100_ibus_intr_gpc(struct nvkm_subdev *ibus, int i)
        u32 data = nvkm_rd32(device, 0x128124 + (i * 0x0400));
        u32 stat = nvkm_rd32(device, 0x128128 + (i * 0x0400));
        nvkm_debug(ibus, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat);
-       nvkm_mask(device, 0x128128 + (i * 0x0400), 0x00000200, 0x00000000);
 }
 
 void
@@ -90,6 +88,12 @@ gf100_ibus_intr(struct nvkm_subdev *ibus)
                        intr1 &= ~stat;
                }
        }
+
+       nvkm_mask(device, 0x121c4c, 0x0000003f, 0x00000002);
+       nvkm_msec(device, 2000,
+               if (!(nvkm_rd32(device, 0x121c4c) & 0x0000003f))
+                       break;
+       );
 }
 
 static int
index f3915f8..22e487b 100644 (file)
@@ -22,6 +22,7 @@
  * Authors: Ben Skeggs
  */
 #include "priv.h"
+#include <subdev/timer.h>
 
 static void
 gk104_ibus_intr_hub(struct nvkm_subdev *ibus, int i)
@@ -31,7 +32,6 @@ gk104_ibus_intr_hub(struct nvkm_subdev *ibus, int i)
        u32 data = nvkm_rd32(device, 0x122124 + (i * 0x0800));
        u32 stat = nvkm_rd32(device, 0x122128 + (i * 0x0800));
        nvkm_debug(ibus, "HUB%d: %06x %08x (%08x)\n", i, addr, data, stat);
-       nvkm_mask(device, 0x122128 + (i * 0x0800), 0x00000200, 0x00000000);
 }
 
 static void
@@ -42,7 +42,6 @@ gk104_ibus_intr_rop(struct nvkm_subdev *ibus, int i)
        u32 data = nvkm_rd32(device, 0x124124 + (i * 0x0800));
        u32 stat = nvkm_rd32(device, 0x124128 + (i * 0x0800));
        nvkm_debug(ibus, "ROP%d: %06x %08x (%08x)\n", i, addr, data, stat);
-       nvkm_mask(device, 0x124128 + (i * 0x0800), 0x00000200, 0x00000000);
 }
 
 static void
@@ -53,7 +52,6 @@ gk104_ibus_intr_gpc(struct nvkm_subdev *ibus, int i)
        u32 data = nvkm_rd32(device, 0x128124 + (i * 0x0800));
        u32 stat = nvkm_rd32(device, 0x128128 + (i * 0x0800));
        nvkm_debug(ibus, "GPC%d: %06x %08x (%08x)\n", i, addr, data, stat);
-       nvkm_mask(device, 0x128128 + (i * 0x0800), 0x00000200, 0x00000000);
 }
 
 void
@@ -90,6 +88,12 @@ gk104_ibus_intr(struct nvkm_subdev *ibus)
                        intr1 &= ~stat;
                }
        }
+
+       nvkm_mask(device, 0x12004c, 0x0000003f, 0x00000002);
+       nvkm_msec(device, 2000,
+               if (!(nvkm_rd32(device, 0x12004c) & 0x0000003f))
+                       break;
+       );
 }
 
 static int
index 2585ef0..ac2b34e 100644 (file)
@@ -14,3 +14,4 @@ nvkm-y += nvkm/subdev/mc/gk20a.o
 nvkm-y += nvkm/subdev/mc/gp100.o
 nvkm-y += nvkm/subdev/mc/gp10b.o
 nvkm-y += nvkm/subdev/mc/tu102.o
+nvkm-y += nvkm/subdev/mc/ga100.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mc/ga100.c
new file mode 100644 (file)
index 0000000..967eb3a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "priv.h"
+
+static void
+ga100_mc_intr_unarm(struct nvkm_mc *mc)
+{
+       nvkm_wr32(mc->subdev.device, 0xb81610, 0x00000004);
+}
+
+static void
+ga100_mc_intr_rearm(struct nvkm_mc *mc)
+{
+       nvkm_wr32(mc->subdev.device, 0xb81608, 0x00000004);
+}
+
+static void
+ga100_mc_intr_mask(struct nvkm_mc *mc, u32 mask, u32 intr)
+{
+       nvkm_wr32(mc->subdev.device, 0xb81210,          mask & intr );
+       nvkm_wr32(mc->subdev.device, 0xb81410, mask & ~(mask & intr));
+}
+
+static u32
+ga100_mc_intr_stat(struct nvkm_mc *mc)
+{
+       u32 intr_top = nvkm_rd32(mc->subdev.device, 0xb81600), intr = 0x00000000;
+       if (intr_top & 0x00000004)
+               intr = nvkm_mask(mc->subdev.device, 0xb81010, 0x00000000, 0x00000000);
+       return intr;
+}
+
+static void
+ga100_mc_init(struct nvkm_mc *mc)
+{
+       nv50_mc_init(mc);
+       nvkm_wr32(mc->subdev.device, 0xb81210, 0xffffffff);
+}
+
+static const struct nvkm_mc_func
+ga100_mc = {
+       .init = ga100_mc_init,
+       .intr = gp100_mc_intr,
+       .intr_unarm = ga100_mc_intr_unarm,
+       .intr_rearm = ga100_mc_intr_rearm,
+       .intr_mask = ga100_mc_intr_mask,
+       .intr_stat = ga100_mc_intr_stat,
+       .reset = gk104_mc_reset,
+};
+
+int
+ga100_mc_new(struct nvkm_device *device, int index, struct nvkm_mc **pmc)
+{
+       return nvkm_mc_new_(&ga100_mc, device, index, pmc);
+}
index de91e9a..6d5212a 100644 (file)
@@ -316,9 +316,9 @@ nvkm_mmu_vram(struct nvkm_mmu *mmu)
 {
        struct nvkm_device *device = mmu->subdev.device;
        struct nvkm_mm *mm = &device->fb->ram->vram;
-       const u32 sizeN = nvkm_mm_heap_size(mm, NVKM_RAM_MM_NORMAL);
-       const u32 sizeU = nvkm_mm_heap_size(mm, NVKM_RAM_MM_NOMAP);
-       const u32 sizeM = nvkm_mm_heap_size(mm, NVKM_RAM_MM_MIXED);
+       const u64 sizeN = nvkm_mm_heap_size(mm, NVKM_RAM_MM_NORMAL);
+       const u64 sizeU = nvkm_mm_heap_size(mm, NVKM_RAM_MM_NOMAP);
+       const u64 sizeM = nvkm_mm_heap_size(mm, NVKM_RAM_MM_MIXED);
        u8 type = NVKM_MEM_KIND * !!mmu->func->kind;
        u8 heap = NVKM_MEM_VRAM;
        int heapM, heapN, heapU;
index d59ef6e..23195d5 100644 (file)
@@ -730,9 +730,6 @@ int radeon_ttm_init(struct radeon_device *rdev)
        }
        rdev->mman.initialized = true;
 
-       ttm_pool_init(&rdev->mman.bdev.pool, rdev->dev, rdev->need_swiotlb,
-                     dma_addressing_limited(&rdev->pdev->dev));
-
        r = radeon_ttm_init_vram(rdev);
        if (r) {
                DRM_ERROR("Failed initializing VRAM heap.\n");
index 7b2f606..11e0313 100644 (file)
@@ -66,7 +66,7 @@ static struct ttm_pool_type global_uncached[MAX_ORDER];
 static struct ttm_pool_type global_dma32_write_combined[MAX_ORDER];
 static struct ttm_pool_type global_dma32_uncached[MAX_ORDER];
 
-static spinlock_t shrinker_lock;
+static struct mutex shrinker_lock;
 static struct list_head shrinker_list;
 static struct shrinker mm_shrinker;
 
@@ -79,12 +79,13 @@ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags,
        struct page *p;
        void *vaddr;
 
-       if (order) {
-               gfp_flags |= GFP_TRANSHUGE_LIGHT | __GFP_NORETRY |
+       /* Don't set the __GFP_COMP flag for higher order allocations.
+        * Mapping pages directly into an userspace process and calling
+        * put_page() on a TTM allocated page is illegal.
+        */
+       if (order)
+               gfp_flags |= __GFP_NOMEMALLOC | __GFP_NORETRY |
                        __GFP_KSWAPD_RECLAIM;
-               gfp_flags &= ~__GFP_MOVABLE;
-               gfp_flags &= ~__GFP_COMP;
-       }
 
        if (!pool->use_dma_alloc) {
                p = alloc_pages(gfp_flags, order);
@@ -190,7 +191,7 @@ static int ttm_pool_map(struct ttm_pool *pool, unsigned int order,
                size_t size = (1ULL << order) * PAGE_SIZE;
 
                addr = dma_map_page(pool->dev, p, 0, size, DMA_BIDIRECTIONAL);
-               if (dma_mapping_error(pool->dev, **dma_addr))
+               if (dma_mapping_error(pool->dev, addr))
                        return -EFAULT;
        }
 
@@ -249,9 +250,9 @@ static void ttm_pool_type_init(struct ttm_pool_type *pt, struct ttm_pool *pool,
        spin_lock_init(&pt->lock);
        INIT_LIST_HEAD(&pt->pages);
 
-       spin_lock(&shrinker_lock);
+       mutex_lock(&shrinker_lock);
        list_add_tail(&pt->shrinker_list, &shrinker_list);
-       spin_unlock(&shrinker_lock);
+       mutex_unlock(&shrinker_lock);
 }
 
 /* Remove a pool_type from the global shrinker list and free all pages */
@@ -259,9 +260,9 @@ static void ttm_pool_type_fini(struct ttm_pool_type *pt)
 {
        struct page *p, *tmp;
 
-       spin_lock(&shrinker_lock);
+       mutex_lock(&shrinker_lock);
        list_del(&pt->shrinker_list);
-       spin_unlock(&shrinker_lock);
+       mutex_unlock(&shrinker_lock);
 
        list_for_each_entry_safe(p, tmp, &pt->pages, lru)
                ttm_pool_free_page(pt->pool, pt->caching, pt->order, p);
@@ -302,7 +303,7 @@ static unsigned int ttm_pool_shrink(void)
        unsigned int num_freed;
        struct page *p;
 
-       spin_lock(&shrinker_lock);
+       mutex_lock(&shrinker_lock);
        pt = list_first_entry(&shrinker_list, typeof(*pt), shrinker_list);
 
        p = ttm_pool_type_take(pt);
@@ -314,7 +315,7 @@ static unsigned int ttm_pool_shrink(void)
        }
 
        list_move_tail(&pt->shrinker_list, &shrinker_list);
-       spin_unlock(&shrinker_lock);
+       mutex_unlock(&shrinker_lock);
 
        return num_freed;
 }
@@ -507,7 +508,6 @@ void ttm_pool_init(struct ttm_pool *pool, struct device *dev,
                        ttm_pool_type_init(&pool->caching[i].orders[j],
                                           pool, i, j);
 }
-EXPORT_SYMBOL(ttm_pool_init);
 
 /**
  * ttm_pool_fini - Cleanup a pool
@@ -525,7 +525,6 @@ void ttm_pool_fini(struct ttm_pool *pool)
                for (j = 0; j < MAX_ORDER; ++j)
                        ttm_pool_type_fini(&pool->caching[i].orders[j]);
 }
-EXPORT_SYMBOL(ttm_pool_fini);
 
 #ifdef CONFIG_DEBUG_FS
 /* Count the number of pages available in a pool_type */
@@ -566,7 +565,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)
 {
        unsigned int i;
 
-       spin_lock(&shrinker_lock);
+       mutex_lock(&shrinker_lock);
 
        seq_puts(m, "\t ");
        for (i = 0; i < MAX_ORDER; ++i)
@@ -602,7 +601,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)
        seq_printf(m, "\ntotal\t: %8lu of %8lu\n",
                   atomic_long_read(&allocated_pages), page_pool_size);
 
-       spin_unlock(&shrinker_lock);
+       mutex_unlock(&shrinker_lock);
 
        return 0;
 }
@@ -646,7 +645,7 @@ int ttm_pool_mgr_init(unsigned long num_pages)
        if (!page_pool_size)
                page_pool_size = num_pages;
 
-       spin_lock_init(&shrinker_lock);
+       mutex_init(&shrinker_lock);
        INIT_LIST_HEAD(&shrinker_list);
 
        for (i = 0; i < MAX_ORDER; ++i) {
index 5551062..98cab0b 100644 (file)
@@ -1267,6 +1267,7 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
        card->dai_link = dai_link;
        card->num_links = 1;
        card->name = vc4_hdmi->variant->card_name;
+       card->driver_name = "vc4-hdmi";
        card->dev = dev;
        card->owner = THIS_MODULE;
 
index 7bdda1b..09fa75a 100644 (file)
@@ -899,6 +899,7 @@ config HID_SONY
        depends on NEW_LEDS
        depends on LEDS_CLASS
        select POWER_SUPPLY
+       select CRC32
        help
        Support for
 
index 3d1ccac..2ab38b7 100644 (file)
@@ -154,7 +154,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
 
        for (i = 0; i < cl_data->num_hid_devices; i++) {
                cl_data->sensor_virt_addr[i] = dma_alloc_coherent(dev, sizeof(int) * 8,
-                                                                 &cl_data->sensor_phys_addr[i],
+                                                                 &cl_data->sensor_dma_addr[i],
                                                                  GFP_KERNEL);
                cl_data->sensor_sts[i] = 0;
                cl_data->sensor_requested_cnt[i] = 0;
@@ -187,7 +187,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
                }
                info.period = msecs_to_jiffies(AMD_SFH_IDLE_LOOP);
                info.sensor_idx = cl_idx;
-               info.phys_address = cl_data->sensor_phys_addr[i];
+               info.dma_address = cl_data->sensor_dma_addr[i];
 
                cl_data->report_descr[i] = kzalloc(cl_data->report_descr_sz[i], GFP_KERNEL);
                if (!cl_data->report_descr[i]) {
@@ -212,7 +212,7 @@ cleanup:
                if (cl_data->sensor_virt_addr[i]) {
                        dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int),
                                          cl_data->sensor_virt_addr[i],
-                                         cl_data->sensor_phys_addr[i]);
+                                         cl_data->sensor_dma_addr[i]);
                }
                kfree(cl_data->feature_report[i]);
                kfree(cl_data->input_report[i]);
@@ -238,7 +238,7 @@ int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata)
                if (cl_data->sensor_virt_addr[i]) {
                        dma_free_coherent(&privdata->pdev->dev, 8 * sizeof(int),
                                          cl_data->sensor_virt_addr[i],
-                                         cl_data->sensor_phys_addr[i]);
+                                         cl_data->sensor_dma_addr[i]);
                }
        }
        kfree(cl_data);
index 6be0783..d7eac17 100644 (file)
@@ -27,7 +27,7 @@ struct amdtp_cl_data {
        int hid_descr_size[MAX_HID_DEVICES];
        phys_addr_t phys_addr_base;
        u32 *sensor_virt_addr[MAX_HID_DEVICES];
-       phys_addr_t sensor_phys_addr[MAX_HID_DEVICES];
+       dma_addr_t sensor_dma_addr[MAX_HID_DEVICES];
        u32 sensor_sts[MAX_HID_DEVICES];
        u32 sensor_requested_cnt[MAX_HID_DEVICES];
        u8 report_type[MAX_HID_DEVICES];
index a51c7b7..dbac166 100644 (file)
@@ -41,7 +41,7 @@ void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info i
        cmd_param.s.buf_layout = 1;
        cmd_param.s.buf_length = 16;
 
-       writeq(info.phys_address, privdata->mmio + AMD_C2P_MSG2);
+       writeq(info.dma_address, privdata->mmio + AMD_C2P_MSG2);
        writel(cmd_param.ul, privdata->mmio + AMD_C2P_MSG1);
        writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0);
 }
index e8be94f..8f8d19b 100644 (file)
@@ -67,7 +67,7 @@ struct amd_mp2_dev {
 struct amd_mp2_sensor_info {
        u8 sensor_idx;
        u32 period;
-       phys_addr_t phys_address;
+       dma_addr_t dma_address;
 };
 
 void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info);
index 4c5f236..5ba0aa1 100644 (file)
 #define USB_DEVICE_ID_TOSHIBA_CLICK_L9W        0x0401
 #define USB_DEVICE_ID_HP_X2            0x074d
 #define USB_DEVICE_ID_HP_X2_10_COVER   0x0755
+#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN   0x2706
 
 #define USB_VENDOR_ID_ELECOM           0x056e
 #define USB_DEVICE_ID_ELECOM_BM084     0x0061
index dc7f6b4..f23027d 100644 (file)
@@ -322,6 +322,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD),
          HID_BATTERY_QUIRK_IGNORE },
+       { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
+         HID_BATTERY_QUIRK_IGNORE },
        {}
 };
 
index 1ffcfc9..45e7e0b 100644 (file)
@@ -1869,6 +1869,10 @@ static const struct hid_device_id logi_dj_receivers[] = {
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
                0xc531),
         .driver_data = recvr_type_gaming_hidpp},
+       { /* Logitech G602 receiver (0xc537) */
+         HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+               0xc537),
+        .driver_data = recvr_type_gaming_hidpp},
        { /* Logitech lightspeed receiver (0xc539) */
          HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
                USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1),
index f857814..7eb9a6d 100644 (file)
@@ -4053,6 +4053,8 @@ static const struct hid_device_id hidpp_devices[] = {
        { /* MX Master mouse over Bluetooth */
          HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb012),
          .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
+       { /* MX Ergo trackball over Bluetooth */
+         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01d) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb01e),
          .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 },
        { /* MX Master 3 mouse over Bluetooth */
index d670bcd..8429ebe 100644 (file)
@@ -758,7 +758,8 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        MT_STORE_FIELD(inrange_state);
                        return 1;
                case HID_DG_CONFIDENCE:
-                       if (cls->name == MT_CLS_WIN_8 &&
+                       if ((cls->name == MT_CLS_WIN_8 ||
+                            cls->name == MT_CLS_WIN_8_FORCE_MULTI_INPUT) &&
                                (field->application == HID_DG_TOUCHPAD ||
                                 field->application == HID_DG_TOUCHSCREEN))
                                app->quirks |= MT_QUIRK_CONFIDENCE;
@@ -2054,6 +2055,10 @@ static const struct hid_device_id mt_devices[] = {
                HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
                        USB_VENDOR_ID_SYNAPTICS, 0xce08) },
 
+       { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT,
+               HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8,
+                       USB_VENDOR_ID_SYNAPTICS, 0xce09) },
+
        /* TopSeed panels */
        { .driver_data = MT_CLS_TOPSEED,
                MT_USB_DEVICE(USB_VENDOR_ID_TOPSEED2,
index d26d8cd..56406ce 100644 (file)
@@ -90,7 +90,7 @@ static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
                goto cleanup;
        } else if (rc < 0) {
                hid_err(hdev,
-                       "failed retrieving string descriptor #%hhu: %d\n",
+                       "failed retrieving string descriptor #%u: %d\n",
                        idx, rc);
                goto cleanup;
        }
index 4101268..4399d6c 100644 (file)
@@ -1482,7 +1482,7 @@ static void handler_return(struct wiimote_data *wdata, const __u8 *payload)
                wdata->state.cmd_err = err;
                wiimote_cmd_complete(wdata);
        } else if (err) {
-               hid_warn(wdata->hdev, "Remote error %hhu on req %hhu\n", err,
+               hid_warn(wdata->hdev, "Remote error %u on req %u\n", err,
                                                                        cmd);
        }
 }
index 045c464..aa9e488 100644 (file)
@@ -147,9 +147,9 @@ static int wacom_wac_pen_serial_enforce(struct hid_device *hdev,
        }
 
        if (flush)
-               wacom_wac_queue_flush(hdev, &wacom_wac->pen_fifo);
+               wacom_wac_queue_flush(hdev, wacom_wac->pen_fifo);
        else if (insert)
-               wacom_wac_queue_insert(hdev, &wacom_wac->pen_fifo,
+               wacom_wac_queue_insert(hdev, wacom_wac->pen_fifo,
                                       raw_data, report_size);
 
        return insert && !flush;
@@ -1270,6 +1270,38 @@ static int wacom_devm_sysfs_create_group(struct wacom *wacom,
                                               group);
 }
 
+static void wacom_devm_kfifo_release(struct device *dev, void *res)
+{
+       struct kfifo_rec_ptr_2 *devres = res;
+
+       kfifo_free(devres);
+}
+
+static int wacom_devm_kfifo_alloc(struct wacom *wacom)
+{
+       struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+       struct kfifo_rec_ptr_2 *pen_fifo;
+       int error;
+
+       pen_fifo = devres_alloc(wacom_devm_kfifo_release,
+                             sizeof(struct kfifo_rec_ptr_2),
+                             GFP_KERNEL);
+
+       if (!pen_fifo)
+               return -ENOMEM;
+
+       error = kfifo_alloc(pen_fifo, WACOM_PKGLEN_MAX, GFP_KERNEL);
+       if (error) {
+               devres_free(pen_fifo);
+               return error;
+       }
+
+       devres_add(&wacom->hdev->dev, pen_fifo);
+       wacom_wac->pen_fifo = pen_fifo;
+
+       return 0;
+}
+
 enum led_brightness wacom_leds_brightness_get(struct wacom_led *led)
 {
        struct wacom *wacom = led->wacom;
@@ -2724,7 +2756,7 @@ static int wacom_probe(struct hid_device *hdev,
        if (features->check_for_hid_type && features->hid_type != hdev->type)
                return -ENODEV;
 
-       error = kfifo_alloc(&wacom_wac->pen_fifo, WACOM_PKGLEN_MAX, GFP_KERNEL);
+       error = wacom_devm_kfifo_alloc(wacom);
        if (error)
                return error;
 
@@ -2786,8 +2818,6 @@ static void wacom_remove(struct hid_device *hdev)
 
        if (wacom->wacom_wac.features.type != REMOTE)
                wacom_release_resources(wacom);
-
-       kfifo_free(&wacom_wac->pen_fifo);
 }
 
 #ifdef CONFIG_PM
index da612b6..195910d 100644 (file)
@@ -342,7 +342,7 @@ struct wacom_wac {
        struct input_dev *pen_input;
        struct input_dev *touch_input;
        struct input_dev *pad_input;
-       struct kfifo_rec_ptr_2 pen_fifo;
+       struct kfifo_rec_ptr_2 *pen_fifo;
        int pid;
        int num_contacts_left;
        u8 bt_features;
index 502f8cd..d491fdc 100644 (file)
@@ -2550,7 +2550,6 @@ static void hv_kexec_handler(void)
        /* Make sure conn_state is set as hv_synic_cleanup checks for it */
        mb();
        cpuhp_remove_state(hyperv_cpuhp_online);
-       hyperv_cleanup();
 };
 
 static void hv_crash_handler(struct pt_regs *regs)
@@ -2566,7 +2565,6 @@ static void hv_crash_handler(struct pt_regs *regs)
        cpu = smp_processor_id();
        hv_stimer_cleanup(cpu);
        hv_synic_disable_regs(cpu);
-       hyperv_cleanup();
 };
 
 static int hv_synic_suspend(void)
index 9b30644..822c2e7 100644 (file)
@@ -222,7 +222,7 @@ static int amd_create_sensor(struct device *dev,
         */
        cpus = num_present_cpus() / num_siblings;
 
-       s_config = devm_kcalloc(dev, cpus + sockets,
+       s_config = devm_kcalloc(dev, cpus + sockets + 1,
                                sizeof(u32), GFP_KERNEL);
        if (!s_config)
                return -ENOMEM;
@@ -254,6 +254,7 @@ static int amd_create_sensor(struct device *dev,
                        scnprintf(label_l[i], 10, "Esocket%u", (i - cpus));
        }
 
+       s_config[i] = 0;
        return 0;
 }
 
index 777439f..111a91d 100644 (file)
@@ -334,8 +334,18 @@ static int pwm_fan_probe(struct platform_device *pdev)
 
        ctx->pwm_value = MAX_PWM;
 
-       /* Set duty cycle to maximum allowed and enable PWM output */
        pwm_init_state(ctx->pwm, &state);
+       /*
+        * __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
+        * long. Check this here to prevent the fan running at a too low
+        * frequency.
+        */
+       if (state.period > ULONG_MAX / MAX_PWM + 1) {
+               dev_err(dev, "Configured period too big\n");
+               return -EINVAL;
+       }
+
+       /* Set duty cycle to maximum allowed and enable PWM output */
        state.duty_cycle = ctx->pwm->args.period - 1;
        state.enabled = true;
 
index 52acd77..251e75c 100644 (file)
@@ -268,6 +268,11 @@ static const struct pci_device_id intel_th_pci_id_table[] = {
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x7aa6),
                .driver_data = (kernel_ulong_t)&intel_th_2x,
        },
+       {
+               /* Alder Lake-P */
+               PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x51a6),
+               .driver_data = (kernel_ulong_t)&intel_th_2x,
+       },
        {
                /* Alder Lake CPU */
                PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x466f),
index 3e7df1c..81d7b21 100644 (file)
@@ -64,7 +64,7 @@ static void stm_heartbeat_unlink(struct stm_source_data *data)
 
 static int stm_heartbeat_init(void)
 {
-       int i, ret = -ENOMEM;
+       int i, ret;
 
        if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX)
                return -EINVAL;
@@ -72,8 +72,10 @@ static int stm_heartbeat_init(void)
        for (i = 0; i < nr_devs; i++) {
                stm_heartbeat[i].data.name =
                        kasprintf(GFP_KERNEL, "heartbeat.%d", i);
-               if (!stm_heartbeat[i].data.name)
+               if (!stm_heartbeat[i].data.name) {
+                       ret = -ENOMEM;
                        goto fail_unregister;
+               }
 
                stm_heartbeat[i].data.nr_chans  = 1;
                stm_heartbeat[i].data.link      = stm_heartbeat_link;
index d4d60ad..ab1f39a 100644 (file)
@@ -1013,6 +1013,7 @@ config I2C_SIRF
 config I2C_SPRD
        tristate "Spreadtrum I2C interface"
        depends on I2C=y && (ARCH_SPRD || COMPILE_TEST)
+       depends on COMMON_CLK
        help
          If you say yes to this option, support will be included for the
          Spreadtrum I2C interface.
index ae90713..877fe37 100644 (file)
@@ -1449,7 +1449,7 @@ static int i801_add_mux(struct i801_priv *priv)
 
        /* Register GPIO descriptor lookup table */
        lookup = devm_kzalloc(dev,
-                             struct_size(lookup, table, mux_config->n_gpios),
+                             struct_size(lookup, table, mux_config->n_gpios + 1),
                              GFP_KERNEL);
        if (!lookup)
                return -ENOMEM;
index b444fbf..a8e8af5 100644 (file)
@@ -241,6 +241,19 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = {
 
 };
 
+static const struct platform_device_id imx_i2c_devtype[] = {
+       {
+               .name = "imx1-i2c",
+               .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
+       }, {
+               .name = "imx21-i2c",
+               .driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);
+
 static const struct of_device_id i2c_imx_dt_ids[] = {
        { .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
        { .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
@@ -1330,7 +1343,11 @@ static int i2c_imx_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        match = device_get_match_data(&pdev->dev);
-       i2c_imx->hwdata = match;
+       if (match)
+               i2c_imx->hwdata = match;
+       else
+               i2c_imx->hwdata = (struct imx_i2c_hwdata *)
+                               platform_get_device_id(pdev)->driver_data;
 
        /* Setup i2c_imx driver structure */
        strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));
@@ -1498,6 +1515,7 @@ static struct platform_driver i2c_imx_driver = {
                .of_match_table = i2c_imx_dt_ids,
                .acpi_match_table = i2c_imx_acpi_ids,
        },
+       .id_table = imx_i2c_devtype,
 };
 
 static int __init i2c_adap_imx_init(void)
index 33de99b..0818d3e 100644 (file)
@@ -38,6 +38,7 @@
 #define I2C_IO_CONFIG_OPEN_DRAIN       0x0003
 #define I2C_IO_CONFIG_PUSH_PULL                0x0000
 #define I2C_SOFT_RST                   0x0001
+#define I2C_HANDSHAKE_RST              0x0020
 #define I2C_FIFO_ADDR_CLR              0x0001
 #define I2C_DELAY_LEN                  0x0002
 #define I2C_TIME_CLR_VALUE             0x0000
@@ -45,6 +46,7 @@
 #define I2C_WRRD_TRANAC_VALUE          0x0002
 #define I2C_RD_TRANAC_VALUE            0x0001
 #define I2C_SCL_MIS_COMP_VALUE         0x0000
+#define I2C_CHN_CLR_FLAG               0x0000
 
 #define I2C_DMA_CON_TX                 0x0000
 #define I2C_DMA_CON_RX                 0x0001
@@ -54,7 +56,9 @@
 #define I2C_DMA_START_EN               0x0001
 #define I2C_DMA_INT_FLAG_NONE          0x0000
 #define I2C_DMA_CLR_FLAG               0x0000
+#define I2C_DMA_WARM_RST               0x0001
 #define I2C_DMA_HARD_RST               0x0002
+#define I2C_DMA_HANDSHAKE_RST          0x0004
 
 #define MAX_SAMPLE_CNT_DIV             8
 #define MAX_STEP_CNT_DIV               64
@@ -475,11 +479,24 @@ static void mtk_i2c_init_hw(struct mtk_i2c *i2c)
 {
        u16 control_reg;
 
-       writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
-       udelay(50);
-       writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
-
-       mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET);
+       if (i2c->dev_comp->dma_sync) {
+               writel(I2C_DMA_WARM_RST, i2c->pdmabase + OFFSET_RST);
+               udelay(10);
+               writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+               udelay(10);
+               writel(I2C_DMA_HANDSHAKE_RST | I2C_DMA_HARD_RST,
+                      i2c->pdmabase + OFFSET_RST);
+               mtk_i2c_writew(i2c, I2C_HANDSHAKE_RST | I2C_SOFT_RST,
+                              OFFSET_SOFTRESET);
+               udelay(10);
+               writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+               mtk_i2c_writew(i2c, I2C_CHN_CLR_FLAG, OFFSET_SOFTRESET);
+       } else {
+               writel(I2C_DMA_HARD_RST, i2c->pdmabase + OFFSET_RST);
+               udelay(50);
+               writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_RST);
+               mtk_i2c_writew(i2c, I2C_SOFT_RST, OFFSET_SOFTRESET);
+       }
 
        /* Set ioconfig */
        if (i2c->use_push_pull)
index d960790..845eda7 100644 (file)
@@ -347,7 +347,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
                if (result)
                        return result;
                if (recv_len && i == 0) {
-                       if (data[i] > I2C_SMBUS_BLOCK_MAX + 1)
+                       if (data[i] > I2C_SMBUS_BLOCK_MAX)
                                return -EPROTO;
                        length += data[i];
                }
index 19cda67..2917fec 100644 (file)
@@ -72,6 +72,8 @@
 
 /* timeout (ms) for pm runtime autosuspend */
 #define SPRD_I2C_PM_TIMEOUT    1000
+/* timeout (ms) for transfer message */
+#define I2C_XFER_TIMEOUT       1000
 
 /* SPRD i2c data structure */
 struct sprd_i2c {
@@ -244,6 +246,7 @@ static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap,
                               struct i2c_msg *msg, bool is_last_msg)
 {
        struct sprd_i2c *i2c_dev = i2c_adap->algo_data;
+       unsigned long time_left;
 
        i2c_dev->msg = msg;
        i2c_dev->buf = msg->buf;
@@ -273,7 +276,10 @@ static int sprd_i2c_handle_msg(struct i2c_adapter *i2c_adap,
 
        sprd_i2c_opt_start(i2c_dev);
 
-       wait_for_completion(&i2c_dev->complete);
+       time_left = wait_for_completion_timeout(&i2c_dev->complete,
+                               msecs_to_jiffies(I2C_XFER_TIMEOUT));
+       if (!time_left)
+               return -ETIMEDOUT;
 
        return i2c_dev->err;
 }
index ec7a7e9..c0c7d01 100644 (file)
@@ -80,7 +80,7 @@ static int tegra_bpmp_xlate_flags(u16 flags, u16 *out)
                flags &= ~I2C_M_RECV_LEN;
        }
 
-       return (flags != 0) ? -EINVAL : 0;
+       return 0;
 }
 
 /**
index 6f08c0c..8b113ae 100644 (file)
@@ -326,6 +326,8 @@ static void i2c_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned int reg)
        /* read back register to make sure that register writes completed */
        if (reg != I2C_TX_FIFO)
                readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg));
+       else if (i2c_dev->is_vi)
+               readl_relaxed(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, I2C_INT_STATUS));
 }
 
 static u32 i2c_readl(struct tegra_i2c_dev *i2c_dev, unsigned int reg)
@@ -339,6 +341,21 @@ static void i2c_writesl(struct tegra_i2c_dev *i2c_dev, void *data,
        writesl(i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg), data, len);
 }
 
+static void i2c_writesl_vi(struct tegra_i2c_dev *i2c_dev, void *data,
+                          unsigned int reg, unsigned int len)
+{
+       u32 *data32 = data;
+
+       /*
+        * VI I2C controller has known hardware bug where writes get stuck
+        * when immediate multiple writes happen to TX_FIFO register.
+        * Recommended software work around is to read I2C register after
+        * each write to TX_FIFO register to flush out the data.
+        */
+       while (len--)
+               i2c_writel(i2c_dev, *data32++, reg);
+}
+
 static void i2c_readsl(struct tegra_i2c_dev *i2c_dev, void *data,
                       unsigned int reg, unsigned int len)
 {
@@ -533,7 +550,7 @@ static int tegra_i2c_poll_register(struct tegra_i2c_dev *i2c_dev,
        void __iomem *addr = i2c_dev->base + tegra_i2c_reg_addr(i2c_dev, reg);
        u32 val;
 
-       if (!i2c_dev->atomic_mode)
+       if (!i2c_dev->atomic_mode && !in_irq())
                return readl_relaxed_poll_timeout(addr, val, !(val & mask),
                                                  delay_us, timeout_us);
 
@@ -811,7 +828,10 @@ static int tegra_i2c_fill_tx_fifo(struct tegra_i2c_dev *i2c_dev)
                i2c_dev->msg_buf_remaining = buf_remaining;
                i2c_dev->msg_buf = buf + words_to_transfer * BYTES_PER_FIFO_WORD;
 
-               i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+               if (i2c_dev->is_vi)
+                       i2c_writesl_vi(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
+               else
+                       i2c_writesl(i2c_dev, buf, I2C_TX_FIFO, words_to_transfer);
 
                buf += words_to_transfer * BYTES_PER_FIFO_WORD;
        }
index 2162bc8..013ad33 100644 (file)
@@ -223,7 +223,6 @@ void ide_prep_sense(ide_drive_t *drive, struct request *rq)
        sense_rq->rq_disk = rq->rq_disk;
        sense_rq->cmd_flags = REQ_OP_DRV_IN;
        ide_req(sense_rq)->type = ATA_PRIV_SENSE;
-       sense_rq->rq_flags |= RQF_PREEMPT;
 
        req->cmd[0] = GPCMD_REQUEST_SENSE;
        req->cmd[4] = cmd_len;
index 1a53c7a..4867b67 100644 (file)
@@ -515,15 +515,10 @@ repeat:
                 * above to return us whatever is in the queue. Since we call
                 * ide_do_request() ourselves, we end up taking requests while
                 * the queue is blocked...
-                * 
-                * We let requests forced at head of queue with ide-preempt
-                * though. I hope that doesn't happen too much, hopefully not
-                * unless the subdriver triggers such a thing in its own PM
-                * state machine.
                 */
                if ((drive->dev_flags & IDE_DFLAG_BLOCKED) &&
                    ata_pm_request(rq) == 0 &&
-                   (rq->rq_flags & RQF_PREEMPT) == 0) {
+                   (rq->rq_flags & RQF_PM) == 0) {
                        /* there should be no pending command at this point */
                        ide_unlock_port(hwif);
                        goto plug_device;
index 192e6c6..82ab308 100644 (file)
@@ -77,7 +77,7 @@ int generic_ide_resume(struct device *dev)
        }
 
        memset(&rqpm, 0, sizeof(rqpm));
-       rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, BLK_MQ_REQ_PREEMPT);
+       rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, BLK_MQ_REQ_PM);
        ide_req(rq)->type = ATA_PRIV_PM_RESUME;
        ide_req(rq)->special = &rqpm;
        rqpm.pm_step = IDE_PM_START_RESUME;
index d793355..28f93b9 100644 (file)
@@ -963,6 +963,39 @@ static struct cpuidle_state dnv_cstates[] __initdata = {
                .enter = NULL }
 };
 
+/*
+ * Note, depending on HW and FW revision, SnowRidge SoC may or may not support
+ * C6, and this is indicated in the CPUID mwait leaf.
+ */
+static struct cpuidle_state snr_cstates[] __initdata = {
+       {
+               .name = "C1",
+               .desc = "MWAIT 0x00",
+               .flags = MWAIT2flg(0x00),
+               .exit_latency = 2,
+               .target_residency = 2,
+               .enter = &intel_idle,
+               .enter_s2idle = intel_idle_s2idle, },
+       {
+               .name = "C1E",
+               .desc = "MWAIT 0x01",
+               .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_ALWAYS_ENABLE,
+               .exit_latency = 15,
+               .target_residency = 25,
+               .enter = &intel_idle,
+               .enter_s2idle = intel_idle_s2idle, },
+       {
+               .name = "C6",
+               .desc = "MWAIT 0x20",
+               .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TLB_FLUSHED,
+               .exit_latency = 130,
+               .target_residency = 500,
+               .enter = &intel_idle,
+               .enter_s2idle = intel_idle_s2idle, },
+       {
+               .enter = NULL }
+};
+
 static const struct idle_cpu idle_cpu_nehalem __initconst = {
        .state_table = nehalem_cstates,
        .auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE,
@@ -1084,6 +1117,12 @@ static const struct idle_cpu idle_cpu_dnv __initconst = {
        .use_acpi = true,
 };
 
+static const struct idle_cpu idle_cpu_snr __initconst = {
+       .state_table = snr_cstates,
+       .disable_promotion_to_c1e = true,
+       .use_acpi = true,
+};
+
 static const struct x86_cpu_id intel_idle_ids[] __initconst = {
        X86_MATCH_INTEL_FAM6_MODEL(NEHALEM_EP,          &idle_cpu_nhx),
        X86_MATCH_INTEL_FAM6_MODEL(NEHALEM,             &idle_cpu_nehalem),
@@ -1122,7 +1161,7 @@ static const struct x86_cpu_id intel_idle_ids[] __initconst = {
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT,       &idle_cpu_bxt),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_PLUS,  &idle_cpu_bxt),
        X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D,     &idle_cpu_dnv),
-       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      &idle_cpu_dnv),
+       X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D,      &idle_cpu_snr),
        {}
 };
 
index b11c8c4..e946903 100644 (file)
@@ -397,16 +397,12 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev,
        ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
                                flags, indio_dev->name, indio_dev);
        if (ret)
-               goto error_kfifo_free;
+               return ret;
 
        indio_dev->setup_ops = setup_ops;
        indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
 
        return 0;
-
-error_kfifo_free:
-       iio_kfifo_free(indio_dev->buffer);
-       return ret;
 }
 
 static const char * const chan_name_ain[] = {
index 0507283..2dbd264 100644 (file)
  * @sdata: Sensor data.
  *
  * returns:
- * 0 - no new samples available
- * 1 - new samples available
- * negative - error or unknown
+ * false - no new samples available or read error
+ * true - new samples available
  */
-static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
-                                           struct st_sensor_data *sdata)
+static bool st_sensors_new_samples_available(struct iio_dev *indio_dev,
+                                            struct st_sensor_data *sdata)
 {
        int ret, status;
 
        /* How would I know if I can't check it? */
        if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr)
-               return -EINVAL;
+               return true;
 
        /* No scan mask, no interrupt */
        if (!indio_dev->active_scan_mask)
-               return 0;
+               return false;
 
        ret = regmap_read(sdata->regmap,
                          sdata->sensor_settings->drdy_irq.stat_drdy.addr,
                          &status);
        if (ret < 0) {
                dev_err(sdata->dev, "error checking samples available\n");
-               return ret;
+               return false;
        }
 
-       if (status & sdata->sensor_settings->drdy_irq.stat_drdy.mask)
-               return 1;
-
-       return 0;
+       return !!(status & sdata->sensor_settings->drdy_irq.stat_drdy.mask);
 }
 
 /**
@@ -180,9 +176,15 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
 
        /* Tell the interrupt handler that we're dealing with edges */
        if (irq_trig == IRQF_TRIGGER_FALLING ||
-           irq_trig == IRQF_TRIGGER_RISING)
+           irq_trig == IRQF_TRIGGER_RISING) {
+               if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr) {
+                       dev_err(&indio_dev->dev,
+                               "edge IRQ not supported w/o stat register.\n");
+                       err = -EOPNOTSUPP;
+                       goto iio_trigger_free;
+               }
                sdata->edge_irq = true;
-       else
+       } else {
                /*
                 * If we're not using edges (i.e. level interrupts) we
                 * just mask off the IRQ, handle one interrupt, then
@@ -190,6 +192,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
                 * interrupt handler top half again and start over.
                 */
                irq_trig |= IRQF_ONESHOT;
+       }
 
        /*
         * If the interrupt pin is Open Drain, by definition this
index 28921b6..e9297c2 100644 (file)
@@ -187,9 +187,9 @@ static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev,
                return ret;
 
        if (pwr_down)
-               st->pwr_down_mask |= (1 << chan->channel);
-       else
                st->pwr_down_mask &= ~(1 << chan->channel);
+       else
+               st->pwr_down_mask |= (1 << chan->channel);
 
        ret = ad5504_spi_write(st, AD5504_ADDR_CTRL,
                                AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) |
index a2f8209..37fd0b6 100644 (file)
@@ -601,7 +601,7 @@ static int sx9310_read_thresh(struct sx9310_data *data,
                return ret;
 
        regval = FIELD_GET(SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
-       if (regval > ARRAY_SIZE(sx9310_pthresh_codes))
+       if (regval >= ARRAY_SIZE(sx9310_pthresh_codes))
                return -EINVAL;
 
        *val = sx9310_pthresh_codes[regval];
@@ -1305,7 +1305,8 @@ sx9310_get_default_reg(struct sx9310_data *data, int i,
                if (ret)
                        break;
 
-               pos = min(max(ilog2(pos), 3), 10) - 3;
+               /* Powers of 2, except for a gap between 16 and 64 */
+               pos = clamp(ilog2(pos), 3, 11) - (pos >= 32 ? 4 : 3);
                reg_def->def &= ~SX9310_REG_PROX_CTRL7_AVGPOSFILT_MASK;
                reg_def->def |= FIELD_PREP(SX9310_REG_PROX_CTRL7_AVGPOSFILT_MASK,
                                           pos);
index 503fe54..608ccb1 100644 (file)
@@ -248,6 +248,12 @@ static int mlx90632_set_meas_type(struct regmap *regmap, u8 type)
        if (ret < 0)
                return ret;
 
+       /*
+        * Give the mlx90632 some time to reset properly before sending a new I2C command
+        * if this is not done, the following I2C command(s) will not be accepted.
+        */
+       usleep_range(150, 200);
+
        ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL,
                                 (MLX90632_CFG_MTYP_MASK | MLX90632_CFG_PWR_MASK),
                                 (MLX90632_MTYP_STATUS(type) | MLX90632_PWR_STATUS_HALT));
index 7f70e5a..97a77ea 100644 (file)
@@ -131,8 +131,10 @@ static ssize_t default_roce_mode_store(struct config_item *item,
                return ret;
 
        gid_type = ib_cache_gid_parse_type_str(buf);
-       if (gid_type < 0)
+       if (gid_type < 0) {
+               cma_configfs_params_put(cma_dev);
                return -EINVAL;
+       }
 
        ret = cma_set_default_gid_type(cma_dev, group->port_num, gid_type);
 
index e0a41c8..ff1551b 100644 (file)
@@ -254,6 +254,7 @@ void rdma_restrack_add(struct rdma_restrack_entry *res)
        } else {
                ret = xa_alloc_cyclic(&rt->xa, &res->id, res, xa_limit_32b,
                                      &rt->next_id, GFP_KERNEL);
+               ret = (ret < 0) ? ret : 0;
        }
 
 out:
index 7dab9a2..da2512c 100644 (file)
@@ -95,8 +95,6 @@ struct ucma_context {
        u64                     uid;
 
        struct list_head        list;
-       /* sync between removal event and id destroy, protected by file mut */
-       int                     destroying;
        struct work_struct      close_work;
 };
 
@@ -122,7 +120,7 @@ static DEFINE_XARRAY_ALLOC(ctx_table);
 static DEFINE_XARRAY_ALLOC(multicast_table);
 
 static const struct file_operations ucma_fops;
-static int __destroy_id(struct ucma_context *ctx);
+static int ucma_destroy_private_ctx(struct ucma_context *ctx);
 
 static inline struct ucma_context *_ucma_find_context(int id,
                                                      struct ucma_file *file)
@@ -179,19 +177,14 @@ static void ucma_close_id(struct work_struct *work)
 
        /* once all inflight tasks are finished, we close all underlying
         * resources. The context is still alive till its explicit destryoing
-        * by its creator.
+        * by its creator. This puts back the xarray's reference.
         */
        ucma_put_ctx(ctx);
        wait_for_completion(&ctx->comp);
        /* No new events will be generated after destroying the id. */
        rdma_destroy_id(ctx->cm_id);
 
-       /*
-        * At this point ctx->ref is zero so the only place the ctx can be is in
-        * a uevent or in __destroy_id(). Since the former doesn't touch
-        * ctx->cm_id and the latter sync cancels this, there is no races with
-        * this store.
-        */
+       /* Reading the cm_id without holding a positive ref is not allowed */
        ctx->cm_id = NULL;
 }
 
@@ -204,7 +197,6 @@ static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file)
                return NULL;
 
        INIT_WORK(&ctx->close_work, ucma_close_id);
-       refcount_set(&ctx->ref, 1);
        init_completion(&ctx->comp);
        /* So list_del() will work if we don't do ucma_finish_ctx() */
        INIT_LIST_HEAD(&ctx->list);
@@ -218,6 +210,13 @@ static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file)
        return ctx;
 }
 
+static void ucma_set_ctx_cm_id(struct ucma_context *ctx,
+                              struct rdma_cm_id *cm_id)
+{
+       refcount_set(&ctx->ref, 1);
+       ctx->cm_id = cm_id;
+}
+
 static void ucma_finish_ctx(struct ucma_context *ctx)
 {
        lockdep_assert_held(&ctx->file->mut);
@@ -303,7 +302,7 @@ static int ucma_connect_event_handler(struct rdma_cm_id *cm_id,
        ctx = ucma_alloc_ctx(listen_ctx->file);
        if (!ctx)
                goto err_backlog;
-       ctx->cm_id = cm_id;
+       ucma_set_ctx_cm_id(ctx, cm_id);
 
        uevent = ucma_create_uevent(listen_ctx, event);
        if (!uevent)
@@ -321,8 +320,7 @@ static int ucma_connect_event_handler(struct rdma_cm_id *cm_id,
        return 0;
 
 err_alloc:
-       xa_erase(&ctx_table, ctx->id);
-       kfree(ctx);
+       ucma_destroy_private_ctx(ctx);
 err_backlog:
        atomic_inc(&listen_ctx->backlog);
        /* Returning error causes the new ID to be destroyed */
@@ -356,8 +354,12 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
                wake_up_interruptible(&ctx->file->poll_wait);
        }
 
-       if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL && !ctx->destroying)
-               queue_work(system_unbound_wq, &ctx->close_work);
+       if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
+               xa_lock(&ctx_table);
+               if (xa_load(&ctx_table, ctx->id) == ctx)
+                       queue_work(system_unbound_wq, &ctx->close_work);
+               xa_unlock(&ctx_table);
+       }
        return 0;
 }
 
@@ -461,13 +463,12 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,
                ret = PTR_ERR(cm_id);
                goto err1;
        }
-       ctx->cm_id = cm_id;
+       ucma_set_ctx_cm_id(ctx, cm_id);
 
        resp.id = ctx->id;
        if (copy_to_user(u64_to_user_ptr(cmd.response),
                         &resp, sizeof(resp))) {
-               xa_erase(&ctx_table, ctx->id);
-               __destroy_id(ctx);
+               ucma_destroy_private_ctx(ctx);
                return -EFAULT;
        }
 
@@ -477,8 +478,7 @@ static ssize_t ucma_create_id(struct ucma_file *file, const char __user *inbuf,
        return 0;
 
 err1:
-       xa_erase(&ctx_table, ctx->id);
-       kfree(ctx);
+       ucma_destroy_private_ctx(ctx);
        return ret;
 }
 
@@ -516,68 +516,73 @@ static void ucma_cleanup_mc_events(struct ucma_multicast *mc)
        rdma_unlock_handler(mc->ctx->cm_id);
 }
 
-/*
- * ucma_free_ctx is called after the underlying rdma CM-ID is destroyed. At
- * this point, no new events will be reported from the hardware. However, we
- * still need to cleanup the UCMA context for this ID. Specifically, there
- * might be events that have not yet been consumed by the user space software.
- * mutex. After that we release them as needed.
- */
-static int ucma_free_ctx(struct ucma_context *ctx)
+static int ucma_cleanup_ctx_events(struct ucma_context *ctx)
 {
        int events_reported;
        struct ucma_event *uevent, *tmp;
        LIST_HEAD(list);
 
-       ucma_cleanup_multicast(ctx);
-
-       /* Cleanup events not yet reported to the user. */
+       /* Cleanup events not yet reported to the user.*/
        mutex_lock(&ctx->file->mut);
        list_for_each_entry_safe(uevent, tmp, &ctx->file->event_list, list) {
-               if (uevent->ctx == ctx || uevent->conn_req_ctx == ctx)
+               if (uevent->ctx != ctx)
+                       continue;
+
+               if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST &&
+                   xa_cmpxchg(&ctx_table, uevent->conn_req_ctx->id,
+                              uevent->conn_req_ctx, XA_ZERO_ENTRY,
+                              GFP_KERNEL) == uevent->conn_req_ctx) {
                        list_move_tail(&uevent->list, &list);
+                       continue;
+               }
+               list_del(&uevent->list);
+               kfree(uevent);
        }
        list_del(&ctx->list);
        events_reported = ctx->events_reported;
        mutex_unlock(&ctx->file->mut);
 
        /*
-        * If this was a listening ID then any connections spawned from it
-        * that have not been delivered to userspace are cleaned up too.
-        * Must be done outside any locks.
+        * If this was a listening ID then any connections spawned from it that
+        * have not been delivered to userspace are cleaned up too. Must be done
+        * outside any locks.
         */
        list_for_each_entry_safe(uevent, tmp, &list, list) {
-               list_del(&uevent->list);
-               if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST &&
-                   uevent->conn_req_ctx != ctx)
-                       __destroy_id(uevent->conn_req_ctx);
+               ucma_destroy_private_ctx(uevent->conn_req_ctx);
                kfree(uevent);
        }
-
-       mutex_destroy(&ctx->mutex);
-       kfree(ctx);
        return events_reported;
 }
 
-static int __destroy_id(struct ucma_context *ctx)
+/*
+ * When this is called the xarray must have a XA_ZERO_ENTRY in the ctx->id (ie
+ * the ctx is not public to the user). This either because:
+ *  - ucma_finish_ctx() hasn't been called
+ *  - xa_cmpxchg() succeed to remove the entry (only one thread can succeed)
+ */
+static int ucma_destroy_private_ctx(struct ucma_context *ctx)
 {
+       int events_reported;
+
        /*
-        * If the refcount is already 0 then ucma_close_id() has already
-        * destroyed the cm_id, otherwise holding the refcount keeps cm_id
-        * valid. Prevent queue_work() from being called.
+        * Destroy the underlying cm_id. New work queuing is prevented now by
+        * the removal from the xarray. Once the work is cancled ref will either
+        * be 0 because the work ran to completion and consumed the ref from the
+        * xarray, or it will be positive because we still have the ref from the
+        * xarray. This can also be 0 in cases where cm_id was never set
         */
-       if (refcount_inc_not_zero(&ctx->ref)) {
-               rdma_lock_handler(ctx->cm_id);
-               ctx->destroying = 1;
-               rdma_unlock_handler(ctx->cm_id);
-               ucma_put_ctx(ctx);
-       }
-
        cancel_work_sync(&ctx->close_work);
-       /* At this point it's guaranteed that there is no inflight closing task */
-       if (ctx->cm_id)
+       if (refcount_read(&ctx->ref))
                ucma_close_id(&ctx->close_work);
-       return ucma_free_ctx(ctx);
+
+       events_reported = ucma_cleanup_ctx_events(ctx);
+       ucma_cleanup_multicast(ctx);
+
+       WARN_ON(xa_cmpxchg(&ctx_table, ctx->id, XA_ZERO_ENTRY, NULL,
+                          GFP_KERNEL) != NULL);
+       mutex_destroy(&ctx->mutex);
+       kfree(ctx);
+       return events_reported;
 }
 
 static ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf,
@@ -596,14 +601,17 @@ static ssize_t ucma_destroy_id(struct ucma_file *file, const char __user *inbuf,
 
        xa_lock(&ctx_table);
        ctx = _ucma_find_context(cmd.id, file);
-       if (!IS_ERR(ctx))
-               __xa_erase(&ctx_table, ctx->id);
+       if (!IS_ERR(ctx)) {
+               if (__xa_cmpxchg(&ctx_table, ctx->id, ctx, XA_ZERO_ENTRY,
+                                GFP_KERNEL) != ctx)
+                       ctx = ERR_PTR(-ENOENT);
+       }
        xa_unlock(&ctx_table);
 
        if (IS_ERR(ctx))
                return PTR_ERR(ctx);
 
-       resp.events_reported = __destroy_id(ctx);
+       resp.events_reported = ucma_destroy_private_ctx(ctx);
        if (copy_to_user(u64_to_user_ptr(cmd.response),
                         &resp, sizeof(resp)))
                ret = -EFAULT;
@@ -1777,15 +1785,16 @@ static int ucma_close(struct inode *inode, struct file *filp)
         * prevented by this being a FD release function. The list_add_tail() in
         * ucma_connect_event_handler() can run concurrently, however it only
         * adds to the list *after* a listening ID. By only reading the first of
-        * the list, and relying on __destroy_id() to block
+        * the list, and relying on ucma_destroy_private_ctx() to block
         * ucma_connect_event_handler(), no additional locking is needed.
         */
        while (!list_empty(&file->ctx_list)) {
                struct ucma_context *ctx = list_first_entry(
                        &file->ctx_list, struct ucma_context, list);
 
-               xa_erase(&ctx_table, ctx->id);
-               __destroy_id(ctx);
+               WARN_ON(xa_cmpxchg(&ctx_table, ctx->id, ctx, XA_ZERO_ENTRY,
+                                  GFP_KERNEL) != ctx);
+               ucma_destroy_private_ctx(ctx);
        }
        kfree(file);
        return 0;
index 7ca4112..917338d 100644 (file)
@@ -135,7 +135,7 @@ unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem,
         */
        if (mask)
                pgsz_bitmap &= GENMASK(count_trailing_zeros(mask), 0);
-       return rounddown_pow_of_two(pgsz_bitmap);
+       return pgsz_bitmap ? rounddown_pow_of_two(pgsz_bitmap) : 0;
 }
 EXPORT_SYMBOL(ib_umem_find_best_pgsz);
 
index a740139..d109bb3 100644 (file)
@@ -2474,7 +2474,7 @@ int c4iw_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
        init_attr->cap.max_send_wr = qhp->attr.sq_num_entries;
        init_attr->cap.max_recv_wr = qhp->attr.rq_num_entries;
        init_attr->cap.max_send_sge = qhp->attr.sq_max_sges;
-       init_attr->cap.max_recv_sge = qhp->attr.sq_max_sges;
+       init_attr->cap.max_recv_sge = qhp->attr.rq_max_sges;
        init_attr->cap.max_inline_data = T4_MAX_SEND_INLINE;
        init_attr->sq_sig_type = qhp->sq_sig_all ? IB_SIGNAL_ALL_WR : 0;
        return 0;
index 55d5386..ad82532 100644 (file)
@@ -532,7 +532,7 @@ struct hns_roce_qp_table {
        struct hns_roce_hem_table       sccc_table;
        struct mutex                    scc_mutex;
        struct hns_roce_bank bank[HNS_ROCE_QP_BANK_NUM];
-       spinlock_t bank_lock;
+       struct mutex bank_mutex;
 };
 
 struct hns_roce_cq_table {
index d8e2fe5..1116371 100644 (file)
@@ -209,7 +209,7 @@ static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp)
 
                hr_qp->doorbell_qpn = 1;
        } else {
-               spin_lock(&qp_table->bank_lock);
+               mutex_lock(&qp_table->bank_mutex);
                bankid = get_least_load_bankid_for_qp(qp_table->bank);
 
                ret = alloc_qpn_with_bankid(&qp_table->bank[bankid], bankid,
@@ -217,12 +217,12 @@ static int alloc_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp)
                if (ret) {
                        ibdev_err(&hr_dev->ib_dev,
                                  "failed to alloc QPN, ret = %d\n", ret);
-                       spin_unlock(&qp_table->bank_lock);
+                       mutex_unlock(&qp_table->bank_mutex);
                        return ret;
                }
 
                qp_table->bank[bankid].inuse++;
-               spin_unlock(&qp_table->bank_lock);
+               mutex_unlock(&qp_table->bank_mutex);
 
                hr_qp->doorbell_qpn = (u32)num;
        }
@@ -408,9 +408,9 @@ static void free_qpn(struct hns_roce_dev *hr_dev, struct hns_roce_qp *hr_qp)
 
        ida_free(&hr_dev->qp_table.bank[bankid].ida, hr_qp->qpn >> 3);
 
-       spin_lock(&hr_dev->qp_table.bank_lock);
+       mutex_lock(&hr_dev->qp_table.bank_mutex);
        hr_dev->qp_table.bank[bankid].inuse--;
-       spin_unlock(&hr_dev->qp_table.bank_lock);
+       mutex_unlock(&hr_dev->qp_table.bank_mutex);
 }
 
 static int set_rq_size(struct hns_roce_dev *hr_dev, struct ib_qp_cap *cap,
@@ -1371,6 +1371,7 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev)
        unsigned int i;
 
        mutex_init(&qp_table->scc_mutex);
+       mutex_init(&qp_table->bank_mutex);
        xa_init(&hr_dev->qp_table_xa);
 
        reserved_from_bot = hr_dev->caps.reserved_qps;
index 3bae9ba..aabdc07 100644 (file)
@@ -3311,8 +3311,7 @@ static int mlx5_add_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num)
        int err;
 
        dev->port[port_num].roce.nb.notifier_call = mlx5_netdev_event;
-       err = register_netdevice_notifier_net(mlx5_core_net(dev->mdev),
-                                             &dev->port[port_num].roce.nb);
+       err = register_netdevice_notifier(&dev->port[port_num].roce.nb);
        if (err) {
                dev->port[port_num].roce.nb.notifier_call = NULL;
                return err;
@@ -3324,8 +3323,7 @@ static int mlx5_add_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num)
 static void mlx5_remove_netdev_notifier(struct mlx5_ib_dev *dev, u8 port_num)
 {
        if (dev->port[port_num].roce.nb.notifier_call) {
-               unregister_netdevice_notifier_net(mlx5_core_net(dev->mdev),
-                                                 &dev->port[port_num].roce.nb);
+               unregister_netdevice_notifier(&dev->port[port_num].roce.nb);
                dev->port[port_num].roce.nb.notifier_call = NULL;
        }
 }
@@ -3956,7 +3954,7 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev)
 
        err = set_has_smi_cap(dev);
        if (err)
-               return err;
+               goto err_mp;
 
        if (!mlx5_core_mp_enabled(mdev)) {
                for (i = 1; i <= dev->num_ports; i++) {
@@ -4319,7 +4317,7 @@ static int mlx5_ib_stage_bfrag_init(struct mlx5_ib_dev *dev)
 
        err = mlx5_alloc_bfreg(dev->mdev, &dev->fp_bfreg, false, true);
        if (err)
-               mlx5_free_bfreg(dev->mdev, &dev->fp_bfreg);
+               mlx5_free_bfreg(dev->mdev, &dev->bfreg);
 
        return err;
 }
index bc98bd9..3acb5c1 100644 (file)
@@ -434,9 +434,9 @@ static void ocrdma_dealloc_ucontext_pd(struct ocrdma_ucontext *uctx)
                pr_err("%s(%d) Freeing in use pdid=0x%x.\n",
                       __func__, dev->id, pd->id);
        }
-       kfree(uctx->cntxt_pd);
        uctx->cntxt_pd = NULL;
        _ocrdma_dealloc_pd(dev, pd);
+       kfree(pd);
 }
 
 static struct ocrdma_pd *ocrdma_get_ucontext_pd(struct ocrdma_ucontext *uctx)
index e59615a..586b0e5 100644 (file)
@@ -214,7 +214,7 @@ static ssize_t summary_show(struct usnic_ib_qp_grp *qp_grp, char *buf)
        struct usnic_vnic_res *vnic_res;
        int len;
 
-       len = sysfs_emit(buf, "QPN: %d State: (%s) PID: %u VF Idx: %hu ",
+       len = sysfs_emit(buf, "QPN: %d State: (%s) PID: %u VF Idx: %hu",
                         qp_grp->ibqp.qp_num,
                         usnic_ib_qp_grp_state_to_string(qp_grp->state),
                         qp_grp->owner_pid,
@@ -224,14 +224,13 @@ static ssize_t summary_show(struct usnic_ib_qp_grp *qp_grp, char *buf)
                res_chunk = qp_grp->res_chunk_list[i];
                for (j = 0; j < res_chunk->cnt; j++) {
                        vnic_res = res_chunk->res[j];
-                       len += sysfs_emit_at(
-                               buf, len, "%s[%d] ",
+                       len += sysfs_emit_at(buf, len, " %s[%d]",
                                usnic_vnic_res_type_to_str(vnic_res->type),
                                vnic_res->vnic_idx);
                }
        }
 
-       len = sysfs_emit_at(buf, len, "\n");
+       len += sysfs_emit_at(buf, len, "\n");
 
        return len;
 }
index 38a3777..3705c6b 100644 (file)
@@ -214,6 +214,7 @@ find_free_vf_and_create_qp_grp(struct usnic_ib_dev *us_ibdev,
 
                }
                usnic_uiom_free_dev_list(dev_list);
+               dev_list = NULL;
        }
 
        /* Try to find resources on an unused vf */
@@ -239,6 +240,8 @@ find_free_vf_and_create_qp_grp(struct usnic_ib_dev *us_ibdev,
 qp_grp_check:
        if (IS_ERR_OR_NULL(qp_grp)) {
                usnic_err("Failed to allocate qp_grp\n");
+               if (usnic_ib_share_vf)
+                       usnic_uiom_free_dev_list(dev_list);
                return ERR_PTR(qp_grp ? PTR_ERR(qp_grp) : -ENOMEM);
        }
        return qp_grp;
index c142f5e..de57f2f 100644 (file)
@@ -509,6 +509,20 @@ static inline int ib_send_flags_to_pvrdma(int flags)
        return flags & PVRDMA_MASK(PVRDMA_SEND_FLAGS_MAX);
 }
 
+static inline int pvrdma_network_type_to_ib(enum pvrdma_network_type type)
+{
+       switch (type) {
+       case PVRDMA_NETWORK_ROCE_V1:
+               return RDMA_NETWORK_ROCE_V1;
+       case PVRDMA_NETWORK_IPV4:
+               return RDMA_NETWORK_IPV4;
+       case PVRDMA_NETWORK_IPV6:
+               return RDMA_NETWORK_IPV6;
+       default:
+               return RDMA_NETWORK_IPV6;
+       }
+}
+
 void pvrdma_qp_cap_to_ib(struct ib_qp_cap *dst,
                         const struct pvrdma_qp_cap *src);
 void ib_qp_cap_to_pvrdma(struct pvrdma_qp_cap *dst,
index a119ac3..6aa40bd 100644 (file)
@@ -367,7 +367,7 @@ retry:
        wc->dlid_path_bits = cqe->dlid_path_bits;
        wc->port_num = cqe->port_num;
        wc->vendor_err = cqe->vendor_err;
-       wc->network_hdr_type = cqe->network_hdr_type;
+       wc->network_hdr_type = pvrdma_network_type_to_ib(cqe->network_hdr_type);
 
        /* Update shared ring state */
        pvrdma_idx_ring_inc(&cq->ring_state->rx.cons_head, cq->ibcq.cqe);
index c4b06ce..943914c 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
 #include <linux/if.h>
+#include <linux/if_vlan.h>
 #include <net/udp_tunnel.h>
 #include <net/sch_generic.h>
 #include <linux/netfilter.h>
@@ -153,9 +154,14 @@ static int rxe_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
        struct udphdr *udph;
        struct net_device *ndev = skb->dev;
+       struct net_device *rdev = ndev;
        struct rxe_dev *rxe = rxe_get_dev_from_net(ndev);
        struct rxe_pkt_info *pkt = SKB_TO_PKT(skb);
 
+       if (!rxe && is_vlan_dev(rdev)) {
+               rdev = vlan_dev_real_dev(ndev);
+               rxe = rxe_get_dev_from_net(rdev);
+       }
        if (!rxe)
                goto drop;
 
index 5a09808..c7e3b6a 100644 (file)
@@ -872,6 +872,11 @@ static enum resp_states do_complete(struct rxe_qp *qp,
                        else
                                wc->network_hdr_type = RDMA_NETWORK_IPV6;
 
+                       if (is_vlan_dev(skb->dev)) {
+                               wc->wc_flags |= IB_WC_WITH_VLAN;
+                               wc->vlan_id = vlan_dev_vlan_id(skb->dev);
+                       }
+
                        if (pkt->mask & RXE_IMMDT_MASK) {
                                wc->wc_flags |= IB_WC_WITH_IMM;
                                wc->ex.imm_data = immdt_imm(pkt);
index 41dba70..c770951 100644 (file)
@@ -96,9 +96,10 @@ static int imx_icc_node_init_qos(struct icc_provider *provider,
                        return -ENODEV;
                }
                /* Allow scaling to be disabled on a per-node basis */
-               if (!dn || !of_device_is_available(dn)) {
+               if (!of_device_is_available(dn)) {
                        dev_warn(dev, "Missing property %s, skip scaling %s\n",
                                 adj->phandle_name, node->name);
+                       of_node_put(dn);
                        return 0;
                }
 
index ba43a15..d7768d3 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/interconnect-provider.h>
 #include <dt-bindings/interconnect/imx8mq.h>
 
 #include "imx.h"
@@ -94,6 +95,7 @@ static struct platform_driver imx8mq_icc_driver = {
        .remove = imx8mq_icc_remove,
        .driver = {
                .name = "imx8mq-interconnect",
+               .sync_state = icc_sync_state,
        },
 };
 
index a8f93ba..b3fb5b0 100644 (file)
@@ -42,13 +42,23 @@ config INTERCONNECT_QCOM_QCS404
          This is a driver for the Qualcomm Network-on-Chip on qcs404-based
          platforms.
 
+config INTERCONNECT_QCOM_RPMH_POSSIBLE
+       tristate
+       default INTERCONNECT_QCOM
+       depends on QCOM_RPMH || (COMPILE_TEST && !QCOM_RPMH)
+       depends on QCOM_COMMAND_DB || (COMPILE_TEST && !QCOM_COMMAND_DB)
+       depends on OF || COMPILE_TEST
+       help
+         Compile-testing RPMH drivers is possible on other platforms,
+         but in order to avoid link failures, drivers must not be built-in
+         when QCOM_RPMH or QCOM_COMMAND_DB are loadable modules
+
 config INTERCONNECT_QCOM_RPMH
        tristate
 
 config INTERCONNECT_QCOM_SC7180
        tristate "Qualcomm SC7180 interconnect driver"
-       depends on INTERCONNECT_QCOM
-       depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+       depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
        select INTERCONNECT_QCOM_RPMH
        select INTERCONNECT_QCOM_BCM_VOTER
        help
@@ -57,8 +67,7 @@ config INTERCONNECT_QCOM_SC7180
 
 config INTERCONNECT_QCOM_SDM845
        tristate "Qualcomm SDM845 interconnect driver"
-       depends on INTERCONNECT_QCOM
-       depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+       depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
        select INTERCONNECT_QCOM_RPMH
        select INTERCONNECT_QCOM_BCM_VOTER
        help
@@ -67,8 +76,7 @@ config INTERCONNECT_QCOM_SDM845
 
 config INTERCONNECT_QCOM_SM8150
        tristate "Qualcomm SM8150 interconnect driver"
-       depends on INTERCONNECT_QCOM
-       depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+       depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
        select INTERCONNECT_QCOM_RPMH
        select INTERCONNECT_QCOM_BCM_VOTER
        help
@@ -77,8 +85,7 @@ config INTERCONNECT_QCOM_SM8150
 
 config INTERCONNECT_QCOM_SM8250
        tristate "Qualcomm SM8250 interconnect driver"
-       depends on INTERCONNECT_QCOM
-       depends on (QCOM_RPMH && QCOM_COMMAND_DB && OF) || COMPILE_TEST
+       depends on INTERCONNECT_QCOM_RPMH_POSSIBLE
        select INTERCONNECT_QCOM_RPMH
        select INTERCONNECT_QCOM_BCM_VOTER
        help
index f54cd79..6a1f704 100644 (file)
@@ -1973,8 +1973,6 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
                return r;
        }
 
-       iommu->int_enabled = true;
-
        return 0;
 }
 
@@ -2169,6 +2167,7 @@ static int iommu_init_irq(struct amd_iommu *iommu)
        if (ret)
                return ret;
 
+       iommu->int_enabled = true;
 enable_faults:
        iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
 
index 7e2c445..f0adbc4 100644 (file)
@@ -3854,6 +3854,9 @@ static int irq_remapping_select(struct irq_domain *d, struct irq_fwspec *fwspec,
        struct amd_iommu *iommu;
        int devid = -1;
 
+       if (!amd_iommu_irq_remap)
+               return 0;
+
        if (x86_fwspec_is_ioapic(fwspec))
                devid = get_ioapic_devid(fwspec->param[0]);
        else if (x86_fwspec_is_hpet(fwspec))
index 5dff7ff..bcda170 100644 (file)
@@ -196,6 +196,8 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
 
                set_bit(qsmmu->bypass_cbndx, smmu->context_map);
 
+               arm_smmu_cb_write(smmu, qsmmu->bypass_cbndx, ARM_SMMU_CB_SCTLR, 0);
+
                reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS);
                arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg);
        }
@@ -323,7 +325,9 @@ static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu,
 }
 
 static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
+       { .compatible = "qcom,msm8998-smmu-v2" },
        { .compatible = "qcom,sc7180-smmu-500" },
+       { .compatible = "qcom,sdm630-smmu-v2" },
        { .compatible = "qcom,sdm845-smmu-500" },
        { .compatible = "qcom,sm8150-smmu-500" },
        { .compatible = "qcom,sm8250-smmu-500" },
index f0305e6..4078358 100644 (file)
@@ -863,33 +863,6 @@ static int __finalise_sg(struct device *dev, struct scatterlist *sg, int nents,
        unsigned int cur_len = 0, max_len = dma_get_max_seg_size(dev);
        int i, count = 0;
 
-       /*
-        * The Intel graphic driver is used to assume that the returned
-        * sg list is not combound. This blocks the efforts of converting
-        * Intel IOMMU driver to dma-iommu api's. Add this quirk to make the
-        * device driver work and should be removed once it's fixed in i915
-        * driver.
-        */
-       if (IS_ENABLED(CONFIG_DRM_I915) && dev_is_pci(dev) &&
-           to_pci_dev(dev)->vendor == PCI_VENDOR_ID_INTEL &&
-           (to_pci_dev(dev)->class >> 16) == PCI_BASE_CLASS_DISPLAY) {
-               for_each_sg(sg, s, nents, i) {
-                       unsigned int s_iova_off = sg_dma_address(s);
-                       unsigned int s_length = sg_dma_len(s);
-                       unsigned int s_iova_len = s->length;
-
-                       s->offset += s_iova_off;
-                       s->length = s_length;
-                       sg_dma_address(s) = dma_addr + s_iova_off;
-                       sg_dma_len(s) = s_length;
-                       dma_addr += s_iova_len;
-
-                       pr_info_once("sg combining disabled due to i915 driver\n");
-               }
-
-               return nents;
-       }
-
        for_each_sg(sg, s, nents, i) {
                /* Restore this segment's original unaligned fields first */
                unsigned int s_iova_off = sg_dma_address(s);
index b46dbfa..004feae 100644 (file)
@@ -1461,8 +1461,8 @@ void qi_flush_piotlb(struct intel_iommu *iommu, u16 did, u32 pasid, u64 addr,
                int mask = ilog2(__roundup_pow_of_two(npages));
                unsigned long align = (1ULL << (VTD_PAGE_SHIFT + mask));
 
-               if (WARN_ON_ONCE(!ALIGN(addr, align)))
-                       addr &= ~(align - 1);
+               if (WARN_ON_ONCE(!IS_ALIGNED(addr, align)))
+                       addr = ALIGN_DOWN(addr, align);
 
                desc.qw0 = QI_EIOTLB_PASID(pasid) |
                                QI_EIOTLB_DID(did) |
index 788119c..f665322 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/dmi.h>
 #include <linux/pci-ats.h>
 #include <linux/memblock.h>
-#include <linux/dma-map-ops.h>
 #include <linux/dma-direct.h>
 #include <linux/crash_dump.h>
 #include <linux/numa.h>
@@ -719,6 +718,8 @@ static int domain_update_device_node(struct dmar_domain *domain)
        return nid;
 }
 
+static void domain_update_iotlb(struct dmar_domain *domain);
+
 /* Some capabilities may be different across iommus */
 static void domain_update_iommu_cap(struct dmar_domain *domain)
 {
@@ -744,6 +745,8 @@ static void domain_update_iommu_cap(struct dmar_domain *domain)
                domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw - 1);
        else
                domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw);
+
+       domain_update_iotlb(domain);
 }
 
 struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
@@ -1464,17 +1467,22 @@ static void domain_update_iotlb(struct dmar_domain *domain)
 
        assert_spin_locked(&device_domain_lock);
 
-       list_for_each_entry(info, &domain->devices, link) {
-               struct pci_dev *pdev;
-
-               if (!info->dev || !dev_is_pci(info->dev))
-                       continue;
-
-               pdev = to_pci_dev(info->dev);
-               if (pdev->ats_enabled) {
+       list_for_each_entry(info, &domain->devices, link)
+               if (info->ats_enabled) {
                        has_iotlb_device = true;
                        break;
                }
+
+       if (!has_iotlb_device) {
+               struct subdev_domain_info *sinfo;
+
+               list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
+                       info = get_domain_info(sinfo->pdev);
+                       if (info && info->ats_enabled) {
+                               has_iotlb_device = true;
+                               break;
+                       }
+               }
        }
 
        domain->has_iotlb_device = has_iotlb_device;
@@ -1555,25 +1563,37 @@ static void iommu_disable_dev_iotlb(struct device_domain_info *info)
 #endif
 }
 
+static void __iommu_flush_dev_iotlb(struct device_domain_info *info,
+                                   u64 addr, unsigned int mask)
+{
+       u16 sid, qdep;
+
+       if (!info || !info->ats_enabled)
+               return;
+
+       sid = info->bus << 8 | info->devfn;
+       qdep = info->ats_qdep;
+       qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
+                          qdep, addr, mask);
+}
+
 static void iommu_flush_dev_iotlb(struct dmar_domain *domain,
                                  u64 addr, unsigned mask)
 {
-       u16 sid, qdep;
        unsigned long flags;
        struct device_domain_info *info;
+       struct subdev_domain_info *sinfo;
 
        if (!domain->has_iotlb_device)
                return;
 
        spin_lock_irqsave(&device_domain_lock, flags);
-       list_for_each_entry(info, &domain->devices, link) {
-               if (!info->ats_enabled)
-                       continue;
+       list_for_each_entry(info, &domain->devices, link)
+               __iommu_flush_dev_iotlb(info, addr, mask);
 
-               sid = info->bus << 8 | info->devfn;
-               qdep = info->ats_qdep;
-               qi_flush_dev_iotlb(info->iommu, sid, info->pfsid,
-                               qdep, addr, mask);
+       list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
+               info = get_domain_info(sinfo->pdev);
+               __iommu_flush_dev_iotlb(info, addr, mask);
        }
        spin_unlock_irqrestore(&device_domain_lock, flags);
 }
@@ -1877,6 +1897,7 @@ static struct dmar_domain *alloc_domain(int flags)
                domain->flags |= DOMAIN_FLAG_USE_FIRST_LEVEL;
        domain->has_iotlb_device = false;
        INIT_LIST_HEAD(&domain->devices);
+       INIT_LIST_HEAD(&domain->subdevices);
 
        return domain;
 }
@@ -2547,7 +2568,7 @@ static struct dmar_domain *dmar_insert_one_dev_info(struct intel_iommu *iommu,
        info->iommu = iommu;
        info->pasid_table = NULL;
        info->auxd_enabled = 0;
-       INIT_LIST_HEAD(&info->auxiliary_domains);
+       INIT_LIST_HEAD(&info->subdevices);
 
        if (dev && dev_is_pci(dev)) {
                struct pci_dev *pdev = to_pci_dev(info->dev);
@@ -4475,33 +4496,61 @@ is_aux_domain(struct device *dev, struct iommu_domain *domain)
                        domain->type == IOMMU_DOMAIN_UNMANAGED;
 }
 
-static void auxiliary_link_device(struct dmar_domain *domain,
-                                 struct device *dev)
+static inline struct subdev_domain_info *
+lookup_subdev_info(struct dmar_domain *domain, struct device *dev)
+{
+       struct subdev_domain_info *sinfo;
+
+       if (!list_empty(&domain->subdevices)) {
+               list_for_each_entry(sinfo, &domain->subdevices, link_domain) {
+                       if (sinfo->pdev == dev)
+                               return sinfo;
+               }
+       }
+
+       return NULL;
+}
+
+static int auxiliary_link_device(struct dmar_domain *domain,
+                                struct device *dev)
 {
        struct device_domain_info *info = get_domain_info(dev);
+       struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
 
        assert_spin_locked(&device_domain_lock);
        if (WARN_ON(!info))
-               return;
+               return -EINVAL;
 
-       domain->auxd_refcnt++;
-       list_add(&domain->auxd, &info->auxiliary_domains);
+       if (!sinfo) {
+               sinfo = kzalloc(sizeof(*sinfo), GFP_ATOMIC);
+               sinfo->domain = domain;
+               sinfo->pdev = dev;
+               list_add(&sinfo->link_phys, &info->subdevices);
+               list_add(&sinfo->link_domain, &domain->subdevices);
+       }
+
+       return ++sinfo->users;
 }
 
-static void auxiliary_unlink_device(struct dmar_domain *domain,
-                                   struct device *dev)
+static int auxiliary_unlink_device(struct dmar_domain *domain,
+                                  struct device *dev)
 {
        struct device_domain_info *info = get_domain_info(dev);
+       struct subdev_domain_info *sinfo = lookup_subdev_info(domain, dev);
+       int ret;
 
        assert_spin_locked(&device_domain_lock);
-       if (WARN_ON(!info))
-               return;
+       if (WARN_ON(!info || !sinfo || sinfo->users <= 0))
+               return -EINVAL;
 
-       list_del(&domain->auxd);
-       domain->auxd_refcnt--;
+       ret = --sinfo->users;
+       if (!ret) {
+               list_del(&sinfo->link_phys);
+               list_del(&sinfo->link_domain);
+               kfree(sinfo);
+       }
 
-       if (!domain->auxd_refcnt && domain->default_pasid > 0)
-               ioasid_put(domain->default_pasid);
+       return ret;
 }
 
 static int aux_domain_add_dev(struct dmar_domain *domain,
@@ -4530,6 +4579,19 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
        }
 
        spin_lock_irqsave(&device_domain_lock, flags);
+       ret = auxiliary_link_device(domain, dev);
+       if (ret <= 0)
+               goto link_failed;
+
+       /*
+        * Subdevices from the same physical device can be attached to the
+        * same domain. For such cases, only the first subdevice attachment
+        * needs to go through the full steps in this function. So if ret >
+        * 1, just goto out.
+        */
+       if (ret > 1)
+               goto out;
+
        /*
         * iommu->lock must be held to attach domain to iommu and setup the
         * pasid entry for second level translation.
@@ -4548,10 +4610,9 @@ static int aux_domain_add_dev(struct dmar_domain *domain,
                                                     domain->default_pasid);
        if (ret)
                goto table_failed;
-       spin_unlock(&iommu->lock);
-
-       auxiliary_link_device(domain, dev);
 
+       spin_unlock(&iommu->lock);
+out:
        spin_unlock_irqrestore(&device_domain_lock, flags);
 
        return 0;
@@ -4560,8 +4621,10 @@ table_failed:
        domain_detach_iommu(domain, iommu);
 attach_failed:
        spin_unlock(&iommu->lock);
+       auxiliary_unlink_device(domain, dev);
+link_failed:
        spin_unlock_irqrestore(&device_domain_lock, flags);
-       if (!domain->auxd_refcnt && domain->default_pasid > 0)
+       if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
                ioasid_put(domain->default_pasid);
 
        return ret;
@@ -4581,14 +4644,18 @@ static void aux_domain_remove_dev(struct dmar_domain *domain,
        info = get_domain_info(dev);
        iommu = info->iommu;
 
-       auxiliary_unlink_device(domain, dev);
-
-       spin_lock(&iommu->lock);
-       intel_pasid_tear_down_entry(iommu, dev, domain->default_pasid, false);
-       domain_detach_iommu(domain, iommu);
-       spin_unlock(&iommu->lock);
+       if (!auxiliary_unlink_device(domain, dev)) {
+               spin_lock(&iommu->lock);
+               intel_pasid_tear_down_entry(iommu, dev,
+                                           domain->default_pasid, false);
+               domain_detach_iommu(domain, iommu);
+               spin_unlock(&iommu->lock);
+       }
 
        spin_unlock_irqrestore(&device_domain_lock, flags);
+
+       if (list_empty(&domain->subdevices) && domain->default_pasid > 0)
+               ioasid_put(domain->default_pasid);
 }
 
 static int prepare_domain_attach_device(struct iommu_domain *domain,
index aeffda9..685200a 100644 (file)
@@ -1353,6 +1353,8 @@ static int intel_irq_remapping_alloc(struct irq_domain *domain,
                irq_data = irq_domain_get_irq_data(domain, virq + i);
                irq_cfg = irqd_cfg(irq_data);
                if (!irq_data || !irq_cfg) {
+                       if (!i)
+                               kfree(data);
                        ret = -EINVAL;
                        goto out_free_data;
                }
index 4fa248b..18a9f05 100644 (file)
@@ -118,8 +118,10 @@ void intel_svm_check(struct intel_iommu *iommu)
        iommu->flags |= VTD_FLAG_SVM_CAPABLE;
 }
 
-static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_dev *sdev,
-                               unsigned long address, unsigned long pages, int ih)
+static void __flush_svm_range_dev(struct intel_svm *svm,
+                                 struct intel_svm_dev *sdev,
+                                 unsigned long address,
+                                 unsigned long pages, int ih)
 {
        struct qi_desc desc;
 
@@ -142,7 +144,7 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
        }
        desc.qw2 = 0;
        desc.qw3 = 0;
-       qi_submit_sync(svm->iommu, &desc, 1, 0);
+       qi_submit_sync(sdev->iommu, &desc, 1, 0);
 
        if (sdev->dev_iotlb) {
                desc.qw0 = QI_DEV_EIOTLB_PASID(svm->pasid) |
@@ -166,7 +168,23 @@ static void intel_flush_svm_range_dev (struct intel_svm *svm, struct intel_svm_d
                }
                desc.qw2 = 0;
                desc.qw3 = 0;
-               qi_submit_sync(svm->iommu, &desc, 1, 0);
+               qi_submit_sync(sdev->iommu, &desc, 1, 0);
+       }
+}
+
+static void intel_flush_svm_range_dev(struct intel_svm *svm,
+                                     struct intel_svm_dev *sdev,
+                                     unsigned long address,
+                                     unsigned long pages, int ih)
+{
+       unsigned long shift = ilog2(__roundup_pow_of_two(pages));
+       unsigned long align = (1ULL << (VTD_PAGE_SHIFT + shift));
+       unsigned long start = ALIGN_DOWN(address, align);
+       unsigned long end = ALIGN(address + (pages << VTD_PAGE_SHIFT), align);
+
+       while (start < end) {
+               __flush_svm_range_dev(svm, sdev, start, align >> VTD_PAGE_SHIFT, ih);
+               start += align;
        }
 }
 
@@ -211,7 +229,7 @@ static void intel_mm_release(struct mmu_notifier *mn, struct mm_struct *mm)
         */
        rcu_read_lock();
        list_for_each_entry_rcu(sdev, &svm->devs, list)
-               intel_pasid_tear_down_entry(svm->iommu, sdev->dev,
+               intel_pasid_tear_down_entry(sdev->iommu, sdev->dev,
                                            svm->pasid, true);
        rcu_read_unlock();
 
@@ -281,6 +299,7 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
        struct dmar_domain *dmar_domain;
        struct device_domain_info *info;
        struct intel_svm *svm = NULL;
+       unsigned long iflags;
        int ret = 0;
 
        if (WARN_ON(!iommu) || !data)
@@ -363,6 +382,7 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
        }
        sdev->dev = dev;
        sdev->sid = PCI_DEVID(info->bus, info->devfn);
+       sdev->iommu = iommu;
 
        /* Only count users if device has aux domains */
        if (iommu_dev_feature_enabled(dev, IOMMU_DEV_FEAT_AUX))
@@ -381,12 +401,12 @@ int intel_svm_bind_gpasid(struct iommu_domain *domain, struct device *dev,
         * each bind of a new device even with an existing PASID, we need to
         * call the nested mode setup function here.
         */
-       spin_lock(&iommu->lock);
+       spin_lock_irqsave(&iommu->lock, iflags);
        ret = intel_pasid_setup_nested(iommu, dev,
                                       (pgd_t *)(uintptr_t)data->gpgd,
                                       data->hpasid, &data->vendor.vtd, dmar_domain,
                                       data->addr_width);
-       spin_unlock(&iommu->lock);
+       spin_unlock_irqrestore(&iommu->lock, iflags);
        if (ret) {
                dev_err_ratelimited(dev, "Failed to set up PASID %llu in nested mode, Err %d\n",
                                    data->hpasid, ret);
@@ -486,6 +506,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
        struct device_domain_info *info;
        struct intel_svm_dev *sdev;
        struct intel_svm *svm = NULL;
+       unsigned long iflags;
        int pasid_max;
        int ret;
 
@@ -546,6 +567,7 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
                goto out;
        }
        sdev->dev = dev;
+       sdev->iommu = iommu;
 
        ret = intel_iommu_enable_pasid(iommu, dev);
        if (ret) {
@@ -575,7 +597,6 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
                        kfree(sdev);
                        goto out;
                }
-               svm->iommu = iommu;
 
                if (pasid_max > intel_pasid_max_id)
                        pasid_max = intel_pasid_max_id;
@@ -605,14 +626,14 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
                        }
                }
 
-               spin_lock(&iommu->lock);
+               spin_lock_irqsave(&iommu->lock, iflags);
                ret = intel_pasid_setup_first_level(iommu, dev,
                                mm ? mm->pgd : init_mm.pgd,
                                svm->pasid, FLPT_DEFAULT_DID,
                                (mm ? 0 : PASID_FLAG_SUPERVISOR_MODE) |
                                (cpu_feature_enabled(X86_FEATURE_LA57) ?
                                 PASID_FLAG_FL5LP : 0));
-               spin_unlock(&iommu->lock);
+               spin_unlock_irqrestore(&iommu->lock, iflags);
                if (ret) {
                        if (mm)
                                mmu_notifier_unregister(&svm->notifier, mm);
@@ -632,14 +653,14 @@ intel_svm_bind_mm(struct device *dev, unsigned int flags,
                 * Binding a new device with existing PASID, need to setup
                 * the PASID entry.
                 */
-               spin_lock(&iommu->lock);
+               spin_lock_irqsave(&iommu->lock, iflags);
                ret = intel_pasid_setup_first_level(iommu, dev,
                                                mm ? mm->pgd : init_mm.pgd,
                                                svm->pasid, FLPT_DEFAULT_DID,
                                                (mm ? 0 : PASID_FLAG_SUPERVISOR_MODE) |
                                                (cpu_feature_enabled(X86_FEATURE_LA57) ?
                                                PASID_FLAG_FL5LP : 0));
-               spin_unlock(&iommu->lock);
+               spin_unlock_irqrestore(&iommu->lock, iflags);
                if (ret) {
                        kfree(sdev);
                        goto out;
index 4bb3293..d20b8b3 100644 (file)
@@ -358,7 +358,7 @@ static void private_free_iova(struct iova_domain *iovad, struct iova *iova)
  * @iovad: - iova domain in question.
  * @pfn: - page frame number
  * This function finds and returns an iova belonging to the
- * given doamin which matches the given pfn.
+ * given domain which matches the given pfn.
  */
 struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn)
 {
@@ -601,7 +601,7 @@ void queue_iova(struct iova_domain *iovad,
 EXPORT_SYMBOL_GPL(queue_iova);
 
 /**
- * put_iova_domain - destroys the iova doamin
+ * put_iova_domain - destroys the iova domain
  * @iovad: - iova domain in question.
  * All the iova's in that domain are destroyed.
  */
@@ -712,9 +712,9 @@ EXPORT_SYMBOL_GPL(reserve_iova);
 
 /**
  * copy_reserved_iova - copies the reserved between domains
- * @from: - source doamin from where to copy
+ * @from: - source domain from where to copy
  * @to: - destination domin where to copy
- * This function copies reserved iova's from one doamin to
+ * This function copies reserved iova's from one domain to
  * other.
  */
 void
index 94920a5..b147f22 100644 (file)
@@ -493,8 +493,9 @@ config TI_SCI_INTA_IRQCHIP
          TI System Controller, say Y here. Otherwise, say N.
 
 config TI_PRUSS_INTC
-       tristate "TI PRU-ICSS Interrupt Controller"
-       depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX || SOC_DRA7XX || ARCH_KEYSTONE || ARCH_K3
+       tristate
+       depends on TI_PRUSS
+       default TI_PRUSS
        select IRQ_DOMAIN
        help
          This enables support for the PRU-ICSS Local Interrupt Controller
index 5f5eb88..25c9a9c 100644 (file)
@@ -167,7 +167,7 @@ static void bcm2836_arm_irqchip_handle_ipi(struct irq_desc *desc)
        chained_irq_exit(chip, desc);
 }
 
-static void bcm2836_arm_irqchip_ipi_eoi(struct irq_data *d)
+static void bcm2836_arm_irqchip_ipi_ack(struct irq_data *d)
 {
        int cpu = smp_processor_id();
 
@@ -195,7 +195,7 @@ static struct irq_chip bcm2836_arm_irqchip_ipi = {
        .name           = "IPI",
        .irq_mask       = bcm2836_arm_irqchip_dummy_op,
        .irq_unmask     = bcm2836_arm_irqchip_dummy_op,
-       .irq_eoi        = bcm2836_arm_irqchip_ipi_eoi,
+       .irq_ack        = bcm2836_arm_irqchip_ipi_ack,
        .ipi_send_mask  = bcm2836_arm_irqchip_ipi_send_mask,
 };
 
index 9ed1bc4..09b91b8 100644 (file)
@@ -142,8 +142,8 @@ static void liointc_resume(struct irq_chip_generic *gc)
 
 static const char * const parent_names[] = {"int0", "int1", "int2", "int3"};
 
-int __init liointc_of_init(struct device_node *node,
-                               struct device_node *parent)
+static int __init liointc_of_init(struct device_node *node,
+                                 struct device_node *parent)
 {
        struct irq_chip_generic *gc;
        struct irq_domain *domain;
index 95d4fd8..0bbb0b2 100644 (file)
@@ -197,6 +197,13 @@ static int mips_cpu_ipi_alloc(struct irq_domain *domain, unsigned int virq,
                if (ret)
                        return ret;
 
+               ret = irq_domain_set_hwirq_and_chip(domain->parent, virq + i, hwirq,
+                                                   &mips_mt_cpu_irq_controller,
+                                                   NULL);
+
+               if (ret)
+                       return ret;
+
                ret = irq_set_irq_type(virq + i, IRQ_TYPE_LEVEL_HIGH);
                if (ret)
                        return ret;
index 0aa50d0..fbb3544 100644 (file)
@@ -66,7 +66,7 @@ static int sl28cpld_intc_probe(struct platform_device *pdev)
        irqchip->chip.num_regs = 1;
        irqchip->chip.status_base = base + INTC_IP;
        irqchip->chip.mask_base = base + INTC_IE;
-       irqchip->chip.mask_invert = true,
+       irqchip->chip.mask_invert = true;
        irqchip->chip.ack_base = base + INTC_IP;
 
        return devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev),
index 26cf0ac..c9a53c2 100644 (file)
@@ -13,6 +13,7 @@ if MISDN != n
 config MISDN_DSP
        tristate "Digital Audio Processing of transparent data"
        depends on MISDN
+       select BITREVERSE
        help
          Enable support for digital audio processing capability.
 
index 8f39f9b..4c2ce21 100644 (file)
@@ -19,6 +19,7 @@ if NVM
 
 config NVM_PBLK
        tristate "Physical Block Device Open-Channel SSD target"
+       select CRC32
        help
          Allows an open-channel SSD to be exposed as a block device to the
          host. The target assumes the device exposes raw flash and must be
index c1bcac7..28ddcaa 100644 (file)
@@ -844,11 +844,10 @@ static int nvm_bb_chunk_sense(struct nvm_dev *dev, struct ppa_addr ppa)
        rqd.ppa_addr = generic_to_dev_addr(dev, ppa);
 
        ret = nvm_submit_io_sync_raw(dev, &rqd);
+       __free_page(page);
        if (ret)
                return ret;
 
-       __free_page(page);
-
        return rqd.error;
 }
 
index b7e2d96..9e44c09 100644 (file)
@@ -605,6 +605,7 @@ config DM_INTEGRITY
        select BLK_DEV_INTEGRITY
        select DM_BUFIO
        select CRYPTO
+       select CRYPTO_SKCIPHER
        select ASYNC_XOR
        help
          This device-mapper target emulates a block device that has
@@ -622,6 +623,7 @@ config DM_ZONED
        tristate "Drive-managed zoned block device target support"
        depends on BLK_DEV_DM
        depends on BLK_DEV_ZONED
+       select CRC32
        help
          This device-mapper target takes a host-managed or host-aware zoned
          block device and exposes most of its capacity as a regular block
index 6469223..d636b7b 100644 (file)
@@ -17,7 +17,7 @@ struct feature {
 };
 
 static struct feature feature_list[] = {
-       {BCH_FEATURE_INCOMPAT, BCH_FEATURE_INCOMPAT_LARGE_BUCKET,
+       {BCH_FEATURE_INCOMPAT, BCH_FEATURE_INCOMPAT_LOG_LARGE_BUCKET_SIZE,
                "large_bucket"},
        {0, 0, 0 },
 };
index a1653c4..84fc2c0 100644 (file)
 
 /* Feature set definition */
 /* Incompat feature set */
-#define BCH_FEATURE_INCOMPAT_LARGE_BUCKET      0x0001 /* 32bit bucket size */
+/* 32bit bucket size, obsoleted */
+#define BCH_FEATURE_INCOMPAT_OBSO_LARGE_BUCKET         0x0001
+/* real bucket size is (1 << bucket_size) */
+#define BCH_FEATURE_INCOMPAT_LOG_LARGE_BUCKET_SIZE     0x0002
 
-#define BCH_FEATURE_COMPAT_SUUP                0
-#define BCH_FEATURE_RO_COMPAT_SUUP     0
-#define BCH_FEATURE_INCOMPAT_SUUP      BCH_FEATURE_INCOMPAT_LARGE_BUCKET
+#define BCH_FEATURE_COMPAT_SUPP                0
+#define BCH_FEATURE_RO_COMPAT_SUPP     0
+#define BCH_FEATURE_INCOMPAT_SUPP      (BCH_FEATURE_INCOMPAT_OBSO_LARGE_BUCKET| \
+                                        BCH_FEATURE_INCOMPAT_LOG_LARGE_BUCKET_SIZE)
 
 #define BCH_HAS_COMPAT_FEATURE(sb, mask) \
                ((sb)->feature_compat & (mask))
@@ -77,7 +81,23 @@ static inline void bch_clear_feature_##name(struct cache_sb *sb) \
                ~BCH##_FEATURE_INCOMPAT_##flagname; \
 }
 
-BCH_FEATURE_INCOMPAT_FUNCS(large_bucket, LARGE_BUCKET);
+BCH_FEATURE_INCOMPAT_FUNCS(obso_large_bucket, OBSO_LARGE_BUCKET);
+BCH_FEATURE_INCOMPAT_FUNCS(large_bucket, LOG_LARGE_BUCKET_SIZE);
+
+static inline bool bch_has_unknown_compat_features(struct cache_sb *sb)
+{
+       return ((sb->feature_compat & ~BCH_FEATURE_COMPAT_SUPP) != 0);
+}
+
+static inline bool bch_has_unknown_ro_compat_features(struct cache_sb *sb)
+{
+       return ((sb->feature_ro_compat & ~BCH_FEATURE_RO_COMPAT_SUPP) != 0);
+}
+
+static inline bool bch_has_unknown_incompat_features(struct cache_sb *sb)
+{
+       return ((sb->feature_incompat & ~BCH_FEATURE_INCOMPAT_SUPP) != 0);
+}
 
 int bch_print_cache_set_feature_compat(struct cache_set *c, char *buf, int size);
 int bch_print_cache_set_feature_ro_compat(struct cache_set *c, char *buf, int size);
index a4752ac..2047a9c 100644 (file)
@@ -64,9 +64,25 @@ static unsigned int get_bucket_size(struct cache_sb *sb, struct cache_sb_disk *s
 {
        unsigned int bucket_size = le16_to_cpu(s->bucket_size);
 
-       if (sb->version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES &&
-            bch_has_feature_large_bucket(sb))
-               bucket_size |= le16_to_cpu(s->bucket_size_hi) << 16;
+       if (sb->version >= BCACHE_SB_VERSION_CDEV_WITH_FEATURES) {
+               if (bch_has_feature_large_bucket(sb)) {
+                       unsigned int max, order;
+
+                       max = sizeof(unsigned int) * BITS_PER_BYTE - 1;
+                       order = le16_to_cpu(s->bucket_size);
+                       /*
+                        * bcache tool will make sure the overflow won't
+                        * happen, an error message here is enough.
+                        */
+                       if (order > max)
+                               pr_err("Bucket size (1 << %u) overflows\n",
+                                       order);
+                       bucket_size = 1 << order;
+               } else if (bch_has_feature_obso_large_bucket(sb)) {
+                       bucket_size +=
+                               le16_to_cpu(s->obso_bucket_size_hi) << 16;
+               }
+       }
 
        return bucket_size;
 }
@@ -228,6 +244,20 @@ static const char *read_super(struct cache_sb *sb, struct block_device *bdev,
                sb->feature_compat = le64_to_cpu(s->feature_compat);
                sb->feature_incompat = le64_to_cpu(s->feature_incompat);
                sb->feature_ro_compat = le64_to_cpu(s->feature_ro_compat);
+
+               /* Check incompatible features */
+               err = "Unsupported compatible feature found";
+               if (bch_has_unknown_compat_features(sb))
+                       goto err;
+
+               err = "Unsupported read-only compatible feature found";
+               if (bch_has_unknown_ro_compat_features(sb))
+                       goto err;
+
+               err = "Unsupported incompatible feature found";
+               if (bch_has_unknown_incompat_features(sb))
+                       goto err;
+
                err = read_super_common(sb, bdev, s);
                if (err)
                        goto err;
@@ -1302,6 +1332,12 @@ int bch_cached_dev_attach(struct cached_dev *dc, struct cache_set *c,
        bcache_device_link(&dc->disk, c, "bdev");
        atomic_inc(&c->attached_dev_nr);
 
+       if (bch_has_feature_obso_large_bucket(&(c->cache->sb))) {
+               pr_err("The obsoleted large bucket layout is unsupported, set the bcache device into read-only\n");
+               pr_err("Please update to the latest bcache-tools to create the cache device\n");
+               set_disk_ro(dc->disk.disk, 1);
+       }
+
        /* Allow the writeback thread to proceed */
        up_write(&dc->writeback_lock);
 
@@ -1524,6 +1560,12 @@ static int flash_dev_run(struct cache_set *c, struct uuid_entry *u)
 
        bcache_device_link(d, c, "volume");
 
+       if (bch_has_feature_obso_large_bucket(&c->cache->sb)) {
+               pr_err("The obsoleted large bucket layout is unsupported, set the bcache device into read-only\n");
+               pr_err("Please update to the latest bcache-tools to create the cache device\n");
+               set_disk_ro(d->disk, 1);
+       }
+
        return 0;
 err:
        kobject_put(&d->kobj);
@@ -2083,6 +2125,9 @@ static int run_cache_set(struct cache_set *c)
        c->cache->sb.last_mount = (u32)ktime_get_real_seconds();
        bcache_write_super(c);
 
+       if (bch_has_feature_obso_large_bucket(&c->cache->sb))
+               pr_err("Detect obsoleted large bucket layout, all attached bcache device will be read-only\n");
+
        list_for_each_entry_safe(dc, t, &uncached_devices, list)
                bch_cached_dev_attach(dc, c, NULL);
 
@@ -2644,8 +2689,8 @@ static ssize_t bch_pending_bdevs_cleanup(struct kobject *k,
        }
 
        list_for_each_entry_safe(pdev, tpdev, &pending_devs, list) {
+               char *pdev_set_uuid = pdev->dc->sb.set_uuid;
                list_for_each_entry_safe(c, tc, &bch_cache_sets, list) {
-                       char *pdev_set_uuid = pdev->dc->sb.set_uuid;
                        char *set_uuid = c->set_uuid;
 
                        if (!memcmp(pdev_set_uuid, set_uuid, 16)) {
index 9c1a86b..fce4cbf 100644 (file)
@@ -1534,6 +1534,12 @@ sector_t dm_bufio_get_device_size(struct dm_bufio_client *c)
 }
 EXPORT_SYMBOL_GPL(dm_bufio_get_device_size);
 
+struct dm_io_client *dm_bufio_get_dm_io_client(struct dm_bufio_client *c)
+{
+       return c->dm_io;
+}
+EXPORT_SYMBOL_GPL(dm_bufio_get_dm_io_client);
+
 sector_t dm_bufio_get_block_number(struct dm_buffer *b)
 {
        return b->block;
index 5f9f9b3..5a55617 100644 (file)
@@ -1454,13 +1454,16 @@ static int crypt_convert_block_skcipher(struct crypt_config *cc,
 static void kcryptd_async_done(struct crypto_async_request *async_req,
                               int error);
 
-static void crypt_alloc_req_skcipher(struct crypt_config *cc,
+static int crypt_alloc_req_skcipher(struct crypt_config *cc,
                                     struct convert_context *ctx)
 {
        unsigned key_index = ctx->cc_sector & (cc->tfms_count - 1);
 
-       if (!ctx->r.req)
-               ctx->r.req = mempool_alloc(&cc->req_pool, GFP_NOIO);
+       if (!ctx->r.req) {
+               ctx->r.req = mempool_alloc(&cc->req_pool, in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
+               if (!ctx->r.req)
+                       return -ENOMEM;
+       }
 
        skcipher_request_set_tfm(ctx->r.req, cc->cipher_tfm.tfms[key_index]);
 
@@ -1471,13 +1474,18 @@ static void crypt_alloc_req_skcipher(struct crypt_config *cc,
        skcipher_request_set_callback(ctx->r.req,
            CRYPTO_TFM_REQ_MAY_BACKLOG,
            kcryptd_async_done, dmreq_of_req(cc, ctx->r.req));
+
+       return 0;
 }
 
-static void crypt_alloc_req_aead(struct crypt_config *cc,
+static int crypt_alloc_req_aead(struct crypt_config *cc,
                                 struct convert_context *ctx)
 {
-       if (!ctx->r.req_aead)
-               ctx->r.req_aead = mempool_alloc(&cc->req_pool, GFP_NOIO);
+       if (!ctx->r.req_aead) {
+               ctx->r.req_aead = mempool_alloc(&cc->req_pool, in_interrupt() ? GFP_ATOMIC : GFP_NOIO);
+               if (!ctx->r.req_aead)
+                       return -ENOMEM;
+       }
 
        aead_request_set_tfm(ctx->r.req_aead, cc->cipher_tfm.tfms_aead[0]);
 
@@ -1488,15 +1496,17 @@ static void crypt_alloc_req_aead(struct crypt_config *cc,
        aead_request_set_callback(ctx->r.req_aead,
            CRYPTO_TFM_REQ_MAY_BACKLOG,
            kcryptd_async_done, dmreq_of_req(cc, ctx->r.req_aead));
+
+       return 0;
 }
 
-static void crypt_alloc_req(struct crypt_config *cc,
+static int crypt_alloc_req(struct crypt_config *cc,
                            struct convert_context *ctx)
 {
        if (crypt_integrity_aead(cc))
-               crypt_alloc_req_aead(cc, ctx);
+               return crypt_alloc_req_aead(cc, ctx);
        else
-               crypt_alloc_req_skcipher(cc, ctx);
+               return crypt_alloc_req_skcipher(cc, ctx);
 }
 
 static void crypt_free_req_skcipher(struct crypt_config *cc,
@@ -1529,17 +1539,28 @@ static void crypt_free_req(struct crypt_config *cc, void *req, struct bio *base_
  * Encrypt / decrypt data from one bio to another one (can be the same one)
  */
 static blk_status_t crypt_convert(struct crypt_config *cc,
-                        struct convert_context *ctx, bool atomic)
+                        struct convert_context *ctx, bool atomic, bool reset_pending)
 {
        unsigned int tag_offset = 0;
        unsigned int sector_step = cc->sector_size >> SECTOR_SHIFT;
        int r;
 
-       atomic_set(&ctx->cc_pending, 1);
+       /*
+        * if reset_pending is set we are dealing with the bio for the first time,
+        * else we're continuing to work on the previous bio, so don't mess with
+        * the cc_pending counter
+        */
+       if (reset_pending)
+               atomic_set(&ctx->cc_pending, 1);
 
        while (ctx->iter_in.bi_size && ctx->iter_out.bi_size) {
 
-               crypt_alloc_req(cc, ctx);
+               r = crypt_alloc_req(cc, ctx);
+               if (r) {
+                       complete(&ctx->restart);
+                       return BLK_STS_DEV_RESOURCE;
+               }
+
                atomic_inc(&ctx->cc_pending);
 
                if (crypt_integrity_aead(cc))
@@ -1553,7 +1574,25 @@ static blk_status_t crypt_convert(struct crypt_config *cc,
                 * but the driver request queue is full, let's wait.
                 */
                case -EBUSY:
-                       wait_for_completion(&ctx->restart);
+                       if (in_interrupt()) {
+                               if (try_wait_for_completion(&ctx->restart)) {
+                                       /*
+                                        * we don't have to block to wait for completion,
+                                        * so proceed
+                                        */
+                               } else {
+                                       /*
+                                        * we can't wait for completion without blocking
+                                        * exit and continue processing in a workqueue
+                                        */
+                                       ctx->r.req = NULL;
+                                       ctx->cc_sector += sector_step;
+                                       tag_offset++;
+                                       return BLK_STS_DEV_RESOURCE;
+                               }
+                       } else {
+                               wait_for_completion(&ctx->restart);
+                       }
                        reinit_completion(&ctx->restart);
                        fallthrough;
                /*
@@ -1691,6 +1730,12 @@ static void crypt_inc_pending(struct dm_crypt_io *io)
        atomic_inc(&io->io_pending);
 }
 
+static void kcryptd_io_bio_endio(struct work_struct *work)
+{
+       struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);
+       bio_endio(io->base_bio);
+}
+
 /*
  * One of the bios was finished. Check for completion of
  * the whole request and correctly clean up the buffer.
@@ -1713,7 +1758,23 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
                kfree(io->integrity_metadata);
 
        base_bio->bi_status = error;
-       bio_endio(base_bio);
+
+       /*
+        * If we are running this function from our tasklet,
+        * we can't call bio_endio() here, because it will call
+        * clone_endio() from dm.c, which in turn will
+        * free the current struct dm_crypt_io structure with
+        * our tasklet. In this case we need to delay bio_endio()
+        * execution to after the tasklet is done and dequeued.
+        */
+       if (tasklet_trylock(&io->tasklet)) {
+               tasklet_unlock(&io->tasklet);
+               bio_endio(base_bio);
+               return;
+       }
+
+       INIT_WORK(&io->work, kcryptd_io_bio_endio);
+       queue_work(cc->io_queue, &io->work);
 }
 
 /*
@@ -1945,6 +2006,37 @@ static bool kcryptd_crypt_write_inline(struct crypt_config *cc,
        }
 }
 
+static void kcryptd_crypt_write_continue(struct work_struct *work)
+{
+       struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);
+       struct crypt_config *cc = io->cc;
+       struct convert_context *ctx = &io->ctx;
+       int crypt_finished;
+       sector_t sector = io->sector;
+       blk_status_t r;
+
+       wait_for_completion(&ctx->restart);
+       reinit_completion(&ctx->restart);
+
+       r = crypt_convert(cc, &io->ctx, true, false);
+       if (r)
+               io->error = r;
+       crypt_finished = atomic_dec_and_test(&ctx->cc_pending);
+       if (!crypt_finished && kcryptd_crypt_write_inline(cc, ctx)) {
+               /* Wait for completion signaled by kcryptd_async_done() */
+               wait_for_completion(&ctx->restart);
+               crypt_finished = 1;
+       }
+
+       /* Encryption was already finished, submit io now */
+       if (crypt_finished) {
+               kcryptd_crypt_write_io_submit(io, 0);
+               io->sector = sector;
+       }
+
+       crypt_dec_pending(io);
+}
+
 static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
 {
        struct crypt_config *cc = io->cc;
@@ -1973,7 +2065,17 @@ static void kcryptd_crypt_write_convert(struct dm_crypt_io *io)
 
        crypt_inc_pending(io);
        r = crypt_convert(cc, ctx,
-                         test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags));
+                         test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags), true);
+       /*
+        * Crypto API backlogged the request, because its queue was full
+        * and we're in softirq context, so continue from a workqueue
+        * (TODO: is it actually possible to be in softirq in the write path?)
+        */
+       if (r == BLK_STS_DEV_RESOURCE) {
+               INIT_WORK(&io->work, kcryptd_crypt_write_continue);
+               queue_work(cc->crypt_queue, &io->work);
+               return;
+       }
        if (r)
                io->error = r;
        crypt_finished = atomic_dec_and_test(&ctx->cc_pending);
@@ -1998,6 +2100,25 @@ static void kcryptd_crypt_read_done(struct dm_crypt_io *io)
        crypt_dec_pending(io);
 }
 
+static void kcryptd_crypt_read_continue(struct work_struct *work)
+{
+       struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);
+       struct crypt_config *cc = io->cc;
+       blk_status_t r;
+
+       wait_for_completion(&io->ctx.restart);
+       reinit_completion(&io->ctx.restart);
+
+       r = crypt_convert(cc, &io->ctx, true, false);
+       if (r)
+               io->error = r;
+
+       if (atomic_dec_and_test(&io->ctx.cc_pending))
+               kcryptd_crypt_read_done(io);
+
+       crypt_dec_pending(io);
+}
+
 static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
 {
        struct crypt_config *cc = io->cc;
@@ -2009,7 +2130,16 @@ static void kcryptd_crypt_read_convert(struct dm_crypt_io *io)
                           io->sector);
 
        r = crypt_convert(cc, &io->ctx,
-                         test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags));
+                         test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags), true);
+       /*
+        * Crypto API backlogged the request, because its queue was full
+        * and we're in softirq context, so continue from a workqueue
+        */
+       if (r == BLK_STS_DEV_RESOURCE) {
+               INIT_WORK(&io->work, kcryptd_crypt_read_continue);
+               queue_work(cc->crypt_queue, &io->work);
+               return;
+       }
        if (r)
                io->error = r;
 
@@ -2091,8 +2221,12 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io)
 
        if ((bio_data_dir(io->base_bio) == READ && test_bit(DM_CRYPT_NO_READ_WORKQUEUE, &cc->flags)) ||
            (bio_data_dir(io->base_bio) == WRITE && test_bit(DM_CRYPT_NO_WRITE_WORKQUEUE, &cc->flags))) {
-               if (in_irq()) {
-                       /* Crypto API's "skcipher_walk_first() refuses to work in hard IRQ context */
+               /*
+                * in_irq(): Crypto API's skcipher_walk_first() refuses to work in hard IRQ context.
+                * irqs_disabled(): the kernel may run some IO completion from the idle thread, but
+                * it is being executed with irqs disabled.
+                */
+               if (in_irq() || irqs_disabled()) {
                        tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work);
                        tasklet_schedule(&io->tasklet);
                        return;
@@ -3166,12 +3300,11 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
 
        if (test_bit(DM_CRYPT_SAME_CPU, &cc->flags))
-               cc->crypt_queue = alloc_workqueue("kcryptd-%s", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM,
+               cc->crypt_queue = alloc_workqueue("kcryptd/%s", WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM,
                                                  1, devname);
        else
-               cc->crypt_queue = alloc_workqueue("kcryptd-%s",
-                                                 WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM |
-                                                 WQ_UNBOUND | WQ_SYSFS,
+               cc->crypt_queue = alloc_workqueue("kcryptd/%s",
+                                                 WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM | WQ_UNBOUND,
                                                  num_online_cpus(), devname);
        if (!cc->crypt_queue) {
                ti->error = "Couldn't create kcryptd queue";
index 5a7a1b9..b64fede 100644 (file)
@@ -257,8 +257,9 @@ struct dm_integrity_c {
        bool journal_uptodate;
        bool just_formatted;
        bool recalculate_flag;
-       bool fix_padding;
        bool discard;
+       bool fix_padding;
+       bool legacy_recalculate;
 
        struct alg_spec internal_hash_alg;
        struct alg_spec journal_crypt_alg;
@@ -386,6 +387,14 @@ static int dm_integrity_failed(struct dm_integrity_c *ic)
        return READ_ONCE(ic->failed);
 }
 
+static bool dm_integrity_disable_recalculate(struct dm_integrity_c *ic)
+{
+       if ((ic->internal_hash_alg.key || ic->journal_mac_alg.key) &&
+           !ic->legacy_recalculate)
+               return true;
+       return false;
+}
+
 static commit_id_t dm_integrity_commit_id(struct dm_integrity_c *ic, unsigned i,
                                          unsigned j, unsigned char seq)
 {
@@ -1379,12 +1388,52 @@ thorough_test:
 #undef MAY_BE_HASH
 }
 
-static void dm_integrity_flush_buffers(struct dm_integrity_c *ic)
+struct flush_request {
+       struct dm_io_request io_req;
+       struct dm_io_region io_reg;
+       struct dm_integrity_c *ic;
+       struct completion comp;
+};
+
+static void flush_notify(unsigned long error, void *fr_)
+{
+       struct flush_request *fr = fr_;
+       if (unlikely(error != 0))
+               dm_integrity_io_error(fr->ic, "flusing disk cache", -EIO);
+       complete(&fr->comp);
+}
+
+static void dm_integrity_flush_buffers(struct dm_integrity_c *ic, bool flush_data)
 {
        int r;
+
+       struct flush_request fr;
+
+       if (!ic->meta_dev)
+               flush_data = false;
+       if (flush_data) {
+               fr.io_req.bi_op = REQ_OP_WRITE,
+               fr.io_req.bi_op_flags = REQ_PREFLUSH | REQ_SYNC,
+               fr.io_req.mem.type = DM_IO_KMEM,
+               fr.io_req.mem.ptr.addr = NULL,
+               fr.io_req.notify.fn = flush_notify,
+               fr.io_req.notify.context = &fr;
+               fr.io_req.client = dm_bufio_get_dm_io_client(ic->bufio),
+               fr.io_reg.bdev = ic->dev->bdev,
+               fr.io_reg.sector = 0,
+               fr.io_reg.count = 0,
+               fr.ic = ic;
+               init_completion(&fr.comp);
+               r = dm_io(&fr.io_req, 1, &fr.io_reg, NULL);
+               BUG_ON(r);
+       }
+
        r = dm_bufio_write_dirty_buffers(ic->bufio);
        if (unlikely(r))
                dm_integrity_io_error(ic, "writing tags", r);
+
+       if (flush_data)
+               wait_for_completion(&fr.comp);
 }
 
 static void sleep_on_endio_wait(struct dm_integrity_c *ic)
@@ -2110,7 +2159,7 @@ offload_to_thread:
 
        if (unlikely(dio->op == REQ_OP_DISCARD) && likely(ic->mode != 'D')) {
                integrity_metadata(&dio->work);
-               dm_integrity_flush_buffers(ic);
+               dm_integrity_flush_buffers(ic, false);
 
                dio->in_flight = (atomic_t)ATOMIC_INIT(1);
                dio->completion = NULL;
@@ -2195,7 +2244,7 @@ static void integrity_commit(struct work_struct *w)
        flushes = bio_list_get(&ic->flush_bio_list);
        if (unlikely(ic->mode != 'J')) {
                spin_unlock_irq(&ic->endio_wait.lock);
-               dm_integrity_flush_buffers(ic);
+               dm_integrity_flush_buffers(ic, true);
                goto release_flush_bios;
        }
 
@@ -2409,7 +2458,7 @@ skip_io:
        complete_journal_op(&comp);
        wait_for_completion_io(&comp.comp);
 
-       dm_integrity_flush_buffers(ic);
+       dm_integrity_flush_buffers(ic, true);
 }
 
 static void integrity_writer(struct work_struct *w)
@@ -2451,7 +2500,7 @@ static void recalc_write_super(struct dm_integrity_c *ic)
 {
        int r;
 
-       dm_integrity_flush_buffers(ic);
+       dm_integrity_flush_buffers(ic, false);
        if (dm_integrity_failed(ic))
                return;
 
@@ -2654,7 +2703,7 @@ static void bitmap_flush_work(struct work_struct *work)
        unsigned long limit;
        struct bio *bio;
 
-       dm_integrity_flush_buffers(ic);
+       dm_integrity_flush_buffers(ic, false);
 
        range.logical_sector = 0;
        range.n_sectors = ic->provided_data_sectors;
@@ -2663,9 +2712,7 @@ static void bitmap_flush_work(struct work_struct *work)
        add_new_range_and_wait(ic, &range);
        spin_unlock_irq(&ic->endio_wait.lock);
 
-       dm_integrity_flush_buffers(ic);
-       if (ic->meta_dev)
-               blkdev_issue_flush(ic->dev->bdev, GFP_NOIO);
+       dm_integrity_flush_buffers(ic, true);
 
        limit = ic->provided_data_sectors;
        if (ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING)) {
@@ -2934,11 +2981,11 @@ static void dm_integrity_postsuspend(struct dm_target *ti)
                if (ic->meta_dev)
                        queue_work(ic->writer_wq, &ic->writer_work);
                drain_workqueue(ic->writer_wq);
-               dm_integrity_flush_buffers(ic);
+               dm_integrity_flush_buffers(ic, true);
        }
 
        if (ic->mode == 'B') {
-               dm_integrity_flush_buffers(ic);
+               dm_integrity_flush_buffers(ic, true);
 #if 1
                /* set to 0 to test bitmap replay code */
                init_journal(ic, 0, ic->journal_sections, 0);
@@ -3102,6 +3149,7 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type,
                arg_count += !!ic->journal_crypt_alg.alg_string;
                arg_count += !!ic->journal_mac_alg.alg_string;
                arg_count += (ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0;
+               arg_count += ic->legacy_recalculate;
                DMEMIT("%s %llu %u %c %u", ic->dev->name, ic->start,
                       ic->tag_size, ic->mode, arg_count);
                if (ic->meta_dev)
@@ -3125,6 +3173,8 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type,
                }
                if ((ic->sb->flags & cpu_to_le32(SB_FLAG_FIXED_PADDING)) != 0)
                        DMEMIT(" fix_padding");
+               if (ic->legacy_recalculate)
+                       DMEMIT(" legacy_recalculate");
 
 #define EMIT_ALG(a, n)                                                 \
                do {                                                    \
@@ -3754,7 +3804,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
        unsigned extra_args;
        struct dm_arg_set as;
        static const struct dm_arg _args[] = {
-               {0, 9, "Invalid number of feature args"},
+               {0, 16, "Invalid number of feature args"},
        };
        unsigned journal_sectors, interleave_sectors, buffer_sectors, journal_watermark, sync_msec;
        bool should_write_sb;
@@ -3902,6 +3952,8 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
                        ic->discard = true;
                } else if (!strcmp(opt_string, "fix_padding")) {
                        ic->fix_padding = true;
+               } else if (!strcmp(opt_string, "legacy_recalculate")) {
+                       ic->legacy_recalculate = true;
                } else {
                        r = -EINVAL;
                        ti->error = "Invalid argument";
@@ -4197,6 +4249,20 @@ try_smaller_buffer:
                        r = -ENOMEM;
                        goto bad;
                }
+       } else {
+               if (ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING)) {
+                       ti->error = "Recalculate can only be specified with internal_hash";
+                       r = -EINVAL;
+                       goto bad;
+               }
+       }
+
+       if (ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING) &&
+           le64_to_cpu(ic->sb->recalc_sector) < ic->provided_data_sectors &&
+           dm_integrity_disable_recalculate(ic)) {
+               ti->error = "Recalculating with HMAC is disabled for security reasons - if you really need it, use the argument \"legacy_recalculate\"";
+               r = -EOPNOTSUPP;
+               goto bad;
        }
 
        ic->bufio = dm_bufio_client_create(ic->meta_dev ? ic->meta_dev->bdev : ic->dev->bdev,
index 23c3877..cab12b2 100644 (file)
@@ -3729,10 +3729,10 @@ static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
        blk_limits_io_opt(limits, chunk_size_bytes * mddev_data_stripes(rs));
 
        /*
-        * RAID1 and RAID10 personalities require bio splitting,
-        * RAID0/4/5/6 don't and process large discard bios properly.
+        * RAID0 and RAID10 personalities require bio splitting,
+        * RAID1/4/5/6 don't and process large discard bios properly.
         */
-       if (rs_is_raid1(rs) || rs_is_raid10(rs)) {
+       if (rs_is_raid0(rs) || rs_is_raid10(rs)) {
                limits->discard_granularity = chunk_size_bytes;
                limits->max_discard_sectors = rs->md.chunk_sectors;
        }
index 4668b2c..11890db 100644 (file)
@@ -141,6 +141,11 @@ struct dm_snapshot {
         * for them to be committed.
         */
        struct bio_list bios_queued_during_merge;
+
+       /*
+        * Flush data after merge.
+        */
+       struct bio flush_bio;
 };
 
 /*
@@ -1121,6 +1126,17 @@ shut:
 
 static void error_bios(struct bio *bio);
 
+static int flush_data(struct dm_snapshot *s)
+{
+       struct bio *flush_bio = &s->flush_bio;
+
+       bio_reset(flush_bio);
+       bio_set_dev(flush_bio, s->origin->bdev);
+       flush_bio->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH;
+
+       return submit_bio_wait(flush_bio);
+}
+
 static void merge_callback(int read_err, unsigned long write_err, void *context)
 {
        struct dm_snapshot *s = context;
@@ -1134,6 +1150,11 @@ static void merge_callback(int read_err, unsigned long write_err, void *context)
                goto shut;
        }
 
+       if (flush_data(s) < 0) {
+               DMERR("Flush after merge failed: shutting down merge");
+               goto shut;
+       }
+
        if (s->store->type->commit_merge(s->store,
                                         s->num_merging_chunks) < 0) {
                DMERR("Write error in exception store: shutting down merge");
@@ -1318,6 +1339,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        s->first_merging_chunk = 0;
        s->num_merging_chunks = 0;
        bio_list_init(&s->bios_queued_during_merge);
+       bio_init(&s->flush_bio, NULL, 0);
 
        /* Allocate hash table for COW data */
        if (init_hash_tables(s)) {
@@ -1504,6 +1526,8 @@ static void snapshot_dtr(struct dm_target *ti)
 
        dm_exception_store_destroy(s->store);
 
+       bio_uninit(&s->flush_bio);
+
        dm_put_device(ti, s->cow);
 
        dm_put_device(ti, s->origin);
index 188f412..4acf234 100644 (file)
@@ -363,14 +363,23 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
 {
        int r;
        dev_t dev;
+       unsigned int major, minor;
+       char dummy;
        struct dm_dev_internal *dd;
        struct dm_table *t = ti->table;
 
        BUG_ON(!t);
 
-       dev = dm_get_dev_t(path);
-       if (!dev)
-               return -ENODEV;
+       if (sscanf(path, "%u:%u%c", &major, &minor, &dummy) == 2) {
+               /* Extract the major/minor numbers */
+               dev = MKDEV(major, minor);
+               if (MAJOR(dev) != major || MINOR(dev) != minor)
+                       return -EOVERFLOW;
+       } else {
+               dev = dm_get_dev_t(path);
+               if (!dev)
+                       return -ENODEV;
+       }
 
        dd = find_device(&t->devices, dev);
        if (!dd) {
index b3c3c8b..7bac564 100644 (file)
@@ -562,7 +562,7 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
                 * subset of the parent bdev; require extra privileges.
                 */
                if (!capable(CAP_SYS_RAWIO)) {
-                       DMWARN_LIMIT(
+                       DMDEBUG_LIMIT(
        "%s: sending ioctl %x to DM device without required privilege.",
                                current->comm, cmd);
                        r = -ENOIOCTLCMD;
index ca40942..0438445 100644 (file)
@@ -639,8 +639,10 @@ static void md_submit_flush_data(struct work_struct *ws)
         * could wait for this and below md_handle_request could wait for those
         * bios because of suspend check
         */
+       spin_lock_irq(&mddev->lock);
        mddev->prev_flush_start = mddev->start_flush;
        mddev->flush_bio = NULL;
+       spin_unlock_irq(&mddev->lock);
        wake_up(&mddev->sb_wait);
 
        if (bio->bi_iter.bi_size == 0) {
index 3a94715..ea6f8ee 100644 (file)
@@ -10,5 +10,6 @@ obj-$(CONFIG_CEC_MESON_AO)    += meson/
 obj-$(CONFIG_CEC_SAMSUNG_S5P)  += s5p/
 obj-$(CONFIG_CEC_SECO)         += seco/
 obj-$(CONFIG_CEC_STI)          += sti/
+obj-$(CONFIG_CEC_STM32)                += stm32/
 obj-$(CONFIG_CEC_TEGRA)                += tegra/
 
index 96d3b2b..3f61f58 100644 (file)
@@ -118,8 +118,7 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b)
                                return -EINVAL;
                }
        } else {
-               length = (b->memory == VB2_MEMORY_USERPTR ||
-                         b->memory == VB2_MEMORY_DMABUF)
+               length = (b->memory == VB2_MEMORY_USERPTR)
                        ? b->length : vb->planes[0].length;
 
                if (b->bytesused > length)
index eb7b6f0..58ca47e 100644 (file)
@@ -772,14 +772,8 @@ int ccs_pll_calculate(struct device *dev, const struct ccs_pll_limits *lim,
 
        switch (pll->bus_type) {
        case CCS_PLL_BUS_TYPE_CSI2_DPHY:
-               /* CSI transfers 2 bits per clock per lane; thus times 2 */
-               op_sys_clk_freq_hz_sdr = pll->link_freq * 2
-                       * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
-                          1 : pll->csi2.lanes);
-               break;
        case CCS_PLL_BUS_TYPE_CSI2_CPHY:
-               op_sys_clk_freq_hz_sdr =
-                       pll->link_freq
+               op_sys_clk_freq_hz_sdr = pll->link_freq * 2
                        * (pll->flags & CCS_PLL_FLAG_LANE_SPEED_MODEL ?
                           1 : pll->csi2.lanes);
                break;
index 9a6097b..6555bd4 100644 (file)
@@ -152,7 +152,7 @@ static int ccs_data_parse_version(struct bin_container *bin,
        vv->version_major = ((u16)v->static_data_version_major[0] << 8) +
                v->static_data_version_major[1];
        vv->version_minor = ((u16)v->static_data_version_minor[0] << 8) +
-               v->static_data_version_major[1];
+               v->static_data_version_minor[1];
        vv->date_year =  ((u16)v->year[0] << 8) + v->year[1];
        vv->date_month = v->month;
        vv->date_day = v->day;
index 36e354e..6cada8a 100644 (file)
@@ -302,7 +302,7 @@ static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q,
        if (!q->sensor)
                return -ENODEV;
 
-       freq = v4l2_get_link_rate(q->sensor->ctrl_handler, bpp, lanes);
+       freq = v4l2_get_link_freq(q->sensor->ctrl_handler, bpp, lanes);
        if (freq < 0) {
                dev_err(dev, "error %lld, invalid link_freq\n", freq);
                return freq;
index bdd293f..7233a73 100644 (file)
@@ -349,8 +349,10 @@ static void venus_core_shutdown(struct platform_device *pdev)
 {
        struct venus_core *core = platform_get_drvdata(pdev);
 
+       pm_runtime_get_sync(core->dev);
        venus_shutdown(core);
        venus_firmware_deinit(core);
+       pm_runtime_put_sync(core->dev);
 }
 
 static __maybe_unused int venus_runtime_suspend(struct device *dev)
index 98bff76..e48d666 100644 (file)
@@ -654,7 +654,7 @@ static int rvin_parallel_parse_of(struct rvin_dev *vin)
 out:
        fwnode_handle_put(fwnode);
 
-       return 0;
+       return ret;
 }
 
 static int rvin_parallel_init(struct rvin_dev *vin)
index be8f275..1524dc0 100644 (file)
@@ -320,7 +320,7 @@ again:
                                data->body);
                        spin_lock(&data->keylock);
                        if (scancode) {
-                               delay = nsecs_to_jiffies(dev->timeout) +
+                               delay = usecs_to_jiffies(dev->timeout) +
                                        msecs_to_jiffies(100);
                                mod_timer(&data->rx_timeout, jiffies + delay);
                        } else {
index a905113..0c62295 100644 (file)
@@ -1551,7 +1551,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
        rdev->s_rx_carrier_range = ite_set_rx_carrier_range;
        /* FIFO threshold is 17 bytes, so 17 * 8 samples minimum */
        rdev->min_timeout = 17 * 8 * ITE_BAUDRATE_DIVISOR *
-                           itdev->params.sample_period;
+                           itdev->params.sample_period / 1000;
        rdev->timeout = IR_DEFAULT_TIMEOUT;
        rdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
        rdev->rx_resolution = ITE_BAUDRATE_DIVISOR *
index 1d811e5..1fd62c1 100644 (file)
@@ -737,7 +737,7 @@ static unsigned int repeat_period(int protocol)
 void rc_repeat(struct rc_dev *dev)
 {
        unsigned long flags;
-       unsigned int timeout = nsecs_to_jiffies(dev->timeout) +
+       unsigned int timeout = usecs_to_jiffies(dev->timeout) +
                msecs_to_jiffies(repeat_period(dev->last_protocol));
        struct lirc_scancode sc = {
                .scancode = dev->last_scancode, .rc_proto = dev->last_protocol,
@@ -855,7 +855,7 @@ void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u64 scancode,
        ir_do_keydown(dev, protocol, scancode, keycode, toggle);
 
        if (dev->keypressed) {
-               dev->keyup_jiffies = jiffies + nsecs_to_jiffies(dev->timeout) +
+               dev->keyup_jiffies = jiffies + usecs_to_jiffies(dev->timeout) +
                        msecs_to_jiffies(repeat_period(protocol));
                mod_timer(&dev->timer_keyup, dev->keyup_jiffies);
        }
@@ -1928,6 +1928,8 @@ int rc_register_device(struct rc_dev *dev)
                        goto out_raw;
        }
 
+       dev->registered = true;
+
        rc = device_add(&dev->dev);
        if (rc)
                goto out_rx_free;
@@ -1937,8 +1939,6 @@ int rc_register_device(struct rc_dev *dev)
                 dev->device_name ?: "Unspecified device", path ?: "N/A");
        kfree(path);
 
-       dev->registered = true;
-
        /*
         * once the the input device is registered in rc_setup_rx_device,
         * userspace can open the input device and rc_open() will be called
index 8cc28c9..96ae029 100644 (file)
@@ -385,7 +385,7 @@ static irqreturn_t serial_ir_irq_handler(int i, void *blah)
        } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
 
        mod_timer(&serial_ir.timeout_timer,
-                 jiffies + nsecs_to_jiffies(serial_ir.rcdev->timeout));
+                 jiffies + usecs_to_jiffies(serial_ir.rcdev->timeout));
 
        ir_raw_event_handle(serial_ir.rcdev);
 
index 78007db..133d20e 100644 (file)
@@ -442,7 +442,7 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
 }
 EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt);
 
-s64 v4l2_get_link_rate(struct v4l2_ctrl_handler *handler, unsigned int mul,
+s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul,
                       unsigned int div)
 {
        struct v4l2_ctrl *ctrl;
@@ -473,4 +473,4 @@ s64 v4l2_get_link_rate(struct v4l2_ctrl_handler *handler, unsigned int mul,
 
        return freq > 0 ? freq : -EINVAL;
 }
-EXPORT_SYMBOL_GPL(v4l2_get_link_rate);
+EXPORT_SYMBOL_GPL(v4l2_get_link_freq);
index 2aa6648..5a491d2 100644 (file)
@@ -1512,6 +1512,7 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
        struct pcr_handle *handle;
        u32 base, len;
        int ret, i, bar = 0;
+       u8 val;
 
        dev_dbg(&(pcidev->dev),
                ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n",
@@ -1577,7 +1578,11 @@ static int rtsx_pci_probe(struct pci_dev *pcidev,
        pcr->host_cmds_addr = pcr->rtsx_resv_buf_addr;
        pcr->host_sg_tbl_ptr = pcr->rtsx_resv_buf + HOST_CMDS_BUF_LEN;
        pcr->host_sg_tbl_addr = pcr->rtsx_resv_buf_addr + HOST_CMDS_BUF_LEN;
-
+       rtsx_pci_read_register(pcr, ASPM_FORCE_CTL, &val);
+       if (val & FORCE_ASPM_CTL0 && val & FORCE_ASPM_CTL1)
+               pcr->aspm_enabled = false;
+       else
+               pcr->aspm_enabled = true;
        pcr->card_inserted = 0;
        pcr->card_removed = 0;
        INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect);
index beb4823..b2b3d2b 100644 (file)
@@ -472,8 +472,11 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx,
        cntr = &hdev->aggregated_cs_counters;
 
        cs = kzalloc(sizeof(*cs), GFP_ATOMIC);
-       if (!cs)
+       if (!cs) {
+               atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
+               atomic64_inc(&cntr->out_of_mem_drop_cnt);
                return -ENOMEM;
+       }
 
        cs->ctx = ctx;
        cs->submitted = false;
@@ -486,6 +489,8 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx,
 
        cs_cmpl = kmalloc(sizeof(*cs_cmpl), GFP_ATOMIC);
        if (!cs_cmpl) {
+               atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
+               atomic64_inc(&cntr->out_of_mem_drop_cnt);
                rc = -ENOMEM;
                goto free_cs;
        }
@@ -513,6 +518,8 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx,
        cs->jobs_in_queue_cnt = kcalloc(hdev->asic_prop.max_queues,
                        sizeof(*cs->jobs_in_queue_cnt), GFP_ATOMIC);
        if (!cs->jobs_in_queue_cnt) {
+               atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
+               atomic64_inc(&cntr->out_of_mem_drop_cnt);
                rc = -ENOMEM;
                goto free_fence;
        }
@@ -562,7 +569,7 @@ void hl_cs_rollback_all(struct hl_device *hdev)
        for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
                flush_workqueue(hdev->cq_wq[i]);
 
-       /* Make sure we don't have leftovers in the H/W queues mirror list */
+       /* Make sure we don't have leftovers in the CS mirror list */
        list_for_each_entry_safe(cs, tmp, &hdev->cs_mirror_list, mirror_node) {
                cs_get(cs);
                cs->aborted = true;
@@ -764,11 +771,14 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args)
 
 static int hl_cs_copy_chunk_array(struct hl_device *hdev,
                                        struct hl_cs_chunk **cs_chunk_array,
-                                       void __user *chunks, u32 num_chunks)
+                                       void __user *chunks, u32 num_chunks,
+                                       struct hl_ctx *ctx)
 {
        u32 size_to_copy;
 
        if (num_chunks > HL_MAX_JOBS_PER_CS) {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
                dev_err(hdev->dev,
                        "Number of chunks can NOT be larger than %d\n",
                        HL_MAX_JOBS_PER_CS);
@@ -777,11 +787,16 @@ static int hl_cs_copy_chunk_array(struct hl_device *hdev,
 
        *cs_chunk_array = kmalloc_array(num_chunks, sizeof(**cs_chunk_array),
                                        GFP_ATOMIC);
-       if (!*cs_chunk_array)
+       if (!*cs_chunk_array) {
+               atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
+               atomic64_inc(&hdev->aggregated_cs_counters.out_of_mem_drop_cnt);
                return -ENOMEM;
+       }
 
        size_to_copy = num_chunks * sizeof(struct hl_cs_chunk);
        if (copy_from_user(*cs_chunk_array, chunks, size_to_copy)) {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
                dev_err(hdev->dev, "Failed to copy cs chunk array from user\n");
                kfree(*cs_chunk_array);
                return -EFAULT;
@@ -797,6 +812,7 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
        struct hl_device *hdev = hpriv->hdev;
        struct hl_cs_chunk *cs_chunk_array;
        struct hl_cs_counters_atomic *cntr;
+       struct hl_ctx *ctx = hpriv->ctx;
        struct hl_cs_job *job;
        struct hl_cs *cs;
        struct hl_cb *cb;
@@ -805,7 +821,8 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
        cntr = &hdev->aggregated_cs_counters;
        *cs_seq = ULLONG_MAX;
 
-       rc = hl_cs_copy_chunk_array(hdev, &cs_chunk_array, chunks, num_chunks);
+       rc = hl_cs_copy_chunk_array(hdev, &cs_chunk_array, chunks, num_chunks,
+                       hpriv->ctx);
        if (rc)
                goto out;
 
@@ -832,8 +849,8 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
                rc = validate_queue_index(hdev, chunk, &queue_type,
                                                &is_kernel_allocated_cb);
                if (rc) {
-                       atomic64_inc(&hpriv->ctx->cs_counters.parsing_drop_cnt);
-                       atomic64_inc(&cntr->parsing_drop_cnt);
+                       atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+                       atomic64_inc(&cntr->validation_drop_cnt);
                        goto free_cs_object;
                }
 
@@ -841,8 +858,8 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
                        cb = get_cb_from_cs_chunk(hdev, &hpriv->cb_mgr, chunk);
                        if (!cb) {
                                atomic64_inc(
-                               &hpriv->ctx->cs_counters.parsing_drop_cnt);
-                               atomic64_inc(&cntr->parsing_drop_cnt);
+                                       &ctx->cs_counters.validation_drop_cnt);
+                               atomic64_inc(&cntr->validation_drop_cnt);
                                rc = -EINVAL;
                                goto free_cs_object;
                        }
@@ -856,8 +873,7 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
                job = hl_cs_allocate_job(hdev, queue_type,
                                                is_kernel_allocated_cb);
                if (!job) {
-                       atomic64_inc(
-                       &hpriv->ctx->cs_counters.out_of_mem_drop_cnt);
+                       atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
                        atomic64_inc(&cntr->out_of_mem_drop_cnt);
                        dev_err(hdev->dev, "Failed to allocate a new job\n");
                        rc = -ENOMEM;
@@ -891,7 +907,7 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
 
                rc = cs_parser(hpriv, job);
                if (rc) {
-                       atomic64_inc(&hpriv->ctx->cs_counters.parsing_drop_cnt);
+                       atomic64_inc(&ctx->cs_counters.parsing_drop_cnt);
                        atomic64_inc(&cntr->parsing_drop_cnt);
                        dev_err(hdev->dev,
                                "Failed to parse JOB %d.%llu.%d, err %d, rejecting the CS\n",
@@ -901,8 +917,8 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks,
        }
 
        if (int_queues_only) {
-               atomic64_inc(&hpriv->ctx->cs_counters.parsing_drop_cnt);
-               atomic64_inc(&cntr->parsing_drop_cnt);
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&cntr->validation_drop_cnt);
                dev_err(hdev->dev,
                        "Reject CS %d.%llu because only internal queues jobs are present\n",
                        cs->ctx->asid, cs->sequence);
@@ -1042,7 +1058,7 @@ out:
 }
 
 static int cs_ioctl_extract_signal_seq(struct hl_device *hdev,
-               struct hl_cs_chunk *chunk, u64 *signal_seq)
+               struct hl_cs_chunk *chunk, u64 *signal_seq, struct hl_ctx *ctx)
 {
        u64 *signal_seq_arr = NULL;
        u32 size_to_copy, signal_seq_arr_len;
@@ -1052,6 +1068,8 @@ static int cs_ioctl_extract_signal_seq(struct hl_device *hdev,
 
        /* currently only one signal seq is supported */
        if (signal_seq_arr_len != 1) {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
                dev_err(hdev->dev,
                        "Wait for signal CS supports only one signal CS seq\n");
                return -EINVAL;
@@ -1060,13 +1078,18 @@ static int cs_ioctl_extract_signal_seq(struct hl_device *hdev,
        signal_seq_arr = kmalloc_array(signal_seq_arr_len,
                                        sizeof(*signal_seq_arr),
                                        GFP_ATOMIC);
-       if (!signal_seq_arr)
+       if (!signal_seq_arr) {
+               atomic64_inc(&ctx->cs_counters.out_of_mem_drop_cnt);
+               atomic64_inc(&hdev->aggregated_cs_counters.out_of_mem_drop_cnt);
                return -ENOMEM;
+       }
 
        size_to_copy = chunk->num_signal_seq_arr * sizeof(*signal_seq_arr);
        if (copy_from_user(signal_seq_arr,
                                u64_to_user_ptr(chunk->signal_seq_arr),
                                size_to_copy)) {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&hdev->aggregated_cs_counters.validation_drop_cnt);
                dev_err(hdev->dev,
                        "Failed to copy signal seq array from user\n");
                rc = -EFAULT;
@@ -1153,6 +1176,7 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
        struct hl_device *hdev = hpriv->hdev;
        struct hl_cs_compl *sig_waitcs_cmpl;
        u32 q_idx, collective_engine_id = 0;
+       struct hl_cs_counters_atomic *cntr;
        struct hl_fence *sig_fence = NULL;
        struct hl_ctx *ctx = hpriv->ctx;
        enum hl_queue_type q_type;
@@ -1160,9 +1184,11 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
        u64 signal_seq;
        int rc;
 
+       cntr = &hdev->aggregated_cs_counters;
        *cs_seq = ULLONG_MAX;
 
-       rc = hl_cs_copy_chunk_array(hdev, &cs_chunk_array, chunks, num_chunks);
+       rc = hl_cs_copy_chunk_array(hdev, &cs_chunk_array, chunks, num_chunks,
+                       ctx);
        if (rc)
                goto out;
 
@@ -1170,6 +1196,8 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
        chunk = &cs_chunk_array[0];
 
        if (chunk->queue_index >= hdev->asic_prop.max_queues) {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&cntr->validation_drop_cnt);
                dev_err(hdev->dev, "Queue index %d is invalid\n",
                        chunk->queue_index);
                rc = -EINVAL;
@@ -1181,6 +1209,8 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
        q_type = hw_queue_prop->type;
 
        if (!hw_queue_prop->supports_sync_stream) {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&cntr->validation_drop_cnt);
                dev_err(hdev->dev,
                        "Queue index %d does not support sync stream operations\n",
                        q_idx);
@@ -1190,6 +1220,8 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
 
        if (cs_type == CS_TYPE_COLLECTIVE_WAIT) {
                if (!(hw_queue_prop->collective_mode == HL_COLLECTIVE_MASTER)) {
+                       atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+                       atomic64_inc(&cntr->validation_drop_cnt);
                        dev_err(hdev->dev,
                                "Queue index %d is invalid\n", q_idx);
                        rc = -EINVAL;
@@ -1200,12 +1232,14 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
        }
 
        if (cs_type == CS_TYPE_WAIT || cs_type == CS_TYPE_COLLECTIVE_WAIT) {
-               rc = cs_ioctl_extract_signal_seq(hdev, chunk, &signal_seq);
+               rc = cs_ioctl_extract_signal_seq(hdev, chunk, &signal_seq, ctx);
                if (rc)
                        goto free_cs_chunk_array;
 
                sig_fence = hl_ctx_get_fence(ctx, signal_seq);
                if (IS_ERR(sig_fence)) {
+                       atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+                       atomic64_inc(&cntr->validation_drop_cnt);
                        dev_err(hdev->dev,
                                "Failed to get signal CS with seq 0x%llx\n",
                                signal_seq);
@@ -1223,6 +1257,8 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
                        container_of(sig_fence, struct hl_cs_compl, base_fence);
 
                if (sig_waitcs_cmpl->type != CS_TYPE_SIGNAL) {
+                       atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+                       atomic64_inc(&cntr->validation_drop_cnt);
                        dev_err(hdev->dev,
                                "CS seq 0x%llx is not of a signal CS\n",
                                signal_seq);
@@ -1270,8 +1306,11 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
        else if (cs_type == CS_TYPE_COLLECTIVE_WAIT)
                rc = hdev->asic_funcs->collective_wait_create_jobs(hdev, ctx,
                                cs, q_idx, collective_engine_id);
-       else
+       else {
+               atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
+               atomic64_inc(&cntr->validation_drop_cnt);
                rc = -EINVAL;
+       }
 
        if (rc)
                goto free_cs_object;
index 5871162..69d04ec 100644 (file)
@@ -17,12 +17,12 @@ enum hl_device_status hl_device_status(struct hl_device *hdev)
 {
        enum hl_device_status status;
 
-       if (hdev->disabled)
-               status = HL_DEVICE_STATUS_MALFUNCTION;
-       else if (atomic_read(&hdev->in_reset))
+       if (atomic_read(&hdev->in_reset))
                status = HL_DEVICE_STATUS_IN_RESET;
        else if (hdev->needs_reset)
                status = HL_DEVICE_STATUS_NEEDS_RESET;
+       else if (hdev->disabled)
+               status = HL_DEVICE_STATUS_MALFUNCTION;
        else
                status = HL_DEVICE_STATUS_OPERATIONAL;
 
@@ -1037,7 +1037,7 @@ kill_processes:
 
        if (hard_reset) {
                /* Release kernel context */
-               if (hl_ctx_put(hdev->kernel_ctx) == 1)
+               if (hdev->kernel_ctx && hl_ctx_put(hdev->kernel_ctx) == 1)
                        hdev->kernel_ctx = NULL;
                hl_vm_fini(hdev);
                hl_mmu_fini(hdev);
@@ -1092,6 +1092,7 @@ kill_processes:
                                                GFP_KERNEL);
                if (!hdev->kernel_ctx) {
                        rc = -ENOMEM;
+                       hl_mmu_fini(hdev);
                        goto out_err;
                }
 
@@ -1103,6 +1104,7 @@ kill_processes:
                                "failed to init kernel ctx in hard reset\n");
                        kfree(hdev->kernel_ctx);
                        hdev->kernel_ctx = NULL;
+                       hl_mmu_fini(hdev);
                        goto out_err;
                }
        }
@@ -1485,6 +1487,15 @@ void hl_device_fini(struct hl_device *hdev)
                }
        }
 
+       /* Disable PCI access from device F/W so it won't send us additional
+        * interrupts. We disable MSI/MSI-X at the halt_engines function and we
+        * can't have the F/W sending us interrupts after that. We need to
+        * disable the access here because if the device is marked disable, the
+        * message won't be send. Also, in case of heartbeat, the device CPU is
+        * marked as disable so this message won't be sent
+        */
+       hl_fw_send_pci_access_msg(hdev, CPUCP_PACKET_DISABLE_PCI_ACCESS);
+
        /* Mark device as disabled */
        hdev->disabled = true;
 
index 0e1c629..c9a1298 100644 (file)
@@ -402,6 +402,10 @@ int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
        }
        counters->rx_throughput = result;
 
+       memset(&pkt, 0, sizeof(pkt));
+       pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_THROUGHPUT_GET <<
+                       CPUCP_PKT_CTL_OPCODE_SHIFT);
+
        /* Fetch PCI tx counter */
        pkt.index = cpu_to_le32(cpucp_pcie_throughput_tx);
        rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
@@ -414,6 +418,7 @@ int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
        counters->tx_throughput = result;
 
        /* Fetch PCI replay counter */
+       memset(&pkt, 0, sizeof(pkt));
        pkt.ctl = cpu_to_le32(CPUCP_PACKET_PCIE_REPLAY_CNT_GET <<
                        CPUCP_PKT_CTL_OPCODE_SHIFT);
 
@@ -627,25 +632,38 @@ int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg,
        security_status = RREG32(cpu_security_boot_status_reg);
 
        /* We read security status multiple times during boot:
-        * 1. preboot - we check if fw security feature is supported
-        * 2. boot cpu - we get boot cpu security status
-        * 3. FW application - we get FW application security status
+        * 1. preboot - a. Check whether the security status bits are valid
+        *              b. Check whether fw security is enabled
+        *              c. Check whether hard reset is done by preboot
+        * 2. boot cpu - a. Fetch boot cpu security status
+        *               b. Check whether hard reset is done by boot cpu
+        * 3. FW application - a. Fetch fw application security status
+        *                     b. Check whether hard reset is done by fw app
         *
         * Preboot:
         * Check security status bit (CPU_BOOT_DEV_STS0_ENABLED), if it is set
         * check security enabled bit (CPU_BOOT_DEV_STS0_SECURITY_EN)
         */
        if (security_status & CPU_BOOT_DEV_STS0_ENABLED) {
-               hdev->asic_prop.fw_security_status_valid = 1;
-               prop->fw_security_disabled =
-                       !(security_status & CPU_BOOT_DEV_STS0_SECURITY_EN);
+               prop->fw_security_status_valid = 1;
+
+               if (security_status & CPU_BOOT_DEV_STS0_SECURITY_EN)
+                       prop->fw_security_disabled = false;
+               else
+                       prop->fw_security_disabled = true;
+
+               if (security_status & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN)
+                       prop->hard_reset_done_by_fw = true;
        } else {
-               hdev->asic_prop.fw_security_status_valid = 0;
+               prop->fw_security_status_valid = 0;
                prop->fw_security_disabled = true;
        }
 
+       dev_dbg(hdev->dev, "Firmware preboot hard-reset is %s\n",
+                       prop->hard_reset_done_by_fw ? "enabled" : "disabled");
+
        dev_info(hdev->dev, "firmware-level security is %s\n",
-               prop->fw_security_disabled ? "disabled" : "enabled");
+                       prop->fw_security_disabled ? "disabled" : "enabled");
 
        return 0;
 }
@@ -655,6 +673,7 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg,
                        u32 cpu_security_boot_status_reg, u32 boot_err0_reg,
                        bool skip_bmc, u32 cpu_timeout, u32 boot_fit_timeout)
 {
+       struct asic_fixed_properties *prop = &hdev->asic_prop;
        u32 status;
        int rc;
 
@@ -723,11 +742,22 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg,
        /* Read U-Boot version now in case we will later fail */
        hdev->asic_funcs->read_device_fw_version(hdev, FW_COMP_UBOOT);
 
+       /* Clear reset status since we need to read it again from boot CPU */
+       prop->hard_reset_done_by_fw = false;
+
        /* Read boot_cpu security bits */
-       if (hdev->asic_prop.fw_security_status_valid)
-               hdev->asic_prop.fw_boot_cpu_security_map =
+       if (prop->fw_security_status_valid) {
+               prop->fw_boot_cpu_security_map =
                                RREG32(cpu_security_boot_status_reg);
 
+               if (prop->fw_boot_cpu_security_map &
+                               CPU_BOOT_DEV_STS0_FW_HARD_RST_EN)
+                       prop->hard_reset_done_by_fw = true;
+       }
+
+       dev_dbg(hdev->dev, "Firmware boot CPU hard-reset is %s\n",
+                       prop->hard_reset_done_by_fw ? "enabled" : "disabled");
+
        if (rc) {
                detect_cpu_boot_status(hdev, status);
                rc = -EIO;
@@ -796,18 +826,21 @@ int hl_fw_init_cpu(struct hl_device *hdev, u32 cpu_boot_status_reg,
                goto out;
        }
 
+       /* Clear reset status since we need to read again from app */
+       prop->hard_reset_done_by_fw = false;
+
        /* Read FW application security bits */
-       if (hdev->asic_prop.fw_security_status_valid) {
-               hdev->asic_prop.fw_app_security_map =
+       if (prop->fw_security_status_valid) {
+               prop->fw_app_security_map =
                                RREG32(cpu_security_boot_status_reg);
 
-               if (hdev->asic_prop.fw_app_security_map &
+               if (prop->fw_app_security_map &
                                CPU_BOOT_DEV_STS0_FW_HARD_RST_EN)
-                       hdev->asic_prop.hard_reset_done_by_fw = true;
+                       prop->hard_reset_done_by_fw = true;
        }
 
-       dev_dbg(hdev->dev, "Firmware hard-reset is %s\n",
-               hdev->asic_prop.hard_reset_done_by_fw ? "enabled" : "disabled");
+       dev_dbg(hdev->dev, "Firmware application CPU hard-reset is %s\n",
+                       prop->hard_reset_done_by_fw ? "enabled" : "disabled");
 
        dev_info(hdev->dev, "Successfully loaded firmware to device\n");
 
index 571eda6..60e16dc 100644 (file)
@@ -944,7 +944,7 @@ struct hl_asic_funcs {
        u32 (*get_signal_cb_size)(struct hl_device *hdev);
        u32 (*get_wait_cb_size)(struct hl_device *hdev);
        u32 (*gen_signal_cb)(struct hl_device *hdev, void *data, u16 sob_id,
-                       u32 size);
+                       u32 size, bool eb);
        u32 (*gen_wait_cb)(struct hl_device *hdev,
                        struct hl_gen_wait_properties *prop);
        void (*reset_sob)(struct hl_device *hdev, void *data);
@@ -1000,6 +1000,7 @@ struct hl_va_range {
  * @queue_full_drop_cnt: dropped due to queue full
  * @device_in_reset_drop_cnt: dropped due to device in reset
  * @max_cs_in_flight_drop_cnt: dropped due to maximum CS in-flight
+ * @validation_drop_cnt: dropped due to error in validation
  */
 struct hl_cs_counters_atomic {
        atomic64_t out_of_mem_drop_cnt;
@@ -1007,6 +1008,7 @@ struct hl_cs_counters_atomic {
        atomic64_t queue_full_drop_cnt;
        atomic64_t device_in_reset_drop_cnt;
        atomic64_t max_cs_in_flight_drop_cnt;
+       atomic64_t validation_drop_cnt;
 };
 
 /**
@@ -2180,6 +2182,7 @@ void hl_mmu_v1_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu);
 int hl_mmu_va_to_pa(struct hl_ctx *ctx, u64 virt_addr, u64 *phys_addr);
 int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,
                        struct hl_mmu_hop_info *hops);
+bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr);
 
 int hl_fw_load_fw_to_device(struct hl_device *hdev, const char *fw_name,
                                void __iomem *dst, u32 src_offset, u32 size);
index 6bbb6bc..032d114 100644 (file)
@@ -544,6 +544,7 @@ static struct pci_driver hl_pci_driver = {
        .id_table = ids,
        .probe = hl_pci_probe,
        .remove = hl_pci_remove,
+       .shutdown = hl_pci_remove,
        .driver.pm = &hl_pm_ops,
        .err_handler = &hl_pci_err_handler,
 };
index 32e6af1..d25892d 100644 (file)
@@ -133,6 +133,8 @@ static int hw_idle(struct hl_device *hdev, struct hl_info_args *args)
 
        hw_idle.is_idle = hdev->asic_funcs->is_device_idle(hdev,
                                        &hw_idle.busy_engines_mask_ext, NULL);
+       hw_idle.busy_engines_mask =
+                       lower_32_bits(hw_idle.busy_engines_mask_ext);
 
        return copy_to_user(out, &hw_idle,
                min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0;
@@ -335,6 +337,8 @@ static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
                        atomic64_read(&cntr->device_in_reset_drop_cnt);
        cs_counters.total_max_cs_in_flight_drop_cnt =
                        atomic64_read(&cntr->max_cs_in_flight_drop_cnt);
+       cs_counters.total_validation_drop_cnt =
+                       atomic64_read(&cntr->validation_drop_cnt);
 
        if (hpriv->ctx) {
                cs_counters.ctx_out_of_mem_drop_cnt =
@@ -352,6 +356,9 @@ static int cs_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
                cs_counters.ctx_max_cs_in_flight_drop_cnt =
                                atomic64_read(
                        &hpriv->ctx->cs_counters.max_cs_in_flight_drop_cnt);
+               cs_counters.ctx_validation_drop_cnt =
+                               atomic64_read(
+                               &hpriv->ctx->cs_counters.validation_drop_cnt);
        }
 
        return copy_to_user(out, &cs_counters,
@@ -406,7 +413,7 @@ static int total_energy_consumption_info(struct hl_fpriv *hpriv,
 static int pll_frequency_info(struct hl_fpriv *hpriv, struct hl_info_args *args)
 {
        struct hl_device *hdev = hpriv->hdev;
-       struct hl_pll_frequency_info freq_info = {0};
+       struct hl_pll_frequency_info freq_info = { {0} };
        u32 max_size = args->return_size;
        void __user *out = (void __user *) (uintptr_t) args->return_pointer;
        int rc;
index 7caf868..7621725 100644 (file)
@@ -418,8 +418,11 @@ static void init_signal_cs(struct hl_device *hdev,
                "generate signal CB, sob_id: %d, sob val: 0x%x, q_idx: %d\n",
                cs_cmpl->hw_sob->sob_id, cs_cmpl->sob_val, q_idx);
 
+       /* we set an EB since we must make sure all oeprations are done
+        * when sending the signal
+        */
        hdev->asic_funcs->gen_signal_cb(hdev, job->patched_cb,
-                               cs_cmpl->hw_sob->sob_id, 0);
+                               cs_cmpl->hw_sob->sob_id, 0, true);
 
        kref_get(&hw_sob->kref);
 
index cbe9da4..5d4fbdc 100644 (file)
@@ -886,8 +886,10 @@ static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
 {
        struct hl_device *hdev = ctx->hdev;
        u64 next_vaddr, i;
+       bool is_host_addr;
        u32 page_size;
 
+       is_host_addr = !hl_is_dram_va(hdev, vaddr);
        page_size = phys_pg_pack->page_size;
        next_vaddr = vaddr;
 
@@ -900,9 +902,13 @@ static void unmap_phys_pg_pack(struct hl_ctx *ctx, u64 vaddr,
                /*
                 * unmapping on Palladium can be really long, so avoid a CPU
                 * soft lockup bug by sleeping a little between unmapping pages
+                *
+                * In addition, when unmapping host memory we pass through
+                * the Linux kernel to unpin the pages and that takes a long
+                * time. Therefore, sleep every 32K pages to avoid soft lockup
                 */
-               if (hdev->pldm)
-                       usleep_range(500, 1000);
+               if (hdev->pldm || (is_host_addr && (i & 0x7FFF) == 0))
+                       usleep_range(50, 200);
        }
 }
 
index 33ae953..28a4638 100644 (file)
@@ -9,7 +9,7 @@
 
 #include "habanalabs.h"
 
-static bool is_dram_va(struct hl_device *hdev, u64 virt_addr)
+bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr)
 {
        struct asic_fixed_properties *prop = &hdev->asic_prop;
 
@@ -156,7 +156,7 @@ int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size,
        if (!hdev->mmu_enable)
                return 0;
 
-       is_dram_addr = is_dram_va(hdev, virt_addr);
+       is_dram_addr = hl_is_dram_va(hdev, virt_addr);
 
        if (is_dram_addr)
                mmu_prop = &prop->dmmu;
@@ -236,7 +236,7 @@ int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,
        if (!hdev->mmu_enable)
                return 0;
 
-       is_dram_addr = is_dram_va(hdev, virt_addr);
+       is_dram_addr = hl_is_dram_va(hdev, virt_addr);
 
        if (is_dram_addr)
                mmu_prop = &prop->dmmu;
index 2ce6ea8..06d8a44 100644 (file)
@@ -467,8 +467,16 @@ static void hl_mmu_v1_fini(struct hl_device *hdev)
 {
        /* MMU H/W fini was already done in device hw_fini() */
 
-       kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0);
-       gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
+       if (!ZERO_OR_NULL_PTR(hdev->mmu_priv.hr.mmu_shadow_hop0)) {
+               kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0);
+               gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);
+       }
+
+       /* Make sure that if we arrive here again without init was called we
+        * won't cause kernel panic. This can happen for example if we fail
+        * during hard reset code at certain points
+        */
+       hdev->mmu_priv.dr.mmu_shadow_hop0 = NULL;
 }
 
 /**
index 923b260..b4725e6 100644 (file)
@@ -130,10 +130,8 @@ static int hl_pci_elbi_write(struct hl_device *hdev, u64 addr, u32 data)
        if ((val & PCI_CONFIG_ELBI_STS_MASK) == PCI_CONFIG_ELBI_STS_DONE)
                return 0;
 
-       if (val & PCI_CONFIG_ELBI_STS_ERR) {
-               dev_err(hdev->dev, "Error writing to ELBI\n");
+       if (val & PCI_CONFIG_ELBI_STS_ERR)
                return -EIO;
-       }
 
        if (!(val & PCI_CONFIG_ELBI_STS_MASK)) {
                dev_err(hdev->dev, "ELBI write didn't finish in time\n");
@@ -160,8 +158,12 @@ int hl_pci_iatu_write(struct hl_device *hdev, u32 addr, u32 data)
 
        dbi_offset = addr & 0xFFF;
 
-       rc = hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0x00300000);
-       rc |= hl_pci_elbi_write(hdev, prop->pcie_dbi_base_address + dbi_offset,
+       /* Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
+        * in case the firmware security is enabled
+        */
+       hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0x00300000);
+
+       rc = hl_pci_elbi_write(hdev, prop->pcie_dbi_base_address + dbi_offset,
                                data);
 
        if (rc)
@@ -244,9 +246,11 @@ int hl_pci_set_inbound_region(struct hl_device *hdev, u8 region,
 
        rc |= hl_pci_iatu_write(hdev, offset + 0x4, ctrl_reg_val);
 
-       /* Return the DBI window to the default location */
-       rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
-       rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0);
+       /* Return the DBI window to the default location
+        * Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
+        * in case the firmware security is enabled
+        */
+       hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
 
        if (rc)
                dev_err(hdev->dev, "failed to map bar %u to 0x%08llx\n",
@@ -294,9 +298,11 @@ int hl_pci_set_outbound_region(struct hl_device *hdev,
        /* Enable */
        rc |= hl_pci_iatu_write(hdev, 0x004, 0x80000000);
 
-       /* Return the DBI window to the default location */
-       rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
-       rc |= hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr + 4, 0);
+       /* Return the DBI window to the default location
+        * Ignore result of writing to pcie_aux_dbi_reg_addr as it could fail
+        * in case the firmware security is enabled
+        */
+       hl_pci_elbi_write(hdev, prop->pcie_aux_dbi_reg_addr, 0);
 
        return rc;
 }
index 1f19266..b328dda 100644 (file)
@@ -151,19 +151,6 @@ static const u16 gaudi_packet_sizes[MAX_PACKET_ID] = {
        [PACKET_LOAD_AND_EXE]   = sizeof(struct packet_load_and_exe)
 };
 
-static const u32 gaudi_pll_base_addresses[GAUDI_PLL_MAX] = {
-       [CPU_PLL] = mmPSOC_CPU_PLL_NR,
-       [PCI_PLL] = mmPSOC_PCI_PLL_NR,
-       [SRAM_PLL] = mmSRAM_W_PLL_NR,
-       [HBM_PLL] = mmPSOC_HBM_PLL_NR,
-       [NIC_PLL] = mmNIC0_PLL_NR,
-       [DMA_PLL] = mmDMA_W_PLL_NR,
-       [MESH_PLL] = mmMESH_W_PLL_NR,
-       [MME_PLL] = mmPSOC_MME_PLL_NR,
-       [TPC_PLL] = mmPSOC_TPC_PLL_NR,
-       [IF_PLL] = mmIF_W_PLL_NR
-};
-
 static inline bool validate_packet_id(enum packet_id id)
 {
        switch (id) {
@@ -374,7 +361,7 @@ static int gaudi_cpucp_info_get(struct hl_device *hdev);
 static void gaudi_disable_clock_gating(struct hl_device *hdev);
 static void gaudi_mmu_prepare(struct hl_device *hdev, u32 asid);
 static u32 gaudi_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id,
-                               u32 size);
+                               u32 size, bool eb);
 static u32 gaudi_gen_wait_cb(struct hl_device *hdev,
                                struct hl_gen_wait_properties *prop);
 
@@ -667,12 +654,6 @@ static int gaudi_early_init(struct hl_device *hdev)
        if (rc)
                goto free_queue_props;
 
-       if (gaudi_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
-               dev_info(hdev->dev,
-                       "H/W state is dirty, must reset before initializing\n");
-               hdev->asic_funcs->hw_fini(hdev, true);
-       }
-
        /* Before continuing in the initialization, we need to read the preboot
         * version to determine whether we run with a security-enabled firmware
         */
@@ -685,6 +666,12 @@ static int gaudi_early_init(struct hl_device *hdev)
                goto pci_fini;
        }
 
+       if (gaudi_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
+               dev_info(hdev->dev,
+                       "H/W state is dirty, must reset before initializing\n");
+               hdev->asic_funcs->hw_fini(hdev, true);
+       }
+
        return 0;
 
 pci_fini:
@@ -703,93 +690,60 @@ static int gaudi_early_fini(struct hl_device *hdev)
 }
 
 /**
- * gaudi_fetch_pll_frequency - Fetch PLL frequency values
+ * gaudi_fetch_psoc_frequency - Fetch PSOC frequency values
  *
  * @hdev: pointer to hl_device structure
- * @pll_index: index of the pll to fetch frequency from
- * @pll_freq: pointer to store the pll frequency in MHz in each of the available
- *            outputs. if a certain output is not available a 0 will be set
  *
  */
-static int gaudi_fetch_pll_frequency(struct hl_device *hdev,
-                               enum gaudi_pll_index pll_index,
-                               u16 *pll_freq_arr)
+static int gaudi_fetch_psoc_frequency(struct hl_device *hdev)
 {
-       u32 nr = 0, nf = 0, od = 0, pll_clk = 0, div_fctr, div_sel,
-                       pll_base_addr = gaudi_pll_base_addresses[pll_index];
-       u16 freq = 0;
-       int i, rc;
-
-       if (hdev->asic_prop.fw_security_status_valid &&
-                       (hdev->asic_prop.fw_app_security_map &
-                                       CPU_BOOT_DEV_STS0_PLL_INFO_EN)) {
-               rc = hl_fw_cpucp_pll_info_get(hdev, pll_index, pll_freq_arr);
+       struct asic_fixed_properties *prop = &hdev->asic_prop;
+       u32 nr = 0, nf = 0, od = 0, div_fctr = 0, pll_clk, div_sel;
+       u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS], freq;
+       int rc;
 
-               if (rc)
-                       return rc;
-       } else if (hdev->asic_prop.fw_security_disabled) {
+       if (hdev->asic_prop.fw_security_disabled) {
                /* Backward compatibility */
-               nr = RREG32(pll_base_addr + PLL_NR_OFFSET);
-               nf = RREG32(pll_base_addr + PLL_NF_OFFSET);
-               od = RREG32(pll_base_addr + PLL_OD_OFFSET);
-
-               for (i = 0; i < HL_PLL_NUM_OUTPUTS; i++) {
-                       div_fctr = RREG32(pll_base_addr +
-                                       PLL_DIV_FACTOR_0_OFFSET + i * 4);
-                       div_sel = RREG32(pll_base_addr +
-                                       PLL_DIV_SEL_0_OFFSET + i * 4);
+               div_fctr = RREG32(mmPSOC_CPU_PLL_DIV_FACTOR_2);
+               div_sel = RREG32(mmPSOC_CPU_PLL_DIV_SEL_2);
+               nr = RREG32(mmPSOC_CPU_PLL_NR);
+               nf = RREG32(mmPSOC_CPU_PLL_NF);
+               od = RREG32(mmPSOC_CPU_PLL_OD);
 
-                       if (div_sel == DIV_SEL_REF_CLK ||
+               if (div_sel == DIV_SEL_REF_CLK ||
                                div_sel == DIV_SEL_DIVIDED_REF) {
-                               if (div_sel == DIV_SEL_REF_CLK)
-                                       freq = PLL_REF_CLK;
-                               else
-                                       freq = PLL_REF_CLK / (div_fctr + 1);
-                       } else if (div_sel == DIV_SEL_PLL_CLK ||
-                                       div_sel == DIV_SEL_DIVIDED_PLL) {
-                               pll_clk = PLL_REF_CLK * (nf + 1) /
-                                               ((nr + 1) * (od + 1));
-                               if (div_sel == DIV_SEL_PLL_CLK)
-                                       freq = pll_clk;
-                               else
-                                       freq = pll_clk / (div_fctr + 1);
-                       } else {
-                               dev_warn(hdev->dev,
-                                       "Received invalid div select value: %d",
-                                       div_sel);
-                       }
-
-                       pll_freq_arr[i] = freq;
+                       if (div_sel == DIV_SEL_REF_CLK)
+                               freq = PLL_REF_CLK;
+                       else
+                               freq = PLL_REF_CLK / (div_fctr + 1);
+               } else if (div_sel == DIV_SEL_PLL_CLK ||
+                       div_sel == DIV_SEL_DIVIDED_PLL) {
+                       pll_clk = PLL_REF_CLK * (nf + 1) /
+                                       ((nr + 1) * (od + 1));
+                       if (div_sel == DIV_SEL_PLL_CLK)
+                               freq = pll_clk;
+                       else
+                               freq = pll_clk / (div_fctr + 1);
+               } else {
+                       dev_warn(hdev->dev,
+                               "Received invalid div select value: %d",
+                               div_sel);
+                       freq = 0;
                }
        } else {
-               dev_err(hdev->dev, "Failed to fetch PLL frequency values\n");
-               return -EIO;
-       }
+               rc = hl_fw_cpucp_pll_info_get(hdev, CPU_PLL, pll_freq_arr);
 
-       return 0;
-}
-
-/**
- * gaudi_fetch_psoc_frequency - Fetch PSOC frequency values
- *
- * @hdev: pointer to hl_device structure
- *
- */
-static int gaudi_fetch_psoc_frequency(struct hl_device *hdev)
-{
-       struct asic_fixed_properties *prop = &hdev->asic_prop;
-       u16 pll_freq[HL_PLL_NUM_OUTPUTS];
-       int rc;
+               if (rc)
+                       return rc;
 
-       rc = gaudi_fetch_pll_frequency(hdev, CPU_PLL, pll_freq);
-       if (rc)
-               return rc;
+               freq = pll_freq_arr[2];
+       }
 
-       prop->psoc_timestamp_frequency = pll_freq[2];
-       prop->psoc_pci_pll_nr = 0;
-       prop->psoc_pci_pll_nf = 0;
-       prop->psoc_pci_pll_od = 0;
-       prop->psoc_pci_pll_div_factor = 0;
+       prop->psoc_timestamp_frequency = freq;
+       prop->psoc_pci_pll_nr = nr;
+       prop->psoc_pci_pll_nf = nf;
+       prop->psoc_pci_pll_od = od;
+       prop->psoc_pci_pll_div_factor = div_fctr;
 
        return 0;
 }
@@ -884,11 +838,17 @@ static int gaudi_init_tpc_mem(struct hl_device *hdev)
        size_t fw_size;
        void *cpu_addr;
        dma_addr_t dma_handle;
-       int rc;
+       int rc, count = 5;
 
+again:
        rc = request_firmware(&fw, GAUDI_TPC_FW_FILE, hdev->dev);
+       if (rc == -EINTR && count-- > 0) {
+               msleep(50);
+               goto again;
+       }
+
        if (rc) {
-               dev_err(hdev->dev, "Firmware file %s is not found!\n",
+               dev_err(hdev->dev, "Failed to load firmware file %s\n",
                                GAUDI_TPC_FW_FILE);
                goto out;
        }
@@ -1110,7 +1070,7 @@ static void gaudi_collective_slave_init_job(struct hl_device *hdev,
                prop->collective_sob_id, queue_id);
 
        cb_size += gaudi_gen_signal_cb(hdev, job->user_cb,
-                       prop->collective_sob_id, cb_size);
+                       prop->collective_sob_id, cb_size, false);
 }
 
 static void gaudi_collective_wait_init_cs(struct hl_cs *cs)
@@ -2449,8 +2409,6 @@ static void gaudi_init_golden_registers(struct hl_device *hdev)
        gaudi_init_e2e(hdev);
        gaudi_init_hbm_cred(hdev);
 
-       hdev->asic_funcs->disable_clock_gating(hdev);
-
        for (tpc_id = 0, tpc_offset = 0;
                                tpc_id < TPC_NUMBER_OF_ENGINES;
                                tpc_id++, tpc_offset += TPC_CFG_OFFSET) {
@@ -3462,6 +3420,9 @@ static void gaudi_set_clock_gating(struct hl_device *hdev)
        if (hdev->in_debug)
                return;
 
+       if (!hdev->asic_prop.fw_security_disabled)
+               return;
+
        for (i = GAUDI_PCI_DMA_1, qman_offset = 0 ; i < GAUDI_HBM_DMA_1 ; i++) {
                enable = !!(hdev->clock_gating_mask &
                                (BIT_ULL(gaudi_dma_assignment[i])));
@@ -3513,7 +3474,7 @@ static void gaudi_disable_clock_gating(struct hl_device *hdev)
        u32 qman_offset;
        int i;
 
-       if (!(gaudi->hw_cap_initialized & HW_CAP_CLK_GATE))
+       if (!hdev->asic_prop.fw_security_disabled)
                return;
 
        for (i = 0, qman_offset = 0 ; i < DMA_NUMBER_OF_CHANNELS ; i++) {
@@ -3806,7 +3767,7 @@ static int gaudi_init_cpu_queues(struct hl_device *hdev, u32 cpu_timeout)
 static void gaudi_pre_hw_init(struct hl_device *hdev)
 {
        /* Perform read from the device to make sure device is up */
-       RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG);
+       RREG32(mmHW_STATE);
 
        if (hdev->asic_prop.fw_security_disabled) {
                /* Set the access through PCI bars (Linux driver only) as
@@ -3847,6 +3808,13 @@ static int gaudi_hw_init(struct hl_device *hdev)
                return rc;
        }
 
+       /* In case the clock gating was enabled in preboot we need to disable
+        * it here before touching the MME/TPC registers.
+        * There is no need to take clk gating mutex because when this function
+        * runs, no other relevant code can run
+        */
+       hdev->asic_funcs->disable_clock_gating(hdev);
+
        /* SRAM scrambler must be initialized after CPU is running from HBM */
        gaudi_init_scrambler_sram(hdev);
 
@@ -3885,7 +3853,7 @@ static int gaudi_hw_init(struct hl_device *hdev)
        }
 
        /* Perform read from the device to flush all configuration */
-       RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG);
+       RREG32(mmHW_STATE);
 
        return 0;
 
@@ -3927,7 +3895,10 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset)
        /* I don't know what is the state of the CPU so make sure it is
         * stopped in any means necessary
         */
-       WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE);
+       if (hdev->asic_prop.hard_reset_done_by_fw)
+               WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_RST_DEV);
+       else
+               WREG32(mmPSOC_GLOBAL_CONF_KMD_MSG_TO_CPU, KMD_MSG_GOTO_WFE);
 
        WREG32(mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR, GAUDI_EVENT_HALT_MACHINE);
 
@@ -3971,11 +3942,15 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset)
 
                WREG32(mmPSOC_GLOBAL_CONF_SW_ALL_RST,
                        1 << PSOC_GLOBAL_CONF_SW_ALL_RST_IND_SHIFT);
-       }
 
-       dev_info(hdev->dev,
-               "Issued HARD reset command, going to wait %dms\n",
-               reset_timeout_ms);
+               dev_info(hdev->dev,
+                       "Issued HARD reset command, going to wait %dms\n",
+                       reset_timeout_ms);
+       } else {
+               dev_info(hdev->dev,
+                       "Firmware performs HARD reset, going to wait %dms\n",
+                       reset_timeout_ms);
+       }
 
        /*
         * After hard reset, we can't poll the BTM_FSM register because the PSOC
@@ -4027,7 +4002,8 @@ static int gaudi_cb_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
        vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
                        VM_DONTCOPY | VM_NORESERVE;
 
-       rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr, dma_addr, size);
+       rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr,
+                               (dma_addr - HOST_PHYS_BASE), size);
        if (rc)
                dev_err(hdev->dev, "dma_mmap_coherent error %d", rc);
 
@@ -7936,7 +7912,7 @@ static u32 gaudi_get_wait_cb_size(struct hl_device *hdev)
 }
 
 static u32 gaudi_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id,
-                               u32 size)
+                               u32 size, bool eb)
 {
        struct hl_cb *cb = (struct hl_cb *) data;
        struct packet_msg_short *pkt;
@@ -7953,7 +7929,7 @@ static u32 gaudi_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id,
        ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OP_MASK, 0); /* write the value */
        ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_BASE_MASK, 3); /* W_S SOB base */
        ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_OPCODE_MASK, PACKET_MSG_SHORT);
-       ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_EB_MASK, 1);
+       ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_EB_MASK, eb);
        ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_RB_MASK, 1);
        ctl |= FIELD_PREP(GAUDI_PKT_SHORT_CTL_MB_MASK, 1);
 
index f2d91f4..a7ab2d7 100644 (file)
 #define MME_ACC_OFFSET         (mmMME1_ACC_BASE - mmMME0_ACC_BASE)
 #define SRAM_BANK_OFFSET       (mmSRAM_Y0_X1_RTR_BASE - mmSRAM_Y0_X0_RTR_BASE)
 
-#define PLL_NR_OFFSET          0
-#define PLL_NF_OFFSET          (mmPSOC_CPU_PLL_NF - mmPSOC_CPU_PLL_NR)
-#define PLL_OD_OFFSET          (mmPSOC_CPU_PLL_OD - mmPSOC_CPU_PLL_NR)
-#define PLL_DIV_FACTOR_0_OFFSET        (mmPSOC_CPU_PLL_DIV_FACTOR_0 - \
-                               mmPSOC_CPU_PLL_NR)
-#define PLL_DIV_SEL_0_OFFSET   (mmPSOC_CPU_PLL_DIV_SEL_0 - mmPSOC_CPU_PLL_NR)
-
 #define NUM_OF_SOB_IN_BLOCK            \
        (((mmSYNC_MNGR_E_N_SYNC_MNGR_OBJS_SOB_OBJ_2047 - \
        mmSYNC_MNGR_E_N_SYNC_MNGR_OBJS_SOB_OBJ_0) + 4) >> 2)
index 2e3612e..88a09d4 100644 (file)
@@ -9,6 +9,7 @@
 #include "../include/gaudi/gaudi_coresight.h"
 #include "../include/gaudi/asic_reg/gaudi_regs.h"
 #include "../include/gaudi/gaudi_masks.h"
+#include "../include/gaudi/gaudi_reg_map.h"
 
 #include <uapi/misc/habanalabs.h>
 #define SPMU_SECTION_SIZE              MME0_ACC_SPMU_MAX_OFFSET
@@ -874,7 +875,7 @@ int gaudi_debug_coresight(struct hl_device *hdev, void *data)
        }
 
        /* Perform read from the device to flush all configuration */
-       RREG32(mmPCIE_DBI_DEVICE_ID_VENDOR_ID_REG);
+       RREG32(mmHW_STATE);
 
        return rc;
 }
index 3e5eb9e..63679a7 100644 (file)
@@ -613,12 +613,6 @@ static int goya_early_init(struct hl_device *hdev)
        if (rc)
                goto free_queue_props;
 
-       if (goya_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
-               dev_info(hdev->dev,
-                       "H/W state is dirty, must reset before initializing\n");
-               hdev->asic_funcs->hw_fini(hdev, true);
-       }
-
        /* Before continuing in the initialization, we need to read the preboot
         * version to determine whether we run with a security-enabled firmware
         */
@@ -631,6 +625,12 @@ static int goya_early_init(struct hl_device *hdev)
                goto pci_fini;
        }
 
+       if (goya_get_hw_state(hdev) == HL_DEVICE_HW_STATE_DIRTY) {
+               dev_info(hdev->dev,
+                       "H/W state is dirty, must reset before initializing\n");
+               hdev->asic_funcs->hw_fini(hdev, true);
+       }
+
        if (!hdev->pldm) {
                val = RREG32(mmPSOC_GLOBAL_CONF_BOOT_STRAP_PINS);
                if (val & PSOC_GLOBAL_CONF_BOOT_STRAP_PINS_SRIOV_EN_MASK)
@@ -694,32 +694,47 @@ static void goya_qman0_set_security(struct hl_device *hdev, bool secure)
 static void goya_fetch_psoc_frequency(struct hl_device *hdev)
 {
        struct asic_fixed_properties *prop = &hdev->asic_prop;
-       u32 trace_freq = 0;
-       u32 pll_clk = 0;
-       u32 div_fctr = RREG32(mmPSOC_PCI_PLL_DIV_FACTOR_1);
-       u32 div_sel = RREG32(mmPSOC_PCI_PLL_DIV_SEL_1);
-       u32 nr = RREG32(mmPSOC_PCI_PLL_NR);
-       u32 nf = RREG32(mmPSOC_PCI_PLL_NF);
-       u32 od = RREG32(mmPSOC_PCI_PLL_OD);
-
-       if (div_sel == DIV_SEL_REF_CLK || div_sel == DIV_SEL_DIVIDED_REF) {
-               if (div_sel == DIV_SEL_REF_CLK)
-                       trace_freq = PLL_REF_CLK;
-               else
-                       trace_freq = PLL_REF_CLK / (div_fctr + 1);
-       } else if (div_sel == DIV_SEL_PLL_CLK ||
-                                       div_sel == DIV_SEL_DIVIDED_PLL) {
-               pll_clk = PLL_REF_CLK * (nf + 1) / ((nr + 1) * (od + 1));
-               if (div_sel == DIV_SEL_PLL_CLK)
-                       trace_freq = pll_clk;
-               else
-                       trace_freq = pll_clk / (div_fctr + 1);
+       u32 nr = 0, nf = 0, od = 0, div_fctr = 0, pll_clk, div_sel;
+       u16 pll_freq_arr[HL_PLL_NUM_OUTPUTS], freq;
+       int rc;
+
+       if (hdev->asic_prop.fw_security_disabled) {
+               div_fctr = RREG32(mmPSOC_PCI_PLL_DIV_FACTOR_1);
+               div_sel = RREG32(mmPSOC_PCI_PLL_DIV_SEL_1);
+               nr = RREG32(mmPSOC_PCI_PLL_NR);
+               nf = RREG32(mmPSOC_PCI_PLL_NF);
+               od = RREG32(mmPSOC_PCI_PLL_OD);
+
+               if (div_sel == DIV_SEL_REF_CLK ||
+                               div_sel == DIV_SEL_DIVIDED_REF) {
+                       if (div_sel == DIV_SEL_REF_CLK)
+                               freq = PLL_REF_CLK;
+                       else
+                               freq = PLL_REF_CLK / (div_fctr + 1);
+               } else if (div_sel == DIV_SEL_PLL_CLK ||
+                               div_sel == DIV_SEL_DIVIDED_PLL) {
+                       pll_clk = PLL_REF_CLK * (nf + 1) /
+                                       ((nr + 1) * (od + 1));
+                       if (div_sel == DIV_SEL_PLL_CLK)
+                               freq = pll_clk;
+                       else
+                               freq = pll_clk / (div_fctr + 1);
+               } else {
+                       dev_warn(hdev->dev,
+                               "Received invalid div select value: %d",
+                               div_sel);
+                       freq = 0;
+               }
        } else {
-               dev_warn(hdev->dev,
-                       "Received invalid div select value: %d", div_sel);
+               rc = hl_fw_cpucp_pll_info_get(hdev, PCI_PLL, pll_freq_arr);
+
+               if (rc)
+                       return;
+
+               freq = pll_freq_arr[1];
        }
 
-       prop->psoc_timestamp_frequency = trace_freq;
+       prop->psoc_timestamp_frequency = freq;
        prop->psoc_pci_pll_nr = nr;
        prop->psoc_pci_pll_nf = nf;
        prop->psoc_pci_pll_od = od;
@@ -2704,7 +2719,8 @@ static int goya_cb_mmap(struct hl_device *hdev, struct vm_area_struct *vma,
        vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
                        VM_DONTCOPY | VM_NORESERVE;
 
-       rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr, dma_addr, size);
+       rc = dma_mmap_coherent(hdev->dev, vma, cpu_addr,
+                               (dma_addr - HOST_PHYS_BASE), size);
        if (rc)
                dev_err(hdev->dev, "dma_mmap_coherent error %d", rc);
 
@@ -5324,7 +5340,7 @@ static u32 goya_get_wait_cb_size(struct hl_device *hdev)
 }
 
 static u32 goya_gen_signal_cb(struct hl_device *hdev, void *data, u16 sob_id,
-               u32 size)
+                               u32 size, bool eb)
 {
        return 0;
 }
index e5801ec..b637dfd 100644 (file)
  *                                     implemented. This means that FW will
  *                                     perform hard reset procedure on
  *                                     receiving the halt-machine event.
- *                                     Initialized in: linux
+ *                                     Initialized in: preboot, u-boot, linux
  *
  * CPU_BOOT_DEV_STS0_PLL_INFO_EN       FW retrieval of PLL info is enabled.
  *                                     Initialized in: linux
  *
+ * CPU_BOOT_DEV_STS0_CLK_GATE_EN       Clock Gating enabled.
+ *                                     FW initialized Clock Gating.
+ *                                     Initialized in: preboot
+ *
  * CPU_BOOT_DEV_STS0_ENABLED           Device status register enabled.
  *                                     This is a main indication that the
  *                                     running FW populates the device status
 #define CPU_BOOT_DEV_STS0_DRAM_SCR_EN                  (1 << 9)
 #define CPU_BOOT_DEV_STS0_FW_HARD_RST_EN               (1 << 10)
 #define CPU_BOOT_DEV_STS0_PLL_INFO_EN                  (1 << 11)
+#define CPU_BOOT_DEV_STS0_CLK_GATE_EN                  (1 << 13)
 #define CPU_BOOT_DEV_STS0_ENABLED                      (1 << 31)
 
 enum cpu_boot_status {
@@ -204,6 +209,8 @@ enum kmd_msg {
        KMD_MSG_GOTO_WFE,
        KMD_MSG_FIT_RDY,
        KMD_MSG_SKIP_BMC,
+       RESERVED,
+       KMD_MSG_RST_DEV,
 };
 
 enum cpu_msg_status {
index 951b37d..41cab29 100644 (file)
@@ -55,12 +55,23 @@ static int pvpanic_mmio_probe(struct platform_device *pdev)
        struct resource *res;
 
        res = platform_get_mem_or_io(pdev, 0);
-       if (res && resource_type(res) == IORESOURCE_IO)
+       if (!res)
+               return -EINVAL;
+
+       switch (resource_type(res)) {
+       case IORESOURCE_IO:
                base = devm_ioport_map(dev, res->start, resource_size(res));
-       else
+               if (!base)
+                       return -ENOMEM;
+               break;
+       case IORESOURCE_MEM:
                base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
+               if (IS_ERR(base))
+                       return PTR_ERR(base);
+               break;
+       default:
+               return -EINVAL;
+       }
 
        atomic_notifier_chain_register(&panic_notifier_list,
                                       &pvpanic_panic_nb);
index de7cb03..002426e 100644 (file)
@@ -384,8 +384,10 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
                     "merging was advertised but not possible");
        blk_queue_max_segments(mq->queue, mmc_get_max_segments(host));
 
-       if (mmc_card_mmc(card))
+       if (mmc_card_mmc(card) && card->ext_csd.data_sector_size) {
                block_size = card->ext_csd.data_sector_size;
+               WARN_ON(block_size != 512 && block_size != 4096);
+       }
 
        blk_queue_logical_block_size(mq->queue, block_size);
        /*
index bbf3496..f9780c6 100644 (file)
@@ -314,11 +314,7 @@ err_clk:
 
 static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
 {
-       int ret;
-
-       ret = sdhci_pltfm_unregister(pdev);
-       if (ret)
-               dev_err(&pdev->dev, "failed to shutdown\n");
+       sdhci_pltfm_suspend(&pdev->dev);
 }
 
 MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
index 4b67379..d90020e 100644 (file)
@@ -16,6 +16,8 @@
 
 #include "sdhci-pltfm.h"
 
+#define SDHCI_DWCMSHC_ARG2_STUFF       GENMASK(31, 16)
+
 /* DWCMSHC specific Mode Select value */
 #define DWCMSHC_CTRL_HS400             0x7
 
@@ -49,6 +51,29 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
        sdhci_adma_write_desc(host, desc, addr, len, cmd);
 }
 
+static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc,
+                                    struct mmc_request *mrq)
+{
+       struct sdhci_host *host = mmc_priv(mmc);
+
+       /*
+        * No matter V4 is enabled or not, ARGUMENT2 register is 32-bit
+        * block count register which doesn't support stuff bits of
+        * CMD23 argument on dwcmsch host controller.
+        */
+       if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF))
+               host->flags &= ~SDHCI_AUTO_CMD23;
+       else
+               host->flags |= SDHCI_AUTO_CMD23;
+}
+
+static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+       dwcmshc_check_auto_cmd23(mmc, mrq);
+
+       sdhci_request(mmc, mrq);
+}
+
 static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
                                      unsigned int timing)
 {
@@ -133,6 +158,8 @@ static int dwcmshc_probe(struct platform_device *pdev)
 
        sdhci_get_of_property(pdev);
 
+       host->mmc_host_ops.request = dwcmshc_request;
+
        err = sdhci_add_host(host);
        if (err)
                goto err_clk;
index c67611f..d19eef5 100644 (file)
@@ -168,7 +168,12 @@ static void xenon_reset_exit(struct sdhci_host *host,
        /* Disable tuning request and auto-retuning again */
        xenon_retune_setup(host);
 
-       xenon_set_acg(host, true);
+       /*
+        * The ACG should be turned off at the early init time, in order
+        * to solve a possible issues with the 1.8V regulator stabilization.
+        * The feature is enabled in later stage.
+        */
+       xenon_set_acg(host, false);
 
        xenon_set_sdclk_off_idle(host, sdhc_id, false);
 
index 5cdf05b..3fa8c22 100644 (file)
@@ -1615,7 +1615,7 @@ static int gpmi_ecc_read_page_raw(struct nand_chip *chip, uint8_t *buf,
        /* Extract interleaved payload data and ECC bits */
        for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
                if (buf)
-                       nand_extract_bits(buf, step * eccsize, tmp_buf,
+                       nand_extract_bits(buf, step * eccsize * 8, tmp_buf,
                                          src_bit_off, eccsize * 8);
                src_bit_off += eccsize * 8;
 
index fdb112e..a304fda 100644 (file)
@@ -579,7 +579,7 @@ static int ebu_nand_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct ebu_nand_controller *ebu_host;
        struct nand_chip *nand;
-       struct mtd_info *mtd = NULL;
+       struct mtd_info *mtd;
        struct resource *res;
        char *resname;
        int ret;
@@ -647,12 +647,13 @@ static int ebu_nand_probe(struct platform_device *pdev)
               ebu_host->ebu + EBU_ADDR_SEL(cs));
 
        nand_set_flash_node(&ebu_host->chip, dev->of_node);
+
+       mtd = nand_to_mtd(&ebu_host->chip);
        if (!mtd->name) {
                dev_err(ebu_host->dev, "NAND label property is mandatory\n");
                return -EINVAL;
        }
 
-       mtd = nand_to_mtd(&ebu_host->chip);
        mtd->dev.parent = dev;
        ebu_host->dev = dev;
 
index f2b9250..0750121 100644 (file)
@@ -2210,6 +2210,9 @@ static int ns_attach_chip(struct nand_chip *chip)
 {
        unsigned int eccsteps, eccbytes;
 
+       chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
+       chip->ecc.algo = bch ? NAND_ECC_ALGO_BCH : NAND_ECC_ALGO_HAMMING;
+
        if (!bch)
                return 0;
 
@@ -2233,8 +2236,6 @@ static int ns_attach_chip(struct nand_chip *chip)
                return -EINVAL;
        }
 
-       chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
-       chip->ecc.algo = NAND_ECC_ALGO_BCH;
        chip->ecc.size = 512;
        chip->ecc.strength = bch;
        chip->ecc.bytes = eccbytes;
@@ -2273,8 +2274,6 @@ static int __init ns_init_module(void)
        nsmtd       = nand_to_mtd(chip);
        nand_set_controller_data(chip, (void *)ns);
 
-       chip->ecc.engine_type = NAND_ECC_ENGINE_TYPE_SOFT;
-       chip->ecc.algo   = NAND_ECC_ALGO_HAMMING;
        /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
        /* and 'badblocks' parameters to work */
        chip->options   |= NAND_SKIP_BBTSCAN;
index fbb9955..2c3e65c 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/jiffies.h>
 #include <linux/sched.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mtd/nand-ecc-sw-bch.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/omap-dma.h>
@@ -1866,18 +1867,19 @@ static const struct mtd_ooblayout_ops omap_ooblayout_ops = {
 static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
                                 struct mtd_oob_region *oobregion)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_device *nand = mtd_to_nanddev(mtd);
+       const struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
        int off = BADBLOCK_MARKER_LENGTH;
 
-       if (section >= chip->ecc.steps)
+       if (section >= engine_conf->nsteps)
                return -ERANGE;
 
        /*
         * When SW correction is employed, one OMAP specific marker byte is
         * reserved after each ECC step.
         */
-       oobregion->offset = off + (section * (chip->ecc.bytes + 1));
-       oobregion->length = chip->ecc.bytes;
+       oobregion->offset = off + (section * (engine_conf->code_size + 1));
+       oobregion->length = engine_conf->code_size;
 
        return 0;
 }
@@ -1885,7 +1887,8 @@ static int omap_sw_ooblayout_ecc(struct mtd_info *mtd, int section,
 static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
                                  struct mtd_oob_region *oobregion)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct nand_device *nand = mtd_to_nanddev(mtd);
+       const struct nand_ecc_sw_bch_conf *engine_conf = nand->ecc.ctx.priv;
        int off = BADBLOCK_MARKER_LENGTH;
 
        if (section)
@@ -1895,7 +1898,7 @@ static int omap_sw_ooblayout_free(struct mtd_info *mtd, int section,
         * When SW correction is employed, one OMAP specific marker byte is
         * reserved after each ECC step.
         */
-       off += ((chip->ecc.bytes + 1) * chip->ecc.steps);
+       off += ((engine_conf->code_size + 1) * engine_conf->nsteps);
        if (off >= mtd->oobsize)
                return -ERANGE;
 
index 8ea545b..61d932c 100644 (file)
@@ -343,6 +343,7 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
                                      const struct nand_page_io_req *req)
 {
        struct nand_device *nand = spinand_to_nand(spinand);
+       struct mtd_info *mtd = spinand_to_mtd(spinand);
        struct spi_mem_dirmap_desc *rdesc;
        unsigned int nbytes = 0;
        void *buf = NULL;
@@ -382,9 +383,16 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
                memcpy(req->databuf.in, spinand->databuf + req->dataoffs,
                       req->datalen);
 
-       if (req->ooblen)
-               memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
-                      req->ooblen);
+       if (req->ooblen) {
+               if (req->mode == MTD_OPS_AUTO_OOB)
+                       mtd_ooblayout_get_databytes(mtd, req->oobbuf.in,
+                                                   spinand->oobbuf,
+                                                   req->ooboffs,
+                                                   req->ooblen);
+               else
+                       memcpy(req->oobbuf.in, spinand->oobbuf + req->ooboffs,
+                              req->ooblen);
+       }
 
        return 0;
 }
index 260f9f4..1ebb4b9 100644 (file)
@@ -42,6 +42,7 @@ config BONDING
        tristate "Bonding driver support"
        depends on INET
        depends on IPV6 || IPV6=n
+       depends on TLS || TLS_DEVICE=n
        help
          Say 'Y' or 'M' if you wish to be able to 'bond' multiple Ethernet
          Channels together. This is called 'Etherchannel' by Cisco,
index cf607ff..81223f6 100644 (file)
@@ -67,7 +67,7 @@ static void regdump(struct net_device *dev)
        /* set up the address register */
        count = 0;
        arcnet_outb((count >> 8) | RDDATAflag | AUTOINCflag,
-                   ioaddr, com20020_REG_W_ADDR_HI);
+                   ioaddr, COM20020_REG_W_ADDR_HI);
        arcnet_outb(count & 0xff, ioaddr, COM20020_REG_W_ADDR_LO);
 
        for (count = 0; count < 256 + 32; count++) {
index 85ebd2b..1b8f597 100644 (file)
@@ -380,7 +380,7 @@ static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
                goto free_dst;
 
        min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len +
-               BAREUDP_BASE_HLEN + info->options_len + sizeof(struct iphdr);
+               BAREUDP_BASE_HLEN + info->options_len + sizeof(struct ipv6hdr);
 
        err = skb_cow_head(skb, min_headroom);
        if (unlikely(err))
@@ -532,10 +532,12 @@ static void bareudp_setup(struct net_device *dev)
        dev->netdev_ops = &bareudp_netdev_ops;
        dev->needs_free_netdev = true;
        SET_NETDEV_DEVTYPE(dev, &bareudp_type);
-       dev->features    |= NETIF_F_SG | NETIF_F_HW_CSUM;
+       dev->features    |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
        dev->features    |= NETIF_F_RXCSUM;
+       dev->features    |= NETIF_F_LLTX;
        dev->features    |= NETIF_F_GSO_SOFTWARE;
-       dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+       dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
+       dev->hw_features |= NETIF_F_RXCSUM;
        dev->hw_features |= NETIF_F_GSO_SOFTWARE;
        dev->hard_header_len = 0;
        dev->addr_len = 0;
@@ -644,6 +646,14 @@ static int bareudp_link_config(struct net_device *dev,
        return 0;
 }
 
+static void bareudp_dellink(struct net_device *dev, struct list_head *head)
+{
+       struct bareudp_dev *bareudp = netdev_priv(dev);
+
+       list_del(&bareudp->next);
+       unregister_netdevice_queue(dev, head);
+}
+
 static int bareudp_newlink(struct net *net, struct net_device *dev,
                           struct nlattr *tb[], struct nlattr *data[],
                           struct netlink_ext_ack *extack)
@@ -661,17 +671,13 @@ static int bareudp_newlink(struct net *net, struct net_device *dev,
 
        err = bareudp_link_config(dev, tb);
        if (err)
-               return err;
+               goto err_unconfig;
 
        return 0;
-}
 
-static void bareudp_dellink(struct net_device *dev, struct list_head *head)
-{
-       struct bareudp_dev *bareudp = netdev_priv(dev);
-
-       list_del(&bareudp->next);
-       unregister_netdevice_queue(dev, head);
+err_unconfig:
+       bareudp_dellink(dev, NULL);
+       return err;
 }
 
 static size_t bareudp_get_size(const struct net_device *dev)
@@ -722,7 +728,6 @@ struct net_device *bareudp_dev_create(struct net *net, const char *name,
 {
        struct nlattr *tb[IFLA_MAX + 1];
        struct net_device *dev;
-       LIST_HEAD(list_kill);
        int err;
 
        memset(tb, 0, sizeof(tb));
@@ -746,8 +751,7 @@ struct net_device *bareudp_dev_create(struct net *net, const char *name,
 
        return dev;
 err:
-       bareudp_dellink(dev, &list_kill);
-       unregister_netdevice_many(&list_kill);
+       bareudp_dellink(dev, NULL);
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(bareudp_dev_create);
index 5fe5232..74cbbb2 100644 (file)
@@ -83,6 +83,9 @@
 #include <net/bonding.h>
 #include <net/bond_3ad.h>
 #include <net/bond_alb.h>
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+#include <net/tls.h>
+#endif
 
 #include "bonding_priv.h"
 
@@ -164,7 +167,7 @@ module_param(xmit_hash_policy, charp, 0);
 MODULE_PARM_DESC(xmit_hash_policy, "balance-alb, balance-tlb, balance-xor, 802.3ad hashing method; "
                                   "0 for layer 2 (default), 1 for layer 3+4, "
                                   "2 for layer 2+3, 3 for encap layer 2+3, "
-                                  "4 for encap layer 3+4");
+                                  "4 for encap layer 3+4, 5 for vlan+srcmac");
 module_param(arp_interval, int, 0);
 MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
 module_param_array(arp_ip_target, charp, NULL, 0);
@@ -301,6 +304,19 @@ netdev_tx_t bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb,
        return dev_queue_xmit(skb);
 }
 
+bool bond_sk_check(struct bonding *bond)
+{
+       switch (BOND_MODE(bond)) {
+       case BOND_MODE_8023AD:
+       case BOND_MODE_XOR:
+               if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34)
+                       return true;
+               fallthrough;
+       default:
+               return false;
+       }
+}
+
 /*---------------------------------- VLAN -----------------------------------*/
 
 /* In the following 2 functions, bond_vlan_rx_add_vid and bond_vlan_rx_kill_vid,
@@ -1212,6 +1228,13 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
        netdev_features_t mask;
        struct slave *slave;
 
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+       if (bond_sk_check(bond))
+               features |= BOND_TLS_FEATURES;
+       else
+               features &= ~BOND_TLS_FEATURES;
+#endif
+
        mask = features;
 
        features &= ~NETIF_F_ONE_FOR_ALL;
@@ -1434,6 +1457,8 @@ static enum netdev_lag_hash bond_lag_hash_type(struct bonding *bond,
                return NETDEV_LAG_HASH_E23;
        case BOND_XMIT_POLICY_ENCAP34:
                return NETDEV_LAG_HASH_E34;
+       case BOND_XMIT_POLICY_VLAN_SRCMAC:
+               return NETDEV_LAG_HASH_VLAN_SRCMAC;
        default:
                return NETDEV_LAG_HASH_UNKNOWN;
        }
@@ -1922,6 +1947,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
                goto err_unregister;
        }
 
+       bond_lower_state_changed(new_slave);
+
        res = bond_sysfs_slave_add(new_slave);
        if (res) {
                slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res);
@@ -3494,6 +3521,27 @@ static bool bond_flow_ip(struct sk_buff *skb, struct flow_keys *fk,
        return true;
 }
 
+static u32 bond_vlan_srcmac_hash(struct sk_buff *skb)
+{
+       struct ethhdr *mac_hdr = (struct ethhdr *)skb_mac_header(skb);
+       u32 srcmac_vendor = 0, srcmac_dev = 0;
+       u16 vlan;
+       int i;
+
+       for (i = 0; i < 3; i++)
+               srcmac_vendor = (srcmac_vendor << 8) | mac_hdr->h_source[i];
+
+       for (i = 3; i < ETH_ALEN; i++)
+               srcmac_dev = (srcmac_dev << 8) | mac_hdr->h_source[i];
+
+       if (!skb_vlan_tag_present(skb))
+               return srcmac_vendor ^ srcmac_dev;
+
+       vlan = skb_vlan_tag_get(skb);
+
+       return vlan ^ srcmac_vendor ^ srcmac_dev;
+}
+
 /* Extract the appropriate headers based on bond's xmit policy */
 static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
                              struct flow_keys *fk)
@@ -3501,10 +3549,14 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
        bool l34 = bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34;
        int noff, proto = -1;
 
-       if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) {
+       switch (bond->params.xmit_policy) {
+       case BOND_XMIT_POLICY_ENCAP23:
+       case BOND_XMIT_POLICY_ENCAP34:
                memset(fk, 0, sizeof(*fk));
                return __skb_flow_dissect(NULL, skb, &flow_keys_bonding,
                                          fk, NULL, 0, 0, 0, 0);
+       default:
+               break;
        }
 
        fk->ports.ports = 0;
@@ -3539,6 +3591,16 @@ static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb,
        return true;
 }
 
+static u32 bond_ip_hash(u32 hash, struct flow_keys *flow)
+{
+       hash ^= (__force u32)flow_get_u32_dst(flow) ^
+               (__force u32)flow_get_u32_src(flow);
+       hash ^= (hash >> 16);
+       hash ^= (hash >> 8);
+       /* discard lowest hash bit to deal with the common even ports pattern */
+       return hash >> 1;
+}
+
 /**
  * bond_xmit_hash - generate a hash value based on the xmit policy
  * @bond: bonding device
@@ -3556,6 +3618,9 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
            skb->l4_hash)
                return skb->hash;
 
+       if (bond->params.xmit_policy == BOND_XMIT_POLICY_VLAN_SRCMAC)
+               return bond_vlan_srcmac_hash(skb);
+
        if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 ||
            !bond_flow_dissect(bond, skb, &flow))
                return bond_eth_hash(skb);
@@ -3569,12 +3634,8 @@ u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb)
                else
                        memcpy(&hash, &flow.ports.ports, sizeof(hash));
        }
-       hash ^= (__force u32)flow_get_u32_dst(&flow) ^
-               (__force u32)flow_get_u32_src(&flow);
-       hash ^= (hash >> 16);
-       hash ^= (hash >> 8);
 
-       return hash >> 1;
+       return bond_ip_hash(hash, &flow);
 }
 
 /*-------------------------- Device entry points ----------------------------*/
@@ -4547,6 +4608,95 @@ static struct net_device *bond_xmit_get_slave(struct net_device *master_dev,
        return NULL;
 }
 
+static void bond_sk_to_flow(struct sock *sk, struct flow_keys *flow)
+{
+       switch (sk->sk_family) {
+#if IS_ENABLED(CONFIG_IPV6)
+       case AF_INET6:
+               if (sk->sk_ipv6only ||
+                   ipv6_addr_type(&sk->sk_v6_daddr) != IPV6_ADDR_MAPPED) {
+                       flow->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+                       flow->addrs.v6addrs.src = inet6_sk(sk)->saddr;
+                       flow->addrs.v6addrs.dst = sk->sk_v6_daddr;
+                       break;
+               }
+               fallthrough;
+#endif
+       default: /* AF_INET */
+               flow->control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
+               flow->addrs.v4addrs.src = inet_sk(sk)->inet_rcv_saddr;
+               flow->addrs.v4addrs.dst = inet_sk(sk)->inet_daddr;
+               break;
+       }
+
+       flow->ports.src = inet_sk(sk)->inet_sport;
+       flow->ports.dst = inet_sk(sk)->inet_dport;
+}
+
+/**
+ * bond_sk_hash_l34 - generate a hash value based on the socket's L3 and L4 fields
+ * @sk: socket to use for headers
+ *
+ * This function will extract the necessary field from the socket and use
+ * them to generate a hash based on the LAYER34 xmit_policy.
+ * Assumes that sk is a TCP or UDP socket.
+ */
+static u32 bond_sk_hash_l34(struct sock *sk)
+{
+       struct flow_keys flow;
+       u32 hash;
+
+       bond_sk_to_flow(sk, &flow);
+
+       /* L4 */
+       memcpy(&hash, &flow.ports.ports, sizeof(hash));
+       /* L3 */
+       return bond_ip_hash(hash, &flow);
+}
+
+static struct net_device *__bond_sk_get_lower_dev(struct bonding *bond,
+                                                 struct sock *sk)
+{
+       struct bond_up_slave *slaves;
+       struct slave *slave;
+       unsigned int count;
+       u32 hash;
+
+       slaves = rcu_dereference(bond->usable_slaves);
+       count = slaves ? READ_ONCE(slaves->count) : 0;
+       if (unlikely(!count))
+               return NULL;
+
+       hash = bond_sk_hash_l34(sk);
+       slave = slaves->arr[hash % count];
+
+       return slave->dev;
+}
+
+static struct net_device *bond_sk_get_lower_dev(struct net_device *dev,
+                                               struct sock *sk)
+{
+       struct bonding *bond = netdev_priv(dev);
+       struct net_device *lower = NULL;
+
+       rcu_read_lock();
+       if (bond_sk_check(bond))
+               lower = __bond_sk_get_lower_dev(bond, sk);
+       rcu_read_unlock();
+
+       return lower;
+}
+
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+static netdev_tx_t bond_tls_device_xmit(struct bonding *bond, struct sk_buff *skb,
+                                       struct net_device *dev)
+{
+       if (likely(bond_get_slave_by_dev(bond, tls_get_ctx(skb->sk)->netdev)))
+               return bond_dev_queue_xmit(bond, skb, tls_get_ctx(skb->sk)->netdev);
+       return bond_tx_drop(dev, skb);
+}
+#endif
+
 static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
        struct bonding *bond = netdev_priv(dev);
@@ -4555,6 +4705,11 @@ static netdev_tx_t __bond_start_xmit(struct sk_buff *skb, struct net_device *dev
            !bond_slave_override(bond, skb))
                return NETDEV_TX_OK;
 
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+       if (skb->sk && tls_is_sk_tx_device_offloaded(skb->sk))
+               return bond_tls_device_xmit(bond, skb, dev);
+#endif
+
        switch (BOND_MODE(bond)) {
        case BOND_MODE_ROUNDROBIN:
                return bond_xmit_roundrobin(skb, dev);
@@ -4683,6 +4838,7 @@ static const struct net_device_ops bond_netdev_ops = {
        .ndo_fix_features       = bond_fix_features,
        .ndo_features_check     = passthru_features_check,
        .ndo_get_xmit_slave     = bond_xmit_get_slave,
+       .ndo_sk_get_lower_dev   = bond_sk_get_lower_dev,
 };
 
 static const struct device_type bond_type = {
@@ -4754,6 +4910,10 @@ void bond_setup(struct net_device *bond_dev)
        if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
                bond_dev->features |= BOND_XFRM_FEATURES;
 #endif /* CONFIG_XFRM_OFFLOAD */
+#if IS_ENABLED(CONFIG_TLS_DEVICE)
+       if (bond_sk_check(bond))
+               bond_dev->features |= BOND_TLS_FEATURES;
+#endif
 }
 
 /* Destroy a bonding device.
index a4e4e15..77d7c38 100644 (file)
@@ -96,12 +96,13 @@ static const struct bond_opt_value bond_pps_tbl[] = {
 };
 
 static const struct bond_opt_value bond_xmit_hashtype_tbl[] = {
-       { "layer2",   BOND_XMIT_POLICY_LAYER2, BOND_VALFLAG_DEFAULT},
-       { "layer3+4", BOND_XMIT_POLICY_LAYER34, 0},
-       { "layer2+3", BOND_XMIT_POLICY_LAYER23, 0},
-       { "encap2+3", BOND_XMIT_POLICY_ENCAP23, 0},
-       { "encap3+4", BOND_XMIT_POLICY_ENCAP34, 0},
-       { NULL,       -1,                       0},
+       { "layer2",      BOND_XMIT_POLICY_LAYER2,      BOND_VALFLAG_DEFAULT},
+       { "layer3+4",    BOND_XMIT_POLICY_LAYER34,     0},
+       { "layer2+3",    BOND_XMIT_POLICY_LAYER23,     0},
+       { "encap2+3",    BOND_XMIT_POLICY_ENCAP23,     0},
+       { "encap3+4",    BOND_XMIT_POLICY_ENCAP34,     0},
+       { "vlan+srcmac", BOND_XMIT_POLICY_VLAN_SRCMAC, 0},
+       { NULL,          -1,                           0},
 };
 
 static const struct bond_opt_value bond_arp_validate_tbl[] = {
@@ -745,17 +746,30 @@ const struct bond_option *bond_opt_get(unsigned int option)
        return &bond_opts[option];
 }
 
-static void bond_set_xfrm_features(struct net_device *bond_dev, u64 mode)
+static bool bond_set_xfrm_features(struct bonding *bond)
 {
        if (!IS_ENABLED(CONFIG_XFRM_OFFLOAD))
-               return;
+               return false;
+
+       if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP)
+               bond->dev->wanted_features |= BOND_XFRM_FEATURES;
+       else
+               bond->dev->wanted_features &= ~BOND_XFRM_FEATURES;
+
+       return true;
+}
+
+static bool bond_set_tls_features(struct bonding *bond)
+{
+       if (!IS_ENABLED(CONFIG_TLS_DEVICE))
+               return false;
 
-       if (mode == BOND_MODE_ACTIVEBACKUP)
-               bond_dev->wanted_features |= BOND_XFRM_FEATURES;
+       if (bond_sk_check(bond))
+               bond->dev->wanted_features |= BOND_TLS_FEATURES;
        else
-               bond_dev->wanted_features &= ~BOND_XFRM_FEATURES;
+               bond->dev->wanted_features &= ~BOND_TLS_FEATURES;
 
-       netdev_update_features(bond_dev);
+       return true;
 }
 
 static int bond_option_mode_set(struct bonding *bond,
@@ -780,13 +794,20 @@ static int bond_option_mode_set(struct bonding *bond,
        if (newval->value == BOND_MODE_ALB)
                bond->params.tlb_dynamic_lb = 1;
 
-       if (bond->dev->reg_state == NETREG_REGISTERED)
-               bond_set_xfrm_features(bond->dev, newval->value);
-
        /* don't cache arp_validate between modes */
        bond->params.arp_validate = BOND_ARP_VALIDATE_NONE;
        bond->params.mode = newval->value;
 
+       if (bond->dev->reg_state == NETREG_REGISTERED) {
+               bool update = false;
+
+               update |= bond_set_xfrm_features(bond);
+               update |= bond_set_tls_features(bond);
+
+               if (update)
+                       netdev_update_features(bond->dev);
+       }
+
        return 0;
 }
 
@@ -1219,6 +1240,10 @@ static int bond_option_xmit_hash_policy_set(struct bonding *bond,
                   newval->string, newval->value);
        bond->params.xmit_policy = newval->value;
 
+       if (bond->dev->reg_state == NETREG_REGISTERED)
+               if (bond_set_tls_features(bond))
+                       netdev_update_features(bond->dev);
+
        return 0;
 }
 
index 4249709..1c28ead 100644 (file)
@@ -123,6 +123,7 @@ config CAN_JANZ_ICAN3
 config CAN_KVASER_PCIEFD
        depends on PCI
        tristate "Kvaser PCIe FD cards"
+       select CRC32
          help
          This is a driver for the Kvaser PCI Express CAN FD family.
 
index 2216430..a2b4463 100644 (file)
@@ -7,12 +7,7 @@ obj-$(CONFIG_CAN_VCAN)         += vcan.o
 obj-$(CONFIG_CAN_VXCAN)                += vxcan.o
 obj-$(CONFIG_CAN_SLCAN)                += slcan.o
 
-obj-$(CONFIG_CAN_DEV)          += can-dev.o
-can-dev-y                      += dev.o
-can-dev-y                      += rx-offload.o
-
-can-dev-$(CONFIG_CAN_LEDS)     += led.o
-
+obj-y                          += dev/
 obj-y                          += rcar/
 obj-y                          += spi/
 obj-y                          += usb/
index 5284f0a..9ad9b39 100644 (file)
@@ -484,7 +484,7 @@ static netdev_tx_t at91_start_xmit(struct sk_buff *skb, struct net_device *dev)
        stats->tx_bytes += cf->len;
 
        /* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */
-       can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv));
+       can_put_echo_skb(skb, dev, mb - get_mb_tx_first(priv), 0);
 
        /*
         * we have to stop the queue and deliver all messages in case
@@ -856,7 +856,7 @@ static void at91_irq_tx(struct net_device *dev, u32 reg_sr)
                if (likely(reg_msr & AT91_MSR_MRDY &&
                           ~reg_msr & AT91_MSR_MABT)) {
                        /* _NOTE_: subtract AT91_MB_TX_FIRST offset from mb! */
-                       can_get_echo_skb(dev, mb - get_mb_tx_first(priv));
+                       can_get_echo_skb(dev, mb - get_mb_tx_first(priv), NULL);
                        dev->stats.tx_packets++;
                        can_led_event(dev, CAN_LED_EVENT_TX);
                }
index 63f48b0..ef474ba 100644 (file)
@@ -476,7 +476,7 @@ static netdev_tx_t c_can_start_xmit(struct sk_buff *skb,
         */
        c_can_setup_tx_object(dev, IF_TX, frame, idx);
        priv->dlc[idx] = frame->len;
-       can_put_echo_skb(skb, dev, idx);
+       can_put_echo_skb(skb, dev, idx, 0);
 
        /* Update the active bits */
        atomic_add((1 << idx), &priv->tx_active);
@@ -733,7 +733,7 @@ static void c_can_do_tx(struct net_device *dev)
                pend &= ~(1 << idx);
                obj = idx + C_CAN_MSG_OBJ_TX_FIRST;
                c_can_inval_tx_object(dev, IF_RX, obj);
-               can_get_echo_skb(dev, idx);
+               can_get_echo_skb(dev, idx, NULL);
                bytes += priv->dlc[idx];
                pkts++;
        }
index 8d9f332..f8a130f 100644 (file)
@@ -702,8 +702,8 @@ static void cc770_tx_interrupt(struct net_device *dev, unsigned int o)
        stats->tx_bytes += cf->len;
        stats->tx_packets++;
 
-       can_put_echo_skb(priv->tx_skb, dev, 0);
-       can_get_echo_skb(dev, 0);
+       can_put_echo_skb(priv->tx_skb, dev, 0, 0);
+       can_get_echo_skb(dev, 0, NULL);
        priv->tx_skb = NULL;
 
        netif_wake_queue(dev);
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
deleted file mode 100644 (file)
index 3486704..0000000
+++ /dev/null
@@ -1,1338 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
- * Copyright (C) 2006 Andrey Volkov, Varma Electronics
- * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/workqueue.h>
-#include <linux/can.h>
-#include <linux/can/can-ml.h>
-#include <linux/can/dev.h>
-#include <linux/can/skb.h>
-#include <linux/can/netlink.h>
-#include <linux/can/led.h>
-#include <linux/of.h>
-#include <net/rtnetlink.h>
-
-#define MOD_DESC "CAN device driver interface"
-
-MODULE_DESCRIPTION(MOD_DESC);
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
-
-/* CAN DLC to real data length conversion helpers */
-
-static const u8 dlc2len[] = {0, 1, 2, 3, 4, 5, 6, 7,
-                            8, 12, 16, 20, 24, 32, 48, 64};
-
-/* get data length from raw data length code (DLC) */
-u8 can_fd_dlc2len(u8 dlc)
-{
-       return dlc2len[dlc & 0x0F];
-}
-EXPORT_SYMBOL_GPL(can_fd_dlc2len);
-
-static const u8 len2dlc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8,                /* 0 - 8 */
-                            9, 9, 9, 9,                        /* 9 - 12 */
-                            10, 10, 10, 10,                    /* 13 - 16 */
-                            11, 11, 11, 11,                    /* 17 - 20 */
-                            12, 12, 12, 12,                    /* 21 - 24 */
-                            13, 13, 13, 13, 13, 13, 13, 13,    /* 25 - 32 */
-                            14, 14, 14, 14, 14, 14, 14, 14,    /* 33 - 40 */
-                            14, 14, 14, 14, 14, 14, 14, 14,    /* 41 - 48 */
-                            15, 15, 15, 15, 15, 15, 15, 15,    /* 49 - 56 */
-                            15, 15, 15, 15, 15, 15, 15, 15};   /* 57 - 64 */
-
-/* map the sanitized data length to an appropriate data length code */
-u8 can_fd_len2dlc(u8 len)
-{
-       if (unlikely(len > 64))
-               return 0xF;
-
-       return len2dlc[len];
-}
-EXPORT_SYMBOL_GPL(can_fd_len2dlc);
-
-#ifdef CONFIG_CAN_CALC_BITTIMING
-#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
-
-/* Bit-timing calculation derived from:
- *
- * Code based on LinCAN sources and H8S2638 project
- * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
- * Copyright 2005      Stanislav Marek
- * email: pisa@cmp.felk.cvut.cz
- *
- * Calculates proper bit-timing parameters for a specified bit-rate
- * and sample-point, which can then be used to set the bit-timing
- * registers of the CAN controller. You can find more information
- * in the header file linux/can/netlink.h.
- */
-static int
-can_update_sample_point(const struct can_bittiming_const *btc,
-                       unsigned int sample_point_nominal, unsigned int tseg,
-                       unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
-                       unsigned int *sample_point_error_ptr)
-{
-       unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
-       unsigned int sample_point, best_sample_point = 0;
-       unsigned int tseg1, tseg2;
-       int i;
-
-       for (i = 0; i <= 1; i++) {
-               tseg2 = tseg + CAN_SYNC_SEG -
-                       (sample_point_nominal * (tseg + CAN_SYNC_SEG)) /
-                       1000 - i;
-               tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
-               tseg1 = tseg - tseg2;
-               if (tseg1 > btc->tseg1_max) {
-                       tseg1 = btc->tseg1_max;
-                       tseg2 = tseg - tseg1;
-               }
-
-               sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) /
-                       (tseg + CAN_SYNC_SEG);
-               sample_point_error = abs(sample_point_nominal - sample_point);
-
-               if (sample_point <= sample_point_nominal &&
-                   sample_point_error < best_sample_point_error) {
-                       best_sample_point = sample_point;
-                       best_sample_point_error = sample_point_error;
-                       *tseg1_ptr = tseg1;
-                       *tseg2_ptr = tseg2;
-               }
-       }
-
-       if (sample_point_error_ptr)
-               *sample_point_error_ptr = best_sample_point_error;
-
-       return best_sample_point;
-}
-
-static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
-                             const struct can_bittiming_const *btc)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       unsigned int bitrate;                   /* current bitrate */
-       unsigned int bitrate_error;             /* difference between current and nominal value */
-       unsigned int best_bitrate_error = UINT_MAX;
-       unsigned int sample_point_error;        /* difference between current and nominal value */
-       unsigned int best_sample_point_error = UINT_MAX;
-       unsigned int sample_point_nominal;      /* nominal sample point */
-       unsigned int best_tseg = 0;             /* current best value for tseg */
-       unsigned int best_brp = 0;              /* current best value for brp */
-       unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
-       u64 v64;
-
-       /* Use CiA recommended sample points */
-       if (bt->sample_point) {
-               sample_point_nominal = bt->sample_point;
-       } else {
-               if (bt->bitrate > 800000)
-                       sample_point_nominal = 750;
-               else if (bt->bitrate > 500000)
-                       sample_point_nominal = 800;
-               else
-                       sample_point_nominal = 875;
-       }
-
-       /* tseg even = round down, odd = round up */
-       for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
-            tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
-               tsegall = CAN_SYNC_SEG + tseg / 2;
-
-               /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
-               brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
-
-               /* choose brp step which is possible in system */
-               brp = (brp / btc->brp_inc) * btc->brp_inc;
-               if (brp < btc->brp_min || brp > btc->brp_max)
-                       continue;
-
-               bitrate = priv->clock.freq / (brp * tsegall);
-               bitrate_error = abs(bt->bitrate - bitrate);
-
-               /* tseg brp biterror */
-               if (bitrate_error > best_bitrate_error)
-                       continue;
-
-               /* reset sample point error if we have a better bitrate */
-               if (bitrate_error < best_bitrate_error)
-                       best_sample_point_error = UINT_MAX;
-
-               can_update_sample_point(btc, sample_point_nominal, tseg / 2,
-                                       &tseg1, &tseg2, &sample_point_error);
-               if (sample_point_error > best_sample_point_error)
-                       continue;
-
-               best_sample_point_error = sample_point_error;
-               best_bitrate_error = bitrate_error;
-               best_tseg = tseg / 2;
-               best_brp = brp;
-
-               if (bitrate_error == 0 && sample_point_error == 0)
-                       break;
-       }
-
-       if (best_bitrate_error) {
-               /* Error in one-tenth of a percent */
-               v64 = (u64)best_bitrate_error * 1000;
-               do_div(v64, bt->bitrate);
-               bitrate_error = (u32)v64;
-               if (bitrate_error > CAN_CALC_MAX_ERROR) {
-                       netdev_err(dev,
-                                  "bitrate error %d.%d%% too high\n",
-                                  bitrate_error / 10, bitrate_error % 10);
-                       return -EDOM;
-               }
-               netdev_warn(dev, "bitrate error %d.%d%%\n",
-                           bitrate_error / 10, bitrate_error % 10);
-       }
-
-       /* real sample point */
-       bt->sample_point = can_update_sample_point(btc, sample_point_nominal,
-                                                  best_tseg, &tseg1, &tseg2,
-                                                  NULL);
-
-       v64 = (u64)best_brp * 1000 * 1000 * 1000;
-       do_div(v64, priv->clock.freq);
-       bt->tq = (u32)v64;
-       bt->prop_seg = tseg1 / 2;
-       bt->phase_seg1 = tseg1 - bt->prop_seg;
-       bt->phase_seg2 = tseg2;
-
-       /* check for sjw user settings */
-       if (!bt->sjw || !btc->sjw_max) {
-               bt->sjw = 1;
-       } else {
-               /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
-               if (bt->sjw > btc->sjw_max)
-                       bt->sjw = btc->sjw_max;
-               /* bt->sjw must not be higher than tseg2 */
-               if (tseg2 < bt->sjw)
-                       bt->sjw = tseg2;
-       }
-
-       bt->brp = best_brp;
-
-       /* real bitrate */
-       bt->bitrate = priv->clock.freq /
-               (bt->brp * (CAN_SYNC_SEG + tseg1 + tseg2));
-
-       return 0;
-}
-#else /* !CONFIG_CAN_CALC_BITTIMING */
-static int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
-                             const struct can_bittiming_const *btc)
-{
-       netdev_err(dev, "bit-timing calculation not available\n");
-       return -EINVAL;
-}
-#endif /* CONFIG_CAN_CALC_BITTIMING */
-
-/* Checks the validity of the specified bit-timing parameters prop_seg,
- * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
- * prescaler value brp. You can find more information in the header
- * file linux/can/netlink.h.
- */
-static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
-                              const struct can_bittiming_const *btc)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       int tseg1, alltseg;
-       u64 brp64;
-
-       tseg1 = bt->prop_seg + bt->phase_seg1;
-       if (!bt->sjw)
-               bt->sjw = 1;
-       if (bt->sjw > btc->sjw_max ||
-           tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
-           bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
-               return -ERANGE;
-
-       brp64 = (u64)priv->clock.freq * (u64)bt->tq;
-       if (btc->brp_inc > 1)
-               do_div(brp64, btc->brp_inc);
-       brp64 += 500000000UL - 1;
-       do_div(brp64, 1000000000UL); /* the practicable BRP */
-       if (btc->brp_inc > 1)
-               brp64 *= btc->brp_inc;
-       bt->brp = (u32)brp64;
-
-       if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
-               return -EINVAL;
-
-       alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
-       bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
-       bt->sample_point = ((tseg1 + 1) * 1000) / alltseg;
-
-       return 0;
-}
-
-/* Checks the validity of predefined bitrate settings */
-static int
-can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt,
-                    const u32 *bitrate_const,
-                    const unsigned int bitrate_const_cnt)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       unsigned int i;
-
-       for (i = 0; i < bitrate_const_cnt; i++) {
-               if (bt->bitrate == bitrate_const[i])
-                       break;
-       }
-
-       if (i >= priv->bitrate_const_cnt)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
-                            const struct can_bittiming_const *btc,
-                            const u32 *bitrate_const,
-                            const unsigned int bitrate_const_cnt)
-{
-       int err;
-
-       /* Depending on the given can_bittiming parameter structure the CAN
-        * timing parameters are calculated based on the provided bitrate OR
-        * alternatively the CAN timing parameters (tq, prop_seg, etc.) are
-        * provided directly which are then checked and fixed up.
-        */
-       if (!bt->tq && bt->bitrate && btc)
-               err = can_calc_bittiming(dev, bt, btc);
-       else if (bt->tq && !bt->bitrate && btc)
-               err = can_fixup_bittiming(dev, bt, btc);
-       else if (!bt->tq && bt->bitrate && bitrate_const)
-               err = can_validate_bitrate(dev, bt, bitrate_const,
-                                          bitrate_const_cnt);
-       else
-               err = -EINVAL;
-
-       return err;
-}
-
-static void can_update_state_error_stats(struct net_device *dev,
-                                        enum can_state new_state)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       if (new_state <= priv->state)
-               return;
-
-       switch (new_state) {
-       case CAN_STATE_ERROR_WARNING:
-               priv->can_stats.error_warning++;
-               break;
-       case CAN_STATE_ERROR_PASSIVE:
-               priv->can_stats.error_passive++;
-               break;
-       case CAN_STATE_BUS_OFF:
-               priv->can_stats.bus_off++;
-               break;
-       default:
-               break;
-       }
-}
-
-static int can_tx_state_to_frame(struct net_device *dev, enum can_state state)
-{
-       switch (state) {
-       case CAN_STATE_ERROR_ACTIVE:
-               return CAN_ERR_CRTL_ACTIVE;
-       case CAN_STATE_ERROR_WARNING:
-               return CAN_ERR_CRTL_TX_WARNING;
-       case CAN_STATE_ERROR_PASSIVE:
-               return CAN_ERR_CRTL_TX_PASSIVE;
-       default:
-               return 0;
-       }
-}
-
-static int can_rx_state_to_frame(struct net_device *dev, enum can_state state)
-{
-       switch (state) {
-       case CAN_STATE_ERROR_ACTIVE:
-               return CAN_ERR_CRTL_ACTIVE;
-       case CAN_STATE_ERROR_WARNING:
-               return CAN_ERR_CRTL_RX_WARNING;
-       case CAN_STATE_ERROR_PASSIVE:
-               return CAN_ERR_CRTL_RX_PASSIVE;
-       default:
-               return 0;
-       }
-}
-
-static const char *can_get_state_str(const enum can_state state)
-{
-       switch (state) {
-       case CAN_STATE_ERROR_ACTIVE:
-               return "Error Active";
-       case CAN_STATE_ERROR_WARNING:
-               return "Error Warning";
-       case CAN_STATE_ERROR_PASSIVE:
-               return "Error Passive";
-       case CAN_STATE_BUS_OFF:
-               return "Bus Off";
-       case CAN_STATE_STOPPED:
-               return "Stopped";
-       case CAN_STATE_SLEEPING:
-               return "Sleeping";
-       default:
-               return "<unknown>";
-       }
-
-       return "<unknown>";
-}
-
-void can_change_state(struct net_device *dev, struct can_frame *cf,
-                     enum can_state tx_state, enum can_state rx_state)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       enum can_state new_state = max(tx_state, rx_state);
-
-       if (unlikely(new_state == priv->state)) {
-               netdev_warn(dev, "%s: oops, state did not change", __func__);
-               return;
-       }
-
-       netdev_dbg(dev, "Controller changed from %s State (%d) into %s State (%d).\n",
-                  can_get_state_str(priv->state), priv->state,
-                  can_get_state_str(new_state), new_state);
-
-       can_update_state_error_stats(dev, new_state);
-       priv->state = new_state;
-
-       if (!cf)
-               return;
-
-       if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
-               cf->can_id |= CAN_ERR_BUSOFF;
-               return;
-       }
-
-       cf->can_id |= CAN_ERR_CRTL;
-       cf->data[1] |= tx_state >= rx_state ?
-                      can_tx_state_to_frame(dev, tx_state) : 0;
-       cf->data[1] |= tx_state <= rx_state ?
-                      can_rx_state_to_frame(dev, rx_state) : 0;
-}
-EXPORT_SYMBOL_GPL(can_change_state);
-
-/* Local echo of CAN messages
- *
- * CAN network devices *should* support a local echo functionality
- * (see Documentation/networking/can.rst). To test the handling of CAN
- * interfaces that do not support the local echo both driver types are
- * implemented. In the case that the driver does not support the echo
- * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core
- * to perform the echo as a fallback solution.
- */
-static void can_flush_echo_skb(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       struct net_device_stats *stats = &dev->stats;
-       int i;
-
-       for (i = 0; i < priv->echo_skb_max; i++) {
-               if (priv->echo_skb[i]) {
-                       kfree_skb(priv->echo_skb[i]);
-                       priv->echo_skb[i] = NULL;
-                       stats->tx_dropped++;
-                       stats->tx_aborted_errors++;
-               }
-       }
-}
-
-/* Put the skb on the stack to be looped backed locally lateron
- *
- * The function is typically called in the start_xmit function
- * of the device driver. The driver must protect access to
- * priv->echo_skb, if necessary.
- */
-int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
-                    unsigned int idx)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       BUG_ON(idx >= priv->echo_skb_max);
-
-       /* check flag whether this packet has to be looped back */
-       if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK ||
-           (skb->protocol != htons(ETH_P_CAN) &&
-            skb->protocol != htons(ETH_P_CANFD))) {
-               kfree_skb(skb);
-               return 0;
-       }
-
-       if (!priv->echo_skb[idx]) {
-               skb = can_create_echo_skb(skb);
-               if (!skb)
-                       return -ENOMEM;
-
-               /* make settings for echo to reduce code in irq context */
-               skb->pkt_type = PACKET_BROADCAST;
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-               skb->dev = dev;
-
-               /* save this skb for tx interrupt echo handling */
-               priv->echo_skb[idx] = skb;
-       } else {
-               /* locking problem with netif_stop_queue() ?? */
-               netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
-               kfree_skb(skb);
-               return -EBUSY;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(can_put_echo_skb);
-
-struct sk_buff *
-__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       if (idx >= priv->echo_skb_max) {
-               netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
-                          __func__, idx, priv->echo_skb_max);
-               return NULL;
-       }
-
-       if (priv->echo_skb[idx]) {
-               /* Using "struct canfd_frame::len" for the frame
-                * length is supported on both CAN and CANFD frames.
-                */
-               struct sk_buff *skb = priv->echo_skb[idx];
-               struct canfd_frame *cf = (struct canfd_frame *)skb->data;
-
-               /* get the real payload length for netdev statistics */
-               if (cf->can_id & CAN_RTR_FLAG)
-                       *len_ptr = 0;
-               else
-                       *len_ptr = cf->len;
-
-               priv->echo_skb[idx] = NULL;
-
-               return skb;
-       }
-
-       return NULL;
-}
-
-/* Get the skb from the stack and loop it back locally
- *
- * The function is typically called when the TX done interrupt
- * is handled in the device driver. The driver must protect
- * access to priv->echo_skb, if necessary.
- */
-unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx)
-{
-       struct sk_buff *skb;
-       u8 len;
-
-       skb = __can_get_echo_skb(dev, idx, &len);
-       if (!skb)
-               return 0;
-
-       skb_get(skb);
-       if (netif_rx(skb) == NET_RX_SUCCESS)
-               dev_consume_skb_any(skb);
-       else
-               dev_kfree_skb_any(skb);
-
-       return len;
-}
-EXPORT_SYMBOL_GPL(can_get_echo_skb);
-
-/* Remove the skb from the stack and free it.
- *
- * The function is typically called when TX failed.
- */
-void can_free_echo_skb(struct net_device *dev, unsigned int idx)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       BUG_ON(idx >= priv->echo_skb_max);
-
-       if (priv->echo_skb[idx]) {
-               dev_kfree_skb_any(priv->echo_skb[idx]);
-               priv->echo_skb[idx] = NULL;
-       }
-}
-EXPORT_SYMBOL_GPL(can_free_echo_skb);
-
-/* CAN device restart for bus-off recovery */
-static void can_restart(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       struct net_device_stats *stats = &dev->stats;
-       struct sk_buff *skb;
-       struct can_frame *cf;
-       int err;
-
-       BUG_ON(netif_carrier_ok(dev));
-
-       /* No synchronization needed because the device is bus-off and
-        * no messages can come in or go out.
-        */
-       can_flush_echo_skb(dev);
-
-       /* send restart message upstream */
-       skb = alloc_can_err_skb(dev, &cf);
-       if (!skb)
-               goto restart;
-
-       cf->can_id |= CAN_ERR_RESTARTED;
-
-       netif_rx_ni(skb);
-
-       stats->rx_packets++;
-       stats->rx_bytes += cf->len;
-
-restart:
-       netdev_dbg(dev, "restarted\n");
-       priv->can_stats.restarts++;
-
-       /* Now restart the device */
-       err = priv->do_set_mode(dev, CAN_MODE_START);
-
-       netif_carrier_on(dev);
-       if (err)
-               netdev_err(dev, "Error %d during restart", err);
-}
-
-static void can_restart_work(struct work_struct *work)
-{
-       struct delayed_work *dwork = to_delayed_work(work);
-       struct can_priv *priv = container_of(dwork, struct can_priv,
-                                            restart_work);
-
-       can_restart(priv->dev);
-}
-
-int can_restart_now(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       /* A manual restart is only permitted if automatic restart is
-        * disabled and the device is in the bus-off state
-        */
-       if (priv->restart_ms)
-               return -EINVAL;
-       if (priv->state != CAN_STATE_BUS_OFF)
-               return -EBUSY;
-
-       cancel_delayed_work_sync(&priv->restart_work);
-       can_restart(dev);
-
-       return 0;
-}
-
-/* CAN bus-off
- *
- * This functions should be called when the device goes bus-off to
- * tell the netif layer that no more packets can be sent or received.
- * If enabled, a timer is started to trigger bus-off recovery.
- */
-void can_bus_off(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       if (priv->restart_ms)
-               netdev_info(dev, "bus-off, scheduling restart in %d ms\n",
-                           priv->restart_ms);
-       else
-               netdev_info(dev, "bus-off\n");
-
-       netif_carrier_off(dev);
-
-       if (priv->restart_ms)
-               schedule_delayed_work(&priv->restart_work,
-                                     msecs_to_jiffies(priv->restart_ms));
-}
-EXPORT_SYMBOL_GPL(can_bus_off);
-
-static void can_setup(struct net_device *dev)
-{
-       dev->type = ARPHRD_CAN;
-       dev->mtu = CAN_MTU;
-       dev->hard_header_len = 0;
-       dev->addr_len = 0;
-       dev->tx_queue_len = 10;
-
-       /* New-style flags. */
-       dev->flags = IFF_NOARP;
-       dev->features = NETIF_F_HW_CSUM;
-}
-
-struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
-{
-       struct sk_buff *skb;
-
-       skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
-                              sizeof(struct can_frame));
-       if (unlikely(!skb))
-               return NULL;
-
-       skb->protocol = htons(ETH_P_CAN);
-       skb->pkt_type = PACKET_BROADCAST;
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-       skb_reset_mac_header(skb);
-       skb_reset_network_header(skb);
-       skb_reset_transport_header(skb);
-
-       can_skb_reserve(skb);
-       can_skb_prv(skb)->ifindex = dev->ifindex;
-       can_skb_prv(skb)->skbcnt = 0;
-
-       *cf = skb_put_zero(skb, sizeof(struct can_frame));
-
-       return skb;
-}
-EXPORT_SYMBOL_GPL(alloc_can_skb);
-
-struct sk_buff *alloc_canfd_skb(struct net_device *dev,
-                               struct canfd_frame **cfd)
-{
-       struct sk_buff *skb;
-
-       skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
-                              sizeof(struct canfd_frame));
-       if (unlikely(!skb))
-               return NULL;
-
-       skb->protocol = htons(ETH_P_CANFD);
-       skb->pkt_type = PACKET_BROADCAST;
-       skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-       skb_reset_mac_header(skb);
-       skb_reset_network_header(skb);
-       skb_reset_transport_header(skb);
-
-       can_skb_reserve(skb);
-       can_skb_prv(skb)->ifindex = dev->ifindex;
-       can_skb_prv(skb)->skbcnt = 0;
-
-       *cfd = skb_put_zero(skb, sizeof(struct canfd_frame));
-
-       return skb;
-}
-EXPORT_SYMBOL_GPL(alloc_canfd_skb);
-
-struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
-{
-       struct sk_buff *skb;
-
-       skb = alloc_can_skb(dev, cf);
-       if (unlikely(!skb))
-               return NULL;
-
-       (*cf)->can_id = CAN_ERR_FLAG;
-       (*cf)->len = CAN_ERR_DLC;
-
-       return skb;
-}
-EXPORT_SYMBOL_GPL(alloc_can_err_skb);
-
-/* Allocate and setup space for the CAN network device */
-struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
-                                   unsigned int txqs, unsigned int rxqs)
-{
-       struct net_device *dev;
-       struct can_priv *priv;
-       int size;
-
-       /* We put the driver's priv, the CAN mid layer priv and the
-        * echo skb into the netdevice's priv. The memory layout for
-        * the netdev_priv is like this:
-        *
-        * +-------------------------+
-        * | driver's priv           |
-        * +-------------------------+
-        * | struct can_ml_priv      |
-        * +-------------------------+
-        * | array of struct sk_buff |
-        * +-------------------------+
-        */
-
-       size = ALIGN(sizeof_priv, NETDEV_ALIGN) + sizeof(struct can_ml_priv);
-
-       if (echo_skb_max)
-               size = ALIGN(size, sizeof(struct sk_buff *)) +
-                       echo_skb_max * sizeof(struct sk_buff *);
-
-       dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup,
-                              txqs, rxqs);
-       if (!dev)
-               return NULL;
-
-       priv = netdev_priv(dev);
-       priv->dev = dev;
-
-       dev->ml_priv = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);
-
-       if (echo_skb_max) {
-               priv->echo_skb_max = echo_skb_max;
-               priv->echo_skb = (void *)priv +
-                       (size - echo_skb_max * sizeof(struct sk_buff *));
-       }
-
-       priv->state = CAN_STATE_STOPPED;
-
-       INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
-
-       return dev;
-}
-EXPORT_SYMBOL_GPL(alloc_candev_mqs);
-
-/* Free space of the CAN network device */
-void free_candev(struct net_device *dev)
-{
-       free_netdev(dev);
-}
-EXPORT_SYMBOL_GPL(free_candev);
-
-/* changing MTU and control mode for CAN/CANFD devices */
-int can_change_mtu(struct net_device *dev, int new_mtu)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       /* Do not allow changing the MTU while running */
-       if (dev->flags & IFF_UP)
-               return -EBUSY;
-
-       /* allow change of MTU according to the CANFD ability of the device */
-       switch (new_mtu) {
-       case CAN_MTU:
-               /* 'CANFD-only' controllers can not switch to CAN_MTU */
-               if (priv->ctrlmode_static & CAN_CTRLMODE_FD)
-                       return -EINVAL;
-
-               priv->ctrlmode &= ~CAN_CTRLMODE_FD;
-               break;
-
-       case CANFD_MTU:
-               /* check for potential CANFD ability */
-               if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
-                   !(priv->ctrlmode_static & CAN_CTRLMODE_FD))
-                       return -EINVAL;
-
-               priv->ctrlmode |= CAN_CTRLMODE_FD;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       dev->mtu = new_mtu;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(can_change_mtu);
-
-/* Common open function when the device gets opened.
- *
- * This function should be called in the open function of the device
- * driver.
- */
-int open_candev(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       if (!priv->bittiming.bitrate) {
-               netdev_err(dev, "bit-timing not yet defined\n");
-               return -EINVAL;
-       }
-
-       /* For CAN FD the data bitrate has to be >= the arbitration bitrate */
-       if ((priv->ctrlmode & CAN_CTRLMODE_FD) &&
-           (!priv->data_bittiming.bitrate ||
-            priv->data_bittiming.bitrate < priv->bittiming.bitrate)) {
-               netdev_err(dev, "incorrect/missing data bit-timing\n");
-               return -EINVAL;
-       }
-
-       /* Switch carrier on if device was stopped while in bus-off state */
-       if (!netif_carrier_ok(dev))
-               netif_carrier_on(dev);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(open_candev);
-
-#ifdef CONFIG_OF
-/* Common function that can be used to understand the limitation of
- * a transceiver when it provides no means to determine these limitations
- * at runtime.
- */
-void of_can_transceiver(struct net_device *dev)
-{
-       struct device_node *dn;
-       struct can_priv *priv = netdev_priv(dev);
-       struct device_node *np = dev->dev.parent->of_node;
-       int ret;
-
-       dn = of_get_child_by_name(np, "can-transceiver");
-       if (!dn)
-               return;
-
-       ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max);
-       of_node_put(dn);
-       if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max))
-               netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n");
-}
-EXPORT_SYMBOL_GPL(of_can_transceiver);
-#endif
-
-/* Common close function for cleanup before the device gets closed.
- *
- * This function should be called in the close function of the device
- * driver.
- */
-void close_candev(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       cancel_delayed_work_sync(&priv->restart_work);
-       can_flush_echo_skb(dev);
-}
-EXPORT_SYMBOL_GPL(close_candev);
-
-/* CAN netlink interface */
-static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
-       [IFLA_CAN_STATE]        = { .type = NLA_U32 },
-       [IFLA_CAN_CTRLMODE]     = { .len = sizeof(struct can_ctrlmode) },
-       [IFLA_CAN_RESTART_MS]   = { .type = NLA_U32 },
-       [IFLA_CAN_RESTART]      = { .type = NLA_U32 },
-       [IFLA_CAN_BITTIMING]    = { .len = sizeof(struct can_bittiming) },
-       [IFLA_CAN_BITTIMING_CONST]
-                               = { .len = sizeof(struct can_bittiming_const) },
-       [IFLA_CAN_CLOCK]        = { .len = sizeof(struct can_clock) },
-       [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) },
-       [IFLA_CAN_DATA_BITTIMING]
-                               = { .len = sizeof(struct can_bittiming) },
-       [IFLA_CAN_DATA_BITTIMING_CONST]
-                               = { .len = sizeof(struct can_bittiming_const) },
-       [IFLA_CAN_TERMINATION]  = { .type = NLA_U16 },
-};
-
-static int can_validate(struct nlattr *tb[], struct nlattr *data[],
-                       struct netlink_ext_ack *extack)
-{
-       bool is_can_fd = false;
-
-       /* Make sure that valid CAN FD configurations always consist of
-        * - nominal/arbitration bittiming
-        * - data bittiming
-        * - control mode with CAN_CTRLMODE_FD set
-        */
-
-       if (!data)
-               return 0;
-
-       if (data[IFLA_CAN_CTRLMODE]) {
-               struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
-
-               is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
-       }
-
-       if (is_can_fd) {
-               if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
-                       return -EOPNOTSUPP;
-       }
-
-       if (data[IFLA_CAN_DATA_BITTIMING]) {
-               if (!is_can_fd || !data[IFLA_CAN_BITTIMING])
-                       return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
-static int can_changelink(struct net_device *dev, struct nlattr *tb[],
-                         struct nlattr *data[],
-                         struct netlink_ext_ack *extack)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       int err;
-
-       /* We need synchronization with dev->stop() */
-       ASSERT_RTNL();
-
-       if (data[IFLA_CAN_BITTIMING]) {
-               struct can_bittiming bt;
-
-               /* Do not allow changing bittiming while running */
-               if (dev->flags & IFF_UP)
-                       return -EBUSY;
-
-               /* Calculate bittiming parameters based on
-                * bittiming_const if set, otherwise pass bitrate
-                * directly via do_set_bitrate(). Bail out if neither
-                * is given.
-                */
-               if (!priv->bittiming_const && !priv->do_set_bittiming)
-                       return -EOPNOTSUPP;
-
-               memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
-               err = can_get_bittiming(dev, &bt,
-                                       priv->bittiming_const,
-                                       priv->bitrate_const,
-                                       priv->bitrate_const_cnt);
-               if (err)
-                       return err;
-
-               if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) {
-                       netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n",
-                                  priv->bitrate_max);
-                       return -EINVAL;
-               }
-
-               memcpy(&priv->bittiming, &bt, sizeof(bt));
-
-               if (priv->do_set_bittiming) {
-                       /* Finally, set the bit-timing registers */
-                       err = priv->do_set_bittiming(dev);
-                       if (err)
-                               return err;
-               }
-       }
-
-       if (data[IFLA_CAN_CTRLMODE]) {
-               struct can_ctrlmode *cm;
-               u32 ctrlstatic;
-               u32 maskedflags;
-
-               /* Do not allow changing controller mode while running */
-               if (dev->flags & IFF_UP)
-                       return -EBUSY;
-               cm = nla_data(data[IFLA_CAN_CTRLMODE]);
-               ctrlstatic = priv->ctrlmode_static;
-               maskedflags = cm->flags & cm->mask;
-
-               /* check whether provided bits are allowed to be passed */
-               if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
-                       return -EOPNOTSUPP;
-
-               /* do not check for static fd-non-iso if 'fd' is disabled */
-               if (!(maskedflags & CAN_CTRLMODE_FD))
-                       ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
-
-               /* make sure static options are provided by configuration */
-               if ((maskedflags & ctrlstatic) != ctrlstatic)
-                       return -EOPNOTSUPP;
-
-               /* clear bits to be modified and copy the flag values */
-               priv->ctrlmode &= ~cm->mask;
-               priv->ctrlmode |= maskedflags;
-
-               /* CAN_CTRLMODE_FD can only be set when driver supports FD */
-               if (priv->ctrlmode & CAN_CTRLMODE_FD)
-                       dev->mtu = CANFD_MTU;
-               else
-                       dev->mtu = CAN_MTU;
-       }
-
-       if (data[IFLA_CAN_RESTART_MS]) {
-               /* Do not allow changing restart delay while running */
-               if (dev->flags & IFF_UP)
-                       return -EBUSY;
-               priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);
-       }
-
-       if (data[IFLA_CAN_RESTART]) {
-               /* Do not allow a restart while not running */
-               if (!(dev->flags & IFF_UP))
-                       return -EINVAL;
-               err = can_restart_now(dev);
-               if (err)
-                       return err;
-       }
-
-       if (data[IFLA_CAN_DATA_BITTIMING]) {
-               struct can_bittiming dbt;
-
-               /* Do not allow changing bittiming while running */
-               if (dev->flags & IFF_UP)
-                       return -EBUSY;
-
-               /* Calculate bittiming parameters based on
-                * data_bittiming_const if set, otherwise pass bitrate
-                * directly via do_set_bitrate(). Bail out if neither
-                * is given.
-                */
-               if (!priv->data_bittiming_const && !priv->do_set_data_bittiming)
-                       return -EOPNOTSUPP;
-
-               memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]),
-                      sizeof(dbt));
-               err = can_get_bittiming(dev, &dbt,
-                                       priv->data_bittiming_const,
-                                       priv->data_bitrate_const,
-                                       priv->data_bitrate_const_cnt);
-               if (err)
-                       return err;
-
-               if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
-                       netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n",
-                                  priv->bitrate_max);
-                       return -EINVAL;
-               }
-
-               memcpy(&priv->data_bittiming, &dbt, sizeof(dbt));
-
-               if (priv->do_set_data_bittiming) {
-                       /* Finally, set the bit-timing registers */
-                       err = priv->do_set_data_bittiming(dev);
-                       if (err)
-                               return err;
-               }
-       }
-
-       if (data[IFLA_CAN_TERMINATION]) {
-               const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
-               const unsigned int num_term = priv->termination_const_cnt;
-               unsigned int i;
-
-               if (!priv->do_set_termination)
-                       return -EOPNOTSUPP;
-
-               /* check whether given value is supported by the interface */
-               for (i = 0; i < num_term; i++) {
-                       if (termval == priv->termination_const[i])
-                               break;
-               }
-               if (i >= num_term)
-                       return -EINVAL;
-
-               /* Finally, set the termination value */
-               err = priv->do_set_termination(dev, termval);
-               if (err)
-                       return err;
-
-               priv->termination = termval;
-       }
-
-       return 0;
-}
-
-static size_t can_get_size(const struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       size_t size = 0;
-
-       if (priv->bittiming.bitrate)                            /* IFLA_CAN_BITTIMING */
-               size += nla_total_size(sizeof(struct can_bittiming));
-       if (priv->bittiming_const)                              /* IFLA_CAN_BITTIMING_CONST */
-               size += nla_total_size(sizeof(struct can_bittiming_const));
-       size += nla_total_size(sizeof(struct can_clock));       /* IFLA_CAN_CLOCK */
-       size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_STATE */
-       size += nla_total_size(sizeof(struct can_ctrlmode));    /* IFLA_CAN_CTRLMODE */
-       size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_RESTART_MS */
-       if (priv->do_get_berr_counter)                          /* IFLA_CAN_BERR_COUNTER */
-               size += nla_total_size(sizeof(struct can_berr_counter));
-       if (priv->data_bittiming.bitrate)                       /* IFLA_CAN_DATA_BITTIMING */
-               size += nla_total_size(sizeof(struct can_bittiming));
-       if (priv->data_bittiming_const)                         /* IFLA_CAN_DATA_BITTIMING_CONST */
-               size += nla_total_size(sizeof(struct can_bittiming_const));
-       if (priv->termination_const) {
-               size += nla_total_size(sizeof(priv->termination));              /* IFLA_CAN_TERMINATION */
-               size += nla_total_size(sizeof(*priv->termination_const) *       /* IFLA_CAN_TERMINATION_CONST */
-                                      priv->termination_const_cnt);
-       }
-       if (priv->bitrate_const)                                /* IFLA_CAN_BITRATE_CONST */
-               size += nla_total_size(sizeof(*priv->bitrate_const) *
-                                      priv->bitrate_const_cnt);
-       if (priv->data_bitrate_const)                           /* IFLA_CAN_DATA_BITRATE_CONST */
-               size += nla_total_size(sizeof(*priv->data_bitrate_const) *
-                                      priv->data_bitrate_const_cnt);
-       size += sizeof(priv->bitrate_max);                      /* IFLA_CAN_BITRATE_MAX */
-
-       return size;
-}
-
-static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-       struct can_ctrlmode cm = {.flags = priv->ctrlmode};
-       struct can_berr_counter bec;
-       enum can_state state = priv->state;
-
-       if (priv->do_get_state)
-               priv->do_get_state(dev, &state);
-
-       if ((priv->bittiming.bitrate &&
-            nla_put(skb, IFLA_CAN_BITTIMING,
-                    sizeof(priv->bittiming), &priv->bittiming)) ||
-
-           (priv->bittiming_const &&
-            nla_put(skb, IFLA_CAN_BITTIMING_CONST,
-                    sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
-
-           nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
-           nla_put_u32(skb, IFLA_CAN_STATE, state) ||
-           nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
-           nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
-
-           (priv->do_get_berr_counter &&
-            !priv->do_get_berr_counter(dev, &bec) &&
-            nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
-
-           (priv->data_bittiming.bitrate &&
-            nla_put(skb, IFLA_CAN_DATA_BITTIMING,
-                    sizeof(priv->data_bittiming), &priv->data_bittiming)) ||
-
-           (priv->data_bittiming_const &&
-            nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
-                    sizeof(*priv->data_bittiming_const),
-                    priv->data_bittiming_const)) ||
-
-           (priv->termination_const &&
-            (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) ||
-             nla_put(skb, IFLA_CAN_TERMINATION_CONST,
-                     sizeof(*priv->termination_const) *
-                     priv->termination_const_cnt,
-                     priv->termination_const))) ||
-
-           (priv->bitrate_const &&
-            nla_put(skb, IFLA_CAN_BITRATE_CONST,
-                    sizeof(*priv->bitrate_const) *
-                    priv->bitrate_const_cnt,
-                    priv->bitrate_const)) ||
-
-           (priv->data_bitrate_const &&
-            nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST,
-                    sizeof(*priv->data_bitrate_const) *
-                    priv->data_bitrate_const_cnt,
-                    priv->data_bitrate_const)) ||
-
-           (nla_put(skb, IFLA_CAN_BITRATE_MAX,
-                    sizeof(priv->bitrate_max),
-                    &priv->bitrate_max))
-           )
-
-               return -EMSGSIZE;
-
-       return 0;
-}
-
-static size_t can_get_xstats_size(const struct net_device *dev)
-{
-       return sizeof(struct can_device_stats);
-}
-
-static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       if (nla_put(skb, IFLA_INFO_XSTATS,
-                   sizeof(priv->can_stats), &priv->can_stats))
-               goto nla_put_failure;
-       return 0;
-
-nla_put_failure:
-       return -EMSGSIZE;
-}
-
-static int can_newlink(struct net *src_net, struct net_device *dev,
-                      struct nlattr *tb[], struct nlattr *data[],
-                      struct netlink_ext_ack *extack)
-{
-       return -EOPNOTSUPP;
-}
-
-static void can_dellink(struct net_device *dev, struct list_head *head)
-{
-}
-
-static struct rtnl_link_ops can_link_ops __read_mostly = {
-       .kind           = "can",
-       .maxtype        = IFLA_CAN_MAX,
-       .policy         = can_policy,
-       .setup          = can_setup,
-       .validate       = can_validate,
-       .newlink        = can_newlink,
-       .changelink     = can_changelink,
-       .dellink        = can_dellink,
-       .get_size       = can_get_size,
-       .fill_info      = can_fill_info,
-       .get_xstats_size = can_get_xstats_size,
-       .fill_xstats    = can_fill_xstats,
-};
-
-/* Register the CAN network device */
-int register_candev(struct net_device *dev)
-{
-       struct can_priv *priv = netdev_priv(dev);
-
-       /* Ensure termination_const, termination_const_cnt and
-        * do_set_termination consistency. All must be either set or
-        * unset.
-        */
-       if ((!priv->termination_const != !priv->termination_const_cnt) ||
-           (!priv->termination_const != !priv->do_set_termination))
-               return -EINVAL;
-
-       if (!priv->bitrate_const != !priv->bitrate_const_cnt)
-               return -EINVAL;
-
-       if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt)
-               return -EINVAL;
-
-       dev->rtnl_link_ops = &can_link_ops;
-       netif_carrier_off(dev);
-
-       return register_netdev(dev);
-}
-EXPORT_SYMBOL_GPL(register_candev);
-
-/* Unregister the CAN network device */
-void unregister_candev(struct net_device *dev)
-{
-       unregister_netdev(dev);
-}
-EXPORT_SYMBOL_GPL(unregister_candev);
-
-/* Test if a network device is a candev based device
- * and return the can_priv* if so.
- */
-struct can_priv *safe_candev_priv(struct net_device *dev)
-{
-       if (dev->type != ARPHRD_CAN || dev->rtnl_link_ops != &can_link_ops)
-               return NULL;
-
-       return netdev_priv(dev);
-}
-EXPORT_SYMBOL_GPL(safe_candev_priv);
-
-static __init int can_dev_init(void)
-{
-       int err;
-
-       can_led_notifier_init();
-
-       err = rtnl_link_register(&can_link_ops);
-       if (!err)
-               pr_info(MOD_DESC "\n");
-
-       return err;
-}
-module_init(can_dev_init);
-
-static __exit void can_dev_exit(void)
-{
-       rtnl_link_unregister(&can_link_ops);
-
-       can_led_notifier_exit();
-}
-module_exit(can_dev_exit);
-
-MODULE_ALIAS_RTNL_LINK("can");
diff --git a/drivers/net/can/dev/Makefile b/drivers/net/can/dev/Makefile
new file mode 100644 (file)
index 0000000..3e2e207
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_CAN_DEV)          += can-dev.o
+can-dev-y                      += bittiming.o
+can-dev-y                      += dev.o
+can-dev-y                      += length.o
+can-dev-y                      += netlink.o
+can-dev-y                      += rx-offload.o
+can-dev-y                       += skb.o
+
+can-dev-$(CONFIG_CAN_LEDS)     += led.o
diff --git a/drivers/net/can/dev/bittiming.c b/drivers/net/can/dev/bittiming.c
new file mode 100644 (file)
index 0000000..f7fe226
--- /dev/null
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ */
+
+#include <linux/can/dev.h>
+
+#ifdef CONFIG_CAN_CALC_BITTIMING
+#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+
+/* Bit-timing calculation derived from:
+ *
+ * Code based on LinCAN sources and H8S2638 project
+ * Copyright 2004-2006 Pavel Pisa - DCE FELK CVUT cz
+ * Copyright 2005      Stanislav Marek
+ * email: pisa@cmp.felk.cvut.cz
+ *
+ * Calculates proper bit-timing parameters for a specified bit-rate
+ * and sample-point, which can then be used to set the bit-timing
+ * registers of the CAN controller. You can find more information
+ * in the header file linux/can/netlink.h.
+ */
+static int
+can_update_sample_point(const struct can_bittiming_const *btc,
+                       unsigned int sample_point_nominal, unsigned int tseg,
+                       unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
+                       unsigned int *sample_point_error_ptr)
+{
+       unsigned int sample_point_error, best_sample_point_error = UINT_MAX;
+       unsigned int sample_point, best_sample_point = 0;
+       unsigned int tseg1, tseg2;
+       int i;
+
+       for (i = 0; i <= 1; i++) {
+               tseg2 = tseg + CAN_SYNC_SEG -
+                       (sample_point_nominal * (tseg + CAN_SYNC_SEG)) /
+                       1000 - i;
+               tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
+               tseg1 = tseg - tseg2;
+               if (tseg1 > btc->tseg1_max) {
+                       tseg1 = btc->tseg1_max;
+                       tseg2 = tseg - tseg1;
+               }
+
+               sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) /
+                       (tseg + CAN_SYNC_SEG);
+               sample_point_error = abs(sample_point_nominal - sample_point);
+
+               if (sample_point <= sample_point_nominal &&
+                   sample_point_error < best_sample_point_error) {
+                       best_sample_point = sample_point;
+                       best_sample_point_error = sample_point_error;
+                       *tseg1_ptr = tseg1;
+                       *tseg2_ptr = tseg2;
+               }
+       }
+
+       if (sample_point_error_ptr)
+               *sample_point_error_ptr = best_sample_point_error;
+
+       return best_sample_point;
+}
+
+int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
+                      const struct can_bittiming_const *btc)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       unsigned int bitrate;                   /* current bitrate */
+       unsigned int bitrate_error;             /* difference between current and nominal value */
+       unsigned int best_bitrate_error = UINT_MAX;
+       unsigned int sample_point_error;        /* difference between current and nominal value */
+       unsigned int best_sample_point_error = UINT_MAX;
+       unsigned int sample_point_nominal;      /* nominal sample point */
+       unsigned int best_tseg = 0;             /* current best value for tseg */
+       unsigned int best_brp = 0;              /* current best value for brp */
+       unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
+       u64 v64;
+
+       /* Use CiA recommended sample points */
+       if (bt->sample_point) {
+               sample_point_nominal = bt->sample_point;
+       } else {
+               if (bt->bitrate > 800000)
+                       sample_point_nominal = 750;
+               else if (bt->bitrate > 500000)
+                       sample_point_nominal = 800;
+               else
+                       sample_point_nominal = 875;
+       }
+
+       /* tseg even = round down, odd = round up */
+       for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
+            tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) {
+               tsegall = CAN_SYNC_SEG + tseg / 2;
+
+               /* Compute all possible tseg choices (tseg=tseg1+tseg2) */
+               brp = priv->clock.freq / (tsegall * bt->bitrate) + tseg % 2;
+
+               /* choose brp step which is possible in system */
+               brp = (brp / btc->brp_inc) * btc->brp_inc;
+               if (brp < btc->brp_min || brp > btc->brp_max)
+                       continue;
+
+               bitrate = priv->clock.freq / (brp * tsegall);
+               bitrate_error = abs(bt->bitrate - bitrate);
+
+               /* tseg brp biterror */
+               if (bitrate_error > best_bitrate_error)
+                       continue;
+
+               /* reset sample point error if we have a better bitrate */
+               if (bitrate_error < best_bitrate_error)
+                       best_sample_point_error = UINT_MAX;
+
+               can_update_sample_point(btc, sample_point_nominal, tseg / 2,
+                                       &tseg1, &tseg2, &sample_point_error);
+               if (sample_point_error > best_sample_point_error)
+                       continue;
+
+               best_sample_point_error = sample_point_error;
+               best_bitrate_error = bitrate_error;
+               best_tseg = tseg / 2;
+               best_brp = brp;
+
+               if (bitrate_error == 0 && sample_point_error == 0)
+                       break;
+       }
+
+       if (best_bitrate_error) {
+               /* Error in one-tenth of a percent */
+               v64 = (u64)best_bitrate_error * 1000;
+               do_div(v64, bt->bitrate);
+               bitrate_error = (u32)v64;
+               if (bitrate_error > CAN_CALC_MAX_ERROR) {
+                       netdev_err(dev,
+                                  "bitrate error %d.%d%% too high\n",
+                                  bitrate_error / 10, bitrate_error % 10);
+                       return -EDOM;
+               }
+               netdev_warn(dev, "bitrate error %d.%d%%\n",
+                           bitrate_error / 10, bitrate_error % 10);
+       }
+
+       /* real sample point */
+       bt->sample_point = can_update_sample_point(btc, sample_point_nominal,
+                                                  best_tseg, &tseg1, &tseg2,
+                                                  NULL);
+
+       v64 = (u64)best_brp * 1000 * 1000 * 1000;
+       do_div(v64, priv->clock.freq);
+       bt->tq = (u32)v64;
+       bt->prop_seg = tseg1 / 2;
+       bt->phase_seg1 = tseg1 - bt->prop_seg;
+       bt->phase_seg2 = tseg2;
+
+       /* check for sjw user settings */
+       if (!bt->sjw || !btc->sjw_max) {
+               bt->sjw = 1;
+       } else {
+               /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */
+               if (bt->sjw > btc->sjw_max)
+                       bt->sjw = btc->sjw_max;
+               /* bt->sjw must not be higher than tseg2 */
+               if (tseg2 < bt->sjw)
+                       bt->sjw = tseg2;
+       }
+
+       bt->brp = best_brp;
+
+       /* real bitrate */
+       bt->bitrate = priv->clock.freq /
+               (bt->brp * (CAN_SYNC_SEG + tseg1 + tseg2));
+
+       return 0;
+}
+#endif /* CONFIG_CAN_CALC_BITTIMING */
+
+/* Checks the validity of the specified bit-timing parameters prop_seg,
+ * phase_seg1, phase_seg2 and sjw and tries to determine the bitrate
+ * prescaler value brp. You can find more information in the header
+ * file linux/can/netlink.h.
+ */
+static int can_fixup_bittiming(struct net_device *dev, struct can_bittiming *bt,
+                              const struct can_bittiming_const *btc)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       int tseg1, alltseg;
+       u64 brp64;
+
+       tseg1 = bt->prop_seg + bt->phase_seg1;
+       if (!bt->sjw)
+               bt->sjw = 1;
+       if (bt->sjw > btc->sjw_max ||
+           tseg1 < btc->tseg1_min || tseg1 > btc->tseg1_max ||
+           bt->phase_seg2 < btc->tseg2_min || bt->phase_seg2 > btc->tseg2_max)
+               return -ERANGE;
+
+       brp64 = (u64)priv->clock.freq * (u64)bt->tq;
+       if (btc->brp_inc > 1)
+               do_div(brp64, btc->brp_inc);
+       brp64 += 500000000UL - 1;
+       do_div(brp64, 1000000000UL); /* the practicable BRP */
+       if (btc->brp_inc > 1)
+               brp64 *= btc->brp_inc;
+       bt->brp = (u32)brp64;
+
+       if (bt->brp < btc->brp_min || bt->brp > btc->brp_max)
+               return -EINVAL;
+
+       alltseg = bt->prop_seg + bt->phase_seg1 + bt->phase_seg2 + 1;
+       bt->bitrate = priv->clock.freq / (bt->brp * alltseg);
+       bt->sample_point = ((tseg1 + 1) * 1000) / alltseg;
+
+       return 0;
+}
+
+/* Checks the validity of predefined bitrate settings */
+static int
+can_validate_bitrate(struct net_device *dev, struct can_bittiming *bt,
+                    const u32 *bitrate_const,
+                    const unsigned int bitrate_const_cnt)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       unsigned int i;
+
+       for (i = 0; i < bitrate_const_cnt; i++) {
+               if (bt->bitrate == bitrate_const[i])
+                       break;
+       }
+
+       if (i >= priv->bitrate_const_cnt)
+               return -EINVAL;
+
+       return 0;
+}
+
+int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
+                     const struct can_bittiming_const *btc,
+                     const u32 *bitrate_const,
+                     const unsigned int bitrate_const_cnt)
+{
+       int err;
+
+       /* Depending on the given can_bittiming parameter structure the CAN
+        * timing parameters are calculated based on the provided bitrate OR
+        * alternatively the CAN timing parameters (tq, prop_seg, etc.) are
+        * provided directly which are then checked and fixed up.
+        */
+       if (!bt->tq && bt->bitrate && btc)
+               err = can_calc_bittiming(dev, bt, btc);
+       else if (bt->tq && !bt->bitrate && btc)
+               err = can_fixup_bittiming(dev, bt, btc);
+       else if (!bt->tq && bt->bitrate && bitrate_const)
+               err = can_validate_bitrate(dev, bt, bitrate_const,
+                                          bitrate_const_cnt);
+       else
+               err = -EINVAL;
+
+       return err;
+}
diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c
new file mode 100644 (file)
index 0000000..d9281ae
--- /dev/null
@@ -0,0 +1,468 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/workqueue.h>
+#include <linux/can.h>
+#include <linux/can/can-ml.h>
+#include <linux/can/dev.h>
+#include <linux/can/skb.h>
+#include <linux/can/led.h>
+#include <linux/of.h>
+
+#define MOD_DESC "CAN device driver interface"
+
+MODULE_DESCRIPTION(MOD_DESC);
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+
+static void can_update_state_error_stats(struct net_device *dev,
+                                        enum can_state new_state)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (new_state <= priv->state)
+               return;
+
+       switch (new_state) {
+       case CAN_STATE_ERROR_WARNING:
+               priv->can_stats.error_warning++;
+               break;
+       case CAN_STATE_ERROR_PASSIVE:
+               priv->can_stats.error_passive++;
+               break;
+       case CAN_STATE_BUS_OFF:
+               priv->can_stats.bus_off++;
+               break;
+       default:
+               break;
+       }
+}
+
+static int can_tx_state_to_frame(struct net_device *dev, enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return CAN_ERR_CRTL_ACTIVE;
+       case CAN_STATE_ERROR_WARNING:
+               return CAN_ERR_CRTL_TX_WARNING;
+       case CAN_STATE_ERROR_PASSIVE:
+               return CAN_ERR_CRTL_TX_PASSIVE;
+       default:
+               return 0;
+       }
+}
+
+static int can_rx_state_to_frame(struct net_device *dev, enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return CAN_ERR_CRTL_ACTIVE;
+       case CAN_STATE_ERROR_WARNING:
+               return CAN_ERR_CRTL_RX_WARNING;
+       case CAN_STATE_ERROR_PASSIVE:
+               return CAN_ERR_CRTL_RX_PASSIVE;
+       default:
+               return 0;
+       }
+}
+
+const char *can_get_state_str(const enum can_state state)
+{
+       switch (state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               return "Error Active";
+       case CAN_STATE_ERROR_WARNING:
+               return "Error Warning";
+       case CAN_STATE_ERROR_PASSIVE:
+               return "Error Passive";
+       case CAN_STATE_BUS_OFF:
+               return "Bus Off";
+       case CAN_STATE_STOPPED:
+               return "Stopped";
+       case CAN_STATE_SLEEPING:
+               return "Sleeping";
+       default:
+               return "<unknown>";
+       }
+
+       return "<unknown>";
+}
+EXPORT_SYMBOL_GPL(can_get_state_str);
+
+void can_change_state(struct net_device *dev, struct can_frame *cf,
+                     enum can_state tx_state, enum can_state rx_state)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       enum can_state new_state = max(tx_state, rx_state);
+
+       if (unlikely(new_state == priv->state)) {
+               netdev_warn(dev, "%s: oops, state did not change", __func__);
+               return;
+       }
+
+       netdev_dbg(dev, "Controller changed from %s State (%d) into %s State (%d).\n",
+                  can_get_state_str(priv->state), priv->state,
+                  can_get_state_str(new_state), new_state);
+
+       can_update_state_error_stats(dev, new_state);
+       priv->state = new_state;
+
+       if (!cf)
+               return;
+
+       if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
+               cf->can_id |= CAN_ERR_BUSOFF;
+               return;
+       }
+
+       cf->can_id |= CAN_ERR_CRTL;
+       cf->data[1] |= tx_state >= rx_state ?
+                      can_tx_state_to_frame(dev, tx_state) : 0;
+       cf->data[1] |= tx_state <= rx_state ?
+                      can_rx_state_to_frame(dev, rx_state) : 0;
+}
+EXPORT_SYMBOL_GPL(can_change_state);
+
+/* CAN device restart for bus-off recovery */
+static void can_restart(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &dev->stats;
+       struct sk_buff *skb;
+       struct can_frame *cf;
+       int err;
+
+       BUG_ON(netif_carrier_ok(dev));
+
+       /* No synchronization needed because the device is bus-off and
+        * no messages can come in or go out.
+        */
+       can_flush_echo_skb(dev);
+
+       /* send restart message upstream */
+       skb = alloc_can_err_skb(dev, &cf);
+       if (!skb)
+               goto restart;
+
+       cf->can_id |= CAN_ERR_RESTARTED;
+
+       stats->rx_packets++;
+       stats->rx_bytes += cf->len;
+
+       netif_rx_ni(skb);
+
+restart:
+       netdev_dbg(dev, "restarted\n");
+       priv->can_stats.restarts++;
+
+       /* Now restart the device */
+       err = priv->do_set_mode(dev, CAN_MODE_START);
+
+       netif_carrier_on(dev);
+       if (err)
+               netdev_err(dev, "Error %d during restart", err);
+}
+
+static void can_restart_work(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct can_priv *priv = container_of(dwork, struct can_priv,
+                                            restart_work);
+
+       can_restart(priv->dev);
+}
+
+int can_restart_now(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* A manual restart is only permitted if automatic restart is
+        * disabled and the device is in the bus-off state
+        */
+       if (priv->restart_ms)
+               return -EINVAL;
+       if (priv->state != CAN_STATE_BUS_OFF)
+               return -EBUSY;
+
+       cancel_delayed_work_sync(&priv->restart_work);
+       can_restart(dev);
+
+       return 0;
+}
+
+/* CAN bus-off
+ *
+ * This functions should be called when the device goes bus-off to
+ * tell the netif layer that no more packets can be sent or received.
+ * If enabled, a timer is started to trigger bus-off recovery.
+ */
+void can_bus_off(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (priv->restart_ms)
+               netdev_info(dev, "bus-off, scheduling restart in %d ms\n",
+                           priv->restart_ms);
+       else
+               netdev_info(dev, "bus-off\n");
+
+       netif_carrier_off(dev);
+
+       if (priv->restart_ms)
+               schedule_delayed_work(&priv->restart_work,
+                                     msecs_to_jiffies(priv->restart_ms));
+}
+EXPORT_SYMBOL_GPL(can_bus_off);
+
+void can_setup(struct net_device *dev)
+{
+       dev->type = ARPHRD_CAN;
+       dev->mtu = CAN_MTU;
+       dev->hard_header_len = 0;
+       dev->addr_len = 0;
+       dev->tx_queue_len = 10;
+
+       /* New-style flags. */
+       dev->flags = IFF_NOARP;
+       dev->features = NETIF_F_HW_CSUM;
+}
+
+/* Allocate and setup space for the CAN network device */
+struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
+                                   unsigned int txqs, unsigned int rxqs)
+{
+       struct net_device *dev;
+       struct can_priv *priv;
+       int size;
+
+       /* We put the driver's priv, the CAN mid layer priv and the
+        * echo skb into the netdevice's priv. The memory layout for
+        * the netdev_priv is like this:
+        *
+        * +-------------------------+
+        * | driver's priv           |
+        * +-------------------------+
+        * | struct can_ml_priv      |
+        * +-------------------------+
+        * | array of struct sk_buff |
+        * +-------------------------+
+        */
+
+       size = ALIGN(sizeof_priv, NETDEV_ALIGN) + sizeof(struct can_ml_priv);
+
+       if (echo_skb_max)
+               size = ALIGN(size, sizeof(struct sk_buff *)) +
+                       echo_skb_max * sizeof(struct sk_buff *);
+
+       dev = alloc_netdev_mqs(size, "can%d", NET_NAME_UNKNOWN, can_setup,
+                              txqs, rxqs);
+       if (!dev)
+               return NULL;
+
+       priv = netdev_priv(dev);
+       priv->dev = dev;
+
+       dev->ml_priv = (void *)priv + ALIGN(sizeof_priv, NETDEV_ALIGN);
+
+       if (echo_skb_max) {
+               priv->echo_skb_max = echo_skb_max;
+               priv->echo_skb = (void *)priv +
+                       (size - echo_skb_max * sizeof(struct sk_buff *));
+       }
+
+       priv->state = CAN_STATE_STOPPED;
+
+       INIT_DELAYED_WORK(&priv->restart_work, can_restart_work);
+
+       return dev;
+}
+EXPORT_SYMBOL_GPL(alloc_candev_mqs);
+
+/* Free space of the CAN network device */
+void free_candev(struct net_device *dev)
+{
+       free_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(free_candev);
+
+/* changing MTU and control mode for CAN/CANFD devices */
+int can_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* Do not allow changing the MTU while running */
+       if (dev->flags & IFF_UP)
+               return -EBUSY;
+
+       /* allow change of MTU according to the CANFD ability of the device */
+       switch (new_mtu) {
+       case CAN_MTU:
+               /* 'CANFD-only' controllers can not switch to CAN_MTU */
+               if (priv->ctrlmode_static & CAN_CTRLMODE_FD)
+                       return -EINVAL;
+
+               priv->ctrlmode &= ~CAN_CTRLMODE_FD;
+               break;
+
+       case CANFD_MTU:
+               /* check for potential CANFD ability */
+               if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
+                   !(priv->ctrlmode_static & CAN_CTRLMODE_FD))
+                       return -EINVAL;
+
+               priv->ctrlmode |= CAN_CTRLMODE_FD;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       dev->mtu = new_mtu;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(can_change_mtu);
+
+/* Common open function when the device gets opened.
+ *
+ * This function should be called in the open function of the device
+ * driver.
+ */
+int open_candev(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (!priv->bittiming.bitrate) {
+               netdev_err(dev, "bit-timing not yet defined\n");
+               return -EINVAL;
+       }
+
+       /* For CAN FD the data bitrate has to be >= the arbitration bitrate */
+       if ((priv->ctrlmode & CAN_CTRLMODE_FD) &&
+           (!priv->data_bittiming.bitrate ||
+            priv->data_bittiming.bitrate < priv->bittiming.bitrate)) {
+               netdev_err(dev, "incorrect/missing data bit-timing\n");
+               return -EINVAL;
+       }
+
+       /* Switch carrier on if device was stopped while in bus-off state */
+       if (!netif_carrier_ok(dev))
+               netif_carrier_on(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(open_candev);
+
+#ifdef CONFIG_OF
+/* Common function that can be used to understand the limitation of
+ * a transceiver when it provides no means to determine these limitations
+ * at runtime.
+ */
+void of_can_transceiver(struct net_device *dev)
+{
+       struct device_node *dn;
+       struct can_priv *priv = netdev_priv(dev);
+       struct device_node *np = dev->dev.parent->of_node;
+       int ret;
+
+       dn = of_get_child_by_name(np, "can-transceiver");
+       if (!dn)
+               return;
+
+       ret = of_property_read_u32(dn, "max-bitrate", &priv->bitrate_max);
+       of_node_put(dn);
+       if ((ret && ret != -EINVAL) || (!ret && !priv->bitrate_max))
+               netdev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit.\n");
+}
+EXPORT_SYMBOL_GPL(of_can_transceiver);
+#endif
+
+/* Common close function for cleanup before the device gets closed.
+ *
+ * This function should be called in the close function of the device
+ * driver.
+ */
+void close_candev(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       cancel_delayed_work_sync(&priv->restart_work);
+       can_flush_echo_skb(dev);
+}
+EXPORT_SYMBOL_GPL(close_candev);
+
+/* Register the CAN network device */
+int register_candev(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       /* Ensure termination_const, termination_const_cnt and
+        * do_set_termination consistency. All must be either set or
+        * unset.
+        */
+       if ((!priv->termination_const != !priv->termination_const_cnt) ||
+           (!priv->termination_const != !priv->do_set_termination))
+               return -EINVAL;
+
+       if (!priv->bitrate_const != !priv->bitrate_const_cnt)
+               return -EINVAL;
+
+       if (!priv->data_bitrate_const != !priv->data_bitrate_const_cnt)
+               return -EINVAL;
+
+       dev->rtnl_link_ops = &can_link_ops;
+       netif_carrier_off(dev);
+
+       return register_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(register_candev);
+
+/* Unregister the CAN network device */
+void unregister_candev(struct net_device *dev)
+{
+       unregister_netdev(dev);
+}
+EXPORT_SYMBOL_GPL(unregister_candev);
+
+/* Test if a network device is a candev based device
+ * and return the can_priv* if so.
+ */
+struct can_priv *safe_candev_priv(struct net_device *dev)
+{
+       if (dev->type != ARPHRD_CAN || dev->rtnl_link_ops != &can_link_ops)
+               return NULL;
+
+       return netdev_priv(dev);
+}
+EXPORT_SYMBOL_GPL(safe_candev_priv);
+
+static __init int can_dev_init(void)
+{
+       int err;
+
+       can_led_notifier_init();
+
+       err = can_netlink_register();
+       if (!err)
+               pr_info(MOD_DESC "\n");
+
+       return err;
+}
+module_init(can_dev_init);
+
+static __exit void can_dev_exit(void)
+{
+       can_netlink_unregister();
+
+       can_led_notifier_exit();
+}
+module_exit(can_dev_exit);
+
+MODULE_ALIAS_RTNL_LINK("can");
diff --git a/drivers/net/can/dev/length.c b/drivers/net/can/dev/length.c
new file mode 100644 (file)
index 0000000..b48140b
--- /dev/null
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2012, 2020 Oliver Hartkopp <socketcan@hartkopp.net>
+ */
+
+#include <linux/can/dev.h>
+
+/* CAN DLC to real data length conversion helpers */
+
+static const u8 dlc2len[] = {
+       0, 1, 2, 3, 4, 5, 6, 7,
+       8, 12, 16, 20, 24, 32, 48, 64
+};
+
+/* get data length from raw data length code (DLC) */
+u8 can_fd_dlc2len(u8 dlc)
+{
+       return dlc2len[dlc & 0x0F];
+}
+EXPORT_SYMBOL_GPL(can_fd_dlc2len);
+
+static const u8 len2dlc[] = {
+       0, 1, 2, 3, 4, 5, 6, 7, 8,      /* 0 - 8 */
+       9, 9, 9, 9,                     /* 9 - 12 */
+       10, 10, 10, 10,                 /* 13 - 16 */
+       11, 11, 11, 11,                 /* 17 - 20 */
+       12, 12, 12, 12,                 /* 21 - 24 */
+       13, 13, 13, 13, 13, 13, 13, 13, /* 25 - 32 */
+       14, 14, 14, 14, 14, 14, 14, 14, /* 33 - 40 */
+       14, 14, 14, 14, 14, 14, 14, 14, /* 41 - 48 */
+       15, 15, 15, 15, 15, 15, 15, 15, /* 49 - 56 */
+       15, 15, 15, 15, 15, 15, 15, 15  /* 57 - 64 */
+};
+
+/* map the sanitized data length to an appropriate data length code */
+u8 can_fd_len2dlc(u8 len)
+{
+       /* check for length mapping table size at build time */
+       BUILD_BUG_ON(ARRAY_SIZE(len2dlc) != CANFD_MAX_DLEN + 1);
+
+       if (unlikely(len > CANFD_MAX_DLEN))
+               return CANFD_MAX_DLC;
+
+       return len2dlc[len];
+}
+EXPORT_SYMBOL_GPL(can_fd_len2dlc);
+
+/**
+ * can_skb_get_frame_len() - Calculate the CAN Frame length in bytes
+ *     of a given skb.
+ * @skb: socket buffer of a CAN message.
+ *
+ * Do a rough calculation: bit stuffing is ignored and length in bits
+ * is rounded up to a length in bytes.
+ *
+ * Rationale: this function is to be used for the BQL functions
+ * (netdev_sent_queue() and netdev_completed_queue()) which expect a
+ * value in bytes. Just using skb->len is insufficient because it will
+ * return the constant value of CAN(FD)_MTU. Doing the bit stuffing
+ * calculation would be too expensive in term of computing resources
+ * for no noticeable gain.
+ *
+ * Remarks: The payload of CAN FD frames with BRS flag are sent at a
+ * different bitrate. Currently, the can-utils canbusload tool does
+ * not support CAN-FD yet and so we could not run any benchmark to
+ * measure the impact. There might be possible improvement here.
+ *
+ * Return: length in bytes.
+ */
+unsigned int can_skb_get_frame_len(const struct sk_buff *skb)
+{
+       const struct canfd_frame *cf = (const struct canfd_frame *)skb->data;
+       u8 len;
+
+       if (can_is_canfd_skb(skb))
+               len = canfd_sanitize_len(cf->len);
+       else if (cf->can_id & CAN_RTR_FLAG)
+               len = 0;
+       else
+               len = cf->len;
+
+       if (can_is_canfd_skb(skb)) {
+               if (cf->can_id & CAN_EFF_FLAG)
+                       len += CANFD_FRAME_OVERHEAD_EFF;
+               else
+                       len += CANFD_FRAME_OVERHEAD_SFF;
+       } else {
+               if (cf->can_id & CAN_EFF_FLAG)
+                       len += CAN_FRAME_OVERHEAD_EFF;
+               else
+                       len += CAN_FRAME_OVERHEAD_SFF;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(can_skb_get_frame_len);
diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c
new file mode 100644 (file)
index 0000000..867f6be
--- /dev/null
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ */
+
+#include <linux/can/dev.h>
+#include <net/rtnetlink.h>
+
+static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
+       [IFLA_CAN_STATE]        = { .type = NLA_U32 },
+       [IFLA_CAN_CTRLMODE]     = { .len = sizeof(struct can_ctrlmode) },
+       [IFLA_CAN_RESTART_MS]   = { .type = NLA_U32 },
+       [IFLA_CAN_RESTART]      = { .type = NLA_U32 },
+       [IFLA_CAN_BITTIMING]    = { .len = sizeof(struct can_bittiming) },
+       [IFLA_CAN_BITTIMING_CONST]
+                               = { .len = sizeof(struct can_bittiming_const) },
+       [IFLA_CAN_CLOCK]        = { .len = sizeof(struct can_clock) },
+       [IFLA_CAN_BERR_COUNTER] = { .len = sizeof(struct can_berr_counter) },
+       [IFLA_CAN_DATA_BITTIMING]
+                               = { .len = sizeof(struct can_bittiming) },
+       [IFLA_CAN_DATA_BITTIMING_CONST]
+                               = { .len = sizeof(struct can_bittiming_const) },
+       [IFLA_CAN_TERMINATION]  = { .type = NLA_U16 },
+};
+
+static int can_validate(struct nlattr *tb[], struct nlattr *data[],
+                       struct netlink_ext_ack *extack)
+{
+       bool is_can_fd = false;
+
+       /* Make sure that valid CAN FD configurations always consist of
+        * - nominal/arbitration bittiming
+        * - data bittiming
+        * - control mode with CAN_CTRLMODE_FD set
+        */
+
+       if (!data)
+               return 0;
+
+       if (data[IFLA_CAN_CTRLMODE]) {
+               struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+
+               is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD;
+       }
+
+       if (is_can_fd) {
+               if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING])
+                       return -EOPNOTSUPP;
+       }
+
+       if (data[IFLA_CAN_DATA_BITTIMING]) {
+               if (!is_can_fd || !data[IFLA_CAN_BITTIMING])
+                       return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int can_changelink(struct net_device *dev, struct nlattr *tb[],
+                         struct nlattr *data[],
+                         struct netlink_ext_ack *extack)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       int err;
+
+       /* We need synchronization with dev->stop() */
+       ASSERT_RTNL();
+
+       if (data[IFLA_CAN_BITTIMING]) {
+               struct can_bittiming bt;
+
+               /* Do not allow changing bittiming while running */
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               /* Calculate bittiming parameters based on
+                * bittiming_const if set, otherwise pass bitrate
+                * directly via do_set_bitrate(). Bail out if neither
+                * is given.
+                */
+               if (!priv->bittiming_const && !priv->do_set_bittiming)
+                       return -EOPNOTSUPP;
+
+               memcpy(&bt, nla_data(data[IFLA_CAN_BITTIMING]), sizeof(bt));
+               err = can_get_bittiming(dev, &bt,
+                                       priv->bittiming_const,
+                                       priv->bitrate_const,
+                                       priv->bitrate_const_cnt);
+               if (err)
+                       return err;
+
+               if (priv->bitrate_max && bt.bitrate > priv->bitrate_max) {
+                       netdev_err(dev, "arbitration bitrate surpasses transceiver capabilities of %d bps\n",
+                                  priv->bitrate_max);
+                       return -EINVAL;
+               }
+
+               memcpy(&priv->bittiming, &bt, sizeof(bt));
+
+               if (priv->do_set_bittiming) {
+                       /* Finally, set the bit-timing registers */
+                       err = priv->do_set_bittiming(dev);
+                       if (err)
+                               return err;
+               }
+       }
+
+       if (data[IFLA_CAN_CTRLMODE]) {
+               struct can_ctrlmode *cm;
+               u32 ctrlstatic;
+               u32 maskedflags;
+
+               /* Do not allow changing controller mode while running */
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+               cm = nla_data(data[IFLA_CAN_CTRLMODE]);
+               ctrlstatic = priv->ctrlmode_static;
+               maskedflags = cm->flags & cm->mask;
+
+               /* check whether provided bits are allowed to be passed */
+               if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic))
+                       return -EOPNOTSUPP;
+
+               /* do not check for static fd-non-iso if 'fd' is disabled */
+               if (!(maskedflags & CAN_CTRLMODE_FD))
+                       ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO;
+
+               /* make sure static options are provided by configuration */
+               if ((maskedflags & ctrlstatic) != ctrlstatic)
+                       return -EOPNOTSUPP;
+
+               /* clear bits to be modified and copy the flag values */
+               priv->ctrlmode &= ~cm->mask;
+               priv->ctrlmode |= maskedflags;
+
+               /* CAN_CTRLMODE_FD can only be set when driver supports FD */
+               if (priv->ctrlmode & CAN_CTRLMODE_FD)
+                       dev->mtu = CANFD_MTU;
+               else
+                       dev->mtu = CAN_MTU;
+       }
+
+       if (data[IFLA_CAN_RESTART_MS]) {
+               /* Do not allow changing restart delay while running */
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+               priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);
+       }
+
+       if (data[IFLA_CAN_RESTART]) {
+               /* Do not allow a restart while not running */
+               if (!(dev->flags & IFF_UP))
+                       return -EINVAL;
+               err = can_restart_now(dev);
+               if (err)
+                       return err;
+       }
+
+       if (data[IFLA_CAN_DATA_BITTIMING]) {
+               struct can_bittiming dbt;
+
+               /* Do not allow changing bittiming while running */
+               if (dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               /* Calculate bittiming parameters based on
+                * data_bittiming_const if set, otherwise pass bitrate
+                * directly via do_set_bitrate(). Bail out if neither
+                * is given.
+                */
+               if (!priv->data_bittiming_const && !priv->do_set_data_bittiming)
+                       return -EOPNOTSUPP;
+
+               memcpy(&dbt, nla_data(data[IFLA_CAN_DATA_BITTIMING]),
+                      sizeof(dbt));
+               err = can_get_bittiming(dev, &dbt,
+                                       priv->data_bittiming_const,
+                                       priv->data_bitrate_const,
+                                       priv->data_bitrate_const_cnt);
+               if (err)
+                       return err;
+
+               if (priv->bitrate_max && dbt.bitrate > priv->bitrate_max) {
+                       netdev_err(dev, "canfd data bitrate surpasses transceiver capabilities of %d bps\n",
+                                  priv->bitrate_max);
+                       return -EINVAL;
+               }
+
+               memcpy(&priv->data_bittiming, &dbt, sizeof(dbt));
+
+               if (priv->do_set_data_bittiming) {
+                       /* Finally, set the bit-timing registers */
+                       err = priv->do_set_data_bittiming(dev);
+                       if (err)
+                               return err;
+               }
+       }
+
+       if (data[IFLA_CAN_TERMINATION]) {
+               const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
+               const unsigned int num_term = priv->termination_const_cnt;
+               unsigned int i;
+
+               if (!priv->do_set_termination)
+                       return -EOPNOTSUPP;
+
+               /* check whether given value is supported by the interface */
+               for (i = 0; i < num_term; i++) {
+                       if (termval == priv->termination_const[i])
+                               break;
+               }
+               if (i >= num_term)
+                       return -EINVAL;
+
+               /* Finally, set the termination value */
+               err = priv->do_set_termination(dev, termval);
+               if (err)
+                       return err;
+
+               priv->termination = termval;
+       }
+
+       return 0;
+}
+
+static size_t can_get_size(const struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       size_t size = 0;
+
+       if (priv->bittiming.bitrate)                            /* IFLA_CAN_BITTIMING */
+               size += nla_total_size(sizeof(struct can_bittiming));
+       if (priv->bittiming_const)                              /* IFLA_CAN_BITTIMING_CONST */
+               size += nla_total_size(sizeof(struct can_bittiming_const));
+       size += nla_total_size(sizeof(struct can_clock));       /* IFLA_CAN_CLOCK */
+       size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_STATE */
+       size += nla_total_size(sizeof(struct can_ctrlmode));    /* IFLA_CAN_CTRLMODE */
+       size += nla_total_size(sizeof(u32));                    /* IFLA_CAN_RESTART_MS */
+       if (priv->do_get_berr_counter)                          /* IFLA_CAN_BERR_COUNTER */
+               size += nla_total_size(sizeof(struct can_berr_counter));
+       if (priv->data_bittiming.bitrate)                       /* IFLA_CAN_DATA_BITTIMING */
+               size += nla_total_size(sizeof(struct can_bittiming));
+       if (priv->data_bittiming_const)                         /* IFLA_CAN_DATA_BITTIMING_CONST */
+               size += nla_total_size(sizeof(struct can_bittiming_const));
+       if (priv->termination_const) {
+               size += nla_total_size(sizeof(priv->termination));              /* IFLA_CAN_TERMINATION */
+               size += nla_total_size(sizeof(*priv->termination_const) *       /* IFLA_CAN_TERMINATION_CONST */
+                                      priv->termination_const_cnt);
+       }
+       if (priv->bitrate_const)                                /* IFLA_CAN_BITRATE_CONST */
+               size += nla_total_size(sizeof(*priv->bitrate_const) *
+                                      priv->bitrate_const_cnt);
+       if (priv->data_bitrate_const)                           /* IFLA_CAN_DATA_BITRATE_CONST */
+               size += nla_total_size(sizeof(*priv->data_bitrate_const) *
+                                      priv->data_bitrate_const_cnt);
+       size += sizeof(priv->bitrate_max);                      /* IFLA_CAN_BITRATE_MAX */
+
+       return size;
+}
+
+static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       struct can_ctrlmode cm = {.flags = priv->ctrlmode};
+       struct can_berr_counter bec = { };
+       enum can_state state = priv->state;
+
+       if (priv->do_get_state)
+               priv->do_get_state(dev, &state);
+
+       if ((priv->bittiming.bitrate &&
+            nla_put(skb, IFLA_CAN_BITTIMING,
+                    sizeof(priv->bittiming), &priv->bittiming)) ||
+
+           (priv->bittiming_const &&
+            nla_put(skb, IFLA_CAN_BITTIMING_CONST,
+                    sizeof(*priv->bittiming_const), priv->bittiming_const)) ||
+
+           nla_put(skb, IFLA_CAN_CLOCK, sizeof(priv->clock), &priv->clock) ||
+           nla_put_u32(skb, IFLA_CAN_STATE, state) ||
+           nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) ||
+           nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) ||
+
+           (priv->do_get_berr_counter &&
+            !priv->do_get_berr_counter(dev, &bec) &&
+            nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) ||
+
+           (priv->data_bittiming.bitrate &&
+            nla_put(skb, IFLA_CAN_DATA_BITTIMING,
+                    sizeof(priv->data_bittiming), &priv->data_bittiming)) ||
+
+           (priv->data_bittiming_const &&
+            nla_put(skb, IFLA_CAN_DATA_BITTIMING_CONST,
+                    sizeof(*priv->data_bittiming_const),
+                    priv->data_bittiming_const)) ||
+
+           (priv->termination_const &&
+            (nla_put_u16(skb, IFLA_CAN_TERMINATION, priv->termination) ||
+             nla_put(skb, IFLA_CAN_TERMINATION_CONST,
+                     sizeof(*priv->termination_const) *
+                     priv->termination_const_cnt,
+                     priv->termination_const))) ||
+
+           (priv->bitrate_const &&
+            nla_put(skb, IFLA_CAN_BITRATE_CONST,
+                    sizeof(*priv->bitrate_const) *
+                    priv->bitrate_const_cnt,
+                    priv->bitrate_const)) ||
+
+           (priv->data_bitrate_const &&
+            nla_put(skb, IFLA_CAN_DATA_BITRATE_CONST,
+                    sizeof(*priv->data_bitrate_const) *
+                    priv->data_bitrate_const_cnt,
+                    priv->data_bitrate_const)) ||
+
+           (nla_put(skb, IFLA_CAN_BITRATE_MAX,
+                    sizeof(priv->bitrate_max),
+                    &priv->bitrate_max))
+           )
+
+               return -EMSGSIZE;
+
+       return 0;
+}
+
+static size_t can_get_xstats_size(const struct net_device *dev)
+{
+       return sizeof(struct can_device_stats);
+}
+
+static int can_fill_xstats(struct sk_buff *skb, const struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (nla_put(skb, IFLA_INFO_XSTATS,
+                   sizeof(priv->can_stats), &priv->can_stats))
+               goto nla_put_failure;
+       return 0;
+
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static int can_newlink(struct net *src_net, struct net_device *dev,
+                      struct nlattr *tb[], struct nlattr *data[],
+                      struct netlink_ext_ack *extack)
+{
+       return -EOPNOTSUPP;
+}
+
+static void can_dellink(struct net_device *dev, struct list_head *head)
+{
+}
+
+struct rtnl_link_ops can_link_ops __read_mostly = {
+       .kind           = "can",
+       .maxtype        = IFLA_CAN_MAX,
+       .policy         = can_policy,
+       .setup          = can_setup,
+       .validate       = can_validate,
+       .newlink        = can_newlink,
+       .changelink     = can_changelink,
+       .dellink        = can_dellink,
+       .get_size       = can_get_size,
+       .fill_info      = can_fill_info,
+       .get_xstats_size = can_get_xstats_size,
+       .fill_xstats    = can_fill_xstats,
+};
+
+int can_netlink_register(void)
+{
+       return rtnl_link_register(&can_link_ops);
+}
+
+void can_netlink_unregister(void)
+{
+       rtnl_link_unregister(&can_link_ops);
+}
diff --git a/drivers/net/can/dev/rx-offload.c b/drivers/net/can/dev/rx-offload.c
new file mode 100644 (file)
index 0000000..ab2c154
--- /dev/null
@@ -0,0 +1,377 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2014      Protonic Holland,
+ *                         David Jander
+ * Copyright (C) 2014-2017 Pengutronix,
+ *                         Marc Kleine-Budde <kernel@pengutronix.de>
+ */
+
+#include <linux/can/dev.h>
+#include <linux/can/rx-offload.h>
+
+struct can_rx_offload_cb {
+       u32 timestamp;
+};
+
+static inline struct can_rx_offload_cb *
+can_rx_offload_get_cb(struct sk_buff *skb)
+{
+       BUILD_BUG_ON(sizeof(struct can_rx_offload_cb) > sizeof(skb->cb));
+
+       return (struct can_rx_offload_cb *)skb->cb;
+}
+
+static inline bool
+can_rx_offload_le(struct can_rx_offload *offload,
+                 unsigned int a, unsigned int b)
+{
+       if (offload->inc)
+               return a <= b;
+       else
+               return a >= b;
+}
+
+static inline unsigned int
+can_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val)
+{
+       if (offload->inc)
+               return (*val)++;
+       else
+               return (*val)--;
+}
+
+static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota)
+{
+       struct can_rx_offload *offload = container_of(napi,
+                                                     struct can_rx_offload,
+                                                     napi);
+       struct net_device *dev = offload->dev;
+       struct net_device_stats *stats = &dev->stats;
+       struct sk_buff *skb;
+       int work_done = 0;
+
+       while ((work_done < quota) &&
+              (skb = skb_dequeue(&offload->skb_queue))) {
+               struct can_frame *cf = (struct can_frame *)skb->data;
+
+               work_done++;
+               stats->rx_packets++;
+               stats->rx_bytes += cf->len;
+               netif_receive_skb(skb);
+       }
+
+       if (work_done < quota) {
+               napi_complete_done(napi, work_done);
+
+               /* Check if there was another interrupt */
+               if (!skb_queue_empty(&offload->skb_queue))
+                       napi_reschedule(&offload->napi);
+       }
+
+       can_led_event(offload->dev, CAN_LED_EVENT_RX);
+
+       return work_done;
+}
+
+static inline void
+__skb_queue_add_sort(struct sk_buff_head *head, struct sk_buff *new,
+                    int (*compare)(struct sk_buff *a, struct sk_buff *b))
+{
+       struct sk_buff *pos, *insert = NULL;
+
+       skb_queue_reverse_walk(head, pos) {
+               const struct can_rx_offload_cb *cb_pos, *cb_new;
+
+               cb_pos = can_rx_offload_get_cb(pos);
+               cb_new = can_rx_offload_get_cb(new);
+
+               netdev_dbg(new->dev,
+                          "%s: pos=0x%08x, new=0x%08x, diff=%10d, queue_len=%d\n",
+                          __func__,
+                          cb_pos->timestamp, cb_new->timestamp,
+                          cb_new->timestamp - cb_pos->timestamp,
+                          skb_queue_len(head));
+
+               if (compare(pos, new) < 0)
+                       continue;
+               insert = pos;
+               break;
+       }
+       if (!insert)
+               __skb_queue_head(head, new);
+       else
+               __skb_queue_after(head, insert, new);
+}
+
+static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
+{
+       const struct can_rx_offload_cb *cb_a, *cb_b;
+
+       cb_a = can_rx_offload_get_cb(a);
+       cb_b = can_rx_offload_get_cb(b);
+
+       /* Subtract two u32 and return result as int, to keep
+        * difference steady around the u32 overflow.
+        */
+       return cb_b->timestamp - cb_a->timestamp;
+}
+
+/**
+ * can_rx_offload_offload_one() - Read one CAN frame from HW
+ * @offload: pointer to rx_offload context
+ * @n: number of mailbox to read
+ *
+ * The task of this function is to read a CAN frame from mailbox @n
+ * from the device and return the mailbox's content as a struct
+ * sk_buff.
+ *
+ * If the struct can_rx_offload::skb_queue exceeds the maximal queue
+ * length (struct can_rx_offload::skb_queue_len_max) or no skb can be
+ * allocated, the mailbox contents is discarded by reading it into an
+ * overflow buffer. This way the mailbox is marked as free by the
+ * driver.
+ *
+ * Return: A pointer to skb containing the CAN frame on success.
+ *
+ *         NULL if the mailbox @n is empty.
+ *
+ *         ERR_PTR() in case of an error
+ */
+static struct sk_buff *
+can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
+{
+       struct sk_buff *skb;
+       struct can_rx_offload_cb *cb;
+       bool drop = false;
+       u32 timestamp;
+
+       /* If queue is full drop frame */
+       if (unlikely(skb_queue_len(&offload->skb_queue) >
+                    offload->skb_queue_len_max))
+               drop = true;
+
+       skb = offload->mailbox_read(offload, n, &timestamp, drop);
+       /* Mailbox was empty. */
+       if (unlikely(!skb))
+               return NULL;
+
+       /* There was a problem reading the mailbox, propagate
+        * error value.
+        */
+       if (IS_ERR(skb)) {
+               offload->dev->stats.rx_dropped++;
+               offload->dev->stats.rx_fifo_errors++;
+
+               return skb;
+       }
+
+       /* Mailbox was read. */
+       cb = can_rx_offload_get_cb(skb);
+       cb->timestamp = timestamp;
+
+       return skb;
+}
+
+int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
+                                        u64 pending)
+{
+       struct sk_buff_head skb_queue;
+       unsigned int i;
+
+       __skb_queue_head_init(&skb_queue);
+
+       for (i = offload->mb_first;
+            can_rx_offload_le(offload, i, offload->mb_last);
+            can_rx_offload_inc(offload, &i)) {
+               struct sk_buff *skb;
+
+               if (!(pending & BIT_ULL(i)))
+                       continue;
+
+               skb = can_rx_offload_offload_one(offload, i);
+               if (IS_ERR_OR_NULL(skb))
+                       continue;
+
+               __skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare);
+       }
+
+       if (!skb_queue_empty(&skb_queue)) {
+               unsigned long flags;
+               u32 queue_len;
+
+               spin_lock_irqsave(&offload->skb_queue.lock, flags);
+               skb_queue_splice_tail(&skb_queue, &offload->skb_queue);
+               spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
+
+               queue_len = skb_queue_len(&offload->skb_queue);
+               if (queue_len > offload->skb_queue_len_max / 8)
+                       netdev_dbg(offload->dev, "%s: queue_len=%d\n",
+                                  __func__, queue_len);
+
+               can_rx_offload_schedule(offload);
+       }
+
+       return skb_queue_len(&skb_queue);
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_timestamp);
+
+int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
+{
+       struct sk_buff *skb;
+       int received = 0;
+
+       while (1) {
+               skb = can_rx_offload_offload_one(offload, 0);
+               if (IS_ERR(skb))
+                       continue;
+               if (!skb)
+                       break;
+
+               skb_queue_tail(&offload->skb_queue, skb);
+               received++;
+       }
+
+       if (received)
+               can_rx_offload_schedule(offload);
+
+       return received;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
+
+int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
+                               struct sk_buff *skb, u32 timestamp)
+{
+       struct can_rx_offload_cb *cb;
+       unsigned long flags;
+
+       if (skb_queue_len(&offload->skb_queue) >
+           offload->skb_queue_len_max) {
+               dev_kfree_skb_any(skb);
+               return -ENOBUFS;
+       }
+
+       cb = can_rx_offload_get_cb(skb);
+       cb->timestamp = timestamp;
+
+       spin_lock_irqsave(&offload->skb_queue.lock, flags);
+       __skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare);
+       spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
+
+       can_rx_offload_schedule(offload);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted);
+
+unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
+                                        unsigned int idx, u32 timestamp,
+                                        unsigned int *frame_len_ptr)
+{
+       struct net_device *dev = offload->dev;
+       struct net_device_stats *stats = &dev->stats;
+       struct sk_buff *skb;
+       u8 len;
+       int err;
+
+       skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
+       if (!skb)
+               return 0;
+
+       err = can_rx_offload_queue_sorted(offload, skb, timestamp);
+       if (err) {
+               stats->rx_errors++;
+               stats->tx_fifo_errors++;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb);
+
+int can_rx_offload_queue_tail(struct can_rx_offload *offload,
+                             struct sk_buff *skb)
+{
+       if (skb_queue_len(&offload->skb_queue) >
+           offload->skb_queue_len_max) {
+               dev_kfree_skb_any(skb);
+               return -ENOBUFS;
+       }
+
+       skb_queue_tail(&offload->skb_queue, skb);
+       can_rx_offload_schedule(offload);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
+
+static int can_rx_offload_init_queue(struct net_device *dev,
+                                    struct can_rx_offload *offload,
+                                    unsigned int weight)
+{
+       offload->dev = dev;
+
+       /* Limit queue len to 4x the weight (rounted to next power of two) */
+       offload->skb_queue_len_max = 2 << fls(weight);
+       offload->skb_queue_len_max *= 4;
+       skb_queue_head_init(&offload->skb_queue);
+
+       netif_napi_add(dev, &offload->napi, can_rx_offload_napi_poll, weight);
+
+       dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n",
+               __func__, offload->skb_queue_len_max);
+
+       return 0;
+}
+
+int can_rx_offload_add_timestamp(struct net_device *dev,
+                                struct can_rx_offload *offload)
+{
+       unsigned int weight;
+
+       if (offload->mb_first > BITS_PER_LONG_LONG ||
+           offload->mb_last > BITS_PER_LONG_LONG || !offload->mailbox_read)
+               return -EINVAL;
+
+       if (offload->mb_first < offload->mb_last) {
+               offload->inc = true;
+               weight = offload->mb_last - offload->mb_first;
+       } else {
+               offload->inc = false;
+               weight = offload->mb_first - offload->mb_last;
+       }
+
+       return can_rx_offload_init_queue(dev, offload, weight);
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_add_timestamp);
+
+int can_rx_offload_add_fifo(struct net_device *dev,
+                           struct can_rx_offload *offload, unsigned int weight)
+{
+       if (!offload->mailbox_read)
+               return -EINVAL;
+
+       return can_rx_offload_init_queue(dev, offload, weight);
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo);
+
+int can_rx_offload_add_manual(struct net_device *dev,
+                             struct can_rx_offload *offload,
+                             unsigned int weight)
+{
+       if (offload->mailbox_read)
+               return -EINVAL;
+
+       return can_rx_offload_init_queue(dev, offload, weight);
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_add_manual);
+
+void can_rx_offload_enable(struct can_rx_offload *offload)
+{
+       napi_enable(&offload->napi);
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_enable);
+
+void can_rx_offload_del(struct can_rx_offload *offload)
+{
+       netif_napi_del(&offload->napi);
+       skb_queue_purge(&offload->skb_queue);
+}
+EXPORT_SYMBOL_GPL(can_rx_offload_del);
diff --git a/drivers/net/can/dev/skb.c b/drivers/net/can/dev/skb.c
new file mode 100644 (file)
index 0000000..6a64fe4
--- /dev/null
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
+ * Copyright (C) 2006 Andrey Volkov, Varma Electronics
+ * Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ */
+
+#include <linux/can/dev.h>
+
+/* Local echo of CAN messages
+ *
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.rst). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented. In the case that the driver does not support the echo
+ * the IFF_ECHO remains clear in dev->flags. This causes the PF_CAN core
+ * to perform the echo as a fallback solution.
+ */
+void can_flush_echo_skb(struct net_device *dev)
+{
+       struct can_priv *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &dev->stats;
+       int i;
+
+       for (i = 0; i < priv->echo_skb_max; i++) {
+               if (priv->echo_skb[i]) {
+                       kfree_skb(priv->echo_skb[i]);
+                       priv->echo_skb[i] = NULL;
+                       stats->tx_dropped++;
+                       stats->tx_aborted_errors++;
+               }
+       }
+}
+
+/* Put the skb on the stack to be looped backed locally lateron
+ *
+ * The function is typically called in the start_xmit function
+ * of the device driver. The driver must protect access to
+ * priv->echo_skb, if necessary.
+ */
+int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
+                    unsigned int idx, unsigned int frame_len)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       BUG_ON(idx >= priv->echo_skb_max);
+
+       /* check flag whether this packet has to be looped back */
+       if (!(dev->flags & IFF_ECHO) || skb->pkt_type != PACKET_LOOPBACK ||
+           (skb->protocol != htons(ETH_P_CAN) &&
+            skb->protocol != htons(ETH_P_CANFD))) {
+               kfree_skb(skb);
+               return 0;
+       }
+
+       if (!priv->echo_skb[idx]) {
+               skb = can_create_echo_skb(skb);
+               if (!skb)
+                       return -ENOMEM;
+
+               /* make settings for echo to reduce code in irq context */
+               skb->pkt_type = PACKET_BROADCAST;
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+               skb->dev = dev;
+
+               /* save frame_len to reuse it when transmission is completed */
+               can_skb_prv(skb)->frame_len = frame_len;
+
+               skb_tx_timestamp(skb);
+
+               /* save this skb for tx interrupt echo handling */
+               priv->echo_skb[idx] = skb;
+       } else {
+               /* locking problem with netif_stop_queue() ?? */
+               netdev_err(dev, "%s: BUG! echo_skb %d is occupied!\n", __func__, idx);
+               kfree_skb(skb);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(can_put_echo_skb);
+
+struct sk_buff *
+__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr,
+                  unsigned int *frame_len_ptr)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       if (idx >= priv->echo_skb_max) {
+               netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n",
+                          __func__, idx, priv->echo_skb_max);
+               return NULL;
+       }
+
+       if (priv->echo_skb[idx]) {
+               /* Using "struct canfd_frame::len" for the frame
+                * length is supported on both CAN and CANFD frames.
+                */
+               struct sk_buff *skb = priv->echo_skb[idx];
+               struct can_skb_priv *can_skb_priv = can_skb_prv(skb);
+               struct canfd_frame *cf = (struct canfd_frame *)skb->data;
+
+               /* get the real payload length for netdev statistics */
+               if (cf->can_id & CAN_RTR_FLAG)
+                       *len_ptr = 0;
+               else
+                       *len_ptr = cf->len;
+
+               if (frame_len_ptr)
+                       *frame_len_ptr = can_skb_priv->frame_len;
+
+               priv->echo_skb[idx] = NULL;
+
+               return skb;
+       }
+
+       return NULL;
+}
+
+/* Get the skb from the stack and loop it back locally
+ *
+ * The function is typically called when the TX done interrupt
+ * is handled in the device driver. The driver must protect
+ * access to priv->echo_skb, if necessary.
+ */
+unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx,
+                             unsigned int *frame_len_ptr)
+{
+       struct sk_buff *skb;
+       u8 len;
+
+       skb = __can_get_echo_skb(dev, idx, &len, frame_len_ptr);
+       if (!skb)
+               return 0;
+
+       skb_get(skb);
+       if (netif_rx(skb) == NET_RX_SUCCESS)
+               dev_consume_skb_any(skb);
+       else
+               dev_kfree_skb_any(skb);
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(can_get_echo_skb);
+
+/* Remove the skb from the stack and free it.
+ *
+ * The function is typically called when TX failed.
+ */
+void can_free_echo_skb(struct net_device *dev, unsigned int idx)
+{
+       struct can_priv *priv = netdev_priv(dev);
+
+       BUG_ON(idx >= priv->echo_skb_max);
+
+       if (priv->echo_skb[idx]) {
+               dev_kfree_skb_any(priv->echo_skb[idx]);
+               priv->echo_skb[idx] = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(can_free_echo_skb);
+
+struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf)
+{
+       struct sk_buff *skb;
+
+       skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
+                              sizeof(struct can_frame));
+       if (unlikely(!skb))
+               return NULL;
+
+       skb->protocol = htons(ETH_P_CAN);
+       skb->pkt_type = PACKET_BROADCAST;
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       skb_reset_mac_header(skb);
+       skb_reset_network_header(skb);
+       skb_reset_transport_header(skb);
+
+       can_skb_reserve(skb);
+       can_skb_prv(skb)->ifindex = dev->ifindex;
+       can_skb_prv(skb)->skbcnt = 0;
+
+       *cf = skb_put_zero(skb, sizeof(struct can_frame));
+
+       return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_can_skb);
+
+struct sk_buff *alloc_canfd_skb(struct net_device *dev,
+                               struct canfd_frame **cfd)
+{
+       struct sk_buff *skb;
+
+       skb = netdev_alloc_skb(dev, sizeof(struct can_skb_priv) +
+                              sizeof(struct canfd_frame));
+       if (unlikely(!skb))
+               return NULL;
+
+       skb->protocol = htons(ETH_P_CANFD);
+       skb->pkt_type = PACKET_BROADCAST;
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       skb_reset_mac_header(skb);
+       skb_reset_network_header(skb);
+       skb_reset_transport_header(skb);
+
+       can_skb_reserve(skb);
+       can_skb_prv(skb)->ifindex = dev->ifindex;
+       can_skb_prv(skb)->skbcnt = 0;
+
+       *cfd = skb_put_zero(skb, sizeof(struct canfd_frame));
+
+       return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_canfd_skb);
+
+struct sk_buff *alloc_can_err_skb(struct net_device *dev, struct can_frame **cf)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_can_skb(dev, cf);
+       if (unlikely(!skb))
+               return NULL;
+
+       (*cf)->can_id = CAN_ERR_FLAG;
+       (*cf)->len = CAN_ERR_DLC;
+
+       return skb;
+}
+EXPORT_SYMBOL_GPL(alloc_can_err_skb);
index 038fe10..971ada3 100644 (file)
@@ -9,6 +9,7 @@
 //
 // Based on code originally by Andrey Volkov <avolkov@varma-el.com>
 
+#include <dt-bindings/firmware/imx/rsrc.h>
 #include <linux/bitfield.h>
 #include <linux/can.h>
 #include <linux/can/dev.h>
@@ -17,6 +18,7 @@
 #include <linux/can/rx-offload.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
+#include <linux/firmware/imx/sci.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/mfd/syscon.h>
 #define FLEXCAN_QUIRK_SUPPORT_FD BIT(9)
 /* support memory detection and correction */
 #define FLEXCAN_QUIRK_SUPPORT_ECC BIT(10)
+/* Setup stop mode with SCU firmware to support wakeup */
+#define FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW BIT(11)
 
 /* Structure of the message buffer */
 struct flexcan_mb {
@@ -347,6 +351,7 @@ struct flexcan_priv {
        u8 mb_count;
        u8 mb_size;
        u8 clk_src;     /* clock source of CAN Protocol Engine */
+       u8 scu_idx;
 
        u64 rx_mask;
        u64 tx_mask;
@@ -358,6 +363,9 @@ struct flexcan_priv {
        struct regulator *reg_xceiver;
        struct flexcan_stop_mode stm;
 
+       /* IPC handle when setup stop mode by System Controller firmware(scfw) */
+       struct imx_sc_ipc *sc_ipc_handle;
+
        /* Read and Write APIs */
        u32 (*read)(void __iomem *addr);
        void (*write)(u32 val, void __iomem *addr);
@@ -387,7 +395,7 @@ static const struct flexcan_devtype_data fsl_imx6q_devtype_data = {
 static const struct flexcan_devtype_data fsl_imx8qm_devtype_data = {
        .quirks = FLEXCAN_QUIRK_DISABLE_RXFG | FLEXCAN_QUIRK_ENABLE_EACEN_RRS |
                FLEXCAN_QUIRK_USE_OFF_TIMESTAMP | FLEXCAN_QUIRK_BROKEN_PERR_STATE |
-               FLEXCAN_QUIRK_SUPPORT_FD,
+               FLEXCAN_QUIRK_SUPPORT_FD | FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW,
 };
 
 static struct flexcan_devtype_data fsl_imx8mp_devtype_data = {
@@ -546,18 +554,42 @@ static void flexcan_enable_wakeup_irq(struct flexcan_priv *priv, bool enable)
        priv->write(reg_mcr, &regs->mcr);
 }
 
+static int flexcan_stop_mode_enable_scfw(struct flexcan_priv *priv, bool enabled)
+{
+       u8 idx = priv->scu_idx;
+       u32 rsrc_id, val;
+
+       rsrc_id = IMX_SC_R_CAN(idx);
+
+       if (enabled)
+               val = 1;
+       else
+               val = 0;
+
+       /* stop mode request via scu firmware */
+       return imx_sc_misc_set_control(priv->sc_ipc_handle, rsrc_id,
+                                      IMX_SC_C_IPG_STOP, val);
+}
+
 static inline int flexcan_enter_stop_mode(struct flexcan_priv *priv)
 {
        struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_mcr;
+       int ret;
 
        reg_mcr = priv->read(&regs->mcr);
        reg_mcr |= FLEXCAN_MCR_SLF_WAK;
        priv->write(reg_mcr, &regs->mcr);
 
        /* enable stop request */
-       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
-                          1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) {
+               ret = flexcan_stop_mode_enable_scfw(priv, true);
+               if (ret < 0)
+                       return ret;
+       } else {
+               regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                                  1 << priv->stm.req_bit, 1 << priv->stm.req_bit);
+       }
 
        return flexcan_low_power_enter_ack(priv);
 }
@@ -566,10 +598,17 @@ static inline int flexcan_exit_stop_mode(struct flexcan_priv *priv)
 {
        struct flexcan_regs __iomem *regs = priv->regs;
        u32 reg_mcr;
+       int ret;
 
        /* remove stop request */
-       regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
-                          1 << priv->stm.req_bit, 0);
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW) {
+               ret = flexcan_stop_mode_enable_scfw(priv, false);
+               if (ret < 0)
+                       return ret;
+       } else {
+               regmap_update_bits(priv->stm.gpr, priv->stm.req_gpr,
+                                  1 << priv->stm.req_bit, 0);
+       }
 
        reg_mcr = priv->read(&regs->mcr);
        reg_mcr &= ~FLEXCAN_MCR_SLF_WAK;
@@ -776,7 +815,7 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de
                priv->write(data, &priv->tx_mb->data[i / sizeof(u32)]);
        }
 
-       can_put_echo_skb(skb, dev, 0);
+       can_put_echo_skb(skb, dev, 0, 0);
 
        priv->write(can_id, &priv->tx_mb->can_id);
        priv->write(ctrl, &priv->tx_mb->can_ctrl);
@@ -1083,8 +1122,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
                u32 reg_ctrl = priv->read(&priv->tx_mb->can_ctrl);
 
                handled = IRQ_HANDLED;
-               stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload,
-                                                              0, reg_ctrl << 16);
+               stats->tx_bytes +=
+                       can_rx_offload_get_echo_skb(&priv->offload, 0,
+                                                   reg_ctrl << 16, NULL);
                stats->tx_packets++;
                can_led_event(dev, CAN_LED_EVENT_TX);
 
@@ -1867,7 +1907,7 @@ static void unregister_flexcandev(struct net_device *dev)
        unregister_candev(dev);
 }
 
-static int flexcan_setup_stop_mode(struct platform_device *pdev)
+static int flexcan_setup_stop_mode_gpr(struct platform_device *pdev)
 {
        struct net_device *dev = platform_get_drvdata(pdev);
        struct device_node *np = pdev->dev.of_node;
@@ -1912,11 +1952,6 @@ static int flexcan_setup_stop_mode(struct platform_device *pdev)
                "gpr %s req_gpr=0x02%x req_bit=%u\n",
                gpr_np->full_name, priv->stm.req_gpr, priv->stm.req_bit);
 
-       device_set_wakeup_capable(&pdev->dev, true);
-
-       if (of_property_read_bool(np, "wakeup-source"))
-               device_set_wakeup_enable(&pdev->dev, true);
-
        return 0;
 
 out_put_node:
@@ -1924,6 +1959,58 @@ out_put_node:
        return ret;
 }
 
+static int flexcan_setup_stop_mode_scfw(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct flexcan_priv *priv;
+       u8 scu_idx;
+       int ret;
+
+       ret = of_property_read_u8(pdev->dev.of_node, "fsl,scu-index", &scu_idx);
+       if (ret < 0) {
+               dev_dbg(&pdev->dev, "failed to get scu index\n");
+               return ret;
+       }
+
+       priv = netdev_priv(dev);
+       priv->scu_idx = scu_idx;
+
+       /* this function could be deferred probe, return -EPROBE_DEFER */
+       return imx_scu_get_handle(&priv->sc_ipc_handle);
+}
+
+/* flexcan_setup_stop_mode - Setup stop mode for wakeup
+ *
+ * Return: = 0 setup stop mode successfully or doesn't support this feature
+ *         < 0 fail to setup stop mode (could be deferred probe)
+ */
+static int flexcan_setup_stop_mode(struct platform_device *pdev)
+{
+       struct net_device *dev = platform_get_drvdata(pdev);
+       struct flexcan_priv *priv;
+       int ret;
+
+       priv = netdev_priv(dev);
+
+       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_SCFW)
+               ret = flexcan_setup_stop_mode_scfw(pdev);
+       else if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR)
+               ret = flexcan_setup_stop_mode_gpr(pdev);
+       else
+               /* return 0 directly if doesn't support stop mode feature */
+               return 0;
+
+       if (ret)
+               return ret;
+
+       device_set_wakeup_capable(&pdev->dev, true);
+
+       if (of_property_read_bool(pdev->dev.of_node, "wakeup-source"))
+               device_set_wakeup_enable(&pdev->dev, true);
+
+       return 0;
+}
+
 static const struct of_device_id flexcan_of_match[] = {
        { .compatible = "fsl,imx8qm-flexcan", .data = &fsl_imx8qm_devtype_data, },
        { .compatible = "fsl,imx8mp-flexcan", .data = &fsl_imx8mp_devtype_data, },
@@ -2054,17 +2141,20 @@ static int flexcan_probe(struct platform_device *pdev)
                goto failed_register;
        }
 
+       err = flexcan_setup_stop_mode(pdev);
+       if (err < 0) {
+               if (err != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "setup stop mode failed\n");
+               goto failed_setup_stop_mode;
+       }
+
        of_can_transceiver(dev);
        devm_can_led_init(dev);
 
-       if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE_GPR) {
-               err = flexcan_setup_stop_mode(pdev);
-               if (err)
-                       dev_dbg(&pdev->dev, "failed to setup stop-mode\n");
-       }
-
        return 0;
 
+ failed_setup_stop_mode:
+       unregister_flexcandev(dev);
  failed_register:
        pm_runtime_put_noidle(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
index f5d94a6..4a84532 100644 (file)
@@ -517,7 +517,7 @@ static int catch_up_echo_skb(struct net_device *dev, int budget, bool echo)
                        stats->tx_packets++;
                        stats->tx_bytes += priv->txdlc[i];
                        priv->txdlc[i] = 0;
-                       can_get_echo_skb(dev, i);
+                       can_get_echo_skb(dev, i, NULL);
                } else {
                        /* For cleanup of untransmitted messages */
                        can_free_echo_skb(dev, i);
@@ -1448,7 +1448,7 @@ static netdev_tx_t grcan_start_xmit(struct sk_buff *skb,
         * taken.
         */
        priv->txdlc[slotindex] = cf->len; /* Store dlc for statistics */
-       can_put_echo_skb(skb, dev, slotindex);
+       can_put_echo_skb(skb, dev, slotindex, 0);
 
        /* Make sure everything is written before allowing hardware to
         * read from the memory
index 86b0e14..5bb957a 100644 (file)
@@ -629,7 +629,7 @@ static irqreturn_t ifi_canfd_isr(int irq, void *dev_id)
 
        /* TX IRQ */
        if (isr & IFI_CANFD_INTERRUPT_TXFIFO_REMOVE) {
-               stats->tx_bytes += can_get_echo_skb(ndev, 0);
+               stats->tx_bytes += can_get_echo_skb(ndev, 0, NULL);
                stats->tx_packets++;
                can_led_event(ndev, CAN_LED_EVENT_TX);
        }
@@ -922,7 +922,7 @@ static netdev_tx_t ifi_canfd_start_xmit(struct sk_buff *skb,
        writel(0, priv->base + IFI_CANFD_TXFIFO_REPEATCOUNT);
        writel(0, priv->base + IFI_CANFD_TXFIFO_SUSPEND_US);
 
-       can_put_echo_skb(skb, ndev, 0);
+       can_put_echo_skb(skb, ndev, 0, 0);
 
        /* Start the transmission */
        writel(IFI_CANFD_TXSTCMD_ADD_MSG, priv->base + IFI_CANFD_TXSTCMD);
index 969cedb..37e0501 100644 (file)
@@ -778,7 +778,7 @@ static netdev_tx_t kvaser_pciefd_start_xmit(struct sk_buff *skb,
        spin_lock_irqsave(&can->echo_lock, irq_flags);
 
        /* Prepare and save echo skb in internal slot */
-       can_put_echo_skb(skb, netdev, can->echo_idx);
+       can_put_echo_skb(skb, netdev, can->echo_idx, 0);
 
        /* Move echo index to the next slot */
        can->echo_idx = (can->echo_idx + 1) % can->can.echo_skb_max;
@@ -1467,7 +1467,7 @@ static int kvaser_pciefd_handle_eack_packet(struct kvaser_pciefd *pcie,
                                  can->reg_base + KVASER_PCIEFD_KCAN_CTRL_REG);
        } else {
                int echo_idx = p->header[0] & KVASER_PCIEFD_PACKET_SEQ_MSK;
-               int dlc = can_get_echo_skb(can->can.dev, echo_idx);
+               int dlc = can_get_echo_skb(can->can.dev, echo_idx, NULL);
                struct net_device_stats *stats = &can->can.dev->stats;
 
                stats->tx_bytes += dlc;
@@ -1533,7 +1533,7 @@ static int kvaser_pciefd_handle_ack_packet(struct kvaser_pciefd *pcie,
                netdev_dbg(can->can.dev, "Packet was flushed\n");
        } else {
                int echo_idx = p->header[0] & KVASER_PCIEFD_PACKET_SEQ_MSK;
-               int dlc = can_get_echo_skb(can->can.dev, echo_idx);
+               int dlc = can_get_echo_skb(can->can.dev, echo_idx, NULL);
                u8 count = ioread32(can->reg_base +
                                    KVASER_PCIEFD_KCAN_TX_NPACKETS_REG) & 0xff;
 
index ef7963f..d717bbc 100644 (file)
@@ -7,3 +7,7 @@ obj-$(CONFIG_CAN_M_CAN) += m_can.o
 obj-$(CONFIG_CAN_M_CAN_PCI) += m_can_pci.o
 obj-$(CONFIG_CAN_M_CAN_PLATFORM) += m_can_platform.o
 obj-$(CONFIG_CAN_M_CAN_TCAN4X5X) += tcan4x5x.o
+
+tcan4x5x-objs :=
+tcan4x5x-objs += tcan4x5x-core.o
+tcan4x5x-objs += tcan4x5x-regmap.o
index 2c9f124..3752520 100644 (file)
@@ -930,7 +930,7 @@ static void m_can_echo_tx_event(struct net_device *dev)
                                                (fgi << TXEFA_EFAI_SHIFT)));
 
                /* update stats */
-               stats->tx_bytes += can_get_echo_skb(dev, msg_mark);
+               stats->tx_bytes += can_get_echo_skb(dev, msg_mark, NULL);
                stats->tx_packets++;
        }
 }
@@ -972,7 +972,7 @@ static irqreturn_t m_can_isr(int irq, void *dev_id)
        if (cdev->version == 30) {
                if (ir & IR_TC) {
                        /* Transmission Complete Interrupt*/
-                       stats->tx_bytes += can_get_echo_skb(dev, 0);
+                       stats->tx_bytes += can_get_echo_skb(dev, 0, NULL);
                        stats->tx_packets++;
                        can_led_event(dev, CAN_LED_EVENT_TX);
                        netif_wake_queue(dev);
@@ -1483,7 +1483,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
                                         M_CAN_FIFO_DATA(i / 4),
                                         *(u32 *)(cf->data + i));
 
-               can_put_echo_skb(skb, dev, 0);
+               can_put_echo_skb(skb, dev, 0, 0);
 
                if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) {
                        cccr = m_can_read(cdev, M_CAN_CCCR);
@@ -1554,7 +1554,7 @@ static netdev_tx_t m_can_tx_handler(struct m_can_classdev *cdev)
                /* Push loopback echo.
                 * Will be looped back on TX interrupt based on message marker
                 */
-               can_put_echo_skb(skb, dev, putidx);
+               can_put_echo_skb(skb, dev, putidx, 0);
 
                /* Enable TX FIFO element to start transfer  */
                m_can_write(cdev, M_CAN_TXBAR, (1 << putidx));
@@ -1852,8 +1852,6 @@ EXPORT_SYMBOL_GPL(m_can_class_register);
 void m_can_class_unregister(struct m_can_classdev *cdev)
 {
        unregister_candev(cdev->net);
-
-       m_can_clk_stop(cdev);
 }
 EXPORT_SYMBOL_GPL(m_can_class_unregister);
 
diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c
new file mode 100644 (file)
index 0000000..b7caec7
--- /dev/null
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPI to CAN driver for the Texas Instruments TCAN4x5x
+// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
+
+#include "tcan4x5x.h"
+
+#define TCAN4X5X_EXT_CLK_DEF 40000000
+
+#define TCAN4X5X_DEV_ID0 0x00
+#define TCAN4X5X_DEV_ID1 0x04
+#define TCAN4X5X_REV 0x08
+#define TCAN4X5X_STATUS 0x0C
+#define TCAN4X5X_ERROR_STATUS 0x10
+#define TCAN4X5X_CONTROL 0x14
+
+#define TCAN4X5X_CONFIG 0x800
+#define TCAN4X5X_TS_PRESCALE 0x804
+#define TCAN4X5X_TEST_REG 0x808
+#define TCAN4X5X_INT_FLAGS 0x820
+#define TCAN4X5X_MCAN_INT_REG 0x824
+#define TCAN4X5X_INT_EN 0x830
+
+/* Interrupt bits */
+#define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30)
+#define TCAN4X5X_CANHCANL_INT_EN BIT(29)
+#define TCAN4X5X_CANHBAT_INT_EN BIT(28)
+#define TCAN4X5X_CANLGND_INT_EN BIT(27)
+#define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26)
+#define TCAN4X5X_CANBUSGND_INT_EN BIT(25)
+#define TCAN4X5X_CANBUSBAT_INT_EN BIT(24)
+#define TCAN4X5X_UVSUP_INT_EN BIT(22)
+#define TCAN4X5X_UVIO_INT_EN BIT(21)
+#define TCAN4X5X_TSD_INT_EN BIT(19)
+#define TCAN4X5X_ECCERR_INT_EN BIT(16)
+#define TCAN4X5X_CANINT_INT_EN BIT(15)
+#define TCAN4X5X_LWU_INT_EN BIT(14)
+#define TCAN4X5X_CANSLNT_INT_EN BIT(10)
+#define TCAN4X5X_CANDOM_INT_EN BIT(8)
+#define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5)
+#define TCAN4X5X_BUS_FAULT BIT(4)
+#define TCAN4X5X_MCAN_INT BIT(1)
+#define TCAN4X5X_ENABLE_TCAN_INT \
+       (TCAN4X5X_MCAN_INT | TCAN4X5X_BUS_FAULT | \
+        TCAN4X5X_CANBUS_ERR_INT_EN | TCAN4X5X_CANINT_INT_EN)
+
+/* MCAN Interrupt bits */
+#define TCAN4X5X_MCAN_IR_ARA BIT(29)
+#define TCAN4X5X_MCAN_IR_PED BIT(28)
+#define TCAN4X5X_MCAN_IR_PEA BIT(27)
+#define TCAN4X5X_MCAN_IR_WD BIT(26)
+#define TCAN4X5X_MCAN_IR_BO BIT(25)
+#define TCAN4X5X_MCAN_IR_EW BIT(24)
+#define TCAN4X5X_MCAN_IR_EP BIT(23)
+#define TCAN4X5X_MCAN_IR_ELO BIT(22)
+#define TCAN4X5X_MCAN_IR_BEU BIT(21)
+#define TCAN4X5X_MCAN_IR_BEC BIT(20)
+#define TCAN4X5X_MCAN_IR_DRX BIT(19)
+#define TCAN4X5X_MCAN_IR_TOO BIT(18)
+#define TCAN4X5X_MCAN_IR_MRAF BIT(17)
+#define TCAN4X5X_MCAN_IR_TSW BIT(16)
+#define TCAN4X5X_MCAN_IR_TEFL BIT(15)
+#define TCAN4X5X_MCAN_IR_TEFF BIT(14)
+#define TCAN4X5X_MCAN_IR_TEFW BIT(13)
+#define TCAN4X5X_MCAN_IR_TEFN BIT(12)
+#define TCAN4X5X_MCAN_IR_TFE BIT(11)
+#define TCAN4X5X_MCAN_IR_TCF BIT(10)
+#define TCAN4X5X_MCAN_IR_TC BIT(9)
+#define TCAN4X5X_MCAN_IR_HPM BIT(8)
+#define TCAN4X5X_MCAN_IR_RF1L BIT(7)
+#define TCAN4X5X_MCAN_IR_RF1F BIT(6)
+#define TCAN4X5X_MCAN_IR_RF1W BIT(5)
+#define TCAN4X5X_MCAN_IR_RF1N BIT(4)
+#define TCAN4X5X_MCAN_IR_RF0L BIT(3)
+#define TCAN4X5X_MCAN_IR_RF0F BIT(2)
+#define TCAN4X5X_MCAN_IR_RF0W BIT(1)
+#define TCAN4X5X_MCAN_IR_RF0N BIT(0)
+#define TCAN4X5X_ENABLE_MCAN_INT \
+       (TCAN4X5X_MCAN_IR_TC | TCAN4X5X_MCAN_IR_RF0N | \
+        TCAN4X5X_MCAN_IR_RF1N | TCAN4X5X_MCAN_IR_RF0F | \
+        TCAN4X5X_MCAN_IR_RF1F)
+
+#define TCAN4X5X_MRAM_START 0x8000
+#define TCAN4X5X_MCAN_OFFSET 0x1000
+
+#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
+#define TCAN4X5X_SET_ALL_INT 0xffffffff
+
+#define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6))
+#define TCAN4X5X_MODE_SLEEP 0x00
+#define TCAN4X5X_MODE_STANDBY BIT(6)
+#define TCAN4X5X_MODE_NORMAL BIT(7)
+
+#define TCAN4X5X_DISABLE_WAKE_MSK      (BIT(31) | BIT(30))
+#define TCAN4X5X_DISABLE_INH_MSK       BIT(9)
+
+#define TCAN4X5X_SW_RESET BIT(2)
+
+#define TCAN4X5X_MCAN_CONFIGURED BIT(5)
+#define TCAN4X5X_WATCHDOG_EN BIT(3)
+#define TCAN4X5X_WD_60_MS_TIMER 0
+#define TCAN4X5X_WD_600_MS_TIMER BIT(28)
+#define TCAN4X5X_WD_3_S_TIMER BIT(29)
+#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
+
+static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
+{
+       return container_of(cdev, struct tcan4x5x_priv, cdev);
+
+}
+
+static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
+{
+       int wake_state = 0;
+
+       if (priv->device_state_gpio)
+               wake_state = gpiod_get_value(priv->device_state_gpio);
+
+       if (priv->device_wake_gpio && wake_state) {
+               gpiod_set_value(priv->device_wake_gpio, 0);
+               usleep_range(5, 50);
+               gpiod_set_value(priv->device_wake_gpio, 1);
+       }
+}
+
+static int tcan4x5x_reset(struct tcan4x5x_priv *priv)
+{
+       int ret = 0;
+
+       if (priv->reset_gpio) {
+               gpiod_set_value(priv->reset_gpio, 1);
+
+               /* tpulse_width minimum 30us */
+               usleep_range(30, 100);
+               gpiod_set_value(priv->reset_gpio, 0);
+       } else {
+               ret = regmap_write(priv->regmap, TCAN4X5X_CONFIG,
+                                  TCAN4X5X_SW_RESET);
+               if (ret)
+                       return ret;
+       }
+
+       usleep_range(700, 1000);
+
+       return ret;
+}
+
+static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
+{
+       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
+       u32 val;
+
+       regmap_read(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, &val);
+
+       return val;
+}
+
+static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset)
+{
+       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
+       u32 val;
+
+       regmap_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, &val);
+
+       return val;
+}
+
+static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
+{
+       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
+
+       return regmap_write(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, val);
+}
+
+static int tcan4x5x_write_fifo(struct m_can_classdev *cdev,
+                              int addr_offset, int val)
+{
+       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
+
+       return regmap_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val);
+}
+
+static int tcan4x5x_power_enable(struct regulator *reg, int enable)
+{
+       if (IS_ERR_OR_NULL(reg))
+               return 0;
+
+       if (enable)
+               return regulator_enable(reg);
+       else
+               return regulator_disable(reg);
+}
+
+static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev,
+                                  int reg, int val)
+{
+       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
+
+       return regmap_write(priv->regmap, reg, val);
+}
+
+static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
+{
+       int ret;
+
+       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_STATUS,
+                                     TCAN4X5X_CLEAR_ALL_INT);
+       if (ret)
+               return ret;
+
+       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG,
+                                     TCAN4X5X_ENABLE_MCAN_INT);
+       if (ret)
+               return ret;
+
+       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
+                                     TCAN4X5X_CLEAR_ALL_INT);
+       if (ret)
+               return ret;
+
+       return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS,
+                                      TCAN4X5X_CLEAR_ALL_INT);
+}
+
+static int tcan4x5x_init(struct m_can_classdev *cdev)
+{
+       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
+       int ret;
+
+       tcan4x5x_check_wake(tcan4x5x);
+
+       ret = tcan4x5x_clear_interrupts(cdev);
+       if (ret)
+               return ret;
+
+       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_EN,
+                                     TCAN4X5X_ENABLE_TCAN_INT);
+       if (ret)
+               return ret;
+
+       ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
+                                TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
+       if (ret)
+               return ret;
+
+       /* Zero out the MCAN buffers */
+       m_can_init_ram(cdev);
+
+       return ret;
+}
+
+static int tcan4x5x_disable_wake(struct m_can_classdev *cdev)
+{
+       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
+
+       return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
+                                 TCAN4X5X_DISABLE_WAKE_MSK, 0x00);
+}
+
+static int tcan4x5x_disable_state(struct m_can_classdev *cdev)
+{
+       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
+
+       return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
+                                 TCAN4X5X_DISABLE_INH_MSK, 0x01);
+}
+
+static int tcan4x5x_get_gpios(struct m_can_classdev *cdev)
+{
+       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
+       int ret;
+
+       tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake",
+                                                   GPIOD_OUT_HIGH);
+       if (IS_ERR(tcan4x5x->device_wake_gpio)) {
+               if (PTR_ERR(tcan4x5x->device_wake_gpio) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+
+               tcan4x5x_disable_wake(cdev);
+       }
+
+       tcan4x5x->reset_gpio = devm_gpiod_get_optional(cdev->dev, "reset",
+                                                      GPIOD_OUT_LOW);
+       if (IS_ERR(tcan4x5x->reset_gpio))
+               tcan4x5x->reset_gpio = NULL;
+
+       ret = tcan4x5x_reset(tcan4x5x);
+       if (ret)
+               return ret;
+
+       tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev,
+                                                             "device-state",
+                                                             GPIOD_IN);
+       if (IS_ERR(tcan4x5x->device_state_gpio)) {
+               tcan4x5x->device_state_gpio = NULL;
+               tcan4x5x_disable_state(cdev);
+       }
+
+       return 0;
+}
+
+static struct m_can_ops tcan4x5x_ops = {
+       .init = tcan4x5x_init,
+       .read_reg = tcan4x5x_read_reg,
+       .write_reg = tcan4x5x_write_reg,
+       .write_fifo = tcan4x5x_write_fifo,
+       .read_fifo = tcan4x5x_read_fifo,
+       .clear_interrupts = tcan4x5x_clear_interrupts,
+};
+
+static int tcan4x5x_can_probe(struct spi_device *spi)
+{
+       struct tcan4x5x_priv *priv;
+       struct m_can_classdev *mcan_class;
+       int freq, ret;
+
+       mcan_class = m_can_class_allocate_dev(&spi->dev,
+                                             sizeof(struct tcan4x5x_priv));
+       if (!mcan_class)
+               return -ENOMEM;
+
+       priv = cdev_to_priv(mcan_class);
+
+       priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
+       if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
+               ret = -EPROBE_DEFER;
+               goto out_m_can_class_free_dev;
+       } else {
+               priv->power = NULL;
+       }
+
+       m_can_class_get_clocks(mcan_class);
+       if (IS_ERR(mcan_class->cclk)) {
+               dev_err(&spi->dev, "no CAN clock source defined\n");
+               freq = TCAN4X5X_EXT_CLK_DEF;
+       } else {
+               freq = clk_get_rate(mcan_class->cclk);
+       }
+
+       /* Sanity check */
+       if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF) {
+               ret = -ERANGE;
+               goto out_m_can_class_free_dev;
+       }
+
+       priv->spi = spi;
+
+       mcan_class->pm_clock_support = 0;
+       mcan_class->can.clock.freq = freq;
+       mcan_class->dev = &spi->dev;
+       mcan_class->ops = &tcan4x5x_ops;
+       mcan_class->is_peripheral = true;
+       mcan_class->net->irq = spi->irq;
+
+       spi_set_drvdata(spi, priv);
+
+       /* Configure the SPI bus */
+       spi->bits_per_word = 8;
+       ret = spi_setup(spi);
+       if (ret)
+               goto out_m_can_class_free_dev;
+
+       ret = tcan4x5x_regmap_init(priv);
+       if (ret)
+               goto out_m_can_class_free_dev;
+
+       ret = tcan4x5x_power_enable(priv->power, 1);
+       if (ret)
+               goto out_m_can_class_free_dev;
+
+       ret = tcan4x5x_get_gpios(mcan_class);
+       if (ret)
+               goto out_power;
+
+       ret = tcan4x5x_init(mcan_class);
+       if (ret)
+               goto out_power;
+
+       ret = m_can_class_register(mcan_class);
+       if (ret)
+               goto out_power;
+
+       netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
+       return 0;
+
+out_power:
+       tcan4x5x_power_enable(priv->power, 0);
+ out_m_can_class_free_dev:
+       m_can_class_free_dev(mcan_class->net);
+       return ret;
+}
+
+static int tcan4x5x_can_remove(struct spi_device *spi)
+{
+       struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
+
+       m_can_class_unregister(&priv->cdev);
+
+       tcan4x5x_power_enable(priv->power, 0);
+
+       m_can_class_free_dev(priv->cdev.net);
+
+       return 0;
+}
+
+static const struct of_device_id tcan4x5x_of_match[] = {
+       {
+               .compatible = "ti,tcan4x5x",
+       }, {
+               /* sentinel */
+       },
+};
+MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
+
+static const struct spi_device_id tcan4x5x_id_table[] = {
+       {
+               .name = "tcan4x5x",
+       }, {
+               /* sentinel */
+       },
+};
+MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
+
+static struct spi_driver tcan4x5x_can_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = tcan4x5x_of_match,
+               .pm = NULL,
+       },
+       .id_table = tcan4x5x_id_table,
+       .probe = tcan4x5x_can_probe,
+       .remove = tcan4x5x_can_remove,
+};
+module_spi_driver(tcan4x5x_can_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
+MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/m_can/tcan4x5x-regmap.c b/drivers/net/can/m_can/tcan4x5x-regmap.c
new file mode 100644 (file)
index 0000000..ca80dba
--- /dev/null
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver
+//
+// Copyright (c) 2020 Pengutronix,
+//                    Marc Kleine-Budde <kernel@pengutronix.de>
+// Copyright (c) 2018-2019 Texas Instruments Incorporated
+//                    http://www.ti.com/
+
+#include "tcan4x5x.h"
+
+#define TCAN4X5X_SPI_INSTRUCTION_WRITE (0x61 << 24)
+#define TCAN4X5X_SPI_INSTRUCTION_READ (0x41 << 24)
+
+#define TCAN4X5X_MAX_REGISTER 0x8ffc
+
+static int tcan4x5x_regmap_gather_write(void *context,
+                                       const void *reg, size_t reg_len,
+                                       const void *val, size_t val_len)
+{
+       struct spi_device *spi = context;
+       struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
+       struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx;
+       struct spi_transfer xfer[] = {
+               {
+                       .tx_buf = buf_tx,
+                       .len = sizeof(buf_tx->cmd) + val_len,
+               },
+       };
+
+       memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd.cmd) +
+              sizeof(buf_tx->cmd.addr));
+       tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len);
+       memcpy(buf_tx->data, val, val_len);
+
+       return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
+}
+
+static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
+{
+       return tcan4x5x_regmap_gather_write(context, data, sizeof(__be32),
+                                           data + sizeof(__be32),
+                                           count - sizeof(__be32));
+}
+
+static int tcan4x5x_regmap_read(void *context,
+                               const void *reg_buf, size_t reg_len,
+                               void *val_buf, size_t val_len)
+{
+       struct spi_device *spi = context;
+       struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
+       struct tcan4x5x_map_buf *buf_rx = &priv->map_buf_rx;
+       struct tcan4x5x_map_buf *buf_tx = &priv->map_buf_tx;
+       struct spi_transfer xfer[2] = {
+               {
+                       .tx_buf = buf_tx,
+               }
+       };
+       struct spi_message msg;
+       int err;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer[0], &msg);
+
+       memcpy(&buf_tx->cmd, reg_buf, sizeof(buf_tx->cmd.cmd) +
+              sizeof(buf_tx->cmd.addr));
+       tcan4x5x_spi_cmd_set_len(&buf_tx->cmd, val_len);
+
+       if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) {
+               xfer[0].len = sizeof(buf_tx->cmd);
+
+               xfer[1].rx_buf = val_buf;
+               xfer[1].len = val_len;
+               spi_message_add_tail(&xfer[1], &msg);
+       } else {
+               xfer[0].rx_buf = buf_rx;
+               xfer[0].len = sizeof(buf_tx->cmd) + val_len;
+
+               if (TCAN4X5X_SANITIZE_SPI)
+                       memset(buf_tx->data, 0x0, val_len);
+       }
+
+       err = spi_sync(spi, &msg);
+       if (err)
+               return err;
+
+       if (!(spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX))
+               memcpy(val_buf, buf_rx->data, val_len);
+
+       return 0;
+}
+
+static const struct regmap_range tcan4x5x_reg_table_yes_range[] = {
+       regmap_reg_range(0x0000, 0x002c),       /* Device ID and SPI Registers */
+       regmap_reg_range(0x0800, 0x083c),       /* Device configuration registers and Interrupt Flags*/
+       regmap_reg_range(0x1000, 0x10fc),       /* M_CAN */
+       regmap_reg_range(0x8000, 0x87fc),       /* MRAM */
+};
+
+static const struct regmap_access_table tcan4x5x_reg_table = {
+       .yes_ranges = tcan4x5x_reg_table_yes_range,
+       .n_yes_ranges = ARRAY_SIZE(tcan4x5x_reg_table_yes_range),
+};
+
+static const struct regmap_config tcan4x5x_regmap = {
+       .reg_bits = 24,
+       .reg_stride = 4,
+       .pad_bits = 8,
+       .val_bits = 32,
+       .wr_table = &tcan4x5x_reg_table,
+       .rd_table = &tcan4x5x_reg_table,
+       .max_register = TCAN4X5X_MAX_REGISTER,
+       .cache_type = REGCACHE_NONE,
+       .read_flag_mask = (__force unsigned long)
+               cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_READ),
+       .write_flag_mask = (__force unsigned long)
+               cpu_to_be32(TCAN4X5X_SPI_INSTRUCTION_WRITE),
+};
+
+static const struct regmap_bus tcan4x5x_bus = {
+       .write = tcan4x5x_regmap_write,
+       .gather_write = tcan4x5x_regmap_gather_write,
+       .read = tcan4x5x_regmap_read,
+       .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+       .val_format_endian_default = REGMAP_ENDIAN_BIG,
+       .max_raw_read = 256,
+       .max_raw_write = 256,
+};
+
+int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv)
+{
+       priv->regmap = devm_regmap_init(&priv->spi->dev, &tcan4x5x_bus,
+                                       priv->spi, &tcan4x5x_regmap);
+       return PTR_ERR_OR_ZERO(priv->regmap);
+}
diff --git a/drivers/net/can/m_can/tcan4x5x.c b/drivers/net/can/m_can/tcan4x5x.c
deleted file mode 100644 (file)
index 24c737c..0000000
+++ /dev/null
@@ -1,559 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-// SPI to CAN driver for the Texas Instruments TCAN4x5x
-// Copyright (C) 2018-19 Texas Instruments Incorporated - http://www.ti.com/
-
-#include <linux/regmap.h>
-#include <linux/spi/spi.h>
-
-#include <linux/regulator/consumer.h>
-#include <linux/gpio/consumer.h>
-
-#include "m_can.h"
-
-#define DEVICE_NAME "tcan4x5x"
-#define TCAN4X5X_EXT_CLK_DEF 40000000
-
-#define TCAN4X5X_DEV_ID0 0x00
-#define TCAN4X5X_DEV_ID1 0x04
-#define TCAN4X5X_REV 0x08
-#define TCAN4X5X_STATUS 0x0C
-#define TCAN4X5X_ERROR_STATUS 0x10
-#define TCAN4X5X_CONTROL 0x14
-
-#define TCAN4X5X_CONFIG 0x800
-#define TCAN4X5X_TS_PRESCALE 0x804
-#define TCAN4X5X_TEST_REG 0x808
-#define TCAN4X5X_INT_FLAGS 0x820
-#define TCAN4X5X_MCAN_INT_REG 0x824
-#define TCAN4X5X_INT_EN 0x830
-
-/* Interrupt bits */
-#define TCAN4X5X_CANBUSTERMOPEN_INT_EN BIT(30)
-#define TCAN4X5X_CANHCANL_INT_EN BIT(29)
-#define TCAN4X5X_CANHBAT_INT_EN BIT(28)
-#define TCAN4X5X_CANLGND_INT_EN BIT(27)
-#define TCAN4X5X_CANBUSOPEN_INT_EN BIT(26)
-#define TCAN4X5X_CANBUSGND_INT_EN BIT(25)
-#define TCAN4X5X_CANBUSBAT_INT_EN BIT(24)
-#define TCAN4X5X_UVSUP_INT_EN BIT(22)
-#define TCAN4X5X_UVIO_INT_EN BIT(21)
-#define TCAN4X5X_TSD_INT_EN BIT(19)
-#define TCAN4X5X_ECCERR_INT_EN BIT(16)
-#define TCAN4X5X_CANINT_INT_EN BIT(15)
-#define TCAN4X5X_LWU_INT_EN BIT(14)
-#define TCAN4X5X_CANSLNT_INT_EN BIT(10)
-#define TCAN4X5X_CANDOM_INT_EN BIT(8)
-#define TCAN4X5X_CANBUS_ERR_INT_EN BIT(5)
-#define TCAN4X5X_BUS_FAULT BIT(4)
-#define TCAN4X5X_MCAN_INT BIT(1)
-#define TCAN4X5X_ENABLE_TCAN_INT \
-       (TCAN4X5X_MCAN_INT | TCAN4X5X_BUS_FAULT | \
-        TCAN4X5X_CANBUS_ERR_INT_EN | TCAN4X5X_CANINT_INT_EN)
-
-/* MCAN Interrupt bits */
-#define TCAN4X5X_MCAN_IR_ARA BIT(29)
-#define TCAN4X5X_MCAN_IR_PED BIT(28)
-#define TCAN4X5X_MCAN_IR_PEA BIT(27)
-#define TCAN4X5X_MCAN_IR_WD BIT(26)
-#define TCAN4X5X_MCAN_IR_BO BIT(25)
-#define TCAN4X5X_MCAN_IR_EW BIT(24)
-#define TCAN4X5X_MCAN_IR_EP BIT(23)
-#define TCAN4X5X_MCAN_IR_ELO BIT(22)
-#define TCAN4X5X_MCAN_IR_BEU BIT(21)
-#define TCAN4X5X_MCAN_IR_BEC BIT(20)
-#define TCAN4X5X_MCAN_IR_DRX BIT(19)
-#define TCAN4X5X_MCAN_IR_TOO BIT(18)
-#define TCAN4X5X_MCAN_IR_MRAF BIT(17)
-#define TCAN4X5X_MCAN_IR_TSW BIT(16)
-#define TCAN4X5X_MCAN_IR_TEFL BIT(15)
-#define TCAN4X5X_MCAN_IR_TEFF BIT(14)
-#define TCAN4X5X_MCAN_IR_TEFW BIT(13)
-#define TCAN4X5X_MCAN_IR_TEFN BIT(12)
-#define TCAN4X5X_MCAN_IR_TFE BIT(11)
-#define TCAN4X5X_MCAN_IR_TCF BIT(10)
-#define TCAN4X5X_MCAN_IR_TC BIT(9)
-#define TCAN4X5X_MCAN_IR_HPM BIT(8)
-#define TCAN4X5X_MCAN_IR_RF1L BIT(7)
-#define TCAN4X5X_MCAN_IR_RF1F BIT(6)
-#define TCAN4X5X_MCAN_IR_RF1W BIT(5)
-#define TCAN4X5X_MCAN_IR_RF1N BIT(4)
-#define TCAN4X5X_MCAN_IR_RF0L BIT(3)
-#define TCAN4X5X_MCAN_IR_RF0F BIT(2)
-#define TCAN4X5X_MCAN_IR_RF0W BIT(1)
-#define TCAN4X5X_MCAN_IR_RF0N BIT(0)
-#define TCAN4X5X_ENABLE_MCAN_INT \
-       (TCAN4X5X_MCAN_IR_TC | TCAN4X5X_MCAN_IR_RF0N | \
-        TCAN4X5X_MCAN_IR_RF1N | TCAN4X5X_MCAN_IR_RF0F | \
-        TCAN4X5X_MCAN_IR_RF1F)
-
-#define TCAN4X5X_MRAM_START 0x8000
-#define TCAN4X5X_MCAN_OFFSET 0x1000
-#define TCAN4X5X_MAX_REGISTER 0x8fff
-
-#define TCAN4X5X_CLEAR_ALL_INT 0xffffffff
-#define TCAN4X5X_SET_ALL_INT 0xffffffff
-
-#define TCAN4X5X_WRITE_CMD (0x61 << 24)
-#define TCAN4X5X_READ_CMD (0x41 << 24)
-
-#define TCAN4X5X_MODE_SEL_MASK (BIT(7) | BIT(6))
-#define TCAN4X5X_MODE_SLEEP 0x00
-#define TCAN4X5X_MODE_STANDBY BIT(6)
-#define TCAN4X5X_MODE_NORMAL BIT(7)
-
-#define TCAN4X5X_DISABLE_WAKE_MSK      (BIT(31) | BIT(30))
-#define TCAN4X5X_DISABLE_INH_MSK       BIT(9)
-
-#define TCAN4X5X_SW_RESET BIT(2)
-
-#define TCAN4X5X_MCAN_CONFIGURED BIT(5)
-#define TCAN4X5X_WATCHDOG_EN BIT(3)
-#define TCAN4X5X_WD_60_MS_TIMER 0
-#define TCAN4X5X_WD_600_MS_TIMER BIT(28)
-#define TCAN4X5X_WD_3_S_TIMER BIT(29)
-#define TCAN4X5X_WD_6_S_TIMER (BIT(28) | BIT(29))
-
-struct tcan4x5x_priv {
-       struct m_can_classdev cdev;
-
-       struct regmap *regmap;
-       struct spi_device *spi;
-
-       struct gpio_desc *reset_gpio;
-       struct gpio_desc *device_wake_gpio;
-       struct gpio_desc *device_state_gpio;
-       struct regulator *power;
-};
-
-static inline struct tcan4x5x_priv *cdev_to_priv(struct m_can_classdev *cdev)
-{
-       return container_of(cdev, struct tcan4x5x_priv, cdev);
-
-}
-
-static struct can_bittiming_const tcan4x5x_bittiming_const = {
-       .name = DEVICE_NAME,
-       .tseg1_min = 2,
-       .tseg1_max = 31,
-       .tseg2_min = 2,
-       .tseg2_max = 16,
-       .sjw_max = 16,
-       .brp_min = 1,
-       .brp_max = 32,
-       .brp_inc = 1,
-};
-
-static struct can_bittiming_const tcan4x5x_data_bittiming_const = {
-       .name = DEVICE_NAME,
-       .tseg1_min = 1,
-       .tseg1_max = 32,
-       .tseg2_min = 1,
-       .tseg2_max = 16,
-       .sjw_max = 16,
-       .brp_min = 1,
-       .brp_max = 32,
-       .brp_inc = 1,
-};
-
-static void tcan4x5x_check_wake(struct tcan4x5x_priv *priv)
-{
-       int wake_state = 0;
-
-       if (priv->device_state_gpio)
-               wake_state = gpiod_get_value(priv->device_state_gpio);
-
-       if (priv->device_wake_gpio && wake_state) {
-               gpiod_set_value(priv->device_wake_gpio, 0);
-               usleep_range(5, 50);
-               gpiod_set_value(priv->device_wake_gpio, 1);
-       }
-}
-
-static int tcan4x5x_reset(struct tcan4x5x_priv *priv)
-{
-       int ret = 0;
-
-       if (priv->reset_gpio) {
-               gpiod_set_value(priv->reset_gpio, 1);
-
-               /* tpulse_width minimum 30us */
-               usleep_range(30, 100);
-               gpiod_set_value(priv->reset_gpio, 0);
-       } else {
-               ret = regmap_write(priv->regmap, TCAN4X5X_CONFIG,
-                                  TCAN4X5X_SW_RESET);
-               if (ret)
-                       return ret;
-       }
-
-       usleep_range(700, 1000);
-
-       return ret;
-}
-
-static int regmap_spi_gather_write(void *context, const void *reg,
-                                  size_t reg_len, const void *val,
-                                  size_t val_len)
-{
-       struct device *dev = context;
-       struct spi_device *spi = to_spi_device(dev);
-       struct spi_message m;
-       u32 addr;
-       struct spi_transfer t[2] = {
-               { .tx_buf = &addr, .len = reg_len, .cs_change = 0,},
-               { .tx_buf = val, .len = val_len, },
-       };
-
-       addr = TCAN4X5X_WRITE_CMD | (*((u16 *)reg) << 8) | val_len >> 2;
-
-       spi_message_init(&m);
-       spi_message_add_tail(&t[0], &m);
-       spi_message_add_tail(&t[1], &m);
-
-       return spi_sync(spi, &m);
-}
-
-static int tcan4x5x_regmap_write(void *context, const void *data, size_t count)
-{
-       u16 *reg = (u16 *)(data);
-       const u32 *val = data + 4;
-
-       return regmap_spi_gather_write(context, reg, 4, val, count - 4);
-}
-
-static int regmap_spi_async_write(void *context,
-                                 const void *reg, size_t reg_len,
-                                 const void *val, size_t val_len,
-                                 struct regmap_async *a)
-{
-       return -ENOTSUPP;
-}
-
-static struct regmap_async *regmap_spi_async_alloc(void)
-{
-       return NULL;
-}
-
-static int tcan4x5x_regmap_read(void *context,
-                               const void *reg, size_t reg_size,
-                               void *val, size_t val_size)
-{
-       struct device *dev = context;
-       struct spi_device *spi = to_spi_device(dev);
-
-       u32 addr = TCAN4X5X_READ_CMD | (*((u16 *)reg) << 8) | val_size >> 2;
-
-       return spi_write_then_read(spi, &addr, reg_size, (u32 *)val, val_size);
-}
-
-static struct regmap_bus tcan4x5x_bus = {
-       .write = tcan4x5x_regmap_write,
-       .gather_write = regmap_spi_gather_write,
-       .async_write = regmap_spi_async_write,
-       .async_alloc = regmap_spi_async_alloc,
-       .read = tcan4x5x_regmap_read,
-       .read_flag_mask = 0x00,
-       .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
-       .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
-};
-
-static u32 tcan4x5x_read_reg(struct m_can_classdev *cdev, int reg)
-{
-       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
-       u32 val;
-
-       regmap_read(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, &val);
-
-       return val;
-}
-
-static u32 tcan4x5x_read_fifo(struct m_can_classdev *cdev, int addr_offset)
-{
-       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
-       u32 val;
-
-       regmap_read(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, &val);
-
-       return val;
-}
-
-static int tcan4x5x_write_reg(struct m_can_classdev *cdev, int reg, int val)
-{
-       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
-
-       return regmap_write(priv->regmap, TCAN4X5X_MCAN_OFFSET + reg, val);
-}
-
-static int tcan4x5x_write_fifo(struct m_can_classdev *cdev,
-                              int addr_offset, int val)
-{
-       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
-
-       return regmap_write(priv->regmap, TCAN4X5X_MRAM_START + addr_offset, val);
-}
-
-static int tcan4x5x_power_enable(struct regulator *reg, int enable)
-{
-       if (IS_ERR_OR_NULL(reg))
-               return 0;
-
-       if (enable)
-               return regulator_enable(reg);
-       else
-               return regulator_disable(reg);
-}
-
-static int tcan4x5x_write_tcan_reg(struct m_can_classdev *cdev,
-                                  int reg, int val)
-{
-       struct tcan4x5x_priv *priv = cdev_to_priv(cdev);
-
-       return regmap_write(priv->regmap, reg, val);
-}
-
-static int tcan4x5x_clear_interrupts(struct m_can_classdev *cdev)
-{
-       int ret;
-
-       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_STATUS,
-                                     TCAN4X5X_CLEAR_ALL_INT);
-       if (ret)
-               return ret;
-
-       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_MCAN_INT_REG,
-                                     TCAN4X5X_ENABLE_MCAN_INT);
-       if (ret)
-               return ret;
-
-       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_FLAGS,
-                                     TCAN4X5X_CLEAR_ALL_INT);
-       if (ret)
-               return ret;
-
-       return tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_ERROR_STATUS,
-                                      TCAN4X5X_CLEAR_ALL_INT);
-}
-
-static int tcan4x5x_init(struct m_can_classdev *cdev)
-{
-       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
-       int ret;
-
-       tcan4x5x_check_wake(tcan4x5x);
-
-       ret = tcan4x5x_clear_interrupts(cdev);
-       if (ret)
-               return ret;
-
-       ret = tcan4x5x_write_tcan_reg(cdev, TCAN4X5X_INT_EN,
-                                     TCAN4X5X_ENABLE_TCAN_INT);
-       if (ret)
-               return ret;
-
-       ret = regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
-                                TCAN4X5X_MODE_SEL_MASK, TCAN4X5X_MODE_NORMAL);
-       if (ret)
-               return ret;
-
-       /* Zero out the MCAN buffers */
-       m_can_init_ram(cdev);
-
-       return ret;
-}
-
-static int tcan4x5x_disable_wake(struct m_can_classdev *cdev)
-{
-       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
-
-       return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
-                                 TCAN4X5X_DISABLE_WAKE_MSK, 0x00);
-}
-
-static int tcan4x5x_disable_state(struct m_can_classdev *cdev)
-{
-       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
-
-       return regmap_update_bits(tcan4x5x->regmap, TCAN4X5X_CONFIG,
-                                 TCAN4X5X_DISABLE_INH_MSK, 0x01);
-}
-
-static int tcan4x5x_get_gpios(struct m_can_classdev *cdev)
-{
-       struct tcan4x5x_priv *tcan4x5x = cdev_to_priv(cdev);
-       int ret;
-
-       tcan4x5x->device_wake_gpio = devm_gpiod_get(cdev->dev, "device-wake",
-                                                   GPIOD_OUT_HIGH);
-       if (IS_ERR(tcan4x5x->device_wake_gpio)) {
-               if (PTR_ERR(tcan4x5x->device_wake_gpio) == -EPROBE_DEFER)
-                       return -EPROBE_DEFER;
-
-               tcan4x5x_disable_wake(cdev);
-       }
-
-       tcan4x5x->reset_gpio = devm_gpiod_get_optional(cdev->dev, "reset",
-                                                      GPIOD_OUT_LOW);
-       if (IS_ERR(tcan4x5x->reset_gpio))
-               tcan4x5x->reset_gpio = NULL;
-
-       ret = tcan4x5x_reset(tcan4x5x);
-       if (ret)
-               return ret;
-
-       tcan4x5x->device_state_gpio = devm_gpiod_get_optional(cdev->dev,
-                                                             "device-state",
-                                                             GPIOD_IN);
-       if (IS_ERR(tcan4x5x->device_state_gpio)) {
-               tcan4x5x->device_state_gpio = NULL;
-               tcan4x5x_disable_state(cdev);
-       }
-
-       return 0;
-}
-
-static const struct regmap_config tcan4x5x_regmap = {
-       .reg_bits = 32,
-       .val_bits = 32,
-       .cache_type = REGCACHE_NONE,
-       .max_register = TCAN4X5X_MAX_REGISTER,
-};
-
-static struct m_can_ops tcan4x5x_ops = {
-       .init = tcan4x5x_init,
-       .read_reg = tcan4x5x_read_reg,
-       .write_reg = tcan4x5x_write_reg,
-       .write_fifo = tcan4x5x_write_fifo,
-       .read_fifo = tcan4x5x_read_fifo,
-       .clear_interrupts = tcan4x5x_clear_interrupts,
-};
-
-static int tcan4x5x_can_probe(struct spi_device *spi)
-{
-       struct tcan4x5x_priv *priv;
-       struct m_can_classdev *mcan_class;
-       int freq, ret;
-
-       mcan_class = m_can_class_allocate_dev(&spi->dev,
-                                             sizeof(struct tcan4x5x_priv));
-       if (!mcan_class)
-               return -ENOMEM;
-
-       priv = cdev_to_priv(mcan_class);
-
-       priv->power = devm_regulator_get_optional(&spi->dev, "vsup");
-       if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
-               ret = -EPROBE_DEFER;
-               goto out_m_can_class_free_dev;
-       } else {
-               priv->power = NULL;
-       }
-
-       m_can_class_get_clocks(mcan_class);
-       if (IS_ERR(mcan_class->cclk)) {
-               dev_err(&spi->dev, "no CAN clock source defined\n");
-               freq = TCAN4X5X_EXT_CLK_DEF;
-       } else {
-               freq = clk_get_rate(mcan_class->cclk);
-       }
-
-       /* Sanity check */
-       if (freq < 20000000 || freq > TCAN4X5X_EXT_CLK_DEF) {
-               ret = -ERANGE;
-               goto out_m_can_class_free_dev;
-       }
-
-       priv->spi = spi;
-
-       mcan_class->pm_clock_support = 0;
-       mcan_class->can.clock.freq = freq;
-       mcan_class->dev = &spi->dev;
-       mcan_class->ops = &tcan4x5x_ops;
-       mcan_class->is_peripheral = true;
-       mcan_class->bit_timing = &tcan4x5x_bittiming_const;
-       mcan_class->data_timing = &tcan4x5x_data_bittiming_const;
-       mcan_class->net->irq = spi->irq;
-
-       spi_set_drvdata(spi, priv);
-
-       /* Configure the SPI bus */
-       spi->bits_per_word = 32;
-       ret = spi_setup(spi);
-       if (ret)
-               goto out_m_can_class_free_dev;
-
-       priv->regmap = devm_regmap_init(&spi->dev, &tcan4x5x_bus,
-                                       &spi->dev, &tcan4x5x_regmap);
-       if (IS_ERR(priv->regmap)) {
-               ret = PTR_ERR(priv->regmap);
-               goto out_m_can_class_free_dev;
-       }
-
-       ret = tcan4x5x_power_enable(priv->power, 1);
-       if (ret)
-               goto out_m_can_class_free_dev;
-
-       ret = tcan4x5x_get_gpios(mcan_class);
-       if (ret)
-               goto out_power;
-
-       ret = tcan4x5x_init(mcan_class);
-       if (ret)
-               goto out_power;
-
-       ret = m_can_class_register(mcan_class);
-       if (ret)
-               goto out_power;
-
-       netdev_info(mcan_class->net, "TCAN4X5X successfully initialized.\n");
-       return 0;
-
-out_power:
-       tcan4x5x_power_enable(priv->power, 0);
- out_m_can_class_free_dev:
-       m_can_class_free_dev(mcan_class->net);
-       return ret;
-}
-
-static int tcan4x5x_can_remove(struct spi_device *spi)
-{
-       struct tcan4x5x_priv *priv = spi_get_drvdata(spi);
-
-       m_can_class_unregister(&priv->cdev);
-
-       tcan4x5x_power_enable(priv->power, 0);
-
-       m_can_class_free_dev(priv->cdev.net);
-
-       return 0;
-}
-
-static const struct of_device_id tcan4x5x_of_match[] = {
-       { .compatible = "ti,tcan4x5x", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, tcan4x5x_of_match);
-
-static const struct spi_device_id tcan4x5x_id_table[] = {
-       {
-               .name           = "tcan4x5x",
-               .driver_data    = 0,
-       },
-       { }
-};
-MODULE_DEVICE_TABLE(spi, tcan4x5x_id_table);
-
-static struct spi_driver tcan4x5x_can_driver = {
-       .driver = {
-               .name = DEVICE_NAME,
-               .of_match_table = tcan4x5x_of_match,
-               .pm = NULL,
-       },
-       .id_table = tcan4x5x_id_table,
-       .probe = tcan4x5x_can_probe,
-       .remove = tcan4x5x_can_remove,
-};
-module_spi_driver(tcan4x5x_can_driver);
-
-MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
-MODULE_DESCRIPTION("Texas Instruments TCAN4x5x CAN driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/m_can/tcan4x5x.h b/drivers/net/can/m_can/tcan4x5x.h
new file mode 100644 (file)
index 0000000..c66da82
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * tcan4x5x - Texas Instruments TCAN4x5x Family CAN controller driver
+ *
+ * Copyright (c) 2020 Pengutronix,
+ *                    Marc Kleine-Budde <kernel@pengutronix.de>
+ */
+
+#ifndef _TCAN4X5X_H
+#define _TCAN4X5X_H
+
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include "m_can.h"
+
+#define TCAN4X5X_SANITIZE_SPI 1
+
+struct __packed tcan4x5x_buf_cmd {
+       u8 cmd;
+       __be16 addr;
+       u8 len;
+};
+
+struct tcan4x5x_map_buf {
+       struct tcan4x5x_buf_cmd cmd;
+       u8 data[256 * sizeof(u32)];
+} ____cacheline_aligned;
+
+struct tcan4x5x_priv {
+       struct m_can_classdev cdev;
+
+       struct regmap *regmap;
+       struct spi_device *spi;
+
+       struct gpio_desc *reset_gpio;
+       struct gpio_desc *device_wake_gpio;
+       struct gpio_desc *device_state_gpio;
+       struct regulator *power;
+
+       struct tcan4x5x_map_buf map_buf_rx;
+       struct tcan4x5x_map_buf map_buf_tx;
+};
+
+static inline void
+tcan4x5x_spi_cmd_set_len(struct tcan4x5x_buf_cmd *cmd, u8 len)
+{
+       /* number of u32 */
+       cmd->len = len >> 2;
+}
+
+int tcan4x5x_regmap_init(struct tcan4x5x_priv *priv);
+
+#endif
index 5ed00a1..fa32e41 100644 (file)
@@ -270,7 +270,7 @@ static netdev_tx_t mscan_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        list_add_tail(&priv->tx_queue[buf_id].list, &priv->tx_head);
 
-       can_put_echo_skb(skb, dev, buf_id);
+       can_put_echo_skb(skb, dev, buf_id, 0);
 
        /* Enable interrupt. */
        priv->tx_active |= 1 << buf_id;
@@ -448,7 +448,7 @@ static irqreturn_t mscan_isr(int irq, void *dev_id)
                        out_8(&regs->cantbsel, mask);
                        stats->tx_bytes += in_8(&regs->tx.dlr);
                        stats->tx_packets++;
-                       can_get_echo_skb(dev, entry->id);
+                       can_get_echo_skb(dev, entry->id, NULL);
                        priv->tx_active &= ~mask;
                        list_del(pos);
                }
index 4f9e7ec..92a54a5 100644 (file)
@@ -711,7 +711,7 @@ static void pch_can_tx_complete(struct net_device *ndev, u32 int_stat)
        struct net_device_stats *stats = &(priv->ndev->stats);
        u32 dlc;
 
-       can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1);
+       can_get_echo_skb(ndev, int_stat - PCH_RX_OBJ_END - 1, NULL);
        iowrite32(PCH_CMASK_RX_TX_GET | PCH_CMASK_CLRINTPND,
                  &priv->regs->ifregs[1].cmask);
        pch_can_rw_msg_obj(&priv->regs->ifregs[1].creq, int_stat);
@@ -924,7 +924,7 @@ static netdev_tx_t pch_xmit(struct sk_buff *skb, struct net_device *ndev)
                          &priv->regs->ifregs[1].data[i / 2]);
        }
 
-       can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1);
+       can_put_echo_skb(skb, ndev, tx_obj_no - PCH_RX_OBJ_END - 1, 0);
 
        /* Set the size of the data. Update if2_mcont */
        iowrite32(cf->len | PCH_IF_MCONT_NEWDAT | PCH_IF_MCONT_TXRQXT |
index c5334b0..00847cb 100644 (file)
@@ -266,7 +266,7 @@ static int pucan_handle_can_rx(struct peak_canfd_priv *priv,
                unsigned long flags;
 
                spin_lock_irqsave(&priv->echo_lock, flags);
-               can_get_echo_skb(priv->ndev, msg->client);
+               can_get_echo_skb(priv->ndev, msg->client, NULL);
 
                /* count bytes of the echo instead of skb */
                stats->tx_bytes += cf_len;
@@ -716,7 +716,7 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
        spin_lock_irqsave(&priv->echo_lock, flags);
 
        /* prepare and save echo skb in internal slot */
-       can_put_echo_skb(skb, ndev, priv->echo_idx);
+       can_put_echo_skb(skb, ndev, priv->echo_idx, 0);
 
        /* move echo index to the next slot */
        priv->echo_idx = (priv->echo_idx + 1) % priv->can.echo_skb_max;
index 8d36101..29cabc2 100644 (file)
@@ -1,10 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0
 config CAN_RCAR
-       tristate "Renesas R-Car CAN controller"
+       tristate "Renesas R-Car and RZ/G CAN controller"
        depends on ARCH_RENESAS || ARM
        help
          Say Y here if you want to use CAN controller found on Renesas R-Car
-         SoCs.
+         or RZ/G SoCs.
 
          To compile this driver as a module, choose M here: the module will
          be called rcar_can.
index c803327..4870c4e 100644 (file)
@@ -386,7 +386,7 @@ static void rcar_can_tx_done(struct net_device *ndev)
                stats->tx_bytes += priv->tx_dlc[priv->tx_tail %
                                                RCAR_CAN_FIFO_DEPTH];
                priv->tx_dlc[priv->tx_tail % RCAR_CAN_FIFO_DEPTH] = 0;
-               can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH);
+               can_get_echo_skb(ndev, priv->tx_tail % RCAR_CAN_FIFO_DEPTH, NULL);
                priv->tx_tail++;
                netif_wake_queue(ndev);
        }
@@ -617,7 +617,7 @@ static netdev_tx_t rcar_can_start_xmit(struct sk_buff *skb,
        writeb(cf->len, &priv->regs->mb[RCAR_CAN_TX_FIFO_MBX].dlc);
 
        priv->tx_dlc[priv->tx_head % RCAR_CAN_FIFO_DEPTH] = cf->len;
-       can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH);
+       can_put_echo_skb(skb, ndev, priv->tx_head % RCAR_CAN_FIFO_DEPTH, 0);
        priv->tx_head++;
        /* Start Tx: write 0xff to the TFPCR register to increment
         * the CPU-side pointer for the transmit FIFO to the next
index 2778ed5..d8d233e 100644 (file)
@@ -1044,7 +1044,7 @@ static void rcar_canfd_tx_done(struct net_device *ndev)
                stats->tx_packets++;
                stats->tx_bytes += priv->tx_len[sent];
                priv->tx_len[sent] = 0;
-               can_get_echo_skb(ndev, sent);
+               can_get_echo_skb(ndev, sent, NULL);
 
                spin_lock_irqsave(&priv->tx_lock, flags);
                priv->tx_tail++;
@@ -1390,7 +1390,7 @@ static netdev_tx_t rcar_canfd_start_xmit(struct sk_buff *skb,
        }
 
        priv->tx_len[priv->tx_head % RCANFD_FIFO_DEPTH] = cf->len;
-       can_put_echo_skb(skb, ndev, priv->tx_head % RCANFD_FIFO_DEPTH);
+       can_put_echo_skb(skb, ndev, priv->tx_head % RCANFD_FIFO_DEPTH, 0);
 
        spin_lock_irqsave(&priv->tx_lock, flags);
        priv->tx_head++;
diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c
deleted file mode 100644 (file)
index 3c1912c..0000000
+++ /dev/null
@@ -1,376 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2014      Protonic Holland,
- *                         David Jander
- * Copyright (C) 2014-2017 Pengutronix,
- *                         Marc Kleine-Budde <kernel@pengutronix.de>
- */
-
-#include <linux/can/dev.h>
-#include <linux/can/rx-offload.h>
-
-struct can_rx_offload_cb {
-       u32 timestamp;
-};
-
-static inline struct can_rx_offload_cb *
-can_rx_offload_get_cb(struct sk_buff *skb)
-{
-       BUILD_BUG_ON(sizeof(struct can_rx_offload_cb) > sizeof(skb->cb));
-
-       return (struct can_rx_offload_cb *)skb->cb;
-}
-
-static inline bool
-can_rx_offload_le(struct can_rx_offload *offload,
-                 unsigned int a, unsigned int b)
-{
-       if (offload->inc)
-               return a <= b;
-       else
-               return a >= b;
-}
-
-static inline unsigned int
-can_rx_offload_inc(struct can_rx_offload *offload, unsigned int *val)
-{
-       if (offload->inc)
-               return (*val)++;
-       else
-               return (*val)--;
-}
-
-static int can_rx_offload_napi_poll(struct napi_struct *napi, int quota)
-{
-       struct can_rx_offload *offload = container_of(napi,
-                                                     struct can_rx_offload,
-                                                     napi);
-       struct net_device *dev = offload->dev;
-       struct net_device_stats *stats = &dev->stats;
-       struct sk_buff *skb;
-       int work_done = 0;
-
-       while ((work_done < quota) &&
-              (skb = skb_dequeue(&offload->skb_queue))) {
-               struct can_frame *cf = (struct can_frame *)skb->data;
-
-               work_done++;
-               stats->rx_packets++;
-               stats->rx_bytes += cf->len;
-               netif_receive_skb(skb);
-       }
-
-       if (work_done < quota) {
-               napi_complete_done(napi, work_done);
-
-               /* Check if there was another interrupt */
-               if (!skb_queue_empty(&offload->skb_queue))
-                       napi_reschedule(&offload->napi);
-       }
-
-       can_led_event(offload->dev, CAN_LED_EVENT_RX);
-
-       return work_done;
-}
-
-static inline void
-__skb_queue_add_sort(struct sk_buff_head *head, struct sk_buff *new,
-                    int (*compare)(struct sk_buff *a, struct sk_buff *b))
-{
-       struct sk_buff *pos, *insert = NULL;
-
-       skb_queue_reverse_walk(head, pos) {
-               const struct can_rx_offload_cb *cb_pos, *cb_new;
-
-               cb_pos = can_rx_offload_get_cb(pos);
-               cb_new = can_rx_offload_get_cb(new);
-
-               netdev_dbg(new->dev,
-                          "%s: pos=0x%08x, new=0x%08x, diff=%10d, queue_len=%d\n",
-                          __func__,
-                          cb_pos->timestamp, cb_new->timestamp,
-                          cb_new->timestamp - cb_pos->timestamp,
-                          skb_queue_len(head));
-
-               if (compare(pos, new) < 0)
-                       continue;
-               insert = pos;
-               break;
-       }
-       if (!insert)
-               __skb_queue_head(head, new);
-       else
-               __skb_queue_after(head, insert, new);
-}
-
-static int can_rx_offload_compare(struct sk_buff *a, struct sk_buff *b)
-{
-       const struct can_rx_offload_cb *cb_a, *cb_b;
-
-       cb_a = can_rx_offload_get_cb(a);
-       cb_b = can_rx_offload_get_cb(b);
-
-       /* Subtract two u32 and return result as int, to keep
-        * difference steady around the u32 overflow.
-        */
-       return cb_b->timestamp - cb_a->timestamp;
-}
-
-/**
- * can_rx_offload_offload_one() - Read one CAN frame from HW
- * @offload: pointer to rx_offload context
- * @n: number of mailbox to read
- *
- * The task of this function is to read a CAN frame from mailbox @n
- * from the device and return the mailbox's content as a struct
- * sk_buff.
- *
- * If the struct can_rx_offload::skb_queue exceeds the maximal queue
- * length (struct can_rx_offload::skb_queue_len_max) or no skb can be
- * allocated, the mailbox contents is discarded by reading it into an
- * overflow buffer. This way the mailbox is marked as free by the
- * driver.
- *
- * Return: A pointer to skb containing the CAN frame on success.
- *
- *         NULL if the mailbox @n is empty.
- *
- *         ERR_PTR() in case of an error
- */
-static struct sk_buff *
-can_rx_offload_offload_one(struct can_rx_offload *offload, unsigned int n)
-{
-       struct sk_buff *skb;
-       struct can_rx_offload_cb *cb;
-       bool drop = false;
-       u32 timestamp;
-
-       /* If queue is full drop frame */
-       if (unlikely(skb_queue_len(&offload->skb_queue) >
-                    offload->skb_queue_len_max))
-               drop = true;
-
-       skb = offload->mailbox_read(offload, n, &timestamp, drop);
-       /* Mailbox was empty. */
-       if (unlikely(!skb))
-               return NULL;
-
-       /* There was a problem reading the mailbox, propagate
-        * error value.
-        */
-       if (IS_ERR(skb)) {
-               offload->dev->stats.rx_dropped++;
-               offload->dev->stats.rx_fifo_errors++;
-
-               return skb;
-       }
-
-       /* Mailbox was read. */
-       cb = can_rx_offload_get_cb(skb);
-       cb->timestamp = timestamp;
-
-       return skb;
-}
-
-int can_rx_offload_irq_offload_timestamp(struct can_rx_offload *offload,
-                                        u64 pending)
-{
-       struct sk_buff_head skb_queue;
-       unsigned int i;
-
-       __skb_queue_head_init(&skb_queue);
-
-       for (i = offload->mb_first;
-            can_rx_offload_le(offload, i, offload->mb_last);
-            can_rx_offload_inc(offload, &i)) {
-               struct sk_buff *skb;
-
-               if (!(pending & BIT_ULL(i)))
-                       continue;
-
-               skb = can_rx_offload_offload_one(offload, i);
-               if (IS_ERR_OR_NULL(skb))
-                       continue;
-
-               __skb_queue_add_sort(&skb_queue, skb, can_rx_offload_compare);
-       }
-
-       if (!skb_queue_empty(&skb_queue)) {
-               unsigned long flags;
-               u32 queue_len;
-
-               spin_lock_irqsave(&offload->skb_queue.lock, flags);
-               skb_queue_splice_tail(&skb_queue, &offload->skb_queue);
-               spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
-
-               queue_len = skb_queue_len(&offload->skb_queue);
-               if (queue_len > offload->skb_queue_len_max / 8)
-                       netdev_dbg(offload->dev, "%s: queue_len=%d\n",
-                                  __func__, queue_len);
-
-               can_rx_offload_schedule(offload);
-       }
-
-       return skb_queue_len(&skb_queue);
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_timestamp);
-
-int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload)
-{
-       struct sk_buff *skb;
-       int received = 0;
-
-       while (1) {
-               skb = can_rx_offload_offload_one(offload, 0);
-               if (IS_ERR(skb))
-                       continue;
-               if (!skb)
-                       break;
-
-               skb_queue_tail(&offload->skb_queue, skb);
-               received++;
-       }
-
-       if (received)
-               can_rx_offload_schedule(offload);
-
-       return received;
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo);
-
-int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
-                               struct sk_buff *skb, u32 timestamp)
-{
-       struct can_rx_offload_cb *cb;
-       unsigned long flags;
-
-       if (skb_queue_len(&offload->skb_queue) >
-           offload->skb_queue_len_max) {
-               dev_kfree_skb_any(skb);
-               return -ENOBUFS;
-       }
-
-       cb = can_rx_offload_get_cb(skb);
-       cb->timestamp = timestamp;
-
-       spin_lock_irqsave(&offload->skb_queue.lock, flags);
-       __skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare);
-       spin_unlock_irqrestore(&offload->skb_queue.lock, flags);
-
-       can_rx_offload_schedule(offload);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted);
-
-unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
-                                        unsigned int idx, u32 timestamp)
-{
-       struct net_device *dev = offload->dev;
-       struct net_device_stats *stats = &dev->stats;
-       struct sk_buff *skb;
-       u8 len;
-       int err;
-
-       skb = __can_get_echo_skb(dev, idx, &len);
-       if (!skb)
-               return 0;
-
-       err = can_rx_offload_queue_sorted(offload, skb, timestamp);
-       if (err) {
-               stats->rx_errors++;
-               stats->tx_fifo_errors++;
-       }
-
-       return len;
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb);
-
-int can_rx_offload_queue_tail(struct can_rx_offload *offload,
-                             struct sk_buff *skb)
-{
-       if (skb_queue_len(&offload->skb_queue) >
-           offload->skb_queue_len_max) {
-               dev_kfree_skb_any(skb);
-               return -ENOBUFS;
-       }
-
-       skb_queue_tail(&offload->skb_queue, skb);
-       can_rx_offload_schedule(offload);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail);
-
-static int can_rx_offload_init_queue(struct net_device *dev,
-                                    struct can_rx_offload *offload,
-                                    unsigned int weight)
-{
-       offload->dev = dev;
-
-       /* Limit queue len to 4x the weight (rounted to next power of two) */
-       offload->skb_queue_len_max = 2 << fls(weight);
-       offload->skb_queue_len_max *= 4;
-       skb_queue_head_init(&offload->skb_queue);
-
-       netif_napi_add(dev, &offload->napi, can_rx_offload_napi_poll, weight);
-
-       dev_dbg(dev->dev.parent, "%s: skb_queue_len_max=%d\n",
-               __func__, offload->skb_queue_len_max);
-
-       return 0;
-}
-
-int can_rx_offload_add_timestamp(struct net_device *dev,
-                                struct can_rx_offload *offload)
-{
-       unsigned int weight;
-
-       if (offload->mb_first > BITS_PER_LONG_LONG ||
-           offload->mb_last > BITS_PER_LONG_LONG || !offload->mailbox_read)
-               return -EINVAL;
-
-       if (offload->mb_first < offload->mb_last) {
-               offload->inc = true;
-               weight = offload->mb_last - offload->mb_first;
-       } else {
-               offload->inc = false;
-               weight = offload->mb_first - offload->mb_last;
-       }
-
-       return can_rx_offload_init_queue(dev, offload, weight);
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_add_timestamp);
-
-int can_rx_offload_add_fifo(struct net_device *dev,
-                           struct can_rx_offload *offload, unsigned int weight)
-{
-       if (!offload->mailbox_read)
-               return -EINVAL;
-
-       return can_rx_offload_init_queue(dev, offload, weight);
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_add_fifo);
-
-int can_rx_offload_add_manual(struct net_device *dev,
-                             struct can_rx_offload *offload,
-                             unsigned int weight)
-{
-       if (offload->mailbox_read)
-               return -EINVAL;
-
-       return can_rx_offload_init_queue(dev, offload, weight);
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_add_manual);
-
-void can_rx_offload_enable(struct can_rx_offload *offload)
-{
-       napi_enable(&offload->napi);
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_enable);
-
-void can_rx_offload_del(struct can_rx_offload *offload)
-{
-       netif_napi_del(&offload->napi);
-       skb_queue_purge(&offload->skb_queue);
-}
-EXPORT_SYMBOL_GPL(can_rx_offload_del);
index b6a7003..9e86488 100644 (file)
@@ -318,7 +318,7 @@ static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb,
        for (i = 0; i < cf->len; i++)
                priv->write_reg(priv, dreg++, cf->data[i]);
 
-       can_put_echo_skb(skb, dev, 0);
+       can_put_echo_skb(skb, dev, 0, 0);
 
        if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
                cmd_reg_val |= CMD_AT;
@@ -531,7 +531,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
                                stats->tx_bytes +=
                                        priv->read_reg(priv, SJA1000_FI) & 0xf;
                                stats->tx_packets++;
-                               can_get_echo_skb(dev, 0);
+                               can_get_echo_skb(dev, 0, NULL);
                        }
                        netif_wake_queue(dev);
                        can_led_event(dev, CAN_LED_EVENT_TX);
index 40070c9..c44f341 100644 (file)
@@ -104,7 +104,7 @@ static netdev_tx_t softing_netdev_start_xmit(struct sk_buff *skb,
        card->tx.last_bus = priv->index;
        ++card->tx.pending;
        ++priv->tx.pending;
-       can_put_echo_skb(skb, dev, priv->tx.echo_put);
+       can_put_echo_skb(skb, dev, priv->tx.echo_put, 0);
        ++priv->tx.echo_put;
        if (priv->tx.echo_put >= TX_ECHO_SKB_MAX)
                priv->tx.echo_put = 0;
@@ -284,7 +284,7 @@ static int softing_handle_1(struct softing *card)
                        skb = priv->can.echo_skb[priv->tx.echo_get];
                        if (skb)
                                skb->tstamp = ktime;
-                       can_get_echo_skb(netdev, priv->tx.echo_get);
+                       can_get_echo_skb(netdev, priv->tx.echo_get, NULL);
                        ++priv->tx.echo_get;
                        if (priv->tx.echo_get >= TX_ECHO_SKB_MAX)
                                priv->tx.echo_get = 0;
index f9455de..c3e020c 100644 (file)
@@ -586,7 +586,7 @@ static void hi3110_tx_work_handler(struct work_struct *ws)
                        frame = (struct can_frame *)priv->tx_skb->data;
                        hi3110_hw_tx(spi, frame);
                        priv->tx_len = 1 + frame->len;
-                       can_put_echo_skb(priv->tx_skb, net, 0);
+                       can_put_echo_skb(priv->tx_skb, net, 0, 0);
                        priv->tx_skb = NULL;
                }
        }
@@ -725,7 +725,7 @@ static irqreturn_t hi3110_can_ist(int irq, void *dev_id)
                        net->stats.tx_bytes += priv->tx_len - 1;
                        can_led_event(net, CAN_LED_EVENT_TX);
                        if (priv->tx_len) {
-                               can_get_echo_skb(net, 0);
+                               can_get_echo_skb(net, 0, NULL);
                                priv->tx_len = 0;
                        }
                        netif_wake_queue(net);
index 25859d1..f69fb42 100644 (file)
@@ -1002,7 +1002,7 @@ static void mcp251x_tx_work_handler(struct work_struct *ws)
                                frame->len = CAN_FRAME_MAX_DATA_LEN;
                        mcp251x_hw_tx(spi, frame, 0);
                        priv->tx_len = 1 + frame->len;
-                       can_put_echo_skb(priv->tx_skb, net, 0);
+                       can_put_echo_skb(priv->tx_skb, net, 0, 0);
                        priv->tx_skb = NULL;
                }
        }
@@ -1171,7 +1171,7 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id)
                        net->stats.tx_bytes += priv->tx_len - 1;
                        can_led_event(net, CAN_LED_EVENT_TX);
                        if (priv->tx_len) {
-                               can_get_echo_skb(net, 0);
+                               can_get_echo_skb(net, 0, NULL);
                                priv->tx_len = 0;
                        }
                        netif_wake_queue(net);
index 77129d5..3638b47 100644 (file)
@@ -335,6 +335,8 @@ static void mcp251xfd_ring_init(struct mcp251xfd_priv *priv)
        u8 len;
        int i, j;
 
+       netdev_reset_queue(priv->ndev);
+
        /* TEF */
        tef_ring = priv->tef;
        tef_ring->head = 0;
@@ -1249,7 +1251,8 @@ mcp251xfd_handle_tefif_recover(const struct mcp251xfd_priv *priv, const u32 seq)
 
 static int
 mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
-                          const struct mcp251xfd_hw_tef_obj *hw_tef_obj)
+                          const struct mcp251xfd_hw_tef_obj *hw_tef_obj,
+                          unsigned int *frame_len_ptr)
 {
        struct net_device_stats *stats = &priv->ndev->stats;
        u32 seq, seq_masked, tef_tail_masked;
@@ -1271,7 +1274,8 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
        stats->tx_bytes +=
                can_rx_offload_get_echo_skb(&priv->offload,
                                            mcp251xfd_get_tef_tail(priv),
-                                           hw_tef_obj->ts);
+                                           hw_tef_obj->ts,
+                                           frame_len_ptr);
        stats->tx_packets++;
        priv->tef->tail++;
 
@@ -1308,6 +1312,7 @@ mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv,
                       const u8 offset, const u8 len)
 {
        const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
+       const int val_bytes = regmap_get_val_bytes(priv->map_rx);
 
        if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) &&
            (offset > tx_ring->obj_num ||
@@ -1322,12 +1327,13 @@ mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv,
        return regmap_bulk_read(priv->map_rx,
                                mcp251xfd_get_tef_obj_addr(offset),
                                hw_tef_obj,
-                               sizeof(*hw_tef_obj) / sizeof(u32) * len);
+                               sizeof(*hw_tef_obj) / val_bytes * len);
 }
 
 static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
 {
        struct mcp251xfd_hw_tef_obj hw_tef_obj[MCP251XFD_TX_OBJ_NUM_MAX];
+       unsigned int total_frame_len = 0;
        u8 tef_tail, len, l;
        int err, i;
 
@@ -1349,7 +1355,9 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
        }
 
        for (i = 0; i < len; i++) {
-               err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i]);
+               unsigned int frame_len;
+
+               err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i], &frame_len);
                /* -EAGAIN means the Sequence Number in the TEF
                 * doesn't match our tef_tail. This can happen if we
                 * read the TEF objects too early. Leave loop let the
@@ -1359,6 +1367,8 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
                        goto out_netif_wake_queue;
                if (err)
                        return err;
+
+               total_frame_len += frame_len;
        }
 
  out_netif_wake_queue:
@@ -1368,13 +1378,10 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
                struct mcp251xfd_tx_ring *tx_ring = priv->tx;
                struct spi_transfer *last_xfer;
 
-               tx_ring->tail += len;
-
                /* Increment the TEF FIFO tail pointer 'len' times in
                 * a single SPI message.
-                */
-
-               /* Note:
+                *
+                * Note:
                 *
                 * "cs_change == 1" on the last transfer results in an
                 * active chip select after the complete SPI
@@ -1391,6 +1398,9 @@ static int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
                if (err)
                        return err;
 
+               tx_ring->tail += len;
+               netdev_completed_queue(priv->ndev, len, total_frame_len);
+
                err = mcp251xfd_check_tef_tail(priv);
                if (err)
                        return err;
@@ -1439,6 +1449,7 @@ mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv,
                           struct sk_buff *skb)
 {
        struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+       u8 dlc;
 
        if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_IDE) {
                u32 sid, eid;
@@ -1454,9 +1465,10 @@ mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv,
                                        hw_rx_obj->id);
        }
 
+       dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, hw_rx_obj->flags);
+
        /* CANFD */
        if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) {
-               u8 dlc;
 
                if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_ESI)
                        cfd->flags |= CANFD_ESI;
@@ -1464,17 +1476,17 @@ mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv,
                if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_BRS)
                        cfd->flags |= CANFD_BRS;
 
-               dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC, hw_rx_obj->flags);
                cfd->len = can_fd_dlc2len(dlc);
        } else {
                if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR)
                        cfd->can_id |= CAN_RTR_FLAG;
 
-               cfd->len = can_cc_dlc2len(FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC,
-                                                hw_rx_obj->flags));
+               can_frame_set_cc_len((struct can_frame *)cfd, dlc,
+                                    priv->can.ctrlmode);
        }
 
-       memcpy(cfd->data, hw_rx_obj->data, cfd->len);
+       if (!(hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR))
+               memcpy(cfd->data, hw_rx_obj->data, cfd->len);
 }
 
 static int
@@ -1492,7 +1504,7 @@ mcp251xfd_handle_rxif_one(struct mcp251xfd_priv *priv,
        else
                skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cfd);
 
-       if (!cfd) {
+       if (!skb) {
                stats->rx_dropped++;
                return 0;
        }
@@ -1511,12 +1523,13 @@ mcp251xfd_rx_obj_read(const struct mcp251xfd_priv *priv,
                      struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj,
                      const u8 offset, const u8 len)
 {
+       const int val_bytes = regmap_get_val_bytes(priv->map_rx);
        int err;
 
        err = regmap_bulk_read(priv->map_rx,
                               mcp251xfd_get_rx_obj_addr(ring, offset),
                               hw_rx_obj,
-                              len * ring->obj_size / sizeof(u32));
+                              len * ring->obj_size / val_bytes);
 
        return err;
 }
@@ -1553,10 +1566,8 @@ mcp251xfd_handle_rxif_ring(struct mcp251xfd_priv *priv,
 
                /* Increment the RX FIFO tail pointer 'len' times in a
                 * single SPI message.
-                */
-               ring->tail += len;
-
-               /* Note:
+                *
+                * Note:
                 *
                 * "cs_change == 1" on the last transfer results in an
                 * active chip select after the complete SPI
@@ -1572,6 +1583,8 @@ mcp251xfd_handle_rxif_ring(struct mcp251xfd_priv *priv,
                last_xfer->cs_change = 1;
                if (err)
                        return err;
+
+               ring->tail += len;
        }
 
        return 0;
@@ -2138,6 +2151,7 @@ static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv)
 static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)
 {
        struct mcp251xfd_priv *priv = dev_id;
+       const int val_bytes = regmap_get_val_bytes(priv->map_reg);
        irqreturn_t handled = IRQ_NONE;
        int err;
 
@@ -2163,7 +2177,7 @@ static irqreturn_t mcp251xfd_irq(int irq, void *dev_id)
                err = regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT,
                                       &priv->regs_status,
                                       sizeof(priv->regs_status) /
-                                      sizeof(u32));
+                                      val_bytes);
                if (err)
                        goto out_fail;
 
@@ -2301,7 +2315,7 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv,
        union mcp251xfd_tx_obj_load_buf *load_buf;
        u8 dlc;
        u32 id, flags;
-       int offset, len;
+       int len_sanitized = 0, len;
 
        if (cfd->can_id & CAN_EFF_FLAG) {
                u32 sid, eid;
@@ -2322,12 +2336,12 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv,
         * harm, only the lower 7 bits will be transferred into the
         * TEF object.
         */
-       dlc = can_fd_len2dlc(cfd->len);
-       flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, seq) |
-               FIELD_PREP(MCP251XFD_OBJ_FLAGS_DLC, dlc);
+       flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, seq);
 
        if (cfd->can_id & CAN_RTR_FLAG)
                flags |= MCP251XFD_OBJ_FLAGS_RTR;
+       else
+               len_sanitized = canfd_sanitize_len(cfd->len);
 
        /* CANFD */
        if (can_is_canfd_skb(skb)) {
@@ -2338,8 +2352,15 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv,
 
                if (cfd->flags & CANFD_BRS)
                        flags |= MCP251XFD_OBJ_FLAGS_BRS;
+
+               dlc = can_fd_len2dlc(cfd->len);
+       } else {
+               dlc = can_get_cc_dlc((struct can_frame *)cfd,
+                                    priv->can.ctrlmode);
        }
 
+       flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_DLC, dlc);
+
        load_buf = &tx_obj->buf;
        if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_TX)
                hw_tx_obj = &load_buf->crc.hw_tx_obj;
@@ -2349,17 +2370,22 @@ mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv,
        put_unaligned_le32(id, &hw_tx_obj->id);
        put_unaligned_le32(flags, &hw_tx_obj->flags);
 
-       /* Clear data at end of CAN frame */
-       offset = round_down(cfd->len, sizeof(u32));
-       len = round_up(can_fd_dlc2len(dlc), sizeof(u32)) - offset;
-       if (MCP251XFD_SANITIZE_CAN && len)
-               memset(hw_tx_obj->data + offset, 0x0, len);
+       /* Copy data */
        memcpy(hw_tx_obj->data, cfd->data, cfd->len);
 
+       /* Clear unused data at end of CAN frame */
+       if (MCP251XFD_SANITIZE_CAN && len_sanitized) {
+               int pad_len;
+
+               pad_len = len_sanitized - cfd->len;
+               if (pad_len)
+                       memset(hw_tx_obj->data + cfd->len, 0x0, pad_len);
+       }
+
        /* Number of bytes to be written into the RAM of the controller */
        len = sizeof(hw_tx_obj->id) + sizeof(hw_tx_obj->flags);
        if (MCP251XFD_SANITIZE_CAN)
-               len += round_up(can_fd_dlc2len(dlc), sizeof(u32));
+               len += round_up(len_sanitized, sizeof(u32));
        else
                len += round_up(cfd->len, sizeof(u32));
 
@@ -2419,6 +2445,7 @@ static netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb,
        struct mcp251xfd_priv *priv = netdev_priv(ndev);
        struct mcp251xfd_tx_ring *tx_ring = priv->tx;
        struct mcp251xfd_tx_obj *tx_obj;
+       unsigned int frame_len;
        u8 tx_head;
        int err;
 
@@ -2434,10 +2461,12 @@ static netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb,
        /* Stop queue if we occupy the complete TX FIFO */
        tx_head = mcp251xfd_get_tx_head(tx_ring);
        tx_ring->head++;
-       if (tx_ring->head - tx_ring->tail >= tx_ring->obj_num)
+       if (mcp251xfd_get_tx_free(tx_ring) == 0)
                netif_stop_queue(ndev);
 
-       can_put_echo_skb(skb, ndev, tx_head);
+       frame_len = can_skb_get_frame_len(skb);
+       can_put_echo_skb(skb, ndev, tx_head, frame_len);
+       netdev_sent_queue(priv->ndev, frame_len);
 
        err = mcp251xfd_tx_obj_write(priv, tx_obj);
        if (err)
@@ -2886,7 +2915,8 @@ static int mcp251xfd_probe(struct spi_device *spi)
        priv->can.data_bittiming_const = &mcp251xfd_data_bittiming_const;
        priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
                CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING |
-               CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO;
+               CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO |
+               CAN_CTRLMODE_CC_LEN8_DLC;
        priv->ndev = ndev;
        priv->spi = spi;
        priv->rx_int = rx_int;
index 783b632..54aa7c2 100644 (file)
@@ -448,7 +448,7 @@ static netdev_tx_t sun4ican_start_xmit(struct sk_buff *skb, struct net_device *d
 
        writel(msg_flag_n, priv->base + SUN4I_REG_BUF0_ADDR);
 
-       can_put_echo_skb(skb, dev, 0);
+       can_put_echo_skb(skb, dev, 0, 0);
 
        if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
                sun4i_can_write_cmdreg(priv, SUN4I_CMD_SELF_RCV_REQ);
@@ -655,7 +655,7 @@ static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id)
                            readl(priv->base +
                                  SUN4I_REG_RBUF_RBACK_START_ADDR) & 0xf;
                        stats->tx_packets++;
-                       can_get_echo_skb(dev, 0);
+                       can_get_echo_skb(dev, 0, NULL);
                        netif_wake_queue(dev);
                        can_led_event(dev, CAN_LED_EVENT_TX);
                }
index a6850ff..73245d8 100644 (file)
@@ -513,7 +513,7 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev)
                               be32_to_cpu(*(__be32 *)(cf->data + 4)));
        else
                *(u32 *)(cf->data + 4) = 0;
-       can_put_echo_skb(skb, ndev, mbxno);
+       can_put_echo_skb(skb, ndev, mbxno, 0);
 
        spin_lock_irqsave(&priv->mbx_lock, flags);
        --priv->tx_head;
@@ -757,7 +757,7 @@ static irqreturn_t ti_hecc_interrupt(int irq, void *dev_id)
                        stamp = hecc_read_stamp(priv, mbxno);
                        stats->tx_bytes +=
                                can_rx_offload_get_echo_skb(&priv->offload,
-                                                           mbxno, stamp);
+                                                           mbxno, stamp, NULL);
                        stats->tx_packets++;
                        can_led_event(ndev, CAN_LED_EVENT_TX);
                        --priv->tx_tail;
index 25eee44..18f40eb 100644 (file)
@@ -518,7 +518,7 @@ static void ems_usb_write_bulk_callback(struct urb *urb)
        netdev->stats.tx_packets++;
        netdev->stats.tx_bytes += context->dlc;
 
-       can_get_echo_skb(netdev, context->echo_index);
+       can_get_echo_skb(netdev, context->echo_index, NULL);
 
        /* Release context */
        context->echo_index = MAX_TX_URBS;
@@ -801,7 +801,7 @@ static netdev_tx_t ems_usb_start_xmit(struct sk_buff *skb, struct net_device *ne
        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        usb_anchor_urb(urb, &dev->tx_submitted);
 
-       can_put_echo_skb(skb, netdev, context->echo_index);
+       can_put_echo_skb(skb, netdev, context->echo_index, 0);
 
        atomic_inc(&dev->active_tx_urbs);
 
index 9eed75a..562acbf 100644 (file)
@@ -357,7 +357,7 @@ static void esd_usb2_tx_done_msg(struct esd_usb2_net_priv *priv,
        if (!msg->msg.txdone.status) {
                stats->tx_packets++;
                stats->tx_bytes += context->len;
-               can_get_echo_skb(netdev, context->echo_index);
+               can_get_echo_skb(netdev, context->echo_index, NULL);
        } else {
                stats->tx_errors++;
                can_free_echo_skb(netdev, context->echo_index);
@@ -783,7 +783,7 @@ static netdev_tx_t esd_usb2_start_xmit(struct sk_buff *skb,
 
        usb_anchor_urb(urb, &priv->tx_submitted);
 
-       can_put_echo_skb(skb, netdev, context->echo_index);
+       can_put_echo_skb(skb, netdev, context->echo_index, 0);
 
        atomic_inc(&priv->active_tx_jobs);
 
index 0487095..a00dc19 100644 (file)
@@ -370,7 +370,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
                        goto resubmit_urb;
                }
 
-               can_get_echo_skb(netdev, hf->echo_id);
+               can_get_echo_skb(netdev, hf->echo_id, NULL);
 
                gs_free_tx_context(txc);
 
@@ -525,7 +525,7 @@ static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb,
        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        usb_anchor_urb(urb, &dev->tx_submitted);
 
-       can_put_echo_skb(skb, netdev, idx);
+       can_put_echo_skb(skb, netdev, idx, 0);
 
        atomic_inc(&dev->active_tx_urbs);
 
index e2d5884..2b7efd2 100644 (file)
@@ -578,7 +578,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
 
        context->priv = priv;
 
-       can_put_echo_skb(skb, netdev, context->echo_index);
+       can_put_echo_skb(skb, netdev, context->echo_index, 0);
 
        usb_fill_bulk_urb(urb, dev->udev,
                          usb_sndbulkpipe(dev->udev,
index 480bd2e..dcee8dc 100644 (file)
@@ -1151,7 +1151,7 @@ static void kvaser_usb_hydra_tx_acknowledge(const struct kvaser_usb *dev,
 
        spin_lock_irqsave(&priv->tx_contexts_lock, irq_flags);
 
-       can_get_echo_skb(priv->netdev, context->echo_index);
+       can_get_echo_skb(priv->netdev, context->echo_index, NULL);
        context->echo_index = dev->max_tx_urbs;
        --priv->active_tx_contexts;
        netif_wake_queue(priv->netdev);
index 98c016e..59ba7c7 100644 (file)
@@ -594,7 +594,7 @@ static void kvaser_usb_leaf_tx_acknowledge(const struct kvaser_usb *dev,
 
        spin_lock_irqsave(&priv->tx_contexts_lock, flags);
 
-       can_get_echo_skb(priv->netdev, context->echo_index);
+       can_get_echo_skb(priv->netdev, context->echo_index, NULL);
        context->echo_index = dev->max_tx_urbs;
        --priv->active_tx_contexts;
        netif_wake_queue(priv->netdev);
index df54eb7..1f649d1 100644 (file)
@@ -237,7 +237,7 @@ static void mcba_usb_write_bulk_callback(struct urb *urb)
                netdev->stats.tx_bytes += ctx->dlc;
 
                can_led_event(netdev, CAN_LED_EVENT_TX);
-               can_get_echo_skb(netdev, ctx->ndx);
+               can_get_echo_skb(netdev, ctx->ndx, NULL);
        }
 
        if (urb->status)
@@ -355,7 +355,7 @@ static netdev_tx_t mcba_usb_start_xmit(struct sk_buff *skb,
        if (cf->can_id & CAN_RTR_FLAG)
                usb_msg.dlc |= MCBA_DLC_RTR_MASK;
 
-       can_put_echo_skb(skb, priv->netdev, ctx->ndx);
+       can_put_echo_skb(skb, priv->netdev, ctx->ndx, 0);
 
        err = mcba_usb_xmit(priv, (struct mcba_usb_msg *)&usb_msg, ctx);
        if (err)
@@ -466,7 +466,7 @@ static void mcba_usb_process_ka_usb(struct mcba_priv *priv,
                                    struct mcba_usb_msg_ka_usb *msg)
 {
        if (unlikely(priv->usb_ka_first_pass)) {
-               netdev_info(priv->netdev, "PIC USB version %hhu.%hhu\n",
+               netdev_info(priv->netdev, "PIC USB version %u.%u\n",
                            msg->soft_ver_major, msg->soft_ver_minor);
 
                priv->usb_ka_first_pass = false;
@@ -492,7 +492,7 @@ static void mcba_usb_process_ka_can(struct mcba_priv *priv,
                                    struct mcba_usb_msg_ka_can *msg)
 {
        if (unlikely(priv->can_ka_first_pass)) {
-               netdev_info(priv->netdev, "PIC CAN version %hhu.%hhu\n",
+               netdev_info(priv->netdev, "PIC CAN version %u.%u\n",
                            msg->soft_ver_major, msg->soft_ver_minor);
 
                priv->can_ka_first_pass = false;
@@ -554,7 +554,7 @@ static void mcba_usb_process_rx(struct mcba_priv *priv,
                break;
 
        default:
-               netdev_warn(priv->netdev, "Unsupported msg (0x%hhX)",
+               netdev_warn(priv->netdev, "Unsupported msg (0x%X)",
                            msg->cmd_id);
                break;
        }
index 251835e..573b115 100644 (file)
@@ -309,7 +309,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb)
        }
 
        /* should always release echo skb and corresponding context */
-       can_get_echo_skb(netdev, context->echo_index);
+       can_get_echo_skb(netdev, context->echo_index, NULL);
        context->echo_index = PCAN_USB_MAX_TX_URBS;
 
        /* do wakeup tx queue in case of success only */
@@ -365,7 +365,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb,
 
        usb_anchor_urb(urb, &dev->tx_submitted);
 
-       can_put_echo_skb(skb, netdev, context->echo_index);
+       can_put_echo_skb(skb, netdev, context->echo_index, 0);
 
        atomic_inc(&dev->active_tx_urbs);
 
index 61631f4..f347ecc 100644 (file)
@@ -514,11 +514,11 @@ static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if,
        else
                memcpy(cfd->data, rm->d, cfd->len);
 
-       peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(rm->ts_low));
-
        netdev->stats.rx_packets++;
        netdev->stats.rx_bytes += cfd->len;
 
+       peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(rm->ts_low));
+
        return 0;
 }
 
@@ -580,11 +580,11 @@ static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if,
        if (!skb)
                return -ENOMEM;
 
-       peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(sm->ts_low));
-
        netdev->stats.rx_packets++;
        netdev->stats.rx_bytes += cf->len;
 
+       peak_usb_netif_rx(skb, &usb_if->time_ref, le32_to_cpu(sm->ts_low));
+
        return 0;
 }
 
index 7d92da8..fa403c0 100644 (file)
@@ -672,7 +672,7 @@ static void ucan_tx_complete_msg(struct ucan_priv *up,
                        /* update statistics */
                        up->netdev->stats.tx_packets++;
                        up->netdev->stats.tx_bytes += dlc;
-                       can_get_echo_skb(up->netdev, echo_index);
+                       can_get_echo_skb(up->netdev, echo_index, NULL);
                } else {
                        up->netdev->stats.tx_dropped++;
                        can_free_echo_skb(up->netdev, echo_index);
@@ -1137,7 +1137,7 @@ static netdev_tx_t ucan_start_xmit(struct sk_buff *skb,
 
        /* put the skb on can loopback stack */
        spin_lock_irqsave(&up->echo_skb_lock, flags);
-       can_put_echo_skb(skb, up->netdev, echo_index);
+       can_put_echo_skb(skb, up->netdev, echo_index, 0);
        spin_unlock_irqrestore(&up->echo_skb_lock, flags);
 
        /* transmit it */
index 4447830..e8c4243 100644 (file)
@@ -585,7 +585,7 @@ static void usb_8dev_write_bulk_callback(struct urb *urb)
        netdev->stats.tx_packets++;
        netdev->stats.tx_bytes += context->dlc;
 
-       can_get_echo_skb(netdev, context->echo_index);
+       can_get_echo_skb(netdev, context->echo_index, NULL);
 
        can_led_event(netdev, CAN_LED_EVENT_TX);
 
@@ -664,7 +664,7 @@ static netdev_tx_t usb_8dev_start_xmit(struct sk_buff *skb,
        urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
        usb_anchor_urb(urb, &priv->tx_submitted);
 
-       can_put_echo_skb(skb, netdev, context->echo_index);
+       can_put_echo_skb(skb, netdev, context->echo_index, 0);
 
        atomic_inc(&priv->active_tx_urbs);
 
index fa47bab..f9a524c 100644 (file)
@@ -39,6 +39,7 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev)
        struct net_device *peer;
        struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
        struct net_device_stats *peerstats, *srcstats = &dev->stats;
+       u8 len;
 
        if (can_dropped_invalid_skb(dev, skb))
                return NETDEV_TX_OK;
@@ -61,12 +62,13 @@ static netdev_tx_t vxcan_xmit(struct sk_buff *skb, struct net_device *dev)
        skb->dev        = peer;
        skb->ip_summed  = CHECKSUM_UNNECESSARY;
 
+       len = cfd->len;
        if (netif_rx_ni(skb) == NET_RX_SUCCESS) {
                srcstats->tx_packets++;
-               srcstats->tx_bytes += cfd->len;
+               srcstats->tx_bytes += len;
                peerstats = &peer->stats;
                peerstats->rx_packets++;
-               peerstats->rx_bytes += cfd->len;
+               peerstats->rx_bytes += len;
        }
 
 out_unlock:
index 3f54ede..37fa19c 100644 (file)
@@ -592,9 +592,9 @@ static void xcan_write_frame(struct net_device *ndev, struct sk_buff *skb,
 
        if (!(priv->devtype.flags & XCAN_FLAG_TX_MAILBOXES) &&
            (priv->devtype.flags & XCAN_FLAG_TXFEMP))
-               can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max);
+               can_put_echo_skb(skb, ndev, priv->tx_head % priv->tx_max, 0);
        else
-               can_put_echo_skb(skb, ndev, 0);
+               can_put_echo_skb(skb, ndev, 0, 0);
 
        priv->tx_head++;
 
@@ -1292,7 +1292,7 @@ static void xcan_tx_interrupt(struct net_device *ndev, u32 isr)
 
        while (frames_sent--) {
                stats->tx_bytes += can_get_echo_skb(ndev, priv->tx_tail %
-                                                   priv->tx_max);
+                                                   priv->tx_max, NULL);
                priv->tx_tail++;
                stats->tx_packets++;
        }
index f6a0488..3af373e 100644 (file)
@@ -60,6 +60,8 @@ source "drivers/net/dsa/qca/Kconfig"
 
 source "drivers/net/dsa/sja1105/Kconfig"
 
+source "drivers/net/dsa/xrs700x/Kconfig"
+
 config NET_DSA_QCA8K
        tristate "Qualcomm Atheros QCA8K Ethernet switch family support"
        depends on NET_DSA
index a84adb1..f3598c0 100644 (file)
@@ -24,3 +24,4 @@ obj-y                         += mv88e6xxx/
 obj-y                          += ocelot/
 obj-y                          += qca/
 obj-y                          += sja1105/
+obj-y                          += xrs700x/
index 288b5a5..23fc722 100644 (file)
@@ -1374,26 +1374,22 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_phylink_mac_link_up);
 
-int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
-                      struct switchdev_trans *trans)
+int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
 {
        struct b53_device *dev = ds->priv;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering);
 
        return 0;
 }
 EXPORT_SYMBOL(b53_vlan_filtering);
 
-int b53_vlan_prepare(struct dsa_switch *ds, int port,
-                    const struct switchdev_obj_port_vlan *vlan)
+static int b53_vlan_prepare(struct dsa_switch *ds, int port,
+                           const struct switchdev_obj_port_vlan *vlan)
 {
        struct b53_device *dev = ds->priv;
 
-       if ((is5325(dev) || is5365(dev)) && vlan->vid_begin == 0)
+       if ((is5325(dev) || is5365(dev)) && vlan->vid == 0)
                return -EOPNOTSUPP;
 
        /* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of
@@ -1404,47 +1400,50 @@ int b53_vlan_prepare(struct dsa_switch *ds, int port,
            !(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED))
                return -EINVAL;
 
-       if (vlan->vid_end > dev->num_vlans)
+       if (vlan->vid >= dev->num_vlans)
                return -ERANGE;
 
        b53_enable_vlan(dev, true, ds->vlan_filtering);
 
        return 0;
 }
-EXPORT_SYMBOL(b53_vlan_prepare);
 
-void b53_vlan_add(struct dsa_switch *ds, int port,
-                 const struct switchdev_obj_port_vlan *vlan)
+int b53_vlan_add(struct dsa_switch *ds, int port,
+                const struct switchdev_obj_port_vlan *vlan)
 {
        struct b53_device *dev = ds->priv;
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        struct b53_vlan *vl;
-       u16 vid;
+       int err;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               vl = &dev->vlans[vid];
+       err = b53_vlan_prepare(ds, port, vlan);
+       if (err)
+               return err;
 
-               b53_get_vlan_entry(dev, vid, vl);
+       vl = &dev->vlans[vlan->vid];
 
-               if (vid == 0 && vid == b53_default_pvid(dev))
-                       untagged = true;
+       b53_get_vlan_entry(dev, vlan->vid, vl);
 
-               vl->members |= BIT(port);
-               if (untagged && !dsa_is_cpu_port(ds, port))
-                       vl->untag |= BIT(port);
-               else
-                       vl->untag &= ~BIT(port);
+       if (vlan->vid == 0 && vlan->vid == b53_default_pvid(dev))
+               untagged = true;
 
-               b53_set_vlan_entry(dev, vid, vl);
-               b53_fast_age_vlan(dev, vid);
-       }
+       vl->members |= BIT(port);
+       if (untagged && !dsa_is_cpu_port(ds, port))
+               vl->untag |= BIT(port);
+       else
+               vl->untag &= ~BIT(port);
+
+       b53_set_vlan_entry(dev, vlan->vid, vl);
+       b53_fast_age_vlan(dev, vlan->vid);
 
        if (pvid && !dsa_is_cpu_port(ds, port)) {
                b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port),
-                           vlan->vid_end);
-               b53_fast_age_vlan(dev, vid);
+                           vlan->vid);
+               b53_fast_age_vlan(dev, vlan->vid);
        }
+
+       return 0;
 }
 EXPORT_SYMBOL(b53_vlan_add);
 
@@ -1454,27 +1453,24 @@ int b53_vlan_del(struct dsa_switch *ds, int port,
        struct b53_device *dev = ds->priv;
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct b53_vlan *vl;
-       u16 vid;
        u16 pvid;
 
        b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               vl = &dev->vlans[vid];
+       vl = &dev->vlans[vlan->vid];
 
-               b53_get_vlan_entry(dev, vid, vl);
+       b53_get_vlan_entry(dev, vlan->vid, vl);
 
-               vl->members &= ~BIT(port);
+       vl->members &= ~BIT(port);
 
-               if (pvid == vid)
-                       pvid = b53_default_pvid(dev);
+       if (pvid == vlan->vid)
+               pvid = b53_default_pvid(dev);
 
-               if (untagged && !dsa_is_cpu_port(ds, port))
-                       vl->untag &= ~(BIT(port));
+       if (untagged && !dsa_is_cpu_port(ds, port))
+               vl->untag &= ~(BIT(port));
 
-               b53_set_vlan_entry(dev, vid, vl);
-               b53_fast_age_vlan(dev, vid);
-       }
+       b53_set_vlan_entry(dev, vlan->vid, vl);
+       b53_fast_age_vlan(dev, vlan->vid);
 
        b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), pvid);
        b53_fast_age_vlan(dev, pvid);
@@ -1751,8 +1747,8 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_fdb_dump);
 
-int b53_mdb_prepare(struct dsa_switch *ds, int port,
-                   const struct switchdev_obj_port_mdb *mdb)
+int b53_mdb_add(struct dsa_switch *ds, int port,
+               const struct switchdev_obj_port_mdb *mdb)
 {
        struct b53_device *priv = ds->priv;
 
@@ -1762,19 +1758,7 @@ int b53_mdb_prepare(struct dsa_switch *ds, int port,
        if (is5325(priv) || is5365(priv))
                return -EOPNOTSUPP;
 
-       return 0;
-}
-EXPORT_SYMBOL(b53_mdb_prepare);
-
-void b53_mdb_add(struct dsa_switch *ds, int port,
-                const struct switchdev_obj_port_mdb *mdb)
-{
-       struct b53_device *priv = ds->priv;
-       int ret;
-
-       ret = b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true);
-       if (ret)
-               dev_err(ds->dev, "failed to add MDB entry\n");
+       return b53_arl_op(priv, 0, port, mdb->addr, mdb->vid, true);
 }
 EXPORT_SYMBOL(b53_mdb_add);
 
@@ -2207,7 +2191,6 @@ static const struct dsa_switch_ops b53_switch_ops = {
        .port_fast_age          = b53_br_fast_age,
        .port_egress_floods     = b53_br_egress_floods,
        .port_vlan_filtering    = b53_vlan_filtering,
-       .port_vlan_prepare      = b53_vlan_prepare,
        .port_vlan_add          = b53_vlan_add,
        .port_vlan_del          = b53_vlan_del,
        .port_fdb_dump          = b53_fdb_dump,
@@ -2215,7 +2198,6 @@ static const struct dsa_switch_ops b53_switch_ops = {
        .port_fdb_del           = b53_fdb_del,
        .port_mirror_add        = b53_mirror_add,
        .port_mirror_del        = b53_mirror_del,
-       .port_mdb_prepare       = b53_mdb_prepare,
        .port_mdb_add           = b53_mdb_add,
        .port_mdb_del           = b53_mdb_del,
        .port_max_mtu           = b53_get_max_mtu,
@@ -2459,6 +2441,20 @@ static const struct b53_chip_data b53_switch_chips[] = {
                .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
                .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
        },
+       /* Starfighter 2 */
+       {
+               .chip_id = BCM4908_DEVICE_ID,
+               .dev_name = "BCM4908",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .arl_bins = 4,
+               .arl_buckets = 256,
+               .cpu_port = 8, /* TODO: ports 4, 5, 8 */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+       },
        {
                .chip_id = BCM7445_DEVICE_ID,
                .dev_name = "BCM7445",
@@ -2606,9 +2602,8 @@ struct b53_device *b53_switch_alloc(struct device *base,
        dev->priv = priv;
        dev->ops = ops;
        ds->ops = &b53_switch_ops;
-       ds->configure_vlan_while_not_filtering = true;
        ds->untag_bridge_pvid = true;
-       dev->vlan_enabled = ds->configure_vlan_while_not_filtering;
+       dev->vlan_enabled = true;
        mutex_init(&dev->reg_mutex);
        mutex_init(&dev->stats_mutex);
 
index 7c67409..0d2cc04 100644 (file)
@@ -64,6 +64,7 @@ struct b53_io_ops {
 #define B53_INVALID_LANE       0xff
 
 enum {
+       BCM4908_DEVICE_ID = 0x4908,
        BCM5325_DEVICE_ID = 0x25,
        BCM5365_DEVICE_ID = 0x65,
        BCM5389_DEVICE_ID = 0x89,
@@ -347,12 +348,9 @@ void b53_phylink_mac_link_up(struct dsa_switch *ds, int port,
                             struct phy_device *phydev,
                             int speed, int duplex,
                             bool tx_pause, bool rx_pause);
-int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
-                      struct switchdev_trans *trans);
-int b53_vlan_prepare(struct dsa_switch *ds, int port,
-                    const struct switchdev_obj_port_vlan *vlan);
-void b53_vlan_add(struct dsa_switch *ds, int port,
-                 const struct switchdev_obj_port_vlan *vlan);
+int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering);
+int b53_vlan_add(struct dsa_switch *ds, int port,
+                const struct switchdev_obj_port_vlan *vlan);
 int b53_vlan_del(struct dsa_switch *ds, int port,
                 const struct switchdev_obj_port_vlan *vlan);
 int b53_fdb_add(struct dsa_switch *ds, int port,
@@ -361,10 +359,8 @@ int b53_fdb_del(struct dsa_switch *ds, int port,
                const unsigned char *addr, u16 vid);
 int b53_fdb_dump(struct dsa_switch *ds, int port,
                 dsa_fdb_dump_cb_t *cb, void *data);
-int b53_mdb_prepare(struct dsa_switch *ds, int port,
-                   const struct switchdev_obj_port_mdb *mdb);
-void b53_mdb_add(struct dsa_switch *ds, int port,
-                const struct switchdev_obj_port_mdb *mdb);
+int b53_mdb_add(struct dsa_switch *ds, int port,
+               const struct switchdev_obj_port_mdb *mdb);
 int b53_mdb_del(struct dsa_switch *ds, int port,
                const struct switchdev_obj_port_mdb *mdb);
 int b53_mirror_add(struct dsa_switch *ds, int port,
index 1e9a0ad..1857aa9 100644 (file)
@@ -105,7 +105,8 @@ static void bcm_sf2_imp_setup(struct dsa_switch *ds, int port)
        b53_brcm_hdr_setup(ds, port);
 
        if (port == 8) {
-               if (priv->type == BCM7445_DEVICE_ID)
+               if (priv->type == BCM4908_DEVICE_ID ||
+                   priv->type == BCM7445_DEVICE_ID)
                        offset = CORE_STS_OVERRIDE_IMP;
                else
                        offset = CORE_STS_OVERRIDE_IMP2;
@@ -509,15 +510,19 @@ static int bcm_sf2_mdio_register(struct dsa_switch *ds)
        /* Find our integrated MDIO bus node */
        dn = of_find_compatible_node(NULL, NULL, "brcm,unimac-mdio");
        priv->master_mii_bus = of_mdio_find_bus(dn);
-       if (!priv->master_mii_bus)
+       if (!priv->master_mii_bus) {
+               of_node_put(dn);
                return -EPROBE_DEFER;
+       }
 
        get_device(&priv->master_mii_bus->dev);
        priv->master_mii_dn = dn;
 
        priv->slave_mii_bus = devm_mdiobus_alloc(ds->dev);
-       if (!priv->slave_mii_bus)
+       if (!priv->slave_mii_bus) {
+               of_node_put(dn);
                return -ENOMEM;
+       }
 
        priv->slave_mii_bus->priv = priv;
        priv->slave_mii_bus->name = "sf2 slave mii";
@@ -715,7 +720,8 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port,
        u32 reg, offset;
 
        if (port != core_readl(priv, CORE_IMP0_PRT_ID)) {
-               if (priv->type == BCM7445_DEVICE_ID)
+               if (priv->type == BCM4908_DEVICE_ID ||
+                   priv->type == BCM7445_DEVICE_ID)
                        offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
                else
                        offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
@@ -742,7 +748,8 @@ static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port,
        bcm_sf2_sw_mac_link_set(ds, port, interface, true);
 
        if (port != core_readl(priv, CORE_IMP0_PRT_ID)) {
-               if (priv->type == BCM7445_DEVICE_ID)
+               if (priv->type == BCM4908_DEVICE_ID ||
+                   priv->type == BCM7445_DEVICE_ID)
                        offset = CORE_STS_OVERRIDE_GMIIP_PORT(port);
                else
                        offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port);
@@ -1113,7 +1120,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
        .port_stp_state_set     = b53_br_set_stp_state,
        .port_fast_age          = b53_br_fast_age,
        .port_vlan_filtering    = b53_vlan_filtering,
-       .port_vlan_prepare      = b53_vlan_prepare,
        .port_vlan_add          = b53_vlan_add,
        .port_vlan_del          = b53_vlan_del,
        .port_fdb_dump          = b53_fdb_dump,
@@ -1123,7 +1129,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = {
        .set_rxnfc              = bcm_sf2_set_rxnfc,
        .port_mirror_add        = b53_mirror_add,
        .port_mirror_del        = b53_mirror_del,
-       .port_mdb_prepare       = b53_mdb_prepare,
        .port_mdb_add           = b53_mdb_add,
        .port_mdb_del           = b53_mdb_del,
 };
@@ -1135,6 +1140,30 @@ struct bcm_sf2_of_data {
        unsigned int num_cfp_rules;
 };
 
+static const u16 bcm_sf2_4908_reg_offsets[] = {
+       [REG_SWITCH_CNTRL]      = 0x00,
+       [REG_SWITCH_STATUS]     = 0x04,
+       [REG_DIR_DATA_WRITE]    = 0x08,
+       [REG_DIR_DATA_READ]     = 0x0c,
+       [REG_SWITCH_REVISION]   = 0x10,
+       [REG_PHY_REVISION]      = 0x14,
+       [REG_SPHY_CNTRL]        = 0x24,
+       [REG_CROSSBAR]          = 0xc8,
+       [REG_RGMII_0_CNTRL]     = 0xe0,
+       [REG_RGMII_1_CNTRL]     = 0xec,
+       [REG_RGMII_2_CNTRL]     = 0xf8,
+       [REG_LED_0_CNTRL]       = 0x40,
+       [REG_LED_1_CNTRL]       = 0x4c,
+       [REG_LED_2_CNTRL]       = 0x58,
+};
+
+static const struct bcm_sf2_of_data bcm_sf2_4908_data = {
+       .type           = BCM4908_DEVICE_ID,
+       .core_reg_align = 0,
+       .reg_offsets    = bcm_sf2_4908_reg_offsets,
+       .num_cfp_rules  = 0, /* FIXME */
+};
+
 /* Register offsets for the SWITCH_REG_* block */
 static const u16 bcm_sf2_7445_reg_offsets[] = {
        [REG_SWITCH_CNTRL]      = 0x00,
@@ -1183,6 +1212,9 @@ static const struct bcm_sf2_of_data bcm_sf2_7278_data = {
 };
 
 static const struct of_device_id bcm_sf2_of_match[] = {
+       { .compatible = "brcm,bcm4908-switch",
+         .data = &bcm_sf2_4908_data
+       },
        { .compatible = "brcm,bcm7445-switch-v4.0",
          .data = &bcm_sf2_7445_data
        },
index d82cee5..ed45d16 100644 (file)
@@ -885,18 +885,15 @@ static int bcm_sf2_cfp_rule_insert(struct dsa_switch *ds, int port,
                        return -EINVAL;
 
                vid = be16_to_cpu(fs->h_ext.vlan_tci) & VLAN_VID_MASK;
-               vlan.vid_begin = vid;
-               vlan.vid_end = vid;
+               vlan.vid = vid;
                if (cpu_to_be32(fs->h_ext.data[1]) & 1)
                        vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED;
                else
                        vlan.flags = 0;
 
-               ret = ds->ops->port_vlan_prepare(ds, port_num, &vlan);
+               ret = ds->ops->port_vlan_add(ds, port_num, &vlan);
                if (ret)
                        return ret;
-
-               ds->ops->port_vlan_add(ds, port_num, &vlan);
        }
 
        /*
@@ -942,8 +939,7 @@ static int bcm_sf2_cfp_rule_set(struct dsa_switch *ds, int port,
                return -EINVAL;
 
        if ((fs->flow_type & FLOW_EXT) &&
-           !(ds->ops->port_vlan_prepare || ds->ops->port_vlan_add ||
-             ds->ops->port_vlan_del))
+           !(ds->ops->port_vlan_add || ds->ops->port_vlan_del))
                return -EOPNOTSUPP;
 
        if (fs->location != RX_CLS_LOC_ANY &&
index d8a5e62..1d2d55c 100644 (file)
@@ -17,6 +17,7 @@ enum bcm_sf2_reg_offs {
        REG_SWITCH_REVISION,
        REG_PHY_REVISION,
        REG_SPHY_CNTRL,
+       REG_CROSSBAR,
        REG_RGMII_0_CNTRL,
        REG_RGMII_1_CNTRL,
        REG_RGMII_2_CNTRL,
index e38906a..8c283f5 100644 (file)
@@ -190,8 +190,7 @@ static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
 }
 
 static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                       bool vlan_filtering,
-                                       struct switchdev_trans *trans)
+                                       bool vlan_filtering)
 {
        dev_dbg(ds->dev, "%s: port: %d, vlan_filtering: %d\n",
                __func__, port, vlan_filtering);
@@ -199,53 +198,36 @@ static int dsa_loop_port_vlan_filtering(struct dsa_switch *ds, int port,
        return 0;
 }
 
-static int
-dsa_loop_port_vlan_prepare(struct dsa_switch *ds, int port,
-                          const struct switchdev_obj_port_vlan *vlan)
-{
-       struct dsa_loop_priv *ps = ds->priv;
-       struct mii_bus *bus = ps->bus;
-
-       dev_dbg(ds->dev, "%s: port: %d, vlan: %d-%d",
-               __func__, port, vlan->vid_begin, vlan->vid_end);
-
-       /* Just do a sleeping operation to make lockdep checks effective */
-       mdiobus_read(bus, ps->port_base + port, MII_BMSR);
-
-       if (vlan->vid_end > ARRAY_SIZE(ps->vlans))
-               return -ERANGE;
-
-       return 0;
-}
-
-static void dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,
-                                  const struct switchdev_obj_port_vlan *vlan)
+static int dsa_loop_port_vlan_add(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_vlan *vlan)
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        struct dsa_loop_priv *ps = ds->priv;
        struct mii_bus *bus = ps->bus;
        struct dsa_loop_vlan *vl;
-       u16 vid;
+
+       if (vlan->vid >= ARRAY_SIZE(ps->vlans))
+               return -ERANGE;
 
        /* Just do a sleeping operation to make lockdep checks effective */
        mdiobus_read(bus, ps->port_base + port, MII_BMSR);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               vl = &ps->vlans[vid];
+       vl = &ps->vlans[vlan->vid];
 
-               vl->members |= BIT(port);
-               if (untagged)
-                       vl->untagged |= BIT(port);
-               else
-                       vl->untagged &= ~BIT(port);
+       vl->members |= BIT(port);
+       if (untagged)
+               vl->untagged |= BIT(port);
+       else
+               vl->untagged &= ~BIT(port);
 
-               dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n",
-                       __func__, port, vid, untagged ? "un" : "", pvid);
-       }
+       dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n",
+               __func__, port, vlan->vid, untagged ? "un" : "", pvid);
 
        if (pvid)
-               ps->ports[port].pvid = vid;
+               ps->ports[port].pvid = vlan->vid;
+
+       return 0;
 }
 
 static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
@@ -253,26 +235,24 @@ static int dsa_loop_port_vlan_del(struct dsa_switch *ds, int port,
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct dsa_loop_priv *ps = ds->priv;
+       u16 pvid = ps->ports[port].pvid;
        struct mii_bus *bus = ps->bus;
        struct dsa_loop_vlan *vl;
-       u16 vid, pvid = ps->ports[port].pvid;
 
        /* Just do a sleeping operation to make lockdep checks effective */
        mdiobus_read(bus, ps->port_base + port, MII_BMSR);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               vl = &ps->vlans[vid];
+       vl = &ps->vlans[vlan->vid];
 
-               vl->members &= ~BIT(port);
-               if (untagged)
-                       vl->untagged &= ~BIT(port);
+       vl->members &= ~BIT(port);
+       if (untagged)
+               vl->untagged &= ~BIT(port);
 
-               if (pvid == vid)
-                       pvid = 1;
+       if (pvid == vlan->vid)
+               pvid = 1;
 
-               dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n",
-                       __func__, port, vid, untagged ? "un" : "", pvid);
-       }
+       dev_dbg(ds->dev, "%s: port: %d vlan: %d, %stagged, pvid: %d\n",
+               __func__, port, vlan->vid, untagged ? "un" : "", pvid);
        ps->ports[port].pvid = pvid;
 
        return 0;
@@ -307,7 +287,6 @@ static const struct dsa_switch_ops dsa_loop_driver = {
        .port_bridge_leave      = dsa_loop_port_bridge_leave,
        .port_stp_state_set     = dsa_loop_port_stp_state_set,
        .port_vlan_filtering    = dsa_loop_port_vlan_filtering,
-       .port_vlan_prepare      = dsa_loop_port_vlan_prepare,
        .port_vlan_add          = dsa_loop_port_vlan_add,
        .port_vlan_del          = dsa_loop_port_vlan_del,
        .port_change_mtu        = dsa_loop_port_change_mtu,
@@ -344,7 +323,6 @@ static int dsa_loop_drv_probe(struct mdio_device *mdiodev)
        ds->dev = &mdiodev->dev;
        ds->ops = &dsa_loop_driver;
        ds->priv = ps;
-       ds->configure_vlan_while_not_filtering = true;
        ps->bus = mdiodev->bus;
 
        dev_set_drvdata(&mdiodev->dev, ds);
index 222dd35..e011911 100644 (file)
@@ -4,6 +4,7 @@ config NET_DSA_HIRSCHMANN_HELLCREEK
        depends on HAS_IOMEM
        depends on NET_DSA
        depends on PTP_1588_CLOCK
+       depends on LEDS_CLASS
        select NET_DSA_TAG_HELLCREEK
        help
          This driver adds support for Hirschmann Hellcreek TSN switches.
index 6420b76..4cc51fb 100644 (file)
@@ -3,7 +3,7 @@
  * DSA driver for:
  * Hirschmann Hellcreek TSN switch.
  *
- * Copyright (C) 2019,2020 Linutronix GmbH
+ * Copyright (C) 2019-2021 Linutronix GmbH
  * Author Kurt Kanzenbach <kurt@linutronix.de>
  */
 
@@ -153,6 +153,13 @@ static void hellcreek_select_vlan(struct hellcreek *hellcreek, int vid,
        hellcreek_write(hellcreek, val, HR_VIDCFG);
 }
 
+static void hellcreek_select_tgd(struct hellcreek *hellcreek, int port)
+{
+       u16 val = port << TR_TGDSEL_TDGSEL_SHIFT;
+
+       hellcreek_write(hellcreek, val, TR_TGDSEL);
+}
+
 static int hellcreek_wait_until_ready(struct hellcreek *hellcreek)
 {
        u16 val;
@@ -348,14 +355,12 @@ static int hellcreek_vlan_prepare(struct dsa_switch *ds, int port,
         */
        for (i = 0; i < hellcreek->pdata->num_ports; ++i) {
                const u16 restricted_vid = hellcreek_private_vid(i);
-               u16 vid;
 
                if (!dsa_is_user_port(ds, i))
                        continue;
 
-               for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
-                       if (vid == restricted_vid)
-                               return -EBUSY;
+               if (vlan->vid == restricted_vid)
+                       return -EBUSY;
        }
 
        return 0;
@@ -440,34 +445,35 @@ static void hellcreek_unapply_vlan(struct hellcreek *hellcreek, int port,
        mutex_unlock(&hellcreek->reg_lock);
 }
 
-static void hellcreek_vlan_add(struct dsa_switch *ds, int port,
-                              const struct switchdev_obj_port_vlan *vlan)
+static int hellcreek_vlan_add(struct dsa_switch *ds, int port,
+                             const struct switchdev_obj_port_vlan *vlan)
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        struct hellcreek *hellcreek = ds->priv;
-       u16 vid;
+       int err;
+
+       err = hellcreek_vlan_prepare(ds, port, vlan);
+       if (err)
+               return err;
 
-       dev_dbg(hellcreek->dev, "Add VLANs (%d -- %d) on port %d, %s, %s\n",
-               vlan->vid_begin, vlan->vid_end, port,
-               untagged ? "untagged" : "tagged",
+       dev_dbg(hellcreek->dev, "Add VLAN %d on port %d, %s, %s\n",
+               vlan->vid, port, untagged ? "untagged" : "tagged",
                pvid ? "PVID" : "no PVID");
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
-               hellcreek_apply_vlan(hellcreek, port, vid, pvid, untagged);
+       hellcreek_apply_vlan(hellcreek, port, vlan->vid, pvid, untagged);
+
+       return 0;
 }
 
 static int hellcreek_vlan_del(struct dsa_switch *ds, int port,
                              const struct switchdev_obj_port_vlan *vlan)
 {
        struct hellcreek *hellcreek = ds->priv;
-       u16 vid;
 
-       dev_dbg(hellcreek->dev, "Remove VLANs (%d -- %d) on port %d\n",
-               vlan->vid_begin, vlan->vid_end, port);
+       dev_dbg(hellcreek->dev, "Remove VLAN %d on port %d\n", vlan->vid, port);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
-               hellcreek_unapply_vlan(hellcreek, port, vid);
+       hellcreek_unapply_vlan(hellcreek, port, vlan->vid);
 
        return 0;
 }
@@ -866,14 +872,10 @@ static int hellcreek_fdb_dump(struct dsa_switch *ds, int port,
 }
 
 static int hellcreek_vlan_filtering(struct dsa_switch *ds, int port,
-                                   bool vlan_filtering,
-                                   struct switchdev_trans *trans)
+                                   bool vlan_filtering)
 {
        struct hellcreek *hellcreek = ds->priv;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        dev_dbg(hellcreek->dev, "%s VLAN filtering on port %d\n",
                vlan_filtering ? "Enable" : "Disable", port);
 
@@ -1038,11 +1040,6 @@ static int hellcreek_setup(struct dsa_switch *ds)
        /* Configure PCP <-> TC mapping */
        hellcreek_setup_tc_identity_mapping(hellcreek);
 
-       /* Allow VLAN configurations while not filtering which is the default
-        * for new DSA drivers.
-        */
-       ds->configure_vlan_while_not_filtering = true;
-
        /* The VLAN awareness is a global switch setting. Therefore, mixed vlan
         * filtering setups are not supported.
         */
@@ -1135,6 +1132,296 @@ out:
        return ret;
 }
 
+static void hellcreek_setup_gcl(struct hellcreek *hellcreek, int port,
+                               const struct tc_taprio_qopt_offload *schedule)
+{
+       const struct tc_taprio_sched_entry *cur, *initial, *next;
+       size_t i;
+
+       cur = initial = &schedule->entries[0];
+       next = cur + 1;
+
+       for (i = 1; i <= schedule->num_entries; ++i) {
+               u16 data;
+               u8 gates;
+
+               cur++;
+               next++;
+
+               if (i == schedule->num_entries)
+                       gates = initial->gate_mask ^
+                               cur->gate_mask;
+               else
+                       gates = next->gate_mask ^
+                               cur->gate_mask;
+
+               data = gates;
+
+               if (i == schedule->num_entries)
+                       data |= TR_GCLDAT_GCLWRLAST;
+
+               /* Gates states */
+               hellcreek_write(hellcreek, data, TR_GCLDAT);
+
+               /* Time interval */
+               hellcreek_write(hellcreek,
+                               cur->interval & 0x0000ffff,
+                               TR_GCLTIL);
+               hellcreek_write(hellcreek,
+                               (cur->interval & 0xffff0000) >> 16,
+                               TR_GCLTIH);
+
+               /* Commit entry */
+               data = ((i - 1) << TR_GCLCMD_GCLWRADR_SHIFT) |
+                       (initial->gate_mask <<
+                        TR_GCLCMD_INIT_GATE_STATES_SHIFT);
+               hellcreek_write(hellcreek, data, TR_GCLCMD);
+       }
+}
+
+static void hellcreek_set_cycle_time(struct hellcreek *hellcreek,
+                                    const struct tc_taprio_qopt_offload *schedule)
+{
+       u32 cycle_time = schedule->cycle_time;
+
+       hellcreek_write(hellcreek, cycle_time & 0x0000ffff, TR_CTWRL);
+       hellcreek_write(hellcreek, (cycle_time & 0xffff0000) >> 16, TR_CTWRH);
+}
+
+static void hellcreek_switch_schedule(struct hellcreek *hellcreek,
+                                     ktime_t start_time)
+{
+       struct timespec64 ts = ktime_to_timespec64(start_time);
+
+       /* Start schedule at this point of time */
+       hellcreek_write(hellcreek, ts.tv_nsec & 0x0000ffff, TR_ESTWRL);
+       hellcreek_write(hellcreek, (ts.tv_nsec & 0xffff0000) >> 16, TR_ESTWRH);
+
+       /* Arm timer, set seconds and switch schedule */
+       hellcreek_write(hellcreek, TR_ESTCMD_ESTARM | TR_ESTCMD_ESTSWCFG |
+                       ((ts.tv_sec & TR_ESTCMD_ESTSEC_MASK) <<
+                        TR_ESTCMD_ESTSEC_SHIFT), TR_ESTCMD);
+}
+
+static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int port)
+{
+       struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+       s64 base_time_ns, current_ns;
+
+       /* The switch allows a schedule to be started only eight seconds within
+        * the future. Therefore, check the current PTP time if the schedule is
+        * startable or not.
+        */
+
+       /* Use the "cached" time. That should be alright, as it's updated quite
+        * frequently in the PTP code.
+        */
+       mutex_lock(&hellcreek->ptp_lock);
+       current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
+       mutex_unlock(&hellcreek->ptp_lock);
+
+       /* Calculate difference to admin base time */
+       base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time);
+
+       return base_time_ns - current_ns < (s64)8 * NSEC_PER_SEC;
+}
+
+static void hellcreek_start_schedule(struct hellcreek *hellcreek, int port)
+{
+       struct hellcreek_port *hellcreek_port = &hellcreek->ports[port];
+       ktime_t base_time, current_time;
+       s64 current_ns;
+       u32 cycle_time;
+
+       /* First select port */
+       hellcreek_select_tgd(hellcreek, port);
+
+       /* Forward base time into the future if needed */
+       mutex_lock(&hellcreek->ptp_lock);
+       current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts;
+       mutex_unlock(&hellcreek->ptp_lock);
+
+       current_time = ns_to_ktime(current_ns);
+       base_time    = hellcreek_port->current_schedule->base_time;
+       cycle_time   = hellcreek_port->current_schedule->cycle_time;
+
+       if (ktime_compare(current_time, base_time) > 0) {
+               s64 n;
+
+               n = div64_s64(ktime_sub_ns(current_time, base_time),
+                             cycle_time);
+               base_time = ktime_add_ns(base_time, (n + 1) * cycle_time);
+       }
+
+       /* Set admin base time and switch schedule */
+       hellcreek_switch_schedule(hellcreek, base_time);
+
+       taprio_offload_free(hellcreek_port->current_schedule);
+       hellcreek_port->current_schedule = NULL;
+
+       dev_dbg(hellcreek->dev, "Armed EST timer for port %d\n",
+               hellcreek_port->port);
+}
+
+static void hellcreek_check_schedule(struct work_struct *work)
+{
+       struct delayed_work *dw = to_delayed_work(work);
+       struct hellcreek_port *hellcreek_port;
+       struct hellcreek *hellcreek;
+       bool startable;
+
+       hellcreek_port = dw_to_hellcreek_port(dw);
+       hellcreek = hellcreek_port->hellcreek;
+
+       mutex_lock(&hellcreek->reg_lock);
+
+       /* Check starting time */
+       startable = hellcreek_schedule_startable(hellcreek,
+                                                hellcreek_port->port);
+       if (startable) {
+               hellcreek_start_schedule(hellcreek, hellcreek_port->port);
+               mutex_unlock(&hellcreek->reg_lock);
+               return;
+       }
+
+       mutex_unlock(&hellcreek->reg_lock);
+
+       /* Reschedule */
+       schedule_delayed_work(&hellcreek_port->schedule_work,
+                             HELLCREEK_SCHEDULE_PERIOD);
+}
+
+static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port,
+                                      struct tc_taprio_qopt_offload *taprio)
+{
+       struct hellcreek *hellcreek = ds->priv;
+       struct hellcreek_port *hellcreek_port;
+       bool startable;
+       u16 ctrl;
+
+       hellcreek_port = &hellcreek->ports[port];
+
+       dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n",
+               port);
+
+       /* First cancel delayed work */
+       cancel_delayed_work_sync(&hellcreek_port->schedule_work);
+
+       mutex_lock(&hellcreek->reg_lock);
+
+       if (hellcreek_port->current_schedule) {
+               taprio_offload_free(hellcreek_port->current_schedule);
+               hellcreek_port->current_schedule = NULL;
+       }
+       hellcreek_port->current_schedule = taprio_offload_get(taprio);
+
+       /* Then select port */
+       hellcreek_select_tgd(hellcreek, port);
+
+       /* Enable gating and keep defaults */
+       ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN;
+       hellcreek_write(hellcreek, ctrl, TR_TGDCTRL);
+
+       /* Cancel pending schedule */
+       hellcreek_write(hellcreek, 0x00, TR_ESTCMD);
+
+       /* Setup a new schedule */
+       hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule);
+
+       /* Configure cycle time */
+       hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule);
+
+       /* Check starting time */
+       startable = hellcreek_schedule_startable(hellcreek, port);
+       if (startable) {
+               hellcreek_start_schedule(hellcreek, port);
+               mutex_unlock(&hellcreek->reg_lock);
+               return 0;
+       }
+
+       mutex_unlock(&hellcreek->reg_lock);
+
+       /* Schedule periodic schedule check */
+       schedule_delayed_work(&hellcreek_port->schedule_work,
+                             HELLCREEK_SCHEDULE_PERIOD);
+
+       return 0;
+}
+
+static int hellcreek_port_del_schedule(struct dsa_switch *ds, int port)
+{
+       struct hellcreek *hellcreek = ds->priv;
+       struct hellcreek_port *hellcreek_port;
+
+       hellcreek_port = &hellcreek->ports[port];
+
+       dev_dbg(hellcreek->dev, "Remove traffic schedule on port %d\n", port);
+
+       /* First cancel delayed work */
+       cancel_delayed_work_sync(&hellcreek_port->schedule_work);
+
+       mutex_lock(&hellcreek->reg_lock);
+
+       if (hellcreek_port->current_schedule) {
+               taprio_offload_free(hellcreek_port->current_schedule);
+               hellcreek_port->current_schedule = NULL;
+       }
+
+       /* Then select port */
+       hellcreek_select_tgd(hellcreek, port);
+
+       /* Disable gating and return to regular switching flow */
+       hellcreek_write(hellcreek, 0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT,
+                       TR_TGDCTRL);
+
+       mutex_unlock(&hellcreek->reg_lock);
+
+       return 0;
+}
+
+static bool hellcreek_validate_schedule(struct hellcreek *hellcreek,
+                                       struct tc_taprio_qopt_offload *schedule)
+{
+       size_t i;
+
+       /* Does this hellcreek version support Qbv in hardware? */
+       if (!hellcreek->pdata->qbv_support)
+               return false;
+
+       /* cycle time can only be 32bit */
+       if (schedule->cycle_time > (u32)-1)
+               return false;
+
+       /* cycle time extension is not supported */
+       if (schedule->cycle_time_extension)
+               return false;
+
+       /* Only set command is supported */
+       for (i = 0; i < schedule->num_entries; ++i)
+               if (schedule->entries[i].command != TC_TAPRIO_CMD_SET_GATES)
+                       return false;
+
+       return true;
+}
+
+static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port,
+                                  enum tc_setup_type type, void *type_data)
+{
+       struct tc_taprio_qopt_offload *taprio = type_data;
+       struct hellcreek *hellcreek = ds->priv;
+
+       if (type != TC_SETUP_QDISC_TAPRIO)
+               return -EOPNOTSUPP;
+
+       if (!hellcreek_validate_schedule(hellcreek, taprio))
+               return -EOPNOTSUPP;
+
+       if (taprio->enable)
+               return hellcreek_port_set_schedule(ds, port, taprio);
+
+       return hellcreek_port_del_schedule(ds, port);
+}
+
 static const struct dsa_switch_ops hellcreek_ds_ops = {
        .get_ethtool_stats   = hellcreek_get_ethtool_stats,
        .get_sset_count      = hellcreek_get_sset_count,
@@ -1153,12 +1440,12 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
        .port_hwtstamp_get   = hellcreek_port_hwtstamp_get,
        .port_prechangeupper = hellcreek_port_prechangeupper,
        .port_rxtstamp       = hellcreek_port_rxtstamp,
+       .port_setup_tc       = hellcreek_port_setup_tc,
        .port_stp_state_set  = hellcreek_port_stp_state_set,
        .port_txtstamp       = hellcreek_port_txtstamp,
        .port_vlan_add       = hellcreek_vlan_add,
        .port_vlan_del       = hellcreek_vlan_del,
        .port_vlan_filtering = hellcreek_vlan_filtering,
-       .port_vlan_prepare   = hellcreek_vlan_prepare,
        .setup               = hellcreek_setup,
 };
 
@@ -1208,6 +1495,9 @@ static int hellcreek_probe(struct platform_device *pdev)
 
                port->hellcreek = hellcreek;
                port->port      = i;
+
+               INIT_DELAYED_WORK(&port->schedule_work,
+                                 hellcreek_check_schedule);
        }
 
        mutex_init(&hellcreek->reg_lock);
index e81781e..854639f 100644 (file)
@@ -3,7 +3,7 @@
  * DSA driver for:
  * Hirschmann Hellcreek TSN switch.
  *
- * Copyright (C) 2019,2020 Linutronix GmbH
+ * Copyright (C) 2019-2021 Linutronix GmbH
  * Author Kurt Kanzenbach <kurt@linutronix.de>
  */
 
@@ -21,6 +21,7 @@
 #include <linux/ptp_clock_kernel.h>
 #include <linux/timecounter.h>
 #include <net/dsa.h>
+#include <net/pkt_sched.h>
 
 /* Ports:
  *  - 0: CPU
@@ -246,6 +247,10 @@ struct hellcreek_port {
 
        /* Per-port timestamping resources */
        struct hellcreek_port_hwtstamp port_hwtstamp;
+
+       /* Per-port Qbv schedule information */
+       struct tc_taprio_qopt_offload *current_schedule;
+       struct delayed_work schedule_work;
 };
 
 struct hellcreek_fdb_entry {
@@ -283,4 +288,14 @@ struct hellcreek {
        size_t fdb_entries;
 };
 
+/* A Qbv schedule can only started up to 8 seconds in the future. If the delta
+ * between the base time and the current ptp time is larger than 8 seconds, then
+ * use periodic work to check for the schedule to be started. The delayed work
+ * cannot be armed directly to $base_time - 8 + X, because for large deltas the
+ * PTP frequency matters.
+ */
+#define HELLCREEK_SCHEDULE_PERIOD      (2 * HZ)
+#define dw_to_hellcreek_port(dw)                               \
+       container_of(dw, struct hellcreek_port, schedule_work)
+
 #endif /* _HELLCREEK_H_ */
index aa1142d..3443740 100644 (file)
@@ -1232,14 +1232,19 @@ static int lan9303_port_mdb_prepare(struct dsa_switch *ds, int port,
        return 0;
 }
 
-static void lan9303_port_mdb_add(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_mdb *mdb)
+static int lan9303_port_mdb_add(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb)
 {
        struct lan9303 *chip = ds->priv;
+       int err;
+
+       err = lan9303_port_mdb_prepare(ds, port, mdb);
+       if (err)
+               return err;
 
        dev_dbg(chip->dev, "%s(%d, %pM, %d)\n", __func__, port, mdb->addr,
                mdb->vid);
-       lan9303_alr_add_port(chip, mdb->addr, port, false);
+       return lan9303_alr_add_port(chip, mdb->addr, port, false);
 }
 
 static int lan9303_port_mdb_del(struct dsa_switch *ds, int port,
@@ -1274,7 +1279,6 @@ static const struct dsa_switch_ops lan9303_switch_ops = {
        .port_fdb_add           = lan9303_port_fdb_add,
        .port_fdb_del           = lan9303_port_fdb_del,
        .port_fdb_dump          = lan9303_port_fdb_dump,
-       .port_mdb_prepare       = lan9303_port_mdb_prepare,
        .port_mdb_add           = lan9303_port_mdb_add,
        .port_mdb_del           = lan9303_port_mdb_del,
 };
index 09701c1..9fec977 100644 (file)
@@ -92,9 +92,7 @@
                                         GSWIP_MDIO_PHY_FDUP_MASK)
 
 /* GSWIP MII Registers */
-#define GSWIP_MII_CFG0                 0x00
-#define GSWIP_MII_CFG1                 0x02
-#define GSWIP_MII_CFG5                 0x04
+#define GSWIP_MII_CFGp(p)              (0x2 * (p))
 #define  GSWIP_MII_CFG_EN              BIT(14)
 #define  GSWIP_MII_CFG_LDCLKDIS                BIT(12)
 #define  GSWIP_MII_CFG_MODE_MIIP       0x0
@@ -392,17 +390,9 @@ static void gswip_mii_mask(struct gswip_priv *priv, u32 clear, u32 set,
 static void gswip_mii_mask_cfg(struct gswip_priv *priv, u32 clear, u32 set,
                               int port)
 {
-       switch (port) {
-       case 0:
-               gswip_mii_mask(priv, clear, set, GSWIP_MII_CFG0);
-               break;
-       case 1:
-               gswip_mii_mask(priv, clear, set, GSWIP_MII_CFG1);
-               break;
-       case 5:
-               gswip_mii_mask(priv, clear, set, GSWIP_MII_CFG5);
-               break;
-       }
+       /* There's no MII_CFG register for the CPU port */
+       if (!dsa_is_cpu_port(priv->ds, port))
+               gswip_mii_mask(priv, clear, set, GSWIP_MII_CFGp(port));
 }
 
 static void gswip_mii_mask_pcdu(struct gswip_priv *priv, u32 clear, u32 set,
@@ -737,23 +727,14 @@ static int gswip_pce_load_microcode(struct gswip_priv *priv)
 }
 
 static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                    bool vlan_filtering,
-                                    struct switchdev_trans *trans)
+                                    bool vlan_filtering)
 {
+       struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
        struct gswip_priv *priv = ds->priv;
 
        /* Do not allow changing the VLAN filtering options while in bridge */
-       if (switchdev_trans_ph_prepare(trans)) {
-               struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
-
-               if (!bridge)
-                       return 0;
-
-               if (!!(priv->port_vlan_filter & BIT(port)) != vlan_filtering)
-                       return -EIO;
-
-               return 0;
-       }
+       if (bridge && !!(priv->port_vlan_filter & BIT(port)) != vlan_filtering)
+               return -EIO;
 
        if (vlan_filtering) {
                /* Use port based VLAN tag */
@@ -791,15 +772,8 @@ static int gswip_setup(struct dsa_switch *ds)
 
        /* disable port fetch/store dma on all ports */
        for (i = 0; i < priv->hw_info->max_ports; i++) {
-               struct switchdev_trans trans;
-
-               /* Skip the prepare phase, this shouldn't return an error
-                * during setup.
-                */
-               trans.ph_prepare = false;
-
                gswip_port_disable(ds, i);
-               gswip_port_vlan_filtering(ds, i, false, &trans);
+               gswip_port_vlan_filtering(ds, i, false);
        }
 
        /* enable Switch */
@@ -822,9 +796,8 @@ static int gswip_setup(struct dsa_switch *ds)
        gswip_mdio_mask(priv, 0xff, 0x09, GSWIP_MDIO_MDC_CFG1);
 
        /* Disable the xMII link */
-       gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, 0);
-       gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, 1);
-       gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, 5);
+       for (i = 0; i < priv->hw_info->max_ports; i++)
+               gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, i);
 
        /* enable special tag insertion on cpu port */
        gswip_switch_mask(priv, 0, GSWIP_FDMA_PCTRL_STEN,
@@ -854,6 +827,9 @@ static int gswip_setup(struct dsa_switch *ds)
        }
 
        gswip_port_enable(ds, cpu_port, NULL);
+
+       ds->configure_vlan_while_not_filtering = false;
+
        return 0;
 }
 
@@ -1157,56 +1133,55 @@ static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,
        struct gswip_priv *priv = ds->priv;
        struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
        unsigned int max_ports = priv->hw_info->max_ports;
-       u16 vid;
-       int i;
        int pos = max_ports;
+       int i, idx = -1;
 
        /* We only support VLAN filtering on bridges */
        if (!dsa_is_cpu_port(ds, port) && !bridge)
                return -EOPNOTSUPP;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               int idx = -1;
+       /* Check if there is already a page for this VLAN */
+       for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
+               if (priv->vlans[i].bridge == bridge &&
+                   priv->vlans[i].vid == vlan->vid) {
+                       idx = i;
+                       break;
+               }
+       }
 
-               /* Check if there is already a page for this VLAN */
-               for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
-                       if (priv->vlans[i].bridge == bridge &&
-                           priv->vlans[i].vid == vid) {
-                               idx = i;
+       /* If this VLAN is not programmed yet, we have to reserve
+        * one entry in the VLAN table. Make sure we start at the
+        * next position round.
+        */
+       if (idx == -1) {
+               /* Look for a free slot */
+               for (; pos < ARRAY_SIZE(priv->vlans); pos++) {
+                       if (!priv->vlans[pos].bridge) {
+                               idx = pos;
+                               pos++;
                                break;
                        }
                }
 
-               /* If this VLAN is not programmed yet, we have to reserve
-                * one entry in the VLAN table. Make sure we start at the
-                * next position round.
-                */
-               if (idx == -1) {
-                       /* Look for a free slot */
-                       for (; pos < ARRAY_SIZE(priv->vlans); pos++) {
-                               if (!priv->vlans[pos].bridge) {
-                                       idx = pos;
-                                       pos++;
-                                       break;
-                               }
-                       }
-
-                       if (idx == -1)
-                               return -ENOSPC;
-               }
+               if (idx == -1)
+                       return -ENOSPC;
        }
 
        return 0;
 }
 
-static void gswip_port_vlan_add(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_vlan *vlan)
+static int gswip_port_vlan_add(struct dsa_switch *ds, int port,
+                              const struct switchdev_obj_port_vlan *vlan)
 {
        struct gswip_priv *priv = ds->priv;
        struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-       u16 vid;
+       int err;
+
+       err = gswip_port_vlan_prepare(ds, port, vlan);
+       if (err)
+               return err;
 
        /* We have to receive all packets on the CPU port and should not
         * do any VLAN filtering here. This is also called with bridge
@@ -1214,10 +1189,10 @@ static void gswip_port_vlan_add(struct dsa_switch *ds, int port,
         * this.
         */
        if (dsa_is_cpu_port(ds, port))
-               return;
+               return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
-               gswip_vlan_add_aware(priv, bridge, port, vid, untagged, pvid);
+       return gswip_vlan_add_aware(priv, bridge, port, vlan->vid,
+                                   untagged, pvid);
 }
 
 static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
@@ -1226,8 +1201,6 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
        struct gswip_priv *priv = ds->priv;
        struct net_device *bridge = dsa_to_port(ds, port)->bridge_dev;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-       u16 vid;
-       int err;
 
        /* We have to receive all packets on the CPU port and should not
         * do any VLAN filtering here. This is also called with bridge
@@ -1237,13 +1210,7 @@ static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
        if (dsa_is_cpu_port(ds, port))
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               err = gswip_vlan_remove(priv, bridge, port, vid, pvid, true);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return gswip_vlan_remove(priv, bridge, port, vlan->vid, pvid, true);
 }
 
 static void gswip_port_fast_age(struct dsa_switch *ds, int port)
@@ -1447,11 +1414,12 @@ static void gswip_phylink_validate(struct dsa_switch *ds, int port,
        phylink_set(mask, Pause);
        phylink_set(mask, Asym_Pause);
 
-       /* With the exclusion of MII and Reverse MII, we support Gigabit,
-        * including Half duplex
+       /* With the exclusion of MII, Reverse MII and Reduced MII, we
+        * support Gigabit, including Half duplex
         */
        if (state->interface != PHY_INTERFACE_MODE_MII &&
-           state->interface != PHY_INTERFACE_MODE_REVMII) {
+           state->interface != PHY_INTERFACE_MODE_REVMII &&
+           state->interface != PHY_INTERFACE_MODE_RMII) {
                phylink_set(mask, 1000baseT_Full);
                phylink_set(mask, 1000baseT_Half);
        }
@@ -1541,9 +1509,7 @@ static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port,
 {
        struct gswip_priv *priv = ds->priv;
 
-       /* Enable the xMII interface only for the external PHY */
-       if (interface != PHY_INTERFACE_MODE_INTERNAL)
-               gswip_mii_mask_cfg(priv, 0, GSWIP_MII_CFG_EN, port);
+       gswip_mii_mask_cfg(priv, 0, GSWIP_MII_CFG_EN, port);
 }
 
 static void gswip_get_strings(struct dsa_switch *ds, int port, u32 stringset,
@@ -1623,7 +1589,6 @@ static const struct dsa_switch_ops gswip_switch_ops = {
        .port_bridge_leave      = gswip_port_bridge_leave,
        .port_fast_age          = gswip_port_fast_age,
        .port_vlan_filtering    = gswip_port_vlan_filtering,
-       .port_vlan_prepare      = gswip_port_vlan_prepare,
        .port_vlan_add          = gswip_port_vlan_add,
        .port_vlan_del          = gswip_port_vlan_del,
        .port_stp_state_set     = gswip_port_stp_state_set,
index c973db1..c87d445 100644 (file)
@@ -783,55 +783,53 @@ static void ksz8795_flush_dyn_mac_table(struct ksz_device *dev, int port)
 }
 
 static int ksz8795_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                      bool flag,
-                                      struct switchdev_trans *trans)
+                                      bool flag)
 {
        struct ksz_device *dev = ds->priv;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        ksz_cfg(dev, S_MIRROR_CTRL, SW_VLAN_ENABLE, flag);
 
        return 0;
 }
 
-static void ksz8795_port_vlan_add(struct dsa_switch *ds, int port,
-                                 const struct switchdev_obj_port_vlan *vlan)
+static int ksz8795_port_vlan_add(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_vlan *vlan)
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct ksz_device *dev = ds->priv;
-       u16 data, vid, new_pvid = 0;
+       u16 data, new_pvid = 0;
        u8 fid, member, valid;
 
        ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               ksz8795_r_vlan_table(dev, vid, &data);
-               ksz8795_from_vlan(data, &fid, &member, &valid);
+       ksz8795_r_vlan_table(dev, vlan->vid, &data);
+       ksz8795_from_vlan(data, &fid, &member, &valid);
 
-               /* First time to setup the VLAN entry. */
-               if (!valid) {
-                       /* Need to find a way to map VID to FID. */
-                       fid = 1;
-                       valid = 1;
-               }
-               member |= BIT(port);
+       /* First time to setup the VLAN entry. */
+       if (!valid) {
+               /* Need to find a way to map VID to FID. */
+               fid = 1;
+               valid = 1;
+       }
+       member |= BIT(port);
 
-               ksz8795_to_vlan(fid, member, valid, &data);
-               ksz8795_w_vlan_table(dev, vid, data);
+       ksz8795_to_vlan(fid, member, valid, &data);
+       ksz8795_w_vlan_table(dev, vlan->vid, data);
 
-               /* change PVID */
-               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
-                       new_pvid = vid;
-       }
+       /* change PVID */
+       if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+               new_pvid = vlan->vid;
 
        if (new_pvid) {
+               u16 vid;
+
                ksz_pread16(dev, port, REG_PORT_CTRL_VID, &vid);
                vid &= 0xfff;
                vid |= new_pvid;
                ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, vid);
        }
+
+       return 0;
 }
 
 static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port,
@@ -839,7 +837,7 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port,
 {
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct ksz_device *dev = ds->priv;
-       u16 data, vid, pvid, new_pvid = 0;
+       u16 data, pvid, new_pvid = 0;
        u8 fid, member, valid;
 
        ksz_pread16(dev, port, REG_PORT_CTRL_VID, &pvid);
@@ -847,24 +845,22 @@ static int ksz8795_port_vlan_del(struct dsa_switch *ds, int port,
 
        ksz_port_cfg(dev, port, P_TAG_CTRL, PORT_REMOVE_TAG, untagged);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               ksz8795_r_vlan_table(dev, vid, &data);
-               ksz8795_from_vlan(data, &fid, &member, &valid);
+       ksz8795_r_vlan_table(dev, vlan->vid, &data);
+       ksz8795_from_vlan(data, &fid, &member, &valid);
 
-               member &= ~BIT(port);
+       member &= ~BIT(port);
 
-               /* Invalidate the entry if no more member. */
-               if (!member) {
-                       fid = 0;
-                       valid = 0;
-               }
+       /* Invalidate the entry if no more member. */
+       if (!member) {
+               fid = 0;
+               valid = 0;
+       }
 
-               if (pvid == vid)
-                       new_pvid = 1;
+       if (pvid == vlan->vid)
+               new_pvid = 1;
 
-               ksz8795_to_vlan(fid, member, valid, &data);
-               ksz8795_w_vlan_table(dev, vid, data);
-       }
+       ksz8795_to_vlan(fid, member, valid, &data);
+       ksz8795_w_vlan_table(dev, vlan->vid, data);
 
        if (new_pvid != pvid)
                ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, pvid);
@@ -1098,6 +1094,8 @@ static int ksz8795_setup(struct dsa_switch *ds)
 
        ksz_init_mib_timer(dev);
 
+       ds->configure_vlan_while_not_filtering = false;
+
        return 0;
 }
 
@@ -1116,11 +1114,9 @@ static const struct dsa_switch_ops ksz8795_switch_ops = {
        .port_stp_state_set     = ksz8795_port_stp_state_set,
        .port_fast_age          = ksz_port_fast_age,
        .port_vlan_filtering    = ksz8795_port_vlan_filtering,
-       .port_vlan_prepare      = ksz_port_vlan_prepare,
        .port_vlan_add          = ksz8795_port_vlan_add,
        .port_vlan_del          = ksz8795_port_vlan_del,
        .port_fdb_dump          = ksz_port_fdb_dump,
-       .port_mdb_prepare       = ksz_port_mdb_prepare,
        .port_mdb_add           = ksz_port_mdb_add,
        .port_mdb_del           = ksz_port_mdb_del,
        .port_mirror_add        = ksz8795_port_mirror_add,
@@ -1187,6 +1183,20 @@ static const struct ksz_chip_data ksz8795_switch_chips[] = {
                .port_cnt = 5,          /* total cpu and user ports */
        },
        {
+               /*
+                * WARNING
+                * =======
+                * KSZ8794 is similar to KSZ8795, except the port map
+                * contains a gap between external and CPU ports, the
+                * port map is NOT continuous. The per-port register
+                * map is shifted accordingly too, i.e. registers at
+                * offset 0x40 are NOT used on KSZ8794 and they ARE
+                * used on KSZ8795 for external port 3.
+                *           external  cpu
+                * KSZ8794   0,1,2      4
+                * KSZ8795   0,1,2,3    4
+                * KSZ8765   0,1,2,3    4
+                */
                .chip_id = 0x8794,
                .dev_name = "KSZ8794",
                .num_vlans = 4096,
@@ -1220,9 +1230,13 @@ static int ksz8795_switch_init(struct ksz_device *dev)
                        dev->num_vlans = chip->num_vlans;
                        dev->num_alus = chip->num_alus;
                        dev->num_statics = chip->num_statics;
-                       dev->port_cnt = chip->port_cnt;
+                       dev->port_cnt = fls(chip->cpu_ports);
+                       dev->cpu_port = fls(chip->cpu_ports) - 1;
+                       dev->phy_port_cnt = dev->port_cnt - 1;
                        dev->cpu_ports = chip->cpu_ports;
-
+                       dev->host_mask = chip->cpu_ports;
+                       dev->port_mask = (BIT(dev->phy_port_cnt) - 1) |
+                                        chip->cpu_ports;
                        break;
                }
        }
@@ -1231,17 +1245,9 @@ static int ksz8795_switch_init(struct ksz_device *dev)
        if (!dev->cpu_ports)
                return -ENODEV;
 
-       dev->port_mask = BIT(dev->port_cnt) - 1;
-       dev->port_mask |= dev->host_mask;
-
        dev->reg_mib_cnt = KSZ8795_COUNTER_NUM;
        dev->mib_cnt = ARRAY_SIZE(mib_names);
 
-       dev->phy_port_cnt = dev->port_cnt - 1;
-
-       dev->cpu_port = dev->port_cnt - 1;
-       dev->host_mask = BIT(dev->cpu_port);
-
        dev->ports = devm_kzalloc(dev->dev,
                                  dev->port_cnt * sizeof(struct ksz_port),
                                  GFP_KERNEL);
index 42e647b..00e38c8 100644 (file)
@@ -493,14 +493,10 @@ static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port)
 }
 
 static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                      bool flag,
-                                      struct switchdev_trans *trans)
+                                      bool flag)
 {
        struct ksz_device *dev = ds->priv;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        if (flag) {
                ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL,
                             PORT_VLAN_LOOKUP_VID_0, true);
@@ -514,38 +510,40 @@ static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port,
        return 0;
 }
 
-static void ksz9477_port_vlan_add(struct dsa_switch *ds, int port,
-                                 const struct switchdev_obj_port_vlan *vlan)
+static int ksz9477_port_vlan_add(struct dsa_switch *ds, int port,
+                                const struct switchdev_obj_port_vlan *vlan)
 {
        struct ksz_device *dev = ds->priv;
        u32 vlan_table[3];
-       u16 vid;
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       int err;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (ksz9477_get_vlan_table(dev, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to get vlan table\n");
-                       return;
-               }
-
-               vlan_table[0] = VLAN_VALID | (vid & VLAN_FID_M);
-               if (untagged)
-                       vlan_table[1] |= BIT(port);
-               else
-                       vlan_table[1] &= ~BIT(port);
-               vlan_table[1] &= ~(BIT(dev->cpu_port));
+       err = ksz9477_get_vlan_table(dev, vlan->vid, vlan_table);
+       if (err) {
+               dev_dbg(dev->dev, "Failed to get vlan table\n");
+               return err;
+       }
 
-               vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
+       vlan_table[0] = VLAN_VALID | (vlan->vid & VLAN_FID_M);
+       if (untagged)
+               vlan_table[1] |= BIT(port);
+       else
+               vlan_table[1] &= ~BIT(port);
+       vlan_table[1] &= ~(BIT(dev->cpu_port));
 
-               if (ksz9477_set_vlan_table(dev, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to set vlan table\n");
-                       return;
-               }
+       vlan_table[2] |= BIT(port) | BIT(dev->cpu_port);
 
-               /* change PVID */
-               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
-                       ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vid);
+       err = ksz9477_set_vlan_table(dev, vlan->vid, vlan_table);
+       if (err) {
+               dev_dbg(dev->dev, "Failed to set vlan table\n");
+               return err;
        }
+
+       /* change PVID */
+       if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+               ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, vlan->vid);
+
+       return 0;
 }
 
 static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port,
@@ -554,30 +552,27 @@ static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port,
        struct ksz_device *dev = ds->priv;
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        u32 vlan_table[3];
-       u16 vid;
        u16 pvid;
 
        ksz_pread16(dev, port, REG_PORT_DEFAULT_VID, &pvid);
        pvid = pvid & 0xFFF;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (ksz9477_get_vlan_table(dev, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to get vlan table\n");
-                       return -ETIMEDOUT;
-               }
+       if (ksz9477_get_vlan_table(dev, vlan->vid, vlan_table)) {
+               dev_dbg(dev->dev, "Failed to get vlan table\n");
+               return -ETIMEDOUT;
+       }
 
-               vlan_table[2] &= ~BIT(port);
+       vlan_table[2] &= ~BIT(port);
 
-               if (pvid == vid)
-                       pvid = 1;
+       if (pvid == vlan->vid)
+               pvid = 1;
 
-               if (untagged)
-                       vlan_table[1] &= ~BIT(port);
+       if (untagged)
+               vlan_table[1] &= ~BIT(port);
 
-               if (ksz9477_set_vlan_table(dev, vid, vlan_table)) {
-                       dev_dbg(dev->dev, "Failed to set vlan table\n");
-                       return -ETIMEDOUT;
-               }
+       if (ksz9477_set_vlan_table(dev, vlan->vid, vlan_table)) {
+               dev_dbg(dev->dev, "Failed to set vlan table\n");
+               return -ETIMEDOUT;
        }
 
        ksz_pwrite16(dev, port, REG_PORT_DEFAULT_VID, pvid);
@@ -784,14 +779,15 @@ exit:
        return ret;
 }
 
-static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
-                                const struct switchdev_obj_port_mdb *mdb)
+static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
+                               const struct switchdev_obj_port_mdb *mdb)
 {
        struct ksz_device *dev = ds->priv;
        u32 static_table[4];
        u32 data;
        int index;
        u32 mac_hi, mac_lo;
+       int err = 0;
 
        mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]);
        mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16));
@@ -806,7 +802,8 @@ static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
                ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
 
                /* wait to be finished */
-               if (ksz9477_wait_alu_sta_ready(dev)) {
+               err = ksz9477_wait_alu_sta_ready(dev);
+               if (err) {
                        dev_dbg(dev->dev, "Failed to read ALU STATIC\n");
                        goto exit;
                }
@@ -829,8 +826,10 @@ static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
        }
 
        /* no available entry */
-       if (index == dev->num_statics)
+       if (index == dev->num_statics) {
+               err = -ENOSPC;
                goto exit;
+       }
 
        /* add entry */
        static_table[0] = ALU_V_STATIC_VALID;
@@ -852,6 +851,7 @@ static void ksz9477_port_mdb_add(struct dsa_switch *ds, int port,
 
 exit:
        mutex_unlock(&dev->alu_mutex);
+       return err;
 }
 
 static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port,
@@ -1381,6 +1381,8 @@ static int ksz9477_setup(struct dsa_switch *ds)
 
        ksz_init_mib_timer(dev);
 
+       ds->configure_vlan_while_not_filtering = false;
+
        return 0;
 }
 
@@ -1399,13 +1401,11 @@ static const struct dsa_switch_ops ksz9477_switch_ops = {
        .port_stp_state_set     = ksz9477_port_stp_state_set,
        .port_fast_age          = ksz_port_fast_age,
        .port_vlan_filtering    = ksz9477_port_vlan_filtering,
-       .port_vlan_prepare      = ksz_port_vlan_prepare,
        .port_vlan_add          = ksz9477_port_vlan_add,
        .port_vlan_del          = ksz9477_port_vlan_del,
        .port_fdb_dump          = ksz9477_port_fdb_dump,
        .port_fdb_add           = ksz9477_port_fdb_add,
        .port_fdb_del           = ksz9477_port_fdb_del,
-       .port_mdb_prepare       = ksz_port_mdb_prepare,
        .port_mdb_add           = ksz9477_port_mdb_add,
        .port_mdb_del           = ksz9477_port_mdb_del,
        .port_mirror_add        = ksz9477_port_mirror_add,
index cf74313..a7e5ac6 100644 (file)
@@ -213,15 +213,6 @@ void ksz_port_fast_age(struct dsa_switch *ds, int port)
 }
 EXPORT_SYMBOL_GPL(ksz_port_fast_age);
 
-int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
-                         const struct switchdev_obj_port_vlan *vlan)
-{
-       /* nothing needed */
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(ksz_port_vlan_prepare);
-
 int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
                      void *data)
 {
@@ -253,16 +244,8 @@ int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
 }
 EXPORT_SYMBOL_GPL(ksz_port_fdb_dump);
 
-int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_mdb *mdb)
-{
-       /* nothing to do */
-       return 0;
-}
-EXPORT_SYMBOL_GPL(ksz_port_mdb_prepare);
-
-void ksz_port_mdb_add(struct dsa_switch *ds, int port,
-                     const struct switchdev_obj_port_mdb *mdb)
+int ksz_port_mdb_add(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_mdb *mdb)
 {
        struct ksz_device *dev = ds->priv;
        struct alu_struct alu;
@@ -284,7 +267,7 @@ void ksz_port_mdb_add(struct dsa_switch *ds, int port,
 
        /* no available entry */
        if (index == dev->num_statics && !empty)
-               return;
+               return -ENOSPC;
 
        /* add entry */
        if (index == dev->num_statics) {
@@ -301,6 +284,8 @@ void ksz_port_mdb_add(struct dsa_switch *ds, int port,
                alu.fid = mdb->vid;
        }
        dev->dev_ops->w_sta_mac_table(dev, index, &alu);
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(ksz_port_mdb_add);
 
@@ -400,7 +385,7 @@ int ksz_switch_register(struct ksz_device *dev,
                gpiod_set_value_cansleep(dev->reset_gpio, 1);
                usleep_range(10000, 12000);
                gpiod_set_value_cansleep(dev->reset_gpio, 0);
-               usleep_range(100, 1000);
+               msleep(100);
        }
 
        mutex_init(&dev->dev_mutex);
@@ -434,7 +419,7 @@ int ksz_switch_register(struct ksz_device *dev,
                                if (of_property_read_u32(port, "reg",
                                                         &port_num))
                                        continue;
-                               if (port_num >= dev->port_cnt)
+                               if (!(dev->port_mask & BIT(port_num)))
                                        return -EINVAL;
                                of_get_phy_mode(port,
                                                &dev->ports[port_num].interface);
index 720f222..f212775 100644 (file)
@@ -161,14 +161,10 @@ int ksz_port_bridge_join(struct dsa_switch *ds, int port,
 void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
                           struct net_device *br);
 void ksz_port_fast_age(struct dsa_switch *ds, int port);
-int ksz_port_vlan_prepare(struct dsa_switch *ds, int port,
-                         const struct switchdev_obj_port_vlan *vlan);
 int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
                      void *data);
-int ksz_port_mdb_prepare(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_mdb *mdb);
-void ksz_port_mdb_add(struct dsa_switch *ds, int port,
-                     const struct switchdev_obj_port_mdb *mdb);
+int ksz_port_mdb_add(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_mdb *mdb);
 int ksz_port_mdb_del(struct dsa_switch *ds, int port,
                     const struct switchdev_obj_port_mdb *mdb);
 int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy);
index a67cac1..eb13ba7 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 #include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
 #include <net/dsa.h>
 
 #include "mt7530.h"
@@ -1376,12 +1377,8 @@ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
 
 static int
 mt7530_port_vlan_filtering(struct dsa_switch *ds, int port,
-                          bool vlan_filtering,
-                          struct switchdev_trans *trans)
+                          bool vlan_filtering)
 {
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        if (vlan_filtering) {
                /* The port is being kept as VLAN-unaware port when bridge is
                 * set up with vlan_filtering not being set, Otherwise, the
@@ -1397,15 +1394,6 @@ mt7530_port_vlan_filtering(struct dsa_switch *ds, int port,
        return 0;
 }
 
-static int
-mt7530_port_vlan_prepare(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_vlan *vlan)
-{
-       /* nothing needed */
-
-       return 0;
-}
-
 static void
 mt7530_hw_vlan_add(struct mt7530_priv *priv,
                   struct mt7530_hw_vlan_entry *entry)
@@ -1493,7 +1481,7 @@ mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid,
        mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, vid);
 }
 
-static void
+static int
 mt7530_port_vlan_add(struct dsa_switch *ds, int port,
                     const struct switchdev_obj_port_vlan *vlan)
 {
@@ -1501,23 +1489,21 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port,
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        struct mt7530_hw_vlan_entry new_entry;
        struct mt7530_priv *priv = ds->priv;
-       u16 vid;
 
        mutex_lock(&priv->reg_mutex);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               mt7530_hw_vlan_entry_init(&new_entry, port, untagged);
-               mt7530_hw_vlan_update(priv, vid, &new_entry,
-                                     mt7530_hw_vlan_add);
-       }
+       mt7530_hw_vlan_entry_init(&new_entry, port, untagged);
+       mt7530_hw_vlan_update(priv, vlan->vid, &new_entry, mt7530_hw_vlan_add);
 
        if (pvid) {
                mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK,
-                          G0_PORT_VID(vlan->vid_end));
-               priv->ports[port].pvid = vlan->vid_end;
+                          G0_PORT_VID(vlan->vid));
+               priv->ports[port].pvid = vlan->vid;
        }
 
        mutex_unlock(&priv->reg_mutex);
+
+       return 0;
 }
 
 static int
@@ -1526,22 +1512,20 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,
 {
        struct mt7530_hw_vlan_entry target_entry;
        struct mt7530_priv *priv = ds->priv;
-       u16 vid, pvid;
+       u16 pvid;
 
        mutex_lock(&priv->reg_mutex);
 
        pvid = priv->ports[port].pvid;
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               mt7530_hw_vlan_entry_init(&target_entry, port, 0);
-               mt7530_hw_vlan_update(priv, vid, &target_entry,
-                                     mt7530_hw_vlan_del);
+       mt7530_hw_vlan_entry_init(&target_entry, port, 0);
+       mt7530_hw_vlan_update(priv, vlan->vid, &target_entry,
+                             mt7530_hw_vlan_del);
 
-               /* PVID is being restored to the default whenever the PVID port
-                * is being removed from the VLAN.
-                */
-               if (pvid == vid)
-                       pvid = G0_PORT_VID_DEF;
-       }
+       /* PVID is being restored to the default whenever the PVID port
+        * is being removed from the VLAN.
+        */
+       if (pvid == vlan->vid)
+               pvid = G0_PORT_VID_DEF;
 
        mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid);
        priv->ports[port].pvid = pvid;
@@ -1639,6 +1623,109 @@ mtk_get_tag_protocol(struct dsa_switch *ds, int port,
        }
 }
 
+static inline u32
+mt7530_gpio_to_bit(unsigned int offset)
+{
+       /* Map GPIO offset to register bit
+        * [ 2: 0]  port 0 LED 0..2 as GPIO 0..2
+        * [ 6: 4]  port 1 LED 0..2 as GPIO 3..5
+        * [10: 8]  port 2 LED 0..2 as GPIO 6..8
+        * [14:12]  port 3 LED 0..2 as GPIO 9..11
+        * [18:16]  port 4 LED 0..2 as GPIO 12..14
+        */
+       return BIT(offset + offset / 3);
+}
+
+static int
+mt7530_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+       struct mt7530_priv *priv = gpiochip_get_data(gc);
+       u32 bit = mt7530_gpio_to_bit(offset);
+
+       return !!(mt7530_read(priv, MT7530_LED_GPIO_DATA) & bit);
+}
+
+static void
+mt7530_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+       struct mt7530_priv *priv = gpiochip_get_data(gc);
+       u32 bit = mt7530_gpio_to_bit(offset);
+
+       if (value)
+               mt7530_set(priv, MT7530_LED_GPIO_DATA, bit);
+       else
+               mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit);
+}
+
+static int
+mt7530_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+       struct mt7530_priv *priv = gpiochip_get_data(gc);
+       u32 bit = mt7530_gpio_to_bit(offset);
+
+       return (mt7530_read(priv, MT7530_LED_GPIO_DIR) & bit) ?
+               GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int
+mt7530_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+       struct mt7530_priv *priv = gpiochip_get_data(gc);
+       u32 bit = mt7530_gpio_to_bit(offset);
+
+       mt7530_clear(priv, MT7530_LED_GPIO_OE, bit);
+       mt7530_clear(priv, MT7530_LED_GPIO_DIR, bit);
+
+       return 0;
+}
+
+static int
+mt7530_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value)
+{
+       struct mt7530_priv *priv = gpiochip_get_data(gc);
+       u32 bit = mt7530_gpio_to_bit(offset);
+
+       mt7530_set(priv, MT7530_LED_GPIO_DIR, bit);
+
+       if (value)
+               mt7530_set(priv, MT7530_LED_GPIO_DATA, bit);
+       else
+               mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit);
+
+       mt7530_set(priv, MT7530_LED_GPIO_OE, bit);
+
+       return 0;
+}
+
+static int
+mt7530_setup_gpio(struct mt7530_priv *priv)
+{
+       struct device *dev = priv->dev;
+       struct gpio_chip *gc;
+
+       gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
+       if (!gc)
+               return -ENOMEM;
+
+       mt7530_write(priv, MT7530_LED_GPIO_OE, 0);
+       mt7530_write(priv, MT7530_LED_GPIO_DIR, 0);
+       mt7530_write(priv, MT7530_LED_IO_MODE, 0);
+
+       gc->label = "mt7530";
+       gc->parent = dev;
+       gc->owner = THIS_MODULE;
+       gc->get_direction = mt7530_gpio_get_direction;
+       gc->direction_input = mt7530_gpio_direction_input;
+       gc->direction_output = mt7530_gpio_direction_output;
+       gc->get = mt7530_gpio_get;
+       gc->set = mt7530_gpio_set;
+       gc->base = -1;
+       gc->ngpio = 15;
+       gc->can_sleep = true;
+
+       return devm_gpiochip_add_data(dev, gc, priv);
+}
+
 static int
 mt7530_setup(struct dsa_switch *ds)
 {
@@ -1656,7 +1743,6 @@ mt7530_setup(struct dsa_switch *ds)
         * as two netdev instances.
         */
        dn = dsa_to_port(ds, MT7530_CPU_PORT)->master->dev.of_node->parent;
-       ds->configure_vlan_while_not_filtering = true;
        ds->mtu_enforcement_ingress = true;
 
        if (priv->id == ID_MT7530) {
@@ -1781,6 +1867,12 @@ mt7530_setup(struct dsa_switch *ds)
                }
        }
 
+       if (of_property_read_bool(priv->dev->of_node, "gpio-controller")) {
+               ret = mt7530_setup_gpio(priv);
+               if (ret)
+                       return ret;
+       }
+
        mt7530_setup_port5(ds, interface);
 
        /* Flush the FDB table */
@@ -1895,7 +1987,6 @@ mt7531_setup(struct dsa_switch *ds)
                           PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
        }
 
-       ds->configure_vlan_while_not_filtering = true;
        ds->mtu_enforcement_ingress = true;
 
        /* Flush the FDB table */
@@ -2618,7 +2709,6 @@ static const struct dsa_switch_ops mt7530_switch_ops = {
        .port_fdb_del           = mt7530_port_fdb_del,
        .port_fdb_dump          = mt7530_port_fdb_dump,
        .port_vlan_filtering    = mt7530_port_vlan_filtering,
-       .port_vlan_prepare      = mt7530_port_vlan_prepare,
        .port_vlan_add          = mt7530_port_vlan_add,
        .port_vlan_del          = mt7530_port_vlan_del,
        .port_mirror_add        = mt753x_port_mirror_add,
index 32d8969..64a9bb3 100644 (file)
@@ -554,6 +554,26 @@ enum mt7531_clk_skew {
 #define  MT7531_GPIO12_RG_RXD3_MASK    GENMASK(19, 16)
 #define  MT7531_EXT_P_MDIO_12          (2 << 16)
 
+/* Registers for LED GPIO control (MT7530 only)
+ * All registers follow this pattern:
+ * [ 2: 0]  port 0
+ * [ 6: 4]  port 1
+ * [10: 8]  port 2
+ * [14:12]  port 3
+ * [18:16]  port 4
+ */
+
+/* LED enable, 0: Disable, 1: Enable (Default) */
+#define MT7530_LED_EN                  0x7d00
+/* LED mode, 0: GPIO mode, 1: PHY mode (Default) */
+#define MT7530_LED_IO_MODE             0x7d04
+/* GPIO direction, 0: Input, 1: Output */
+#define MT7530_LED_GPIO_DIR            0x7d10
+/* GPIO output enable, 0: Disable, 1: Enable */
+#define MT7530_LED_GPIO_OE             0x7d14
+/* GPIO value, 0: Low, 1: High */
+#define MT7530_LED_GPIO_DATA           0x7d18
+
 #define MT7530_CREV                    0x7ffc
 #define  CHIP_NAME_SHIFT               16
 #define  MT7530_ID                     0x7530
index 51185e4..05af632 100644 (file)
@@ -9,23 +9,10 @@ config NET_DSA_MV88E6XXX
          This driver adds support for most of the Marvell 88E6xxx models of
          Ethernet switch chips, except 88E6060.
 
-config NET_DSA_MV88E6XXX_GLOBAL2
-       bool "Switch Global 2 Registers support"
-       default y
-       depends on NET_DSA_MV88E6XXX
-       help
-         This registers set at internal SMI address 0x1C provides extended
-         features like EEPROM interface, trunking, cross-chip setup, etc.
-
-         It is required on most chips. If the chip you compile the support for
-         doesn't have such registers set, say N here. In doubt, say Y.
-
 config NET_DSA_MV88E6XXX_PTP
        bool "PTP support for Marvell 88E6xxx"
        default n
-       depends on NET_DSA_MV88E6XXX_GLOBAL2
        depends on PTP_1588_CLOCK
-       imply NETWORK_PHY_TIMESTAMPING
        help
          Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
          chips that support it.
index 4b080b4..c8eca2b 100644 (file)
@@ -5,9 +5,9 @@ mv88e6xxx-objs += devlink.o
 mv88e6xxx-objs += global1.o
 mv88e6xxx-objs += global1_atu.o
 mv88e6xxx-objs += global1_vtu.o
-mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
-mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o
-mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_scratch.o
+mv88e6xxx-objs += global2.o
+mv88e6xxx-objs += global2_avb.o
+mv88e6xxx-objs += global2_scratch.o
 mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
 mv88e6xxx-objs += phy.o
 mv88e6xxx-objs += port.o
index eafe6be..b99f27b 100644 (file)
@@ -1396,15 +1396,32 @@ static int mv88e6xxx_mac_setup(struct mv88e6xxx_chip *chip)
 
 static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
 {
+       struct dsa_switch_tree *dst = chip->ds->dst;
+       struct dsa_switch *ds;
+       struct dsa_port *dp;
        u16 pvlan = 0;
 
        if (!mv88e6xxx_has_pvt(chip))
                return 0;
 
        /* Skip the local source device, which uses in-chip port VLAN */
-       if (dev != chip->ds->index)
+       if (dev != chip->ds->index) {
                pvlan = mv88e6xxx_port_vlan(chip, dev, port);
 
+               ds = dsa_switch_find(dst->index, dev);
+               dp = ds ? dsa_to_port(ds, port) : NULL;
+               if (dp && dp->lag_dev) {
+                       /* As the PVT is used to limit flooding of
+                        * FORWARD frames, which use the LAG ID as the
+                        * source port, we must translate dev/port to
+                        * the special "LAG device" in the PVT, using
+                        * the LAG ID as the port number.
+                        */
+                       dev = MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK;
+                       port = dsa_lag_id(dst, dp->lag_dev);
+               }
+       }
+
        return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
 }
 
@@ -1529,72 +1546,69 @@ static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
 }
 
 static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
-                                       u16 vid_begin, u16 vid_end)
+                                       u16 vid)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        struct mv88e6xxx_vtu_entry vlan;
        int i, err;
 
+       if (!vid)
+               return -EOPNOTSUPP;
+
        /* DSA and CPU ports have to be members of multiple vlans */
        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
                return 0;
 
-       if (!vid_begin)
-               return -EOPNOTSUPP;
-
-       vlan.vid = vid_begin - 1;
+       vlan.vid = vid - 1;
        vlan.valid = false;
 
-       do {
-               err = mv88e6xxx_vtu_getnext(chip, &vlan);
-               if (err)
-                       return err;
+       err = mv88e6xxx_vtu_getnext(chip, &vlan);
+       if (err)
+               return err;
 
-               if (!vlan.valid)
-                       break;
+       if (!vlan.valid)
+               return 0;
 
-               if (vlan.vid > vid_end)
-                       break;
+       if (vlan.vid != vid)
+               return 0;
 
-               for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
-                       if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
-                               continue;
+       for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
+               if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
+                       continue;
 
-                       if (!dsa_to_port(ds, i)->slave)
-                               continue;
+               if (!dsa_to_port(ds, i)->slave)
+                       continue;
 
-                       if (vlan.member[i] ==
-                           MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
-                               continue;
+               if (vlan.member[i] ==
+                   MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER)
+                       continue;
 
-                       if (dsa_to_port(ds, i)->bridge_dev ==
-                           dsa_to_port(ds, port)->bridge_dev)
-                               break; /* same bridge, check next VLAN */
+               if (dsa_to_port(ds, i)->bridge_dev ==
+                   dsa_to_port(ds, port)->bridge_dev)
+                       break; /* same bridge, check next VLAN */
 
-                       if (!dsa_to_port(ds, i)->bridge_dev)
-                               continue;
+               if (!dsa_to_port(ds, i)->bridge_dev)
+                       continue;
 
-                       dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n",
-                               port, vlan.vid, i,
-                               netdev_name(dsa_to_port(ds, i)->bridge_dev));
-                       return -EOPNOTSUPP;
-               }
-       } while (vlan.vid < vid_end);
+               dev_err(ds->dev, "p%d: hw VLAN %d already used by port %d in %s\n",
+                       port, vlan.vid, i,
+                       netdev_name(dsa_to_port(ds, i)->bridge_dev));
+               return -EOPNOTSUPP;
+       }
 
        return 0;
 }
 
 static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
-                                        bool vlan_filtering,
-                                        struct switchdev_trans *trans)
+                                        bool vlan_filtering)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        u16 mode = vlan_filtering ? MV88E6XXX_PORT_CTL2_8021Q_MODE_SECURE :
                MV88E6XXX_PORT_CTL2_8021Q_MODE_DISABLED;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return mv88e6xxx_max_vid(chip) ? 0 : -EOPNOTSUPP;
+       if (!mv88e6xxx_max_vid(chip))
+               return -EOPNOTSUPP;
 
        mv88e6xxx_reg_lock(chip);
        err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
@@ -1617,13 +1631,9 @@ mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
         * members, do not support it (yet) and fallback to software VLAN.
         */
        mv88e6xxx_reg_lock(chip);
-       err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
-                                          vlan->vid_end);
+       err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid);
        mv88e6xxx_reg_unlock(chip);
 
-       /* We don't need any dynamic resource from the kernel (yet),
-        * so skip the prepare phase.
-        */
        return err;
 }
 
@@ -1923,9 +1933,6 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port,
        struct mv88e6xxx_vtu_entry vlan;
        int i, err;
 
-       if (!vid)
-               return -EOPNOTSUPP;
-
        vlan.vid = vid - 1;
        vlan.valid = false;
 
@@ -1970,18 +1977,19 @@ static int mv88e6xxx_port_vlan_join(struct mv88e6xxx_chip *chip, int port,
        return 0;
 }
 
-static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
-                                   const struct switchdev_obj_port_vlan *vlan)
+static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
+                                  const struct switchdev_obj_port_vlan *vlan)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        bool warn;
        u8 member;
-       u16 vid;
+       int err;
 
-       if (!mv88e6xxx_max_vid(chip))
-               return;
+       err = mv88e6xxx_port_vlan_prepare(ds, port, vlan);
+       if (err)
+               return err;
 
        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
                member = MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_UNMODIFIED;
@@ -1997,16 +2005,25 @@ static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
 
        mv88e6xxx_reg_lock(chip);
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
-               if (mv88e6xxx_port_vlan_join(chip, port, vid, member, warn))
-                       dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port,
-                               vid, untagged ? 'u' : 't');
-
-       if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
-               dev_err(ds->dev, "p%d: failed to set PVID %d\n", port,
-                       vlan->vid_end);
+       err = mv88e6xxx_port_vlan_join(chip, port, vlan->vid, member, warn);
+       if (err) {
+               dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port,
+                       vlan->vid, untagged ? 'u' : 't');
+               goto out;
+       }
 
+       if (pvid) {
+               err = mv88e6xxx_port_set_pvid(chip, port, vlan->vid);
+               if (err) {
+                       dev_err(ds->dev, "p%d: failed to set PVID %d\n",
+                               port, vlan->vid);
+                       goto out;
+               }
+       }
+out:
        mv88e6xxx_reg_unlock(chip);
+
+       return err;
 }
 
 static int mv88e6xxx_port_vlan_leave(struct mv88e6xxx_chip *chip,
@@ -2055,8 +2072,8 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
                                   const struct switchdev_obj_port_vlan *vlan)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
-       u16 pvid, vid;
        int err = 0;
+       u16 pvid;
 
        if (!mv88e6xxx_max_vid(chip))
                return -EOPNOTSUPP;
@@ -2067,16 +2084,14 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
        if (err)
                goto unlock;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               err = mv88e6xxx_port_vlan_leave(chip, port, vid);
+       err = mv88e6xxx_port_vlan_leave(chip, port, vlan->vid);
+       if (err)
+               goto unlock;
+
+       if (vlan->vid == pvid) {
+               err = mv88e6xxx_port_set_pvid(chip, port, 0);
                if (err)
                        goto unlock;
-
-               if (vid == pvid) {
-                       err = mv88e6xxx_port_set_pvid(chip, port, 0);
-                       if (err)
-                               goto unlock;
-               }
        }
 
 unlock:
@@ -2860,7 +2875,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
 
        chip->ds = ds;
        ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip);
-       ds->configure_vlan_while_not_filtering = true;
 
        mv88e6xxx_reg_lock(chip);
 
@@ -4035,8 +4049,8 @@ static const struct mv88e6xxx_ops mv88e6250_ops = {
        .mgmt_rsvd2cpu = mv88e6352_g2_mgmt_rsvd2cpu,
        .pot_clear = mv88e6xxx_g2_pot_clear,
        .reset = mv88e6250_g1_reset,
-       .vtu_getnext = mv88e6250_g1_vtu_getnext,
-       .vtu_loadpurge = mv88e6250_g1_vtu_loadpurge,
+       .vtu_getnext = mv88e6185_g1_vtu_getnext,
+       .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
        .avb_ops = &mv88e6352_avb_ops,
        .ptp_ops = &mv88e6250_ptp_ops,
        .phylink_validate = mv88e6065_phylink_validate,
@@ -5213,10 +5227,6 @@ static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
        /* Update the compatible info with the probed one */
        chip->info = info;
 
-       err = mv88e6xxx_g2_require(chip);
-       if (err)
-               return err;
-
        dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
                 chip->info->prod_num, chip->info->name, rev);
 
@@ -5249,27 +5259,18 @@ static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds,
        return chip->info->tag_protocol;
 }
 
-static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
-                                     const struct switchdev_obj_port_mdb *mdb)
-{
-       /* We don't need any dynamic resource from the kernel (yet),
-        * so skip the prepare phase.
-        */
-
-       return 0;
-}
-
-static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
-                                  const struct switchdev_obj_port_mdb *mdb)
+static int mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_mdb *mdb)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
+       int err;
 
        mv88e6xxx_reg_lock(chip);
-       if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
-                                        MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC))
-               dev_err(ds->dev, "p%d: failed to load multicast MAC address\n",
-                       port);
+       err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
+                                          MV88E6XXX_G1_ATU_DATA_STATE_MC_STATIC);
        mv88e6xxx_reg_unlock(chip);
+
+       return err;
 }
 
 static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
@@ -5375,6 +5376,275 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
        return err;
 }
 
+static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds,
+                                     struct net_device *lag,
+                                     struct netdev_lag_upper_info *info)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       struct dsa_port *dp;
+       int id, members = 0;
+
+       if (!mv88e6xxx_has_lag(chip))
+               return false;
+
+       id = dsa_lag_id(ds->dst, lag);
+       if (id < 0 || id >= ds->num_lag_ids)
+               return false;
+
+       dsa_lag_foreach_port(dp, ds->dst, lag)
+               /* Includes the port joining the LAG */
+               members++;
+
+       if (members > 8)
+               return false;
+
+       /* We could potentially relax this to include active
+        * backup in the future.
+        */
+       if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+               return false;
+
+       /* Ideally we would also validate that the hash type matches
+        * the hardware. Alas, this is always set to unknown on team
+        * interfaces.
+        */
+       return true;
+}
+
+static int mv88e6xxx_lag_sync_map(struct dsa_switch *ds, struct net_device *lag)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       struct dsa_port *dp;
+       u16 map = 0;
+       int id;
+
+       id = dsa_lag_id(ds->dst, lag);
+
+       /* Build the map of all ports to distribute flows destined for
+        * this LAG. This can be either a local user port, or a DSA
+        * port if the LAG port is on a remote chip.
+        */
+       dsa_lag_foreach_port(dp, ds->dst, lag)
+               map |= BIT(dsa_towards_port(ds, dp->ds->index, dp->index));
+
+       return mv88e6xxx_g2_trunk_mapping_write(chip, id, map);
+}
+
+static const u8 mv88e6xxx_lag_mask_table[8][8] = {
+       /* Row number corresponds to the number of active members in a
+        * LAG. Each column states which of the eight hash buckets are
+        * mapped to the column:th port in the LAG.
+        *
+        * Example: In a LAG with three active ports, the second port
+        * ([2][1]) would be selected for traffic mapped to buckets
+        * 3,4,5 (0x38).
+        */
+       { 0xff,    0,    0,    0,    0,    0,    0,    0 },
+       { 0x0f, 0xf0,    0,    0,    0,    0,    0,    0 },
+       { 0x07, 0x38, 0xc0,    0,    0,    0,    0,    0 },
+       { 0x03, 0x0c, 0x30, 0xc0,    0,    0,    0,    0 },
+       { 0x03, 0x0c, 0x30, 0x40, 0x80,    0,    0,    0 },
+       { 0x03, 0x0c, 0x10, 0x20, 0x40, 0x80,    0,    0 },
+       { 0x03, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,    0 },
+       { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 },
+};
+
+static void mv88e6xxx_lag_set_port_mask(u16 *mask, int port,
+                                       int num_tx, int nth)
+{
+       u8 active = 0;
+       int i;
+
+       num_tx = num_tx <= 8 ? num_tx : 8;
+       if (nth < num_tx)
+               active = mv88e6xxx_lag_mask_table[num_tx - 1][nth];
+
+       for (i = 0; i < 8; i++) {
+               if (BIT(i) & active)
+                       mask[i] |= BIT(port);
+       }
+}
+
+static int mv88e6xxx_lag_sync_masks(struct dsa_switch *ds)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       unsigned int id, num_tx;
+       struct net_device *lag;
+       struct dsa_port *dp;
+       int i, err, nth;
+       u16 mask[8];
+       u16 ivec;
+
+       /* Assume no port is a member of any LAG. */
+       ivec = BIT(mv88e6xxx_num_ports(chip)) - 1;
+
+       /* Disable all masks for ports that _are_ members of a LAG. */
+       list_for_each_entry(dp, &ds->dst->ports, list) {
+               if (!dp->lag_dev || dp->ds != ds)
+                       continue;
+
+               ivec &= ~BIT(dp->index);
+       }
+
+       for (i = 0; i < 8; i++)
+               mask[i] = ivec;
+
+       /* Enable the correct subset of masks for all LAG ports that
+        * are in the Tx set.
+        */
+       dsa_lags_foreach_id(id, ds->dst) {
+               lag = dsa_lag_dev(ds->dst, id);
+               if (!lag)
+                       continue;
+
+               num_tx = 0;
+               dsa_lag_foreach_port(dp, ds->dst, lag) {
+                       if (dp->lag_tx_enabled)
+                               num_tx++;
+               }
+
+               if (!num_tx)
+                       continue;
+
+               nth = 0;
+               dsa_lag_foreach_port(dp, ds->dst, lag) {
+                       if (!dp->lag_tx_enabled)
+                               continue;
+
+                       if (dp->ds == ds)
+                               mv88e6xxx_lag_set_port_mask(mask, dp->index,
+                                                           num_tx, nth);
+
+                       nth++;
+               }
+       }
+
+       for (i = 0; i < 8; i++) {
+               err = mv88e6xxx_g2_trunk_mask_write(chip, i, true, mask[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int mv88e6xxx_lag_sync_masks_map(struct dsa_switch *ds,
+                                       struct net_device *lag)
+{
+       int err;
+
+       err = mv88e6xxx_lag_sync_masks(ds);
+
+       if (!err)
+               err = mv88e6xxx_lag_sync_map(ds, lag);
+
+       return err;
+}
+
+static int mv88e6xxx_port_lag_change(struct dsa_switch *ds, int port)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       int err;
+
+       mv88e6xxx_reg_lock(chip);
+       err = mv88e6xxx_lag_sync_masks(ds);
+       mv88e6xxx_reg_unlock(chip);
+       return err;
+}
+
+static int mv88e6xxx_port_lag_join(struct dsa_switch *ds, int port,
+                                  struct net_device *lag,
+                                  struct netdev_lag_upper_info *info)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       int err, id;
+
+       if (!mv88e6xxx_lag_can_offload(ds, lag, info))
+               return -EOPNOTSUPP;
+
+       id = dsa_lag_id(ds->dst, lag);
+
+       mv88e6xxx_reg_lock(chip);
+
+       err = mv88e6xxx_port_set_trunk(chip, port, true, id);
+       if (err)
+               goto err_unlock;
+
+       err = mv88e6xxx_lag_sync_masks_map(ds, lag);
+       if (err)
+               goto err_clear_trunk;
+
+       mv88e6xxx_reg_unlock(chip);
+       return 0;
+
+err_clear_trunk:
+       mv88e6xxx_port_set_trunk(chip, port, false, 0);
+err_unlock:
+       mv88e6xxx_reg_unlock(chip);
+       return err;
+}
+
+static int mv88e6xxx_port_lag_leave(struct dsa_switch *ds, int port,
+                                   struct net_device *lag)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       int err_sync, err_trunk;
+
+       mv88e6xxx_reg_lock(chip);
+       err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag);
+       err_trunk = mv88e6xxx_port_set_trunk(chip, port, false, 0);
+       mv88e6xxx_reg_unlock(chip);
+       return err_sync ? : err_trunk;
+}
+
+static int mv88e6xxx_crosschip_lag_change(struct dsa_switch *ds, int sw_index,
+                                         int port)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       int err;
+
+       mv88e6xxx_reg_lock(chip);
+       err = mv88e6xxx_lag_sync_masks(ds);
+       mv88e6xxx_reg_unlock(chip);
+       return err;
+}
+
+static int mv88e6xxx_crosschip_lag_join(struct dsa_switch *ds, int sw_index,
+                                       int port, struct net_device *lag,
+                                       struct netdev_lag_upper_info *info)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       int err;
+
+       if (!mv88e6xxx_lag_can_offload(ds, lag, info))
+               return -EOPNOTSUPP;
+
+       mv88e6xxx_reg_lock(chip);
+
+       err = mv88e6xxx_lag_sync_masks_map(ds, lag);
+       if (err)
+               goto unlock;
+
+       err = mv88e6xxx_pvt_map(chip, sw_index, port);
+
+unlock:
+       mv88e6xxx_reg_unlock(chip);
+       return err;
+}
+
+static int mv88e6xxx_crosschip_lag_leave(struct dsa_switch *ds, int sw_index,
+                                        int port, struct net_device *lag)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       int err_sync, err_pvt;
+
+       mv88e6xxx_reg_lock(chip);
+       err_sync = mv88e6xxx_lag_sync_masks_map(ds, lag);
+       err_pvt = mv88e6xxx_pvt_map(chip, sw_index, port);
+       mv88e6xxx_reg_unlock(chip);
+       return err_sync ? : err_pvt;
+}
+
 static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .get_tag_protocol       = mv88e6xxx_get_tag_protocol,
        .setup                  = mv88e6xxx_setup,
@@ -5408,13 +5678,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .port_stp_state_set     = mv88e6xxx_port_stp_state_set,
        .port_fast_age          = mv88e6xxx_port_fast_age,
        .port_vlan_filtering    = mv88e6xxx_port_vlan_filtering,
-       .port_vlan_prepare      = mv88e6xxx_port_vlan_prepare,
        .port_vlan_add          = mv88e6xxx_port_vlan_add,
        .port_vlan_del          = mv88e6xxx_port_vlan_del,
        .port_fdb_add           = mv88e6xxx_port_fdb_add,
        .port_fdb_del           = mv88e6xxx_port_fdb_del,
        .port_fdb_dump          = mv88e6xxx_port_fdb_dump,
-       .port_mdb_prepare       = mv88e6xxx_port_mdb_prepare,
        .port_mdb_add           = mv88e6xxx_port_mdb_add,
        .port_mdb_del           = mv88e6xxx_port_mdb_del,
        .port_mirror_add        = mv88e6xxx_port_mirror_add,
@@ -5429,6 +5697,12 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .devlink_param_get      = mv88e6xxx_devlink_param_get,
        .devlink_param_set      = mv88e6xxx_devlink_param_set,
        .devlink_info_get       = mv88e6xxx_devlink_info_get,
+       .port_lag_change        = mv88e6xxx_port_lag_change,
+       .port_lag_join          = mv88e6xxx_port_lag_join,
+       .port_lag_leave         = mv88e6xxx_port_lag_leave,
+       .crosschip_lag_change   = mv88e6xxx_crosschip_lag_change,
+       .crosschip_lag_join     = mv88e6xxx_crosschip_lag_join,
+       .crosschip_lag_leave    = mv88e6xxx_crosschip_lag_leave,
 };
 
 static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
@@ -5448,6 +5722,12 @@ static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
        ds->ageing_time_min = chip->info->age_time_coeff;
        ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
 
+       /* Some chips support up to 32, but that requires enabling the
+        * 5-bit port mode, which we do not support. 640k^W16 ought to
+        * be enough for anyone.
+        */
+       ds->num_lag_ids = mv88e6xxx_has_lag(chip) ? 16 : 0;
+
        dev_set_drvdata(dev, ds);
 
        return dsa_register_switch(ds);
index 3543055..788b3f5 100644 (file)
@@ -662,6 +662,11 @@ static inline bool mv88e6xxx_has_pvt(struct mv88e6xxx_chip *chip)
        return chip->info->pvt;
 }
 
+static inline bool mv88e6xxx_has_lag(struct mv88e6xxx_chip *chip)
+{
+       return !!chip->info->global2_addr;
+}
+
 static inline unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_chip *chip)
 {
        return chip->info->num_databases;
index 80a182c..7c39696 100644 (file)
@@ -336,10 +336,6 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
                             struct mv88e6xxx_vtu_entry *entry);
 int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
                               struct mv88e6xxx_vtu_entry *entry);
-int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
-                            struct mv88e6xxx_vtu_entry *entry);
-int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
-                              struct mv88e6xxx_vtu_entry *entry);
 int mv88e6352_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
                             struct mv88e6xxx_vtu_entry *entry);
 int mv88e6352_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
index 66ddf67..ae12c98 100644 (file)
@@ -336,35 +336,6 @@ int mv88e6xxx_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
        return mv88e6xxx_g1_vtu_vid_read(chip, entry);
 }
 
-int mv88e6250_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
-                            struct mv88e6xxx_vtu_entry *entry)
-{
-       u16 val;
-       int err;
-
-       err = mv88e6xxx_g1_vtu_getnext(chip, entry);
-       if (err)
-               return err;
-
-       if (entry->valid) {
-               err = mv88e6185_g1_vtu_data_read(chip, entry);
-               if (err)
-                       return err;
-
-               /* VTU DBNum[3:0] are located in VTU Operation 3:0
-                * VTU DBNum[5:4] are located in VTU Operation 9:8
-                */
-               err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
-               if (err)
-                       return err;
-
-               entry->fid = val & 0x000f;
-               entry->fid |= (val & 0x0300) >> 4;
-       }
-
-       return 0;
-}
-
 int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
                             struct mv88e6xxx_vtu_entry *entry)
 {
@@ -385,7 +356,7 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
                        return err;
 
                /* VTU DBNum[3:0] are located in VTU Operation 3:0
-                * VTU DBNum[7:4] are located in VTU Operation 11:8
+                * VTU DBNum[7:4] ([5:4] for 6250) are located in VTU Operation 11:8 (9:8)
                 */
                err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val);
                if (err)
@@ -393,6 +364,7 @@ int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
 
                entry->fid = val & 0x000f;
                entry->fid |= (val & 0x0f00) >> 4;
+               entry->fid &= mv88e6xxx_num_databases(chip) - 1;
        }
 
        return 0;
@@ -462,35 +434,6 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
        return 0;
 }
 
-int mv88e6250_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
-                              struct mv88e6xxx_vtu_entry *entry)
-{
-       u16 op = MV88E6XXX_G1_VTU_OP_VTU_LOAD_PURGE;
-       int err;
-
-       err = mv88e6xxx_g1_vtu_op_wait(chip);
-       if (err)
-               return err;
-
-       err = mv88e6xxx_g1_vtu_vid_write(chip, entry);
-       if (err)
-               return err;
-
-       if (entry->valid) {
-               err = mv88e6185_g1_vtu_data_write(chip, entry);
-               if (err)
-                       return err;
-
-               /* VTU DBNum[3:0] are located in VTU Operation 3:0
-                * VTU DBNum[5:4] are located in VTU Operation 9:8
-                */
-               op |= entry->fid & 0x000f;
-               op |= (entry->fid & 0x0030) << 4;
-       }
-
-       return mv88e6xxx_g1_vtu_op(chip, op);
-}
-
 int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
                               struct mv88e6xxx_vtu_entry *entry)
 {
@@ -512,6 +455,10 @@ int mv88e6185_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip,
 
                /* VTU DBNum[3:0] are located in VTU Operation 3:0
                 * VTU DBNum[7:4] are located in VTU Operation 11:8
+                *
+                * For the 6250/6220, the latter are really [5:4] and
+                * 9:8, but in those cases bits 7:6 of entry->fid are
+                * 0 since they have num_databases = 64.
                 */
                op |= entry->fid & 0x000f;
                op |= (entry->fid & 0x00f0) << 4;
index 75b227d..da8bac8 100644 (file)
@@ -126,8 +126,8 @@ int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
 
 /* Offset 0x07: Trunk Mask Table register */
 
-static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
-                                        bool hash, u16 mask)
+int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
+                                 bool hash, u16 mask)
 {
        u16 val = (num << 12) | (mask & mv88e6xxx_port_mask(chip));
 
@@ -140,8 +140,8 @@ static int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
 
 /* Offset 0x08: Trunk Mapping Table register */
 
-static int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
-                                           u16 map)
+int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
+                                    u16 map)
 {
        const u16 port_mask = BIT(mv88e6xxx_num_ports(chip)) - 1;
        u16 val = (id << 11) | (map & port_mask);
index 1f42ee6..4127f82 100644 (file)
 #define MV88E6XXX_G2_PVT_ADDR_OP_WRITE_PVLAN   0x3000
 #define MV88E6XXX_G2_PVT_ADDR_OP_READ          0x4000
 #define MV88E6XXX_G2_PVT_ADDR_PTR_MASK         0x01ff
+#define MV88E6XXX_G2_PVT_ADRR_DEV_TRUNK                0x1f
 
 /* Offset 0x0C: Cross-chip Port VLAN Data Register */
 #define MV88E6XXX_G2_PVT_DATA          0x0c
 #define MV88E6352_G2_SCRATCH_GPIO_PCTL_TRIG    1
 #define MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ   2
 
-#ifdef CONFIG_NET_DSA_MV88E6XXX_GLOBAL2
-
-static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
-{
-       return 0;
-}
-
 int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val);
 int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val);
 int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip, int reg,
@@ -345,6 +339,10 @@ int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip);
 
 int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip);
 
+int mv88e6xxx_g2_trunk_mask_write(struct mv88e6xxx_chip *chip, int num,
+                                 bool hash, u16 mask);
+int mv88e6xxx_g2_trunk_mapping_write(struct mv88e6xxx_chip *chip, int id,
+                                    u16 map);
 int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip);
 
 int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip, int target,
@@ -365,179 +363,4 @@ int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
 int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip, u16 kind, u16 bin);
 int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip, u16 *stats);
 
-#else /* !CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */
-
-static inline int mv88e6xxx_g2_require(struct mv88e6xxx_chip *chip)
-{
-       if (chip->info->global2_addr) {
-               dev_err(chip->dev, "this chip requires CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 enabled\n");
-               return -EOPNOTSUPP;
-       }
-
-       return 0;
-}
-
-static inline int mv88e6xxx_g2_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_wait_bit(struct mv88e6xxx_chip *chip,
-                                       int reg, int bit, int val)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6352_g2_irl_init_all(struct mv88e6xxx_chip *chip,
-                                           int port)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6390_g2_irl_init_all(struct mv88e6xxx_chip *chip,
-                                           int port)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_smi_phy_read(struct mv88e6xxx_chip *chip,
-                                           struct mii_bus *bus,
-                                           int addr, int reg, u16 *val)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip,
-                                            struct mii_bus *bus,
-                                            int addr, int reg, u16 val)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_set_switch_mac(struct mv88e6xxx_chip *chip,
-                                             u8 *addr)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_get_eeprom8(struct mv88e6xxx_chip *chip,
-                                          struct ethtool_eeprom *eeprom,
-                                          u8 *data)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_set_eeprom8(struct mv88e6xxx_chip *chip,
-                                          struct ethtool_eeprom *eeprom,
-                                          u8 *data)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_get_eeprom16(struct mv88e6xxx_chip *chip,
-                                           struct ethtool_eeprom *eeprom,
-                                           u8 *data)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_set_eeprom16(struct mv88e6xxx_chip *chip,
-                                           struct ethtool_eeprom *eeprom,
-                                           u8 *data)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_pvt_write(struct mv88e6xxx_chip *chip,
-                                        int src_dev, int src_port, u16 data)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_misc_4_bit_port(struct mv88e6xxx_chip *chip)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip)
-{
-}
-
-static inline int mv88e6xxx_g2_irq_mdio_setup(struct mv88e6xxx_chip *chip,
-                                             struct mii_bus *bus)
-{
-       return 0;
-}
-
-static inline void mv88e6xxx_g2_irq_mdio_free(struct mv88e6xxx_chip *chip,
-                                             struct mii_bus *bus)
-{
-}
-
-static inline int mv88e6185_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6352_g2_mgmt_rsvd2cpu(struct mv88e6xxx_chip *chip)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_pot_clear(struct mv88e6xxx_chip *chip)
-{
-       return -EOPNOTSUPP;
-}
-
-static const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = {};
-static const struct mv88e6xxx_irq_ops mv88e6250_watchdog_ops = {};
-static const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = {};
-
-static const struct mv88e6xxx_avb_ops mv88e6165_avb_ops = {};
-static const struct mv88e6xxx_avb_ops mv88e6352_avb_ops = {};
-static const struct mv88e6xxx_avb_ops mv88e6390_avb_ops = {};
-
-static const struct mv88e6xxx_gpio_ops mv88e6352_gpio_ops = {};
-
-static inline int mv88e6xxx_g2_scratch_gpio_set_smi(struct mv88e6xxx_chip *chip,
-                                                   bool external)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_trunk_clear(struct mv88e6xxx_chip *chip)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_device_mapping_write(struct mv88e6xxx_chip *chip,
-                                                   int target, int port)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_atu_stats_set(struct mv88e6xxx_chip *chip,
-                                            u16 kind, u16 bin)
-{
-       return -EOPNOTSUPP;
-}
-
-static inline int mv88e6xxx_g2_atu_stats_get(struct mv88e6xxx_chip *chip,
-                                            u16 *stats)
-{
-       return -EOPNOTSUPP;
-}
-
-#endif /* CONFIG_NET_DSA_MV88E6XXX_GLOBAL2 */
-
 #endif /* _MV88E6XXX_GLOBAL2_H */
index 77a5fd1..4b46e10 100644 (file)
@@ -851,6 +851,27 @@ int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
        return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
 }
 
+int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
+                            bool trunk, u8 id)
+{
+       u16 val;
+       int err;
+
+       err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_CTL1, &val);
+       if (err)
+               return err;
+
+       val &= ~MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK;
+
+       if (trunk)
+               val |= MV88E6XXX_PORT_CTL1_TRUNK_PORT |
+                       (id << MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT);
+       else
+               val &= ~MV88E6XXX_PORT_CTL1_TRUNK_PORT;
+
+       return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL1, val);
+}
+
 /* Offset 0x06: Port Based VLAN Map */
 
 int mv88e6xxx_port_set_vlan_map(struct mv88e6xxx_chip *chip, int port, u16 map)
index 500e1d4..a729bba 100644 (file)
 /* Offset 0x05: Port Control 1 */
 #define MV88E6XXX_PORT_CTL1                    0x05
 #define MV88E6XXX_PORT_CTL1_MESSAGE_PORT       0x8000
+#define MV88E6XXX_PORT_CTL1_TRUNK_PORT         0x4000
+#define MV88E6XXX_PORT_CTL1_TRUNK_ID_MASK      0x0f00
+#define MV88E6XXX_PORT_CTL1_TRUNK_ID_SHIFT     8
 #define MV88E6XXX_PORT_CTL1_FID_11_4_MASK      0x00ff
 
 /* Offset 0x06: Port Based VLAN Map */
@@ -351,6 +354,8 @@ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
                                  u16 etype);
 int mv88e6xxx_port_set_message_port(struct mv88e6xxx_chip *chip, int port,
                                    bool message_port);
+int mv88e6xxx_port_set_trunk(struct mv88e6xxx_chip *chip, int port,
+                            bool trunk, u8 id);
 int mv88e6165_port_set_jumbo_size(struct mv88e6xxx_chip *chip, int port,
                                  size_t size);
 int mv88e6095_port_egress_rate_limiting(struct mv88e6xxx_chip *chip, int port);
index 7dc2306..767cbdc 100644 (file)
@@ -65,19 +65,12 @@ static int felix_fdb_del(struct dsa_switch *ds, int port,
        return ocelot_fdb_del(ocelot, port, addr, vid);
 }
 
-/* This callback needs to be present */
-static int felix_mdb_prepare(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_mdb *mdb)
-{
-       return 0;
-}
-
-static void felix_mdb_add(struct dsa_switch *ds, int port,
-                         const struct switchdev_obj_port_mdb *mdb)
+static int felix_mdb_add(struct dsa_switch *ds, int port,
+                        const struct switchdev_obj_port_mdb *mdb)
 {
        struct ocelot *ocelot = ds->priv;
 
-       ocelot_port_mdb_add(ocelot, port, mdb);
+       return ocelot_port_mdb_add(ocelot, port, mdb);
 }
 
 static int felix_mdb_del(struct dsa_switch *ds, int port,
@@ -116,8 +109,7 @@ static int felix_vlan_prepare(struct dsa_switch *ds, int port,
                              const struct switchdev_obj_port_vlan *vlan)
 {
        struct ocelot *ocelot = ds->priv;
-       u16 vid, flags = vlan->flags;
-       int err;
+       u16 flags = vlan->flags;
 
        /* Ocelot switches copy frames as-is to the CPU, so the flags:
         * egress-untagged or not, pvid or not, make no difference. This
@@ -130,61 +122,40 @@ static int felix_vlan_prepare(struct dsa_switch *ds, int port,
        if (port == ocelot->npi)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               err = ocelot_vlan_prepare(ocelot, port, vid,
-                                         flags & BRIDGE_VLAN_INFO_PVID,
-                                         flags & BRIDGE_VLAN_INFO_UNTAGGED);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return ocelot_vlan_prepare(ocelot, port, vlan->vid,
+                                  flags & BRIDGE_VLAN_INFO_PVID,
+                                  flags & BRIDGE_VLAN_INFO_UNTAGGED);
 }
 
-static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
-                               struct switchdev_trans *trans)
+static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
 {
        struct ocelot *ocelot = ds->priv;
 
-       return ocelot_port_vlan_filtering(ocelot, port, enabled, trans);
+       return ocelot_port_vlan_filtering(ocelot, port, enabled);
 }
 
-static void felix_vlan_add(struct dsa_switch *ds, int port,
-                          const struct switchdev_obj_port_vlan *vlan)
+static int felix_vlan_add(struct dsa_switch *ds, int port,
+                         const struct switchdev_obj_port_vlan *vlan)
 {
        struct ocelot *ocelot = ds->priv;
        u16 flags = vlan->flags;
-       u16 vid;
        int err;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               err = ocelot_vlan_add(ocelot, port, vid,
-                                     flags & BRIDGE_VLAN_INFO_PVID,
-                                     flags & BRIDGE_VLAN_INFO_UNTAGGED);
-               if (err) {
-                       dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n",
-                               vid, port, err);
-                       return;
-               }
-       }
+       err = felix_vlan_prepare(ds, port, vlan);
+       if (err)
+               return err;
+
+       return ocelot_vlan_add(ocelot, port, vlan->vid,
+                              flags & BRIDGE_VLAN_INFO_PVID,
+                              flags & BRIDGE_VLAN_INFO_UNTAGGED);
 }
 
 static int felix_vlan_del(struct dsa_switch *ds, int port,
                          const struct switchdev_obj_port_vlan *vlan)
 {
        struct ocelot *ocelot = ds->priv;
-       u16 vid;
-       int err;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               err = ocelot_vlan_del(ocelot, port, vid);
-               if (err) {
-                       dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n",
-                               vid, port, err);
-                       return err;
-               }
-       }
-       return 0;
+       return ocelot_vlan_del(ocelot, port, vlan->vid);
 }
 
 static int felix_port_enable(struct dsa_switch *ds, int port,
@@ -328,7 +299,7 @@ static void felix_port_qos_map_init(struct ocelot *ocelot, int port)
                       ANA_PORT_QOS_CFG,
                       port);
 
-       for (i = 0; i < FELIX_NUM_TC * 2; i++) {
+       for (i = 0; i < OCELOT_NUM_TC * 2; i++) {
                ocelot_rmw_ix(ocelot,
                              (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & i) |
                              ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(i),
@@ -451,12 +422,12 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
        ocelot->map             = felix->info->map;
        ocelot->stats_layout    = felix->info->stats_layout;
        ocelot->num_stats       = felix->info->num_stats;
-       ocelot->shared_queue_sz = felix->info->shared_queue_sz;
        ocelot->num_mact_rows   = felix->info->num_mact_rows;
        ocelot->vcap            = felix->info->vcap;
        ocelot->ops             = felix->info->ops;
        ocelot->inj_prefix      = OCELOT_TAG_PREFIX_SHORT;
        ocelot->xtr_prefix      = OCELOT_TAG_PREFIX_SHORT;
+       ocelot->devlink         = felix->ds->devlink;
 
        port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t),
                                 GFP_KERNEL);
@@ -618,6 +589,10 @@ static int felix_setup(struct dsa_switch *ds)
                felix_port_qos_map_init(ocelot, port);
        }
 
+       err = ocelot_devlink_sb_register(ocelot);
+       if (err)
+               return err;
+
        /* Include the CPU port module in the forwarding mask for unknown
         * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST
         * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since
@@ -628,7 +603,7 @@ static int felix_setup(struct dsa_switch *ds)
                         ANA_PGID_PGID, PGID_UC);
 
        ds->mtu_enforcement_ingress = true;
-       ds->configure_vlan_while_not_filtering = true;
+       ds->assisted_learning_on_cpu_port = true;
 
        return 0;
 }
@@ -639,14 +614,15 @@ static void felix_teardown(struct dsa_switch *ds)
        struct felix *felix = ocelot_to_felix(ocelot);
        int port;
 
-       if (felix->info->mdio_bus_free)
-               felix->info->mdio_bus_free(ocelot);
+       ocelot_devlink_sb_unregister(ocelot);
+       ocelot_deinit_timestamp(ocelot);
+       ocelot_deinit(ocelot);
 
        for (port = 0; port < ocelot->num_phys_ports; port++)
                ocelot_deinit_port(ocelot, port);
-       ocelot_deinit_timestamp(ocelot);
-       /* stop workqueue thread */
-       ocelot_deinit(ocelot);
+
+       if (felix->info->mdio_bus_free)
+               felix->info->mdio_bus_free(ocelot);
 }
 
 static int felix_hwtstamp_get(struct dsa_switch *ds, int port,
@@ -780,46 +756,156 @@ static int felix_port_setup_tc(struct dsa_switch *ds, int port,
                return -EOPNOTSUPP;
 }
 
+static int felix_sb_pool_get(struct dsa_switch *ds, unsigned int sb_index,
+                            u16 pool_index,
+                            struct devlink_sb_pool_info *pool_info)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_pool_get(ocelot, sb_index, pool_index, pool_info);
+}
+
+static int felix_sb_pool_set(struct dsa_switch *ds, unsigned int sb_index,
+                            u16 pool_index, u32 size,
+                            enum devlink_sb_threshold_type threshold_type,
+                            struct netlink_ext_ack *extack)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_pool_set(ocelot, sb_index, pool_index, size,
+                                 threshold_type, extack);
+}
+
+static int felix_sb_port_pool_get(struct dsa_switch *ds, int port,
+                                 unsigned int sb_index, u16 pool_index,
+                                 u32 *p_threshold)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_port_pool_get(ocelot, port, sb_index, pool_index,
+                                      p_threshold);
+}
+
+static int felix_sb_port_pool_set(struct dsa_switch *ds, int port,
+                                 unsigned int sb_index, u16 pool_index,
+                                 u32 threshold, struct netlink_ext_ack *extack)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_port_pool_set(ocelot, port, sb_index, pool_index,
+                                      threshold, extack);
+}
+
+static int felix_sb_tc_pool_bind_get(struct dsa_switch *ds, int port,
+                                    unsigned int sb_index, u16 tc_index,
+                                    enum devlink_sb_pool_type pool_type,
+                                    u16 *p_pool_index, u32 *p_threshold)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_tc_pool_bind_get(ocelot, port, sb_index, tc_index,
+                                         pool_type, p_pool_index,
+                                         p_threshold);
+}
+
+static int felix_sb_tc_pool_bind_set(struct dsa_switch *ds, int port,
+                                    unsigned int sb_index, u16 tc_index,
+                                    enum devlink_sb_pool_type pool_type,
+                                    u16 pool_index, u32 threshold,
+                                    struct netlink_ext_ack *extack)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_tc_pool_bind_set(ocelot, port, sb_index, tc_index,
+                                         pool_type, pool_index, threshold,
+                                         extack);
+}
+
+static int felix_sb_occ_snapshot(struct dsa_switch *ds,
+                                unsigned int sb_index)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_occ_snapshot(ocelot, sb_index);
+}
+
+static int felix_sb_occ_max_clear(struct dsa_switch *ds,
+                                 unsigned int sb_index)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_occ_max_clear(ocelot, sb_index);
+}
+
+static int felix_sb_occ_port_pool_get(struct dsa_switch *ds, int port,
+                                     unsigned int sb_index, u16 pool_index,
+                                     u32 *p_cur, u32 *p_max)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_occ_port_pool_get(ocelot, port, sb_index, pool_index,
+                                          p_cur, p_max);
+}
+
+static int felix_sb_occ_tc_port_bind_get(struct dsa_switch *ds, int port,
+                                        unsigned int sb_index, u16 tc_index,
+                                        enum devlink_sb_pool_type pool_type,
+                                        u32 *p_cur, u32 *p_max)
+{
+       struct ocelot *ocelot = ds->priv;
+
+       return ocelot_sb_occ_tc_port_bind_get(ocelot, port, sb_index, tc_index,
+                                             pool_type, p_cur, p_max);
+}
+
 const struct dsa_switch_ops felix_switch_ops = {
-       .get_tag_protocol       = felix_get_tag_protocol,
-       .setup                  = felix_setup,
-       .teardown               = felix_teardown,
-       .set_ageing_time        = felix_set_ageing_time,
-       .get_strings            = felix_get_strings,
-       .get_ethtool_stats      = felix_get_ethtool_stats,
-       .get_sset_count         = felix_get_sset_count,
-       .get_ts_info            = felix_get_ts_info,
-       .phylink_validate       = felix_phylink_validate,
-       .phylink_mac_config     = felix_phylink_mac_config,
-       .phylink_mac_link_down  = felix_phylink_mac_link_down,
-       .phylink_mac_link_up    = felix_phylink_mac_link_up,
-       .port_enable            = felix_port_enable,
-       .port_disable           = felix_port_disable,
-       .port_fdb_dump          = felix_fdb_dump,
-       .port_fdb_add           = felix_fdb_add,
-       .port_fdb_del           = felix_fdb_del,
-       .port_mdb_prepare       = felix_mdb_prepare,
-       .port_mdb_add           = felix_mdb_add,
-       .port_mdb_del           = felix_mdb_del,
-       .port_bridge_join       = felix_bridge_join,
-       .port_bridge_leave      = felix_bridge_leave,
-       .port_stp_state_set     = felix_bridge_stp_state_set,
-       .port_vlan_prepare      = felix_vlan_prepare,
-       .port_vlan_filtering    = felix_vlan_filtering,
-       .port_vlan_add          = felix_vlan_add,
-       .port_vlan_del          = felix_vlan_del,
-       .port_hwtstamp_get      = felix_hwtstamp_get,
-       .port_hwtstamp_set      = felix_hwtstamp_set,
-       .port_rxtstamp          = felix_rxtstamp,
-       .port_txtstamp          = felix_txtstamp,
-       .port_change_mtu        = felix_change_mtu,
-       .port_max_mtu           = felix_get_max_mtu,
-       .port_policer_add       = felix_port_policer_add,
-       .port_policer_del       = felix_port_policer_del,
-       .cls_flower_add         = felix_cls_flower_add,
-       .cls_flower_del         = felix_cls_flower_del,
-       .cls_flower_stats       = felix_cls_flower_stats,
-       .port_setup_tc          = felix_port_setup_tc,
+       .get_tag_protocol               = felix_get_tag_protocol,
+       .setup                          = felix_setup,
+       .teardown                       = felix_teardown,
+       .set_ageing_time                = felix_set_ageing_time,
+       .get_strings                    = felix_get_strings,
+       .get_ethtool_stats              = felix_get_ethtool_stats,
+       .get_sset_count                 = felix_get_sset_count,
+       .get_ts_info                    = felix_get_ts_info,
+       .phylink_validate               = felix_phylink_validate,
+       .phylink_mac_config             = felix_phylink_mac_config,
+       .phylink_mac_link_down          = felix_phylink_mac_link_down,
+       .phylink_mac_link_up            = felix_phylink_mac_link_up,
+       .port_enable                    = felix_port_enable,
+       .port_disable                   = felix_port_disable,
+       .port_fdb_dump                  = felix_fdb_dump,
+       .port_fdb_add                   = felix_fdb_add,
+       .port_fdb_del                   = felix_fdb_del,
+       .port_mdb_add                   = felix_mdb_add,
+       .port_mdb_del                   = felix_mdb_del,
+       .port_bridge_join               = felix_bridge_join,
+       .port_bridge_leave              = felix_bridge_leave,
+       .port_stp_state_set             = felix_bridge_stp_state_set,
+       .port_vlan_filtering            = felix_vlan_filtering,
+       .port_vlan_add                  = felix_vlan_add,
+       .port_vlan_del                  = felix_vlan_del,
+       .port_hwtstamp_get              = felix_hwtstamp_get,
+       .port_hwtstamp_set              = felix_hwtstamp_set,
+       .port_rxtstamp                  = felix_rxtstamp,
+       .port_txtstamp                  = felix_txtstamp,
+       .port_change_mtu                = felix_change_mtu,
+       .port_max_mtu                   = felix_get_max_mtu,
+       .port_policer_add               = felix_port_policer_add,
+       .port_policer_del               = felix_port_policer_del,
+       .cls_flower_add                 = felix_cls_flower_add,
+       .cls_flower_del                 = felix_cls_flower_del,
+       .cls_flower_stats               = felix_cls_flower_stats,
+       .port_setup_tc                  = felix_port_setup_tc,
+       .devlink_sb_pool_get            = felix_sb_pool_get,
+       .devlink_sb_pool_set            = felix_sb_pool_set,
+       .devlink_sb_port_pool_get       = felix_sb_port_pool_get,
+       .devlink_sb_port_pool_set       = felix_sb_port_pool_set,
+       .devlink_sb_tc_pool_bind_get    = felix_sb_tc_pool_bind_get,
+       .devlink_sb_tc_pool_bind_set    = felix_sb_tc_pool_bind_set,
+       .devlink_sb_occ_snapshot        = felix_sb_occ_snapshot,
+       .devlink_sb_occ_max_clear       = felix_sb_occ_max_clear,
+       .devlink_sb_occ_port_pool_get   = felix_sb_occ_port_pool_get,
+       .devlink_sb_occ_tc_port_bind_get= felix_sb_occ_tc_port_bind_get,
 };
 
 struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port)
index 4c71732..994835c 100644 (file)
@@ -5,7 +5,6 @@
 #define _MSCC_FELIX_H
 
 #define ocelot_to_felix(o)             container_of((o), struct felix, ocelot)
-#define FELIX_NUM_TC                   8
 
 /* Platform-specific information */
 struct felix_info {
@@ -15,7 +14,6 @@ struct felix_info {
        const struct reg_field          *regfields;
        const u32 *const                *map;
        const struct ocelot_ops         *ops;
-       int                             shared_queue_sz;
        int                             num_mact_rows;
        const struct ocelot_stat_layout *stats_layout;
        unsigned int                    num_stats;
index 2e5bbdc..f9711e6 100644 (file)
@@ -1006,9 +1006,27 @@ static u16 vsc9959_wm_enc(u16 value)
        return value;
 }
 
+static u16 vsc9959_wm_dec(u16 wm)
+{
+       WARN_ON(wm & ~GENMASK(8, 0));
+
+       if (wm & BIT(8))
+               return (wm & GENMASK(7, 0)) * 16;
+
+       return wm;
+}
+
+static void vsc9959_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
+{
+       *inuse = (val & GENMASK(23, 12)) >> 12;
+       *maxuse = val & GENMASK(11, 0);
+}
+
 static const struct ocelot_ops vsc9959_ops = {
        .reset                  = vsc9959_reset,
        .wm_enc                 = vsc9959_wm_enc,
+       .wm_dec                 = vsc9959_wm_dec,
+       .wm_stat                = vsc9959_wm_stat,
        .port_to_netdev         = felix_port_to_netdev,
        .netdev_to_port         = felix_netdev_to_port,
 };
@@ -1356,10 +1374,9 @@ static const struct felix_info felix_info_vsc9959 = {
        .stats_layout           = vsc9959_stats_layout,
        .num_stats              = ARRAY_SIZE(vsc9959_stats_layout),
        .vcap                   = vsc9959_vcap_props,
-       .shared_queue_sz        = 128 * 1024,
        .num_mact_rows          = 2048,
        .num_ports              = 6,
-       .num_tx_queues          = FELIX_NUM_TC,
+       .num_tx_queues          = OCELOT_NUM_TC,
        .switch_pci_bar         = 4,
        .imdio_pci_bar          = 0,
        .ptp_caps               = &vsc9959_ptp_caps,
@@ -1408,17 +1425,6 @@ static int felix_pci_probe(struct pci_dev *pdev,
                goto err_pci_enable;
        }
 
-       /* set up for high or low dma */
-       err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
-       if (err) {
-               err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
-               if (err) {
-                       dev_err(&pdev->dev,
-                               "DMA configuration failed: 0x%x\n", err);
-                       goto err_dma;
-               }
-       }
-
        felix = kzalloc(sizeof(struct felix), GFP_KERNEL);
        if (!felix) {
                err = -ENOMEM;
@@ -1429,7 +1435,7 @@ static int felix_pci_probe(struct pci_dev *pdev,
        pci_set_drvdata(pdev, felix);
        ocelot = &felix->ocelot;
        ocelot->dev = &pdev->dev;
-       ocelot->num_flooding_pgids = FELIX_NUM_TC;
+       ocelot->num_flooding_pgids = OCELOT_NUM_TC;
        felix->info = &felix_info_vsc9959;
        felix->switch_base = pci_resource_start(pdev,
                                                felix->info->switch_pci_bar);
@@ -1474,9 +1480,8 @@ err_register_ds:
        kfree(ds);
 err_alloc_ds:
 err_alloc_irq:
-err_alloc_felix:
        kfree(felix);
-err_dma:
+err_alloc_felix:
        pci_disable_device(pdev);
 err_pci_enable:
        return err;
index ebbaf68..5e9bfde 100644 (file)
@@ -1057,9 +1057,27 @@ static u16 vsc9953_wm_enc(u16 value)
        return value;
 }
 
+static u16 vsc9953_wm_dec(u16 wm)
+{
+       WARN_ON(wm & ~GENMASK(9, 0));
+
+       if (wm & BIT(9))
+               return (wm & GENMASK(8, 0)) * 16;
+
+       return wm;
+}
+
+static void vsc9953_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
+{
+       *inuse = (val & GENMASK(25, 13)) >> 13;
+       *maxuse = val & GENMASK(12, 0);
+}
+
 static const struct ocelot_ops vsc9953_ops = {
        .reset                  = vsc9953_reset,
        .wm_enc                 = vsc9953_wm_enc,
+       .wm_dec                 = vsc9953_wm_dec,
+       .wm_stat                = vsc9953_wm_stat,
        .port_to_netdev         = felix_port_to_netdev,
        .netdev_to_port         = felix_netdev_to_port,
 };
@@ -1181,9 +1199,9 @@ static const struct felix_info seville_info_vsc9953 = {
        .stats_layout           = vsc9953_stats_layout,
        .num_stats              = ARRAY_SIZE(vsc9953_stats_layout),
        .vcap                   = vsc9953_vcap_props,
-       .shared_queue_sz        = 256 * 1024,
        .num_mact_rows          = 2048,
        .num_ports              = 10,
+       .num_tx_queues          = OCELOT_NUM_TC,
        .mdio_bus_alloc         = vsc9953_mdio_bus_alloc,
        .mdio_bus_free          = vsc9953_mdio_bus_free,
        .phylink_validate       = vsc9953_phylink_validate,
index 4d49c5f..ca2ad77 100644 (file)
         AR9331_SW_PORT_STATUS_RX_FLOW_EN | AR9331_SW_PORT_STATUS_TX_FLOW_EN | \
         AR9331_SW_PORT_STATUS_SPEED_M)
 
+/* MIB registers */
+#define AR9331_MIB_COUNTER(x)                  (0x20000 + ((x) * 0x100))
+
 /* Phy bypass mode
  * ------------------------------------------------------------------------
  * Bit:   | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 |13 |14 |15 |
 #define AR9331_SW_MDIO_POLL_SLEEP_US           1
 #define AR9331_SW_MDIO_POLL_TIMEOUT_US         20
 
+/* The interval should be small enough to avoid overflow of 32bit MIBs */
+/*
+ * FIXME: until we can read MIBs from stats64 call directly (i.e. sleep
+ * there), we have to poll stats more frequently then it is actually needed.
+ * For overflow protection, normally, 100 sec interval should have been OK.
+ */
+#define STATS_INTERVAL_JIFFIES                 (3 * HZ)
+
+struct ar9331_sw_stats_raw {
+       u32 rxbroad;                    /* 0x00 */
+       u32 rxpause;                    /* 0x04 */
+       u32 rxmulti;                    /* 0x08 */
+       u32 rxfcserr;                   /* 0x0c */
+       u32 rxalignerr;                 /* 0x10 */
+       u32 rxrunt;                     /* 0x14 */
+       u32 rxfragment;                 /* 0x18 */
+       u32 rx64byte;                   /* 0x1c */
+       u32 rx128byte;                  /* 0x20 */
+       u32 rx256byte;                  /* 0x24 */
+       u32 rx512byte;                  /* 0x28 */
+       u32 rx1024byte;                 /* 0x2c */
+       u32 rx1518byte;                 /* 0x30 */
+       u32 rxmaxbyte;                  /* 0x34 */
+       u32 rxtoolong;                  /* 0x38 */
+       u32 rxgoodbyte;                 /* 0x3c */
+       u32 rxgoodbyte_hi;
+       u32 rxbadbyte;                  /* 0x44 */
+       u32 rxbadbyte_hi;
+       u32 rxoverflow;                 /* 0x4c */
+       u32 filtered;                   /* 0x50 */
+       u32 txbroad;                    /* 0x54 */
+       u32 txpause;                    /* 0x58 */
+       u32 txmulti;                    /* 0x5c */
+       u32 txunderrun;                 /* 0x60 */
+       u32 tx64byte;                   /* 0x64 */
+       u32 tx128byte;                  /* 0x68 */
+       u32 tx256byte;                  /* 0x6c */
+       u32 tx512byte;                  /* 0x70 */
+       u32 tx1024byte;                 /* 0x74 */
+       u32 tx1518byte;                 /* 0x78 */
+       u32 txmaxbyte;                  /* 0x7c */
+       u32 txoversize;                 /* 0x80 */
+       u32 txbyte;                     /* 0x84 */
+       u32 txbyte_hi;
+       u32 txcollision;                /* 0x8c */
+       u32 txabortcol;                 /* 0x90 */
+       u32 txmulticol;                 /* 0x94 */
+       u32 txsinglecol;                /* 0x98 */
+       u32 txexcdefer;                 /* 0x9c */
+       u32 txdefer;                    /* 0xa0 */
+       u32 txlatecol;                  /* 0xa4 */
+};
+
+struct ar9331_sw_port {
+       int idx;
+       struct delayed_work mib_read;
+       struct rtnl_link_stats64 stats;
+       struct spinlock stats_lock;
+};
+
 struct ar9331_sw_priv {
        struct device *dev;
        struct dsa_switch ds;
@@ -165,8 +228,17 @@ struct ar9331_sw_priv {
        struct mii_bus *sbus; /* mdio slave */
        struct regmap *regmap;
        struct reset_control *sw_reset;
+       struct ar9331_sw_port port[AR9331_SW_PORTS];
 };
 
+static struct ar9331_sw_priv *ar9331_sw_port_to_priv(struct ar9331_sw_port *port)
+{
+       struct ar9331_sw_port *p = port - port->idx;
+
+       return (struct ar9331_sw_priv *)((void *)p -
+                                        offsetof(struct ar9331_sw_priv, port));
+}
+
 /* Warning: switch reset will reset last AR9331_SW_MDIO_PHY_MODE_PAGE request
  * If some kind of optimization is used, the request should be repeated.
  */
@@ -330,6 +402,8 @@ static int ar9331_sw_setup(struct dsa_switch *ds)
        if (ret)
                goto error;
 
+       ds->configure_vlan_while_not_filtering = false;
+
        return 0;
 error:
        dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
@@ -424,6 +498,7 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
                                            phy_interface_t interface)
 {
        struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
+       struct ar9331_sw_port *p = &priv->port[port];
        struct regmap *regmap = priv->regmap;
        int ret;
 
@@ -431,6 +506,8 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port,
                                 AR9331_SW_PORT_STATUS_MAC_MASK, 0);
        if (ret)
                dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
+
+       cancel_delayed_work_sync(&p->mib_read);
 }
 
 static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
@@ -441,10 +518,13 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
                                          bool tx_pause, bool rx_pause)
 {
        struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
+       struct ar9331_sw_port *p = &priv->port[port];
        struct regmap *regmap = priv->regmap;
        u32 val;
        int ret;
 
+       schedule_delayed_work(&p->mib_read, 0);
+
        val = AR9331_SW_PORT_STATUS_MAC_MASK;
        switch (speed) {
        case SPEED_1000:
@@ -477,6 +557,73 @@ static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port,
                dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
 }
 
+static void ar9331_read_stats(struct ar9331_sw_port *port)
+{
+       struct ar9331_sw_priv *priv = ar9331_sw_port_to_priv(port);
+       struct rtnl_link_stats64 *stats = &port->stats;
+       struct ar9331_sw_stats_raw raw;
+       int ret;
+
+       /* Do the slowest part first, to avoid needless locking for long time */
+       ret = regmap_bulk_read(priv->regmap, AR9331_MIB_COUNTER(port->idx),
+                              &raw, sizeof(raw) / sizeof(u32));
+       if (ret) {
+               dev_err_ratelimited(priv->dev, "%s: %i\n", __func__, ret);
+               return;
+       }
+       /* All MIB counters are cleared automatically on read */
+
+       spin_lock(&port->stats_lock);
+
+       stats->rx_bytes += raw.rxgoodbyte;
+       stats->tx_bytes += raw.txbyte;
+
+       stats->rx_packets += raw.rx64byte + raw.rx128byte + raw.rx256byte +
+               raw.rx512byte + raw.rx1024byte + raw.rx1518byte + raw.rxmaxbyte;
+       stats->tx_packets += raw.tx64byte + raw.tx128byte + raw.tx256byte +
+               raw.tx512byte + raw.tx1024byte + raw.tx1518byte + raw.txmaxbyte;
+
+       stats->rx_length_errors += raw.rxrunt + raw.rxfragment + raw.rxtoolong;
+       stats->rx_crc_errors += raw.rxfcserr;
+       stats->rx_frame_errors += raw.rxalignerr;
+       stats->rx_missed_errors += raw.rxoverflow;
+       stats->rx_dropped += raw.filtered;
+       stats->rx_errors += raw.rxfcserr + raw.rxalignerr + raw.rxrunt +
+               raw.rxfragment + raw.rxoverflow + raw.rxtoolong;
+
+       stats->tx_window_errors += raw.txlatecol;
+       stats->tx_fifo_errors += raw.txunderrun;
+       stats->tx_aborted_errors += raw.txabortcol;
+       stats->tx_errors += raw.txoversize + raw.txabortcol + raw.txunderrun +
+               raw.txlatecol;
+
+       stats->multicast += raw.rxmulti;
+       stats->collisions += raw.txcollision;
+
+       spin_unlock(&port->stats_lock);
+}
+
+static void ar9331_do_stats_poll(struct work_struct *work)
+{
+       struct ar9331_sw_port *port = container_of(work, struct ar9331_sw_port,
+                                                  mib_read.work);
+
+       ar9331_read_stats(port);
+
+       schedule_delayed_work(&port->mib_read, STATS_INTERVAL_JIFFIES);
+}
+
+static void ar9331_get_stats64(struct dsa_switch *ds, int port,
+                              struct rtnl_link_stats64 *s)
+{
+       struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv;
+       struct ar9331_sw_port *p = &priv->port[port];
+
+       spin_lock(&p->stats_lock);
+       memcpy(s, &p->stats, sizeof(*s));
+       spin_unlock(&p->stats_lock);
+}
+
 static const struct dsa_switch_ops ar9331_sw_ops = {
        .get_tag_protocol       = ar9331_sw_get_tag_protocol,
        .setup                  = ar9331_sw_setup,
@@ -485,6 +632,7 @@ static const struct dsa_switch_ops ar9331_sw_ops = {
        .phylink_mac_config     = ar9331_sw_phylink_mac_config,
        .phylink_mac_link_down  = ar9331_sw_phylink_mac_link_down,
        .phylink_mac_link_up    = ar9331_sw_phylink_mac_link_up,
+       .get_stats64            = ar9331_get_stats64,
 };
 
 static irqreturn_t ar9331_sw_irq(int irq, void *data)
@@ -796,7 +944,7 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
 {
        struct ar9331_sw_priv *priv;
        struct dsa_switch *ds;
-       int ret;
+       int ret, i;
 
        priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -831,6 +979,14 @@ static int ar9331_sw_probe(struct mdio_device *mdiodev)
        ds->ops = &priv->ops;
        dev_set_drvdata(&mdiodev->dev, priv);
 
+       for (i = 0; i < ARRAY_SIZE(priv->port); i++) {
+               struct ar9331_sw_port *port = &priv->port[i];
+
+               port->idx = i;
+               spin_lock_init(&port->stats_lock);
+               INIT_DELAYED_WORK(&port->mib_read, ar9331_do_stats_poll);
+       }
+
        ret = dsa_register_switch(ds);
        if (ret)
                goto err_remove_irq;
@@ -846,6 +1002,13 @@ err_remove_irq:
 static void ar9331_sw_remove(struct mdio_device *mdiodev)
 {
        struct ar9331_sw_priv *priv = dev_get_drvdata(&mdiodev->dev);
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(priv->port); i++) {
+               struct ar9331_sw_port *port = &priv->port[i];
+
+               cancel_delayed_work_sync(&port->mib_read);
+       }
 
        irq_domain_remove(priv->irqdomain);
        mdiobus_unregister(priv->mbus);
index 5bdac66..6127823 100644 (file)
@@ -1294,14 +1294,10 @@ qca8k_port_fdb_dump(struct dsa_switch *ds, int port,
 }
 
 static int
-qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
-                         struct switchdev_trans *trans)
+qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
 {
        struct qca8k_priv *priv = ds->priv;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        if (vlan_filtering) {
                qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port),
                          QCA8K_PORT_LOOKUP_VLAN_MODE,
@@ -1316,13 +1312,6 @@ qca8k_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 }
 
 static int
-qca8k_port_vlan_prepare(struct dsa_switch *ds, int port,
-                       const struct switchdev_obj_port_vlan *vlan)
-{
-       return 0;
-}
-
-static void
 qca8k_port_vlan_add(struct dsa_switch *ds, int port,
                    const struct switchdev_obj_port_vlan *vlan)
 {
@@ -1330,24 +1319,24 @@ qca8k_port_vlan_add(struct dsa_switch *ds, int port,
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        struct qca8k_priv *priv = ds->priv;
        int ret = 0;
-       u16 vid;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid)
-               ret = qca8k_vlan_add(priv, port, vid, untagged);
-
-       if (ret)
+       ret = qca8k_vlan_add(priv, port, vlan->vid, untagged);
+       if (ret) {
                dev_err(priv->dev, "Failed to add VLAN to port %d (%d)", port, ret);
+               return ret;
+       }
 
        if (pvid) {
                int shift = 16 * (port % 2);
 
                qca8k_rmw(priv, QCA8K_EGRESS_VLAN(port),
-                         0xfff << shift,
-                         vlan->vid_end << shift);
+                         0xfff << shift, vlan->vid << shift);
                qca8k_write(priv, QCA8K_REG_PORT_VLAN_CTRL0(port),
-                           QCA8K_PORT_VLAN_CVID(vlan->vid_end) |
-                           QCA8K_PORT_VLAN_SVID(vlan->vid_end));
+                           QCA8K_PORT_VLAN_CVID(vlan->vid) |
+                           QCA8K_PORT_VLAN_SVID(vlan->vid));
        }
+
+       return 0;
 }
 
 static int
@@ -1356,11 +1345,8 @@ qca8k_port_vlan_del(struct dsa_switch *ds, int port,
 {
        struct qca8k_priv *priv = ds->priv;
        int ret = 0;
-       u16 vid;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end && !ret; ++vid)
-               ret = qca8k_vlan_del(priv, port, vid);
 
+       ret = qca8k_vlan_del(priv, port, vlan->vid);
        if (ret)
                dev_err(priv->dev, "Failed to delete VLAN from port %d (%d)", port, ret);
 
@@ -1393,7 +1379,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = {
        .port_fdb_del           = qca8k_port_fdb_del,
        .port_fdb_dump          = qca8k_port_fdb_dump,
        .port_vlan_filtering    = qca8k_port_vlan_filtering,
-       .port_vlan_prepare      = qca8k_port_vlan_prepare,
        .port_vlan_add          = qca8k_port_vlan_add,
        .port_vlan_del          = qca8k_port_vlan_del,
        .phylink_validate       = qca8k_phylink_validate,
@@ -1446,7 +1431,6 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
 
        priv->ds->dev = &mdiodev->dev;
        priv->ds->num_ports = QCA8K_NUM_PORTS;
-       priv->ds->configure_vlan_while_not_filtering = true;
        priv->ds->priv = priv;
        priv->ops = qca8k_switch_ops;
        priv->ds->ops = &priv->ops;
index 6b6a3de..26376b0 100644 (file)
@@ -131,12 +131,9 @@ int rtl8366_enable_vlan(struct realtek_smi *smi, bool enable);
 int rtl8366_reset_vlan(struct realtek_smi *smi);
 int rtl8366_init_vlan(struct realtek_smi *smi);
 int rtl8366_vlan_filtering(struct dsa_switch *ds, int port,
-                          bool vlan_filtering,
-                          struct switchdev_trans *trans);
-int rtl8366_vlan_prepare(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_vlan *vlan);
-void rtl8366_vlan_add(struct dsa_switch *ds, int port,
-                     const struct switchdev_obj_port_vlan *vlan);
+                          bool vlan_filtering);
+int rtl8366_vlan_add(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_vlan *vlan);
 int rtl8366_vlan_del(struct dsa_switch *ds, int port,
                     const struct switchdev_obj_port_vlan *vlan);
 void rtl8366_get_strings(struct dsa_switch *ds, int port, u32 stringset,
index 83d481e..3b24f2e 100644 (file)
@@ -340,20 +340,15 @@ int rtl8366_init_vlan(struct realtek_smi *smi)
 }
 EXPORT_SYMBOL_GPL(rtl8366_init_vlan);
 
-int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
-                          struct switchdev_trans *trans)
+int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
 {
        struct realtek_smi *smi = ds->priv;
        struct rtl8366_vlan_4k vlan4k;
        int ret;
 
        /* Use VLAN nr port + 1 since VLAN0 is not valid */
-       if (switchdev_trans_ph_prepare(trans)) {
-               if (!smi->ops->is_vlan_valid(smi, port + 1))
-                       return -EINVAL;
-
-               return 0;
-       }
+       if (!smi->ops->is_vlan_valid(smi, port + 1))
+               return -EINVAL;
 
        dev_info(smi->dev, "%s filtering on port %d\n",
                 vlan_filtering ? "enable" : "disable",
@@ -379,76 +374,56 @@ int rtl8366_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
 }
 EXPORT_SYMBOL_GPL(rtl8366_vlan_filtering);
 
-int rtl8366_vlan_prepare(struct dsa_switch *ds, int port,
-                        const struct switchdev_obj_port_vlan *vlan)
-{
-       struct realtek_smi *smi = ds->priv;
-       u16 vid;
-
-       for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++)
-               if (!smi->ops->is_vlan_valid(smi, vid))
-                       return -EINVAL;
-
-       dev_info(smi->dev, "prepare VLANs %04x..%04x\n",
-                vlan->vid_begin, vlan->vid_end);
-
-       /* Enable VLAN in the hardware
-        * FIXME: what's with this 4k business?
-        * Just rtl8366_enable_vlan() seems inconclusive.
-        */
-       return rtl8366_enable_vlan4k(smi, true);
-}
-EXPORT_SYMBOL_GPL(rtl8366_vlan_prepare);
-
-void rtl8366_vlan_add(struct dsa_switch *ds, int port,
-                     const struct switchdev_obj_port_vlan *vlan)
+int rtl8366_vlan_add(struct dsa_switch *ds, int port,
+                    const struct switchdev_obj_port_vlan *vlan)
 {
        bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
        bool pvid = !!(vlan->flags & BRIDGE_VLAN_INFO_PVID);
        struct realtek_smi *smi = ds->priv;
        u32 member = 0;
        u32 untag = 0;
-       u16 vid;
        int ret;
 
-       for (vid = vlan->vid_begin; vid < vlan->vid_end; vid++)
-               if (!smi->ops->is_vlan_valid(smi, vid))
-                       return;
+       if (!smi->ops->is_vlan_valid(smi, vlan->vid))
+               return -EINVAL;
+
+       /* Enable VLAN in the hardware
+        * FIXME: what's with this 4k business?
+        * Just rtl8366_enable_vlan() seems inconclusive.
+        */
+       ret = rtl8366_enable_vlan4k(smi, true);
+       if (ret)
+               return ret;
 
        dev_info(smi->dev, "add VLAN %d on port %d, %s, %s\n",
-                vlan->vid_begin,
-                port,
-                untagged ? "untagged" : "tagged",
+                vlan->vid, port, untagged ? "untagged" : "tagged",
                 pvid ? " PVID" : "no PVID");
 
        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
                dev_err(smi->dev, "port is DSA or CPU port\n");
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               member |= BIT(port);
-
-               if (untagged)
-                       untag |= BIT(port);
+       member |= BIT(port);
 
-               ret = rtl8366_set_vlan(smi, vid, member, untag, 0);
-               if (ret)
-                       dev_err(smi->dev,
-                               "failed to set up VLAN %04x",
-                               vid);
+       if (untagged)
+               untag |= BIT(port);
 
-               if (!pvid)
-                       continue;
+       ret = rtl8366_set_vlan(smi, vlan->vid, member, untag, 0);
+       if (ret) {
+               dev_err(smi->dev, "failed to set up VLAN %04x", vlan->vid);
+               return ret;
+       }
 
-               ret = rtl8366_set_pvid(smi, port, vid);
-               if (ret)
-                       dev_err(smi->dev,
-                               "failed to set PVID on port %d to VLAN %04x",
-                               port, vid);
+       if (!pvid)
+               return 0;
 
-               if (!ret)
-                       dev_dbg(smi->dev, "VLAN add: added VLAN %d with PVID on port %d\n",
-                               vid, port);
+       ret = rtl8366_set_pvid(smi, port, vlan->vid);
+       if (ret) {
+               dev_err(smi->dev, "failed to set PVID on port %d to VLAN %04x",
+                       port, vlan->vid);
+               return ret;
        }
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(rtl8366_vlan_add);
 
@@ -456,46 +431,39 @@ int rtl8366_vlan_del(struct dsa_switch *ds, int port,
                     const struct switchdev_obj_port_vlan *vlan)
 {
        struct realtek_smi *smi = ds->priv;
-       u16 vid;
-       int ret;
-
-       dev_info(smi->dev, "del VLAN on port %d\n", port);
+       int ret, i;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               int i;
+       dev_info(smi->dev, "del VLAN %04x on port %d\n", vlan->vid, port);
 
-               dev_info(smi->dev, "del VLAN %04x\n", vid);
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               struct rtl8366_vlan_mc vlanmc;
 
-               for (i = 0; i < smi->num_vlan_mc; i++) {
-                       struct rtl8366_vlan_mc vlanmc;
+               ret = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (ret)
+                       return ret;
 
-                       ret = smi->ops->get_vlan_mc(smi, i, &vlanmc);
-                       if (ret)
+               if (vlan->vid == vlanmc.vid) {
+                       /* Remove this port from the VLAN */
+                       vlanmc.member &= ~BIT(port);
+                       vlanmc.untag &= ~BIT(port);
+                       /*
+                        * If no ports are members of this VLAN
+                        * anymore then clear the whole member
+                        * config so it can be reused.
+                        */
+                       if (!vlanmc.member && vlanmc.untag) {
+                               vlanmc.vid = 0;
+                               vlanmc.priority = 0;
+                               vlanmc.fid = 0;
+                       }
+                       ret = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (ret) {
+                               dev_err(smi->dev,
+                                       "failed to remove VLAN %04x\n",
+                                       vlan->vid);
                                return ret;
-
-                       if (vid == vlanmc.vid) {
-                               /* Remove this port from the VLAN */
-                               vlanmc.member &= ~BIT(port);
-                               vlanmc.untag &= ~BIT(port);
-                               /*
-                                * If no ports are members of this VLAN
-                                * anymore then clear the whole member
-                                * config so it can be reused.
-                                */
-                               if (!vlanmc.member && vlanmc.untag) {
-                                       vlanmc.vid = 0;
-                                       vlanmc.priority = 0;
-                                       vlanmc.fid = 0;
-                               }
-                               ret = smi->ops->set_vlan_mc(smi, i, &vlanmc);
-                               if (ret) {
-                                       dev_err(smi->dev,
-                                               "failed to remove VLAN %04x\n",
-                                               vid);
-                                       return ret;
-                               }
-                               break;
                        }
+                       break;
                }
        }
 
index cfe5696..a89093b 100644 (file)
@@ -601,108 +601,114 @@ static int rtl8366rb_set_addr(struct realtek_smi *smi)
 
 /* Found in a vendor driver */
 
+/* Struct for handling the jam tables' entries */
+struct rtl8366rb_jam_tbl_entry {
+       u16 reg;
+       u16 val;
+};
+
 /* For the "version 0" early silicon, appear in most source releases */
-static const u16 rtl8366rb_init_jam_ver_0[] = {
-       0x000B, 0x0001, 0x03A6, 0x0100, 0x03A7, 0x0001, 0x02D1, 0x3FFF,
-       0x02D2, 0x3FFF, 0x02D3, 0x3FFF, 0x02D4, 0x3FFF, 0x02D5, 0x3FFF,
-       0x02D6, 0x3FFF, 0x02D7, 0x3FFF, 0x02D8, 0x3FFF, 0x022B, 0x0688,
-       0x022C, 0x0FAC, 0x03D0, 0x4688, 0x03D1, 0x01F5, 0x0000, 0x0830,
-       0x02F9, 0x0200, 0x02F7, 0x7FFF, 0x02F8, 0x03FF, 0x0080, 0x03E8,
-       0x0081, 0x00CE, 0x0082, 0x00DA, 0x0083, 0x0230, 0xBE0F, 0x2000,
-       0x0231, 0x422A, 0x0232, 0x422A, 0x0233, 0x422A, 0x0234, 0x422A,
-       0x0235, 0x422A, 0x0236, 0x422A, 0x0237, 0x422A, 0x0238, 0x422A,
-       0x0239, 0x422A, 0x023A, 0x422A, 0x023B, 0x422A, 0x023C, 0x422A,
-       0x023D, 0x422A, 0x023E, 0x422A, 0x023F, 0x422A, 0x0240, 0x422A,
-       0x0241, 0x422A, 0x0242, 0x422A, 0x0243, 0x422A, 0x0244, 0x422A,
-       0x0245, 0x422A, 0x0246, 0x422A, 0x0247, 0x422A, 0x0248, 0x422A,
-       0x0249, 0x0146, 0x024A, 0x0146, 0x024B, 0x0146, 0xBE03, 0xC961,
-       0x024D, 0x0146, 0x024E, 0x0146, 0x024F, 0x0146, 0x0250, 0x0146,
-       0xBE64, 0x0226, 0x0252, 0x0146, 0x0253, 0x0146, 0x024C, 0x0146,
-       0x0251, 0x0146, 0x0254, 0x0146, 0xBE62, 0x3FD0, 0x0084, 0x0320,
-       0x0255, 0x0146, 0x0256, 0x0146, 0x0257, 0x0146, 0x0258, 0x0146,
-       0x0259, 0x0146, 0x025A, 0x0146, 0x025B, 0x0146, 0x025C, 0x0146,
-       0x025D, 0x0146, 0x025E, 0x0146, 0x025F, 0x0146, 0x0260, 0x0146,
-       0x0261, 0xA23F, 0x0262, 0x0294, 0x0263, 0xA23F, 0x0264, 0x0294,
-       0x0265, 0xA23F, 0x0266, 0x0294, 0x0267, 0xA23F, 0x0268, 0x0294,
-       0x0269, 0xA23F, 0x026A, 0x0294, 0x026B, 0xA23F, 0x026C, 0x0294,
-       0x026D, 0xA23F, 0x026E, 0x0294, 0x026F, 0xA23F, 0x0270, 0x0294,
-       0x02F5, 0x0048, 0xBE09, 0x0E00, 0xBE1E, 0x0FA0, 0xBE14, 0x8448,
-       0xBE15, 0x1007, 0xBE4A, 0xA284, 0xC454, 0x3F0B, 0xC474, 0x3F0B,
-       0xBE48, 0x3672, 0xBE4B, 0x17A7, 0xBE4C, 0x0B15, 0xBE52, 0x0EDD,
-       0xBE49, 0x8C00, 0xBE5B, 0x785C, 0xBE5C, 0x785C, 0xBE5D, 0x785C,
-       0xBE61, 0x368A, 0xBE63, 0x9B84, 0xC456, 0xCC13, 0xC476, 0xCC13,
-       0xBE65, 0x307D, 0xBE6D, 0x0005, 0xBE6E, 0xE120, 0xBE2E, 0x7BAF,
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_0[] = {
+       {0x000B, 0x0001}, {0x03A6, 0x0100}, {0x03A7, 0x0001}, {0x02D1, 0x3FFF},
+       {0x02D2, 0x3FFF}, {0x02D3, 0x3FFF}, {0x02D4, 0x3FFF}, {0x02D5, 0x3FFF},
+       {0x02D6, 0x3FFF}, {0x02D7, 0x3FFF}, {0x02D8, 0x3FFF}, {0x022B, 0x0688},
+       {0x022C, 0x0FAC}, {0x03D0, 0x4688}, {0x03D1, 0x01F5}, {0x0000, 0x0830},
+       {0x02F9, 0x0200}, {0x02F7, 0x7FFF}, {0x02F8, 0x03FF}, {0x0080, 0x03E8},
+       {0x0081, 0x00CE}, {0x0082, 0x00DA}, {0x0083, 0x0230}, {0xBE0F, 0x2000},
+       {0x0231, 0x422A}, {0x0232, 0x422A}, {0x0233, 0x422A}, {0x0234, 0x422A},
+       {0x0235, 0x422A}, {0x0236, 0x422A}, {0x0237, 0x422A}, {0x0238, 0x422A},
+       {0x0239, 0x422A}, {0x023A, 0x422A}, {0x023B, 0x422A}, {0x023C, 0x422A},
+       {0x023D, 0x422A}, {0x023E, 0x422A}, {0x023F, 0x422A}, {0x0240, 0x422A},
+       {0x0241, 0x422A}, {0x0242, 0x422A}, {0x0243, 0x422A}, {0x0244, 0x422A},
+       {0x0245, 0x422A}, {0x0246, 0x422A}, {0x0247, 0x422A}, {0x0248, 0x422A},
+       {0x0249, 0x0146}, {0x024A, 0x0146}, {0x024B, 0x0146}, {0xBE03, 0xC961},
+       {0x024D, 0x0146}, {0x024E, 0x0146}, {0x024F, 0x0146}, {0x0250, 0x0146},
+       {0xBE64, 0x0226}, {0x0252, 0x0146}, {0x0253, 0x0146}, {0x024C, 0x0146},
+       {0x0251, 0x0146}, {0x0254, 0x0146}, {0xBE62, 0x3FD0}, {0x0084, 0x0320},
+       {0x0255, 0x0146}, {0x0256, 0x0146}, {0x0257, 0x0146}, {0x0258, 0x0146},
+       {0x0259, 0x0146}, {0x025A, 0x0146}, {0x025B, 0x0146}, {0x025C, 0x0146},
+       {0x025D, 0x0146}, {0x025E, 0x0146}, {0x025F, 0x0146}, {0x0260, 0x0146},
+       {0x0261, 0xA23F}, {0x0262, 0x0294}, {0x0263, 0xA23F}, {0x0264, 0x0294},
+       {0x0265, 0xA23F}, {0x0266, 0x0294}, {0x0267, 0xA23F}, {0x0268, 0x0294},
+       {0x0269, 0xA23F}, {0x026A, 0x0294}, {0x026B, 0xA23F}, {0x026C, 0x0294},
+       {0x026D, 0xA23F}, {0x026E, 0x0294}, {0x026F, 0xA23F}, {0x0270, 0x0294},
+       {0x02F5, 0x0048}, {0xBE09, 0x0E00}, {0xBE1E, 0x0FA0}, {0xBE14, 0x8448},
+       {0xBE15, 0x1007}, {0xBE4A, 0xA284}, {0xC454, 0x3F0B}, {0xC474, 0x3F0B},
+       {0xBE48, 0x3672}, {0xBE4B, 0x17A7}, {0xBE4C, 0x0B15}, {0xBE52, 0x0EDD},
+       {0xBE49, 0x8C00}, {0xBE5B, 0x785C}, {0xBE5C, 0x785C}, {0xBE5D, 0x785C},
+       {0xBE61, 0x368A}, {0xBE63, 0x9B84}, {0xC456, 0xCC13}, {0xC476, 0xCC13},
+       {0xBE65, 0x307D}, {0xBE6D, 0x0005}, {0xBE6E, 0xE120}, {0xBE2E, 0x7BAF},
 };
 
 /* This v1 init sequence is from Belkin F5D8235 U-Boot release */
-static const u16 rtl8366rb_init_jam_ver_1[] = {
-       0x0000, 0x0830, 0x0001, 0x8000, 0x0400, 0x8130, 0xBE78, 0x3C3C,
-       0x0431, 0x5432, 0xBE37, 0x0CE4, 0x02FA, 0xFFDF, 0x02FB, 0xFFE0,
-       0xC44C, 0x1585, 0xC44C, 0x1185, 0xC44C, 0x1585, 0xC46C, 0x1585,
-       0xC46C, 0x1185, 0xC46C, 0x1585, 0xC451, 0x2135, 0xC471, 0x2135,
-       0xBE10, 0x8140, 0xBE15, 0x0007, 0xBE6E, 0xE120, 0xBE69, 0xD20F,
-       0xBE6B, 0x0320, 0xBE24, 0xB000, 0xBE23, 0xFF51, 0xBE22, 0xDF20,
-       0xBE21, 0x0140, 0xBE20, 0x00BB, 0xBE24, 0xB800, 0xBE24, 0x0000,
-       0xBE24, 0x7000, 0xBE23, 0xFF51, 0xBE22, 0xDF60, 0xBE21, 0x0140,
-       0xBE20, 0x0077, 0xBE24, 0x7800, 0xBE24, 0x0000, 0xBE2E, 0x7B7A,
-       0xBE36, 0x0CE4, 0x02F5, 0x0048, 0xBE77, 0x2940, 0x000A, 0x83E0,
-       0xBE79, 0x3C3C, 0xBE00, 0x1340,
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_1[] = {
+       {0x0000, 0x0830}, {0x0001, 0x8000}, {0x0400, 0x8130}, {0xBE78, 0x3C3C},
+       {0x0431, 0x5432}, {0xBE37, 0x0CE4}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0},
+       {0xC44C, 0x1585}, {0xC44C, 0x1185}, {0xC44C, 0x1585}, {0xC46C, 0x1585},
+       {0xC46C, 0x1185}, {0xC46C, 0x1585}, {0xC451, 0x2135}, {0xC471, 0x2135},
+       {0xBE10, 0x8140}, {0xBE15, 0x0007}, {0xBE6E, 0xE120}, {0xBE69, 0xD20F},
+       {0xBE6B, 0x0320}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF20},
+       {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, {0xBE24, 0x0000},
+       {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, {0xBE21, 0x0140},
+       {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, {0xBE2E, 0x7B7A},
+       {0xBE36, 0x0CE4}, {0x02F5, 0x0048}, {0xBE77, 0x2940}, {0x000A, 0x83E0},
+       {0xBE79, 0x3C3C}, {0xBE00, 0x1340},
 };
 
 /* This v2 init sequence is from Belkin F5D8235 U-Boot release */
-static const u16 rtl8366rb_init_jam_ver_2[] = {
-       0x0450, 0x0000, 0x0400, 0x8130, 0x000A, 0x83ED, 0x0431, 0x5432,
-       0xC44F, 0x6250, 0xC46F, 0x6250, 0xC456, 0x0C14, 0xC476, 0x0C14,
-       0xC44C, 0x1C85, 0xC44C, 0x1885, 0xC44C, 0x1C85, 0xC46C, 0x1C85,
-       0xC46C, 0x1885, 0xC46C, 0x1C85, 0xC44C, 0x0885, 0xC44C, 0x0881,
-       0xC44C, 0x0885, 0xC46C, 0x0885, 0xC46C, 0x0881, 0xC46C, 0x0885,
-       0xBE2E, 0x7BA7, 0xBE36, 0x1000, 0xBE37, 0x1000, 0x8000, 0x0001,
-       0xBE69, 0xD50F, 0x8000, 0x0000, 0xBE69, 0xD50F, 0xBE6E, 0x0320,
-       0xBE77, 0x2940, 0xBE78, 0x3C3C, 0xBE79, 0x3C3C, 0xBE6E, 0xE120,
-       0x8000, 0x0001, 0xBE15, 0x1007, 0x8000, 0x0000, 0xBE15, 0x1007,
-       0xBE14, 0x0448, 0xBE1E, 0x00A0, 0xBE10, 0x8160, 0xBE10, 0x8140,
-       0xBE00, 0x1340, 0x0F51, 0x0010,
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_2[] = {
+       {0x0450, 0x0000}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432},
+       {0xC44F, 0x6250}, {0xC46F, 0x6250}, {0xC456, 0x0C14}, {0xC476, 0x0C14},
+       {0xC44C, 0x1C85}, {0xC44C, 0x1885}, {0xC44C, 0x1C85}, {0xC46C, 0x1C85},
+       {0xC46C, 0x1885}, {0xC46C, 0x1C85}, {0xC44C, 0x0885}, {0xC44C, 0x0881},
+       {0xC44C, 0x0885}, {0xC46C, 0x0885}, {0xC46C, 0x0881}, {0xC46C, 0x0885},
+       {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001},
+       {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6E, 0x0320},
+       {0xBE77, 0x2940}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120},
+       {0x8000, 0x0001}, {0xBE15, 0x1007}, {0x8000, 0x0000}, {0xBE15, 0x1007},
+       {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, {0xBE10, 0x8140},
+       {0xBE00, 0x1340}, {0x0F51, 0x0010},
 };
 
 /* Appears in a DDWRT code dump */
-static const u16 rtl8366rb_init_jam_ver_3[] = {
-       0x0000, 0x0830, 0x0400, 0x8130, 0x000A, 0x83ED, 0x0431, 0x5432,
-       0x0F51, 0x0017, 0x02F5, 0x0048, 0x02FA, 0xFFDF, 0x02FB, 0xFFE0,
-       0xC456, 0x0C14, 0xC476, 0x0C14, 0xC454, 0x3F8B, 0xC474, 0x3F8B,
-       0xC450, 0x2071, 0xC470, 0x2071, 0xC451, 0x226B, 0xC471, 0x226B,
-       0xC452, 0xA293, 0xC472, 0xA293, 0xC44C, 0x1585, 0xC44C, 0x1185,
-       0xC44C, 0x1585, 0xC46C, 0x1585, 0xC46C, 0x1185, 0xC46C, 0x1585,
-       0xC44C, 0x0185, 0xC44C, 0x0181, 0xC44C, 0x0185, 0xC46C, 0x0185,
-       0xC46C, 0x0181, 0xC46C, 0x0185, 0xBE24, 0xB000, 0xBE23, 0xFF51,
-       0xBE22, 0xDF20, 0xBE21, 0x0140, 0xBE20, 0x00BB, 0xBE24, 0xB800,
-       0xBE24, 0x0000, 0xBE24, 0x7000, 0xBE23, 0xFF51, 0xBE22, 0xDF60,
-       0xBE21, 0x0140, 0xBE20, 0x0077, 0xBE24, 0x7800, 0xBE24, 0x0000,
-       0xBE2E, 0x7BA7, 0xBE36, 0x1000, 0xBE37, 0x1000, 0x8000, 0x0001,
-       0xBE69, 0xD50F, 0x8000, 0x0000, 0xBE69, 0xD50F, 0xBE6B, 0x0320,
-       0xBE77, 0x2800, 0xBE78, 0x3C3C, 0xBE79, 0x3C3C, 0xBE6E, 0xE120,
-       0x8000, 0x0001, 0xBE10, 0x8140, 0x8000, 0x0000, 0xBE10, 0x8140,
-       0xBE15, 0x1007, 0xBE14, 0x0448, 0xBE1E, 0x00A0, 0xBE10, 0x8160,
-       0xBE10, 0x8140, 0xBE00, 0x1340, 0x0450, 0x0000, 0x0401, 0x0000,
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_3[] = {
+       {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432},
+       {0x0F51, 0x0017}, {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0},
+       {0xC456, 0x0C14}, {0xC476, 0x0C14}, {0xC454, 0x3F8B}, {0xC474, 0x3F8B},
+       {0xC450, 0x2071}, {0xC470, 0x2071}, {0xC451, 0x226B}, {0xC471, 0x226B},
+       {0xC452, 0xA293}, {0xC472, 0xA293}, {0xC44C, 0x1585}, {0xC44C, 0x1185},
+       {0xC44C, 0x1585}, {0xC46C, 0x1585}, {0xC46C, 0x1185}, {0xC46C, 0x1585},
+       {0xC44C, 0x0185}, {0xC44C, 0x0181}, {0xC44C, 0x0185}, {0xC46C, 0x0185},
+       {0xC46C, 0x0181}, {0xC46C, 0x0185}, {0xBE24, 0xB000}, {0xBE23, 0xFF51},
+       {0xBE22, 0xDF20}, {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800},
+       {0xBE24, 0x0000}, {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60},
+       {0xBE21, 0x0140}, {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000},
+       {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001},
+       {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6B, 0x0320},
+       {0xBE77, 0x2800}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120},
+       {0x8000, 0x0001}, {0xBE10, 0x8140}, {0x8000, 0x0000}, {0xBE10, 0x8140},
+       {0xBE15, 0x1007}, {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160},
+       {0xBE10, 0x8140}, {0xBE00, 0x1340}, {0x0450, 0x0000}, {0x0401, 0x0000},
 };
 
 /* Belkin F5D8235 v1, "belkin,f5d8235-v1" */
-static const u16 rtl8366rb_init_jam_f5d8235[] = {
-       0x0242, 0x02BF, 0x0245, 0x02BF, 0x0248, 0x02BF, 0x024B, 0x02BF,
-       0x024E, 0x02BF, 0x0251, 0x02BF, 0x0254, 0x0A3F, 0x0256, 0x0A3F,
-       0x0258, 0x0A3F, 0x025A, 0x0A3F, 0x025C, 0x0A3F, 0x025E, 0x0A3F,
-       0x0263, 0x007C, 0x0100, 0x0004, 0xBE5B, 0x3500, 0x800E, 0x200F,
-       0xBE1D, 0x0F00, 0x8001, 0x5011, 0x800A, 0xA2F4, 0x800B, 0x17A3,
-       0xBE4B, 0x17A3, 0xBE41, 0x5011, 0xBE17, 0x2100, 0x8000, 0x8304,
-       0xBE40, 0x8304, 0xBE4A, 0xA2F4, 0x800C, 0xA8D5, 0x8014, 0x5500,
-       0x8015, 0x0004, 0xBE4C, 0xA8D5, 0xBE59, 0x0008, 0xBE09, 0x0E00,
-       0xBE36, 0x1036, 0xBE37, 0x1036, 0x800D, 0x00FF, 0xBE4D, 0x00FF,
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_f5d8235[] = {
+       {0x0242, 0x02BF}, {0x0245, 0x02BF}, {0x0248, 0x02BF}, {0x024B, 0x02BF},
+       {0x024E, 0x02BF}, {0x0251, 0x02BF}, {0x0254, 0x0A3F}, {0x0256, 0x0A3F},
+       {0x0258, 0x0A3F}, {0x025A, 0x0A3F}, {0x025C, 0x0A3F}, {0x025E, 0x0A3F},
+       {0x0263, 0x007C}, {0x0100, 0x0004}, {0xBE5B, 0x3500}, {0x800E, 0x200F},
+       {0xBE1D, 0x0F00}, {0x8001, 0x5011}, {0x800A, 0xA2F4}, {0x800B, 0x17A3},
+       {0xBE4B, 0x17A3}, {0xBE41, 0x5011}, {0xBE17, 0x2100}, {0x8000, 0x8304},
+       {0xBE40, 0x8304}, {0xBE4A, 0xA2F4}, {0x800C, 0xA8D5}, {0x8014, 0x5500},
+       {0x8015, 0x0004}, {0xBE4C, 0xA8D5}, {0xBE59, 0x0008}, {0xBE09, 0x0E00},
+       {0xBE36, 0x1036}, {0xBE37, 0x1036}, {0x800D, 0x00FF}, {0xBE4D, 0x00FF},
 };
 
 /* DGN3500, "netgear,dgn3500", "netgear,dgn3500b" */
-static const u16 rtl8366rb_init_jam_dgn3500[] = {
-       0x0000, 0x0830, 0x0400, 0x8130, 0x000A, 0x83ED, 0x0F51, 0x0017,
-       0x02F5, 0x0048, 0x02FA, 0xFFDF, 0x02FB, 0xFFE0, 0x0450, 0x0000,
-       0x0401, 0x0000, 0x0431, 0x0960,
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_dgn3500[] = {
+       {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0F51, 0x0017},
+       {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, {0x0450, 0x0000},
+       {0x0401, 0x0000}, {0x0431, 0x0960},
 };
 
 /* This jam table activates "green ethernet", which means low power mode
@@ -710,16 +716,53 @@ static const u16 rtl8366rb_init_jam_dgn3500[] = {
  * necessary, and the ports should enter power saving mode 10 seconds after
  * a cable is disconnected. Seems to always be the same.
  */
-static const u16 rtl8366rb_green_jam[][2] = {
+static const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = {
        {0xBE78, 0x323C}, {0xBE77, 0x5000}, {0xBE2E, 0x7BA7},
        {0xBE59, 0x3459}, {0xBE5A, 0x745A}, {0xBE5B, 0x785C},
        {0xBE5C, 0x785C}, {0xBE6E, 0xE120}, {0xBE79, 0x323C},
 };
 
+/* Function that jams the tables in the proper registers */
+static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table,
+                              int jam_size, struct realtek_smi *smi,
+                              bool write_dbg)
+{
+       u32 val;
+       int ret;
+       int i;
+
+       for (i = 0; i < jam_size; i++) {
+               if ((jam_table[i].reg & 0xBE00) == 0xBE00) {
+                       ret = regmap_read(smi->map,
+                                         RTL8366RB_PHY_ACCESS_BUSY_REG,
+                                         &val);
+                       if (ret)
+                               return ret;
+                       if (!(val & RTL8366RB_PHY_INT_BUSY)) {
+                               ret = regmap_write(smi->map,
+                                               RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                               RTL8366RB_PHY_CTRL_WRITE);
+                               if (ret)
+                                       return ret;
+                       }
+               }
+               if (write_dbg)
+                       dev_dbg(smi->dev, "jam %04x into register %04x\n",
+                               jam_table[i].val,
+                               jam_table[i].reg);
+               ret = regmap_write(smi->map,
+                                  jam_table[i].reg,
+                                  jam_table[i].val);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
 static int rtl8366rb_setup(struct dsa_switch *ds)
 {
        struct realtek_smi *smi = ds->priv;
-       const u16 *jam_table;
+       const struct rtl8366rb_jam_tbl_entry *jam_table;
        struct rtl8366rb *rb;
        u32 chip_ver = 0;
        u32 chip_id = 0;
@@ -788,54 +831,16 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
                jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500);
        }
 
-       i = 0;
-       while (i < jam_size) {
-               if ((jam_table[i] & 0xBE00) == 0xBE00) {
-                       ret = regmap_read(smi->map,
-                                         RTL8366RB_PHY_ACCESS_BUSY_REG,
-                                         &val);
-                       if (ret)
-                               return ret;
-                       if (!(val & RTL8366RB_PHY_INT_BUSY)) {
-                               ret = regmap_write(smi->map,
-                                               RTL8366RB_PHY_ACCESS_CTRL_REG,
-                                               RTL8366RB_PHY_CTRL_WRITE);
-                               if (ret)
-                                       return ret;
-                       }
-               }
-               dev_dbg(smi->dev, "jam %04x into register %04x\n",
-                       jam_table[i + 1],
-                       jam_table[i]);
-               ret = regmap_write(smi->map,
-                                  jam_table[i],
-                                  jam_table[i + 1]);
-               if (ret)
-                       return ret;
-               i += 2;
-       }
+       ret = rtl8366rb_jam_table(jam_table, jam_size, smi, true);
+       if (ret)
+               return ret;
 
        /* Set up the "green ethernet" feature */
-       i = 0;
-       while (i < ARRAY_SIZE(rtl8366rb_green_jam)) {
-               ret = regmap_read(smi->map, RTL8366RB_PHY_ACCESS_BUSY_REG,
-                                 &val);
-               if (ret)
-                       return ret;
-               if (!(val & RTL8366RB_PHY_INT_BUSY)) {
-                       ret = regmap_write(smi->map,
-                                          RTL8366RB_PHY_ACCESS_CTRL_REG,
-                                          RTL8366RB_PHY_CTRL_WRITE);
-                       if (ret)
-                               return ret;
-                       ret = regmap_write(smi->map,
-                                          rtl8366rb_green_jam[i][0],
-                                          rtl8366rb_green_jam[i][1]);
-                       if (ret)
-                               return ret;
-                       i++;
-               }
-       }
+       ret = rtl8366rb_jam_table(rtl8366rb_green_jam,
+                                 ARRAY_SIZE(rtl8366rb_green_jam), smi, false);
+       if (ret)
+               return ret;
+
        ret = regmap_write(smi->map,
                           RTL8366RB_GREEN_FEATURE_REG,
                           (chip_ver == 1) ? 0x0007 : 0x0003);
@@ -972,6 +977,8 @@ static int rtl8366rb_setup(struct dsa_switch *ds)
                return -ENODEV;
        }
 
+       ds->configure_vlan_while_not_filtering = false;
+
        return 0;
 }
 
@@ -1504,7 +1511,6 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = {
        .get_ethtool_stats = rtl8366_get_ethtool_stats,
        .get_sset_count = rtl8366_get_sset_count,
        .port_vlan_filtering = rtl8366_vlan_filtering,
-       .port_vlan_prepare = rtl8366_vlan_prepare,
        .port_vlan_add = rtl8366_vlan_add,
        .port_vlan_del = rtl8366_vlan_del,
        .port_enable = rtl8366rb_port_enable,
index 4ebc4a5..d582308 100644 (file)
@@ -245,8 +245,7 @@ enum sja1105_reset_reason {
 
 int sja1105_static_config_reload(struct sja1105_private *priv,
                                 enum sja1105_reset_reason reason);
-int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
-                          struct switchdev_trans *trans);
+int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled);
 void sja1105_frame_memory_partitioning(struct sja1105_private *priv);
 
 /* From sja1105_devlink.c */
index 4a2ec39..b4bf1b1 100644 (file)
@@ -135,7 +135,6 @@ static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
 
        rtnl_lock();
        for (port = 0; port < ds->num_ports; port++) {
-               struct switchdev_trans trans;
                struct dsa_port *dp;
 
                if (!dsa_is_user_port(ds, port))
@@ -144,13 +143,7 @@ static int sja1105_best_effort_vlan_filtering_set(struct sja1105_private *priv,
                dp = dsa_to_port(ds, port);
                vlan_filtering = dsa_port_is_vlan_filtering(dp);
 
-               trans.ph_prepare = true;
-               rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans);
-               if (rc)
-                       break;
-
-               trans.ph_prepare = false;
-               rc = sja1105_vlan_filtering(ds, port, vlan_filtering, &trans);
+               rc = sja1105_vlan_filtering(ds, port, vlan_filtering);
                if (rc)
                        break;
        }
index 4ca0296..2822535 100644 (file)
@@ -317,7 +317,7 @@ static int sja1105_init_static_vlan(struct sja1105_private *priv)
                table->entry_count = 0;
        }
 
-       table->entries = kcalloc(1, table->ops->unpacked_entry_size,
+       table->entries = kzalloc(table->ops->unpacked_entry_size,
                                 GFP_KERNEL);
        if (!table->entries)
                return -ENOMEM;
@@ -1524,17 +1524,10 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
        return 0;
 }
 
-/* This callback needs to be present */
-static int sja1105_mdb_prepare(struct dsa_switch *ds, int port,
-                              const struct switchdev_obj_port_mdb *mdb)
-{
-       return 0;
-}
-
-static void sja1105_mdb_add(struct dsa_switch *ds, int port,
-                           const struct switchdev_obj_port_mdb *mdb)
+static int sja1105_mdb_add(struct dsa_switch *ds, int port,
+                          const struct switchdev_obj_port_mdb *mdb)
 {
-       sja1105_fdb_add(ds, port, mdb->addr, mdb->vid);
+       return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid);
 }
 
 static int sja1105_mdb_del(struct dsa_switch *ds, int port,
@@ -2607,35 +2600,11 @@ out:
        return rc;
 }
 
-static int sja1105_vlan_prepare(struct dsa_switch *ds, int port,
-                               const struct switchdev_obj_port_vlan *vlan)
-{
-       struct sja1105_private *priv = ds->priv;
-       u16 vid;
-
-       if (priv->vlan_state == SJA1105_VLAN_FILTERING_FULL)
-               return 0;
-
-       /* If the user wants best-effort VLAN filtering (aka vlan_filtering
-        * bridge plus tagging), be sure to at least deny alterations to the
-        * configuration done by dsa_8021q.
-        */
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (vid_is_dsa_8021q(vid)) {
-                       dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n");
-                       return -EBUSY;
-               }
-       }
-
-       return 0;
-}
-
 /* The TPID setting belongs to the General Parameters table,
  * which can only be partially reconfigured at runtime (and not the TPID).
  * So a switch reset is required.
  */
-int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
-                          struct switchdev_trans *trans)
+int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled)
 {
        struct sja1105_l2_lookup_params_entry *l2_lookup_params;
        struct sja1105_general_params_entry *general_params;
@@ -2647,16 +2616,12 @@ int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled,
        u16 tpid, tpid2;
        int rc;
 
-       if (switchdev_trans_ph_prepare(trans)) {
-               list_for_each_entry(rule, &priv->flow_block.rules, list) {
-                       if (rule->type == SJA1105_RULE_VL) {
-                               dev_err(ds->dev,
-                                       "Cannot change VLAN filtering with active VL rules\n");
-                               return -EBUSY;
-                       }
+       list_for_each_entry(rule, &priv->flow_block.rules, list) {
+               if (rule->type == SJA1105_RULE_VL) {
+                       dev_err(ds->dev,
+                               "Cannot change VLAN filtering with active VL rules\n");
+                       return -EBUSY;
                }
-
-               return 0;
        }
 
        if (enabled) {
@@ -2794,29 +2759,34 @@ static int sja1105_vlan_del_one(struct dsa_switch *ds, int port, u16 vid,
        return 0;
 }
 
-static void sja1105_vlan_add(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_vlan *vlan)
+static int sja1105_vlan_add(struct dsa_switch *ds, int port,
+                           const struct switchdev_obj_port_vlan *vlan)
 {
        struct sja1105_private *priv = ds->priv;
        bool vlan_table_changed = false;
-       u16 vid;
        int rc;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               rc = sja1105_vlan_add_one(ds, port, vid, vlan->flags,
-                                         &priv->bridge_vlans);
-               if (rc < 0)
-                       return;
-               if (rc > 0)
-                       vlan_table_changed = true;
+       /* If the user wants best-effort VLAN filtering (aka vlan_filtering
+        * bridge plus tagging), be sure to at least deny alterations to the
+        * configuration done by dsa_8021q.
+        */
+       if (priv->vlan_state != SJA1105_VLAN_FILTERING_FULL &&
+           vid_is_dsa_8021q(vlan->vid)) {
+               dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n");
+               return -EBUSY;
        }
 
+       rc = sja1105_vlan_add_one(ds, port, vlan->vid, vlan->flags,
+                                 &priv->bridge_vlans);
+       if (rc < 0)
+               return rc;
+       if (rc > 0)
+               vlan_table_changed = true;
+
        if (!vlan_table_changed)
-               return;
+               return 0;
 
-       rc = sja1105_build_vlan_table(priv, true);
-       if (rc)
-               dev_err(ds->dev, "Failed to build VLAN table: %d\n", rc);
+       return sja1105_build_vlan_table(priv, true);
 }
 
 static int sja1105_vlan_del(struct dsa_switch *ds, int port,
@@ -2824,14 +2794,11 @@ static int sja1105_vlan_del(struct dsa_switch *ds, int port,
 {
        struct sja1105_private *priv = ds->priv;
        bool vlan_table_changed = false;
-       u16 vid;
        int rc;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               rc = sja1105_vlan_del_one(ds, port, vid, &priv->bridge_vlans);
-               if (rc > 0)
-                       vlan_table_changed = true;
-       }
+       rc = sja1105_vlan_del_one(ds, port, vlan->vid, &priv->bridge_vlans);
+       if (rc > 0)
+               vlan_table_changed = true;
 
        if (!vlan_table_changed)
                return 0;
@@ -2934,8 +2901,6 @@ static int sja1105_setup(struct dsa_switch *ds)
 
        ds->mtu_enforcement_ingress = true;
 
-       ds->configure_vlan_while_not_filtering = true;
-
        rc = sja1105_devlink_setup(ds);
        if (rc < 0)
                return rc;
@@ -3298,11 +3263,9 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
        .port_bridge_join       = sja1105_bridge_join,
        .port_bridge_leave      = sja1105_bridge_leave,
        .port_stp_state_set     = sja1105_bridge_stp_state_set,
-       .port_vlan_prepare      = sja1105_vlan_prepare,
        .port_vlan_filtering    = sja1105_vlan_filtering,
        .port_vlan_add          = sja1105_vlan_add,
        .port_vlan_del          = sja1105_vlan_del,
-       .port_mdb_prepare       = sja1105_mdb_prepare,
        .port_mdb_add           = sja1105_mdb_add,
        .port_mdb_del           = sja1105_mdb_del,
        .port_hwtstamp_get      = sja1105_hwtstamp_get,
diff --git a/drivers/net/dsa/xrs700x/Kconfig b/drivers/net/dsa/xrs700x/Kconfig
new file mode 100644 (file)
index 0000000..d10a4dc
--- /dev/null
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NET_DSA_XRS700X
+       tristate
+       depends on NET_DSA
+       select NET_DSA_TAG_XRS700X
+       select REGMAP
+       help
+         This enables support for Arrow SpeedChips XRS7003/7004 gigabit
+         Ethernet switches.
+
+config NET_DSA_XRS700X_I2C
+       tristate "Arrow XRS7000X series switch in I2C mode"
+       depends on NET_DSA && I2C
+       select NET_DSA_XRS700X
+       select REGMAP_I2C
+       help
+         Enable I2C support for Arrow SpeedChips XRS7003/7004 gigabit Ethernet
+         switches.
+
+config NET_DSA_XRS700X_MDIO
+       tristate "Arrow XRS7000X series switch in MDIO mode"
+       depends on NET_DSA
+       select NET_DSA_XRS700X
+       help
+         Enable MDIO support for Arrow SpeedChips XRS7003/7004 gigabit Ethernet
+         switches.
diff --git a/drivers/net/dsa/xrs700x/Makefile b/drivers/net/dsa/xrs700x/Makefile
new file mode 100644 (file)
index 0000000..51a3a7d
--- /dev/null
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_NET_DSA_XRS700X) += xrs700x.o
+obj-$(CONFIG_NET_DSA_XRS700X_I2C) += xrs700x_i2c.o
+obj-$(CONFIG_NET_DSA_XRS700X_MDIO) += xrs700x_mdio.o
diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c
new file mode 100644 (file)
index 0000000..259f5e6
--- /dev/null
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 NovaTech LLC
+ * George McCollister <george.mccollister@gmail.com>
+ */
+
+#include <net/dsa.h>
+#include <linux/if_bridge.h>
+#include <linux/of_device.h>
+#include "xrs700x.h"
+#include "xrs700x_reg.h"
+
+#define XRS700X_MIB_INTERVAL msecs_to_jiffies(3000)
+
+#define XRS7003E_ID    0x100
+#define XRS7003F_ID    0x101
+#define XRS7004E_ID    0x200
+#define XRS7004F_ID    0x201
+
+const struct xrs700x_info xrs7003e_info = {XRS7003E_ID, "XRS7003E", 3};
+EXPORT_SYMBOL(xrs7003e_info);
+
+const struct xrs700x_info xrs7003f_info = {XRS7003F_ID, "XRS7003F", 3};
+EXPORT_SYMBOL(xrs7003f_info);
+
+const struct xrs700x_info xrs7004e_info = {XRS7004E_ID, "XRS7004E", 4};
+EXPORT_SYMBOL(xrs7004e_info);
+
+const struct xrs700x_info xrs7004f_info = {XRS7004F_ID, "XRS7004F", 4};
+EXPORT_SYMBOL(xrs7004f_info);
+
+struct xrs700x_regfield {
+       struct reg_field rf;
+       struct regmap_field **rmf;
+};
+
+struct xrs700x_mib {
+       unsigned int offset;
+       const char *name;
+       int stats64_offset;
+};
+
+#define XRS700X_MIB_ETHTOOL_ONLY(o, n) {o, n, -1}
+#define XRS700X_MIB(o, n, m) {o, n, offsetof(struct rtnl_link_stats64, m)}
+
+static const struct xrs700x_mib xrs700x_mibs[] = {
+       XRS700X_MIB(XRS_RX_GOOD_OCTETS_L, "rx_good_octets", rx_bytes),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_BAD_OCTETS_L, "rx_bad_octets"),
+       XRS700X_MIB(XRS_RX_UNICAST_L, "rx_unicast", rx_packets),
+       XRS700X_MIB(XRS_RX_BROADCAST_L, "rx_broadcast", rx_packets),
+       XRS700X_MIB(XRS_RX_MULTICAST_L, "rx_multicast", multicast),
+       XRS700X_MIB(XRS_RX_UNDERSIZE_L, "rx_undersize", rx_length_errors),
+       XRS700X_MIB(XRS_RX_FRAGMENTS_L, "rx_fragments", rx_length_errors),
+       XRS700X_MIB(XRS_RX_OVERSIZE_L, "rx_oversize", rx_length_errors),
+       XRS700X_MIB(XRS_RX_JABBER_L, "rx_jabber", rx_length_errors),
+       XRS700X_MIB(XRS_RX_ERR_L, "rx_err", rx_errors),
+       XRS700X_MIB(XRS_RX_CRC_L, "rx_crc", rx_crc_errors),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_64_L, "rx_64"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_65_127_L, "rx_65_127"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_128_255_L, "rx_128_255"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_256_511_L, "rx_256_511"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_512_1023_L, "rx_512_1023"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_1024_1536_L, "rx_1024_1536"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_HSR_PRP_L, "rx_hsr_prp"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_WRONGLAN_L, "rx_wronglan"),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_DUPLICATE_L, "rx_duplicate"),
+       XRS700X_MIB(XRS_TX_OCTETS_L, "tx_octets", tx_bytes),
+       XRS700X_MIB(XRS_TX_UNICAST_L, "tx_unicast", tx_packets),
+       XRS700X_MIB(XRS_TX_BROADCAST_L, "tx_broadcast", tx_packets),
+       XRS700X_MIB(XRS_TX_MULTICAST_L, "tx_multicast", tx_packets),
+       XRS700X_MIB_ETHTOOL_ONLY(XRS_TX_HSR_PRP_L, "tx_hsr_prp"),
+       XRS700X_MIB(XRS_PRIQ_DROP_L, "priq_drop", tx_dropped),
+       XRS700X_MIB(XRS_EARLY_DROP_L, "early_drop", tx_dropped),
+};
+
+static void xrs700x_get_strings(struct dsa_switch *ds, int port,
+                               u32 stringset, u8 *data)
+{
+       int i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+
+       for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) {
+               strscpy(data, xrs700x_mibs[i].name, ETH_GSTRING_LEN);
+               data += ETH_GSTRING_LEN;
+       }
+}
+
+static int xrs700x_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+       if (sset != ETH_SS_STATS)
+               return -EOPNOTSUPP;
+
+       return ARRAY_SIZE(xrs700x_mibs);
+}
+
+static void xrs700x_read_port_counters(struct xrs700x *priv, int port)
+{
+       struct xrs700x_port *p = &priv->ports[port];
+       struct rtnl_link_stats64 stats;
+       int i;
+
+       memset(&stats, 0, sizeof(stats));
+
+       mutex_lock(&p->mib_mutex);
+
+       /* Capture counter values */
+       regmap_write(priv->regmap, XRS_CNT_CTRL(port), 1);
+
+       for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) {
+               unsigned int high = 0, low = 0, reg;
+
+               reg = xrs700x_mibs[i].offset + XRS_PORT_OFFSET * port;
+               regmap_read(priv->regmap, reg, &low);
+               regmap_read(priv->regmap, reg + 2, &high);
+
+               p->mib_data[i] += (high << 16) | low;
+
+               if (xrs700x_mibs[i].stats64_offset >= 0) {
+                       u8 *s = (u8 *)&stats + xrs700x_mibs[i].stats64_offset;
+                       *(u64 *)s += p->mib_data[i];
+               }
+       }
+
+       /* multicast must be added to rx_packets (which already includes
+        * unicast and broadcast)
+        */
+       stats.rx_packets += stats.multicast;
+
+       u64_stats_update_begin(&p->syncp);
+       p->stats64 = stats;
+       u64_stats_update_end(&p->syncp);
+
+       mutex_unlock(&p->mib_mutex);
+}
+
+static void xrs700x_mib_work(struct work_struct *work)
+{
+       struct xrs700x *priv = container_of(work, struct xrs700x,
+                                           mib_work.work);
+       int i;
+
+       for (i = 0; i < priv->ds->num_ports; i++)
+               xrs700x_read_port_counters(priv, i);
+
+       schedule_delayed_work(&priv->mib_work, XRS700X_MIB_INTERVAL);
+}
+
+static void xrs700x_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                     u64 *data)
+{
+       struct xrs700x *priv = ds->priv;
+       struct xrs700x_port *p = &priv->ports[port];
+
+       xrs700x_read_port_counters(priv, port);
+
+       mutex_lock(&p->mib_mutex);
+       memcpy(data, p->mib_data, sizeof(*data) * ARRAY_SIZE(xrs700x_mibs));
+       mutex_unlock(&p->mib_mutex);
+}
+
+static void xrs700x_get_stats64(struct dsa_switch *ds, int port,
+                               struct rtnl_link_stats64 *s)
+{
+       struct xrs700x *priv = ds->priv;
+       struct xrs700x_port *p = &priv->ports[port];
+       unsigned int start;
+
+       do {
+               start = u64_stats_fetch_begin(&p->syncp);
+               *s = p->stats64;
+       } while (u64_stats_fetch_retry(&p->syncp, start));
+}
+
+static int xrs700x_setup_regmap_range(struct xrs700x *priv)
+{
+       struct xrs700x_regfield regfields[] = {
+               {
+                       .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 0, 1,
+                                          priv->ds->num_ports,
+                                          XRS_PORT_OFFSET),
+                       .rmf = &priv->ps_forward
+               },
+               {
+                       .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 2, 3,
+                                          priv->ds->num_ports,
+                                          XRS_PORT_OFFSET),
+                       .rmf = &priv->ps_management
+               },
+               {
+                       .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 4, 9,
+                                          priv->ds->num_ports,
+                                          XRS_PORT_OFFSET),
+                       .rmf = &priv->ps_sel_speed
+               },
+               {
+                       .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 10, 11,
+                                          priv->ds->num_ports,
+                                          XRS_PORT_OFFSET),
+                       .rmf = &priv->ps_cur_speed
+               }
+       };
+       int i = 0;
+
+       for (; i < ARRAY_SIZE(regfields); i++) {
+               *regfields[i].rmf = devm_regmap_field_alloc(priv->dev,
+                                                           priv->regmap,
+                                                           regfields[i].rf);
+               if (IS_ERR(*regfields[i].rmf))
+                       return PTR_ERR(*regfields[i].rmf);
+       }
+
+       return 0;
+}
+
+static enum dsa_tag_protocol xrs700x_get_tag_protocol(struct dsa_switch *ds,
+                                                     int port,
+                                                     enum dsa_tag_protocol m)
+{
+       return DSA_TAG_PROTO_XRS700X;
+}
+
+static int xrs700x_reset(struct dsa_switch *ds)
+{
+       struct xrs700x *priv = ds->priv;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_write(priv->regmap, XRS_GENERAL, XRS_GENERAL_RESET);
+       if (ret)
+               goto error;
+
+       ret = regmap_read_poll_timeout(priv->regmap, XRS_GENERAL,
+                                      val, !(val & XRS_GENERAL_RESET),
+                                      10, 1000);
+error:
+       if (ret) {
+               dev_err_ratelimited(priv->dev, "error resetting switch: %d\n",
+                                   ret);
+       }
+
+       return ret;
+}
+
+static void xrs700x_port_stp_state_set(struct dsa_switch *ds, int port,
+                                      u8 state)
+{
+       struct xrs700x *priv = ds->priv;
+       unsigned int bpdus = 1;
+       unsigned int val;
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+               bpdus = 0;
+               fallthrough;
+       case BR_STATE_BLOCKING:
+       case BR_STATE_LISTENING:
+               val = XRS_PORT_DISABLED;
+               break;
+       case BR_STATE_LEARNING:
+               val = XRS_PORT_LEARNING;
+               break;
+       case BR_STATE_FORWARDING:
+               val = XRS_PORT_FORWARDING;
+               break;
+       default:
+               dev_err(ds->dev, "invalid STP state: %d\n", state);
+               return;
+       }
+
+       regmap_fields_write(priv->ps_forward, port, val);
+
+       /* Enable/disable inbound policy added by xrs700x_port_add_bpdu_ipf()
+        * which allows BPDU forwarding to the CPU port when the front facing
+        * port is in disabled/learning state.
+        */
+       regmap_update_bits(priv->regmap, XRS_ETH_ADDR_CFG(port, 0), 1, bpdus);
+
+       dev_dbg_ratelimited(priv->dev, "%s - port: %d, state: %u, val: 0x%x\n",
+                           __func__, port, state, val);
+}
+
+/* Add an inbound policy filter which matches the BPDU destination MAC
+ * and forwards to the CPU port. Leave the policy disabled, it will be
+ * enabled as needed.
+ */
+static int xrs700x_port_add_bpdu_ipf(struct dsa_switch *ds, int port)
+{
+       struct xrs700x *priv = ds->priv;
+       unsigned int val = 0;
+       int i = 0;
+       int ret;
+
+       /* Compare all 48 bits of the destination MAC address. */
+       ret = regmap_write(priv->regmap, XRS_ETH_ADDR_CFG(port, 0), 48 << 2);
+       if (ret)
+               return ret;
+
+       /* match BPDU destination 01:80:c2:00:00:00 */
+       for (i = 0; i < sizeof(eth_stp_addr); i += 2) {
+               ret = regmap_write(priv->regmap, XRS_ETH_ADDR_0(port, 0) + i,
+                                  eth_stp_addr[i] |
+                                  (eth_stp_addr[i + 1] << 8));
+               if (ret)
+                       return ret;
+       }
+
+       /* Mirror BPDU to CPU port */
+       for (i = 0; i < ds->num_ports; i++) {
+               if (dsa_is_cpu_port(ds, i))
+                       val |= BIT(i);
+       }
+
+       ret = regmap_write(priv->regmap, XRS_ETH_ADDR_FWD_MIRROR(port, 0), val);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(priv->regmap, XRS_ETH_ADDR_FWD_ALLOW(port, 0), 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int xrs700x_port_setup(struct dsa_switch *ds, int port)
+{
+       bool cpu_port = dsa_is_cpu_port(ds, port);
+       struct xrs700x *priv = ds->priv;
+       unsigned int val = 0;
+       int ret, i;
+
+       xrs700x_port_stp_state_set(ds, port, BR_STATE_DISABLED);
+
+       /* Disable forwarding to non-CPU ports */
+       for (i = 0; i < ds->num_ports; i++) {
+               if (!dsa_is_cpu_port(ds, i))
+                       val |= BIT(i);
+       }
+
+       /* 1 = Disable forwarding to the port */
+       ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port), val);
+       if (ret)
+               return ret;
+
+       val = cpu_port ? XRS_PORT_MODE_MANAGEMENT : XRS_PORT_MODE_NORMAL;
+       ret = regmap_fields_write(priv->ps_management, port, val);
+       if (ret)
+               return ret;
+
+       if (!cpu_port) {
+               ret = xrs700x_port_add_bpdu_ipf(ds, port);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int xrs700x_setup(struct dsa_switch *ds)
+{
+       struct xrs700x *priv = ds->priv;
+       int ret, i;
+
+       ret = xrs700x_reset(ds);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ds->num_ports; i++) {
+               ret = xrs700x_port_setup(ds, i);
+               if (ret)
+                       return ret;
+       }
+
+       schedule_delayed_work(&priv->mib_work, XRS700X_MIB_INTERVAL);
+
+       return 0;
+}
+
+static void xrs700x_teardown(struct dsa_switch *ds)
+{
+       struct xrs700x *priv = ds->priv;
+
+       cancel_delayed_work_sync(&priv->mib_work);
+}
+
+static void xrs700x_phylink_validate(struct dsa_switch *ds, int port,
+                                    unsigned long *supported,
+                                    struct phylink_link_state *state)
+{
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+       switch (port) {
+       case 0:
+               break;
+       case 1:
+       case 2:
+       case 3:
+               phylink_set(mask, 1000baseT_Full);
+               break;
+       default:
+               bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+               dev_err(ds->dev, "Unsupported port: %i\n", port);
+               return;
+       }
+
+       phylink_set_port_modes(mask);
+
+       /* The switch only supports full duplex. */
+       phylink_set(mask, 10baseT_Full);
+       phylink_set(mask, 100baseT_Full);
+
+       bitmap_and(supported, supported, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+       bitmap_and(state->advertising, state->advertising, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static void xrs700x_mac_link_up(struct dsa_switch *ds, int port,
+                               unsigned int mode, phy_interface_t interface,
+                               struct phy_device *phydev,
+                               int speed, int duplex,
+                               bool tx_pause, bool rx_pause)
+{
+       struct xrs700x *priv = ds->priv;
+       unsigned int val;
+
+       switch (speed) {
+       case SPEED_1000:
+               val = XRS_PORT_SPEED_1000;
+               break;
+       case SPEED_100:
+               val = XRS_PORT_SPEED_100;
+               break;
+       case SPEED_10:
+               val = XRS_PORT_SPEED_10;
+               break;
+       default:
+               return;
+       }
+
+       regmap_fields_write(priv->ps_sel_speed, port, val);
+
+       dev_dbg_ratelimited(priv->dev, "%s: port: %d mode: %u speed: %u\n",
+                           __func__, port, mode, speed);
+}
+
+static int xrs700x_bridge_common(struct dsa_switch *ds, int port,
+                                struct net_device *bridge, bool join)
+{
+       unsigned int i, cpu_mask = 0, mask = 0;
+       struct xrs700x *priv = ds->priv;
+       int ret;
+
+       for (i = 0; i < ds->num_ports; i++) {
+               if (dsa_is_cpu_port(ds, i))
+                       continue;
+
+               cpu_mask |= BIT(i);
+
+               if (dsa_to_port(ds, i)->bridge_dev == bridge)
+                       continue;
+
+               mask |= BIT(i);
+       }
+
+       for (i = 0; i < ds->num_ports; i++) {
+               if (dsa_to_port(ds, i)->bridge_dev != bridge)
+                       continue;
+
+               /* 1 = Disable forwarding to the port */
+               ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(i), mask);
+               if (ret)
+                       return ret;
+       }
+
+       if (!join) {
+               ret = regmap_write(priv->regmap, XRS_PORT_FWD_MASK(port),
+                                  cpu_mask);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int xrs700x_bridge_join(struct dsa_switch *ds, int port,
+                              struct net_device *bridge)
+{
+       return xrs700x_bridge_common(ds, port, bridge, true);
+}
+
+static void xrs700x_bridge_leave(struct dsa_switch *ds, int port,
+                                struct net_device *bridge)
+{
+       xrs700x_bridge_common(ds, port, bridge, false);
+}
+
+static const struct dsa_switch_ops xrs700x_ops = {
+       .get_tag_protocol       = xrs700x_get_tag_protocol,
+       .setup                  = xrs700x_setup,
+       .teardown               = xrs700x_teardown,
+       .port_stp_state_set     = xrs700x_port_stp_state_set,
+       .phylink_validate       = xrs700x_phylink_validate,
+       .phylink_mac_link_up    = xrs700x_mac_link_up,
+       .get_strings            = xrs700x_get_strings,
+       .get_sset_count         = xrs700x_get_sset_count,
+       .get_ethtool_stats      = xrs700x_get_ethtool_stats,
+       .get_stats64            = xrs700x_get_stats64,
+       .port_bridge_join       = xrs700x_bridge_join,
+       .port_bridge_leave      = xrs700x_bridge_leave,
+};
+
+static int xrs700x_detect(struct xrs700x *priv)
+{
+       const struct xrs700x_info *info;
+       unsigned int id;
+       int ret;
+
+       ret = regmap_read(priv->regmap, XRS_DEV_ID0, &id);
+       if (ret) {
+               dev_err(priv->dev, "error %d while reading switch id.\n",
+                       ret);
+               return ret;
+       }
+
+       info = of_device_get_match_data(priv->dev);
+       if (!info)
+               return -EINVAL;
+
+       if (info->id == id) {
+               priv->ds->num_ports = info->num_ports;
+               dev_info(priv->dev, "%s detected.\n", info->name);
+               return 0;
+       }
+
+       dev_err(priv->dev, "expected switch id 0x%x but found 0x%x.\n",
+               info->id, id);
+
+       return -ENODEV;
+}
+
+struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv)
+{
+       struct dsa_switch *ds;
+       struct xrs700x *priv;
+
+       ds = devm_kzalloc(base, sizeof(*ds), GFP_KERNEL);
+       if (!ds)
+               return NULL;
+
+       ds->dev = base;
+
+       priv = devm_kzalloc(base, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return NULL;
+
+       INIT_DELAYED_WORK(&priv->mib_work, xrs700x_mib_work);
+
+       ds->ops = &xrs700x_ops;
+       ds->priv = priv;
+       priv->dev = base;
+
+       priv->ds = ds;
+       priv->priv = devpriv;
+
+       return priv;
+}
+EXPORT_SYMBOL(xrs700x_switch_alloc);
+
+static int xrs700x_alloc_port_mib(struct xrs700x *priv, int port)
+{
+       struct xrs700x_port *p = &priv->ports[port];
+
+       p->mib_data = devm_kcalloc(priv->dev, ARRAY_SIZE(xrs700x_mibs),
+                                  sizeof(*p->mib_data), GFP_KERNEL);
+       if (!p->mib_data)
+               return -ENOMEM;
+
+       mutex_init(&p->mib_mutex);
+       u64_stats_init(&p->syncp);
+
+       return 0;
+}
+
+int xrs700x_switch_register(struct xrs700x *priv)
+{
+       int ret;
+       int i;
+
+       ret = xrs700x_detect(priv);
+       if (ret)
+               return ret;
+
+       ret = xrs700x_setup_regmap_range(priv);
+       if (ret)
+               return ret;
+
+       priv->ports = devm_kcalloc(priv->dev, priv->ds->num_ports,
+                                  sizeof(*priv->ports), GFP_KERNEL);
+       if (!priv->ports)
+               return -ENOMEM;
+
+       for (i = 0; i < priv->ds->num_ports; i++) {
+               ret = xrs700x_alloc_port_mib(priv, i);
+               if (ret)
+                       return ret;
+       }
+
+       return dsa_register_switch(priv->ds);
+}
+EXPORT_SYMBOL(xrs700x_switch_register);
+
+void xrs700x_switch_remove(struct xrs700x *priv)
+{
+       dsa_unregister_switch(priv->ds);
+}
+EXPORT_SYMBOL(xrs700x_switch_remove);
+
+MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>");
+MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/xrs700x/xrs700x.h b/drivers/net/dsa/xrs700x/xrs700x.h
new file mode 100644 (file)
index 0000000..ff62cf6
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+#include <linux/u64_stats_sync.h>
+#include <uapi/linux/if_link.h>
+
+struct xrs700x_info {
+       unsigned int id;
+       const char *name;
+       size_t num_ports;
+};
+
+extern const struct xrs700x_info xrs7003e_info;
+extern const struct xrs700x_info xrs7003f_info;
+extern const struct xrs700x_info xrs7004e_info;
+extern const struct xrs700x_info xrs7004f_info;
+
+struct xrs700x_port {
+       struct mutex mib_mutex; /* protects mib_data */
+       u64 *mib_data;
+       struct rtnl_link_stats64 stats64;
+       struct u64_stats_sync syncp;
+};
+
+struct xrs700x {
+       struct dsa_switch *ds;
+       struct device *dev;
+       void *priv;
+       struct regmap *regmap;
+       struct regmap_field *ps_forward;
+       struct regmap_field *ps_management;
+       struct regmap_field *ps_sel_speed;
+       struct regmap_field *ps_cur_speed;
+       struct delayed_work mib_work;
+       struct xrs700x_port *ports;
+};
+
+struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv);
+int xrs700x_switch_register(struct xrs700x *priv);
+void xrs700x_switch_remove(struct xrs700x *priv);
diff --git a/drivers/net/dsa/xrs700x/xrs700x_i2c.c b/drivers/net/dsa/xrs700x/xrs700x_i2c.c
new file mode 100644 (file)
index 0000000..a5f8883
--- /dev/null
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 NovaTech LLC
+ * George McCollister <george.mccollister@gmail.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include "xrs700x.h"
+#include "xrs700x_reg.h"
+
+static int xrs700x_i2c_reg_read(void *context, unsigned int reg,
+                               unsigned int *val)
+{
+       struct device *dev = context;
+       struct i2c_client *i2c = to_i2c_client(dev);
+       unsigned char buf[4];
+       int ret;
+
+       buf[0] = reg >> 23 & 0xff;
+       buf[1] = reg >> 15 & 0xff;
+       buf[2] = reg >> 7 & 0xff;
+       buf[3] = (reg & 0x7f) << 1;
+
+       ret = i2c_master_send(i2c, buf, sizeof(buf));
+       if (ret < 0) {
+               dev_err(dev, "xrs i2c_master_send returned %d\n", ret);
+               return ret;
+       }
+
+       ret = i2c_master_recv(i2c, buf, 2);
+       if (ret < 0) {
+               dev_err(dev, "xrs i2c_master_recv returned %d\n", ret);
+               return ret;
+       }
+
+       *val = buf[0] << 8 | buf[1];
+
+       return 0;
+}
+
+static int xrs700x_i2c_reg_write(void *context, unsigned int reg,
+                                unsigned int val)
+{
+       struct device *dev = context;
+       struct i2c_client *i2c = to_i2c_client(dev);
+       unsigned char buf[6];
+       int ret;
+
+       buf[0] = reg >> 23 & 0xff;
+       buf[1] = reg >> 15 & 0xff;
+       buf[2] = reg >> 7 & 0xff;
+       buf[3] = (reg & 0x7f) << 1 | 1;
+       buf[4] = val >> 8 & 0xff;
+       buf[5] = val & 0xff;
+
+       ret = i2c_master_send(i2c, buf, sizeof(buf));
+       if (ret < 0) {
+               dev_err(dev, "xrs i2c_master_send returned %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct regmap_config xrs700x_i2c_regmap_config = {
+       .val_bits = 16,
+       .reg_stride = 2,
+       .reg_bits = 32,
+       .pad_bits = 0,
+       .write_flag_mask = 0,
+       .read_flag_mask = 0,
+       .reg_read = xrs700x_i2c_reg_read,
+       .reg_write = xrs700x_i2c_reg_write,
+       .max_register = 0,
+       .cache_type = REGCACHE_NONE,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG
+};
+
+static int xrs700x_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *i2c_id)
+{
+       struct xrs700x *priv;
+       int ret;
+
+       priv = xrs700x_switch_alloc(&i2c->dev, i2c);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->regmap = devm_regmap_init(&i2c->dev, NULL, &i2c->dev,
+                                       &xrs700x_i2c_regmap_config);
+       if (IS_ERR(priv->regmap)) {
+               ret = PTR_ERR(priv->regmap);
+               dev_err(&i2c->dev, "Failed to initialize regmap: %d\n", ret);
+               return ret;
+       }
+
+       i2c_set_clientdata(i2c, priv);
+
+       ret = xrs700x_switch_register(priv);
+
+       /* Main DSA driver may not be started yet. */
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int xrs700x_i2c_remove(struct i2c_client *i2c)
+{
+       struct xrs700x *priv = i2c_get_clientdata(i2c);
+
+       xrs700x_switch_remove(priv);
+
+       return 0;
+}
+
+static const struct i2c_device_id xrs700x_i2c_id[] = {
+       { "xrs700x-switch", 0 },
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, xrs700x_i2c_id);
+
+static const struct of_device_id xrs700x_i2c_dt_ids[] = {
+       { .compatible = "arrow,xrs7003e", .data = &xrs7003e_info },
+       { .compatible = "arrow,xrs7003f", .data = &xrs7003f_info },
+       { .compatible = "arrow,xrs7004e", .data = &xrs7004e_info },
+       { .compatible = "arrow,xrs7004f", .data = &xrs7004f_info },
+       {},
+};
+MODULE_DEVICE_TABLE(of, xrs700x_i2c_dt_ids);
+
+static struct i2c_driver xrs700x_i2c_driver = {
+       .driver = {
+               .name   = "xrs700x-i2c",
+               .of_match_table = of_match_ptr(xrs700x_i2c_dt_ids),
+       },
+       .probe  = xrs700x_i2c_probe,
+       .remove = xrs700x_i2c_remove,
+       .id_table = xrs700x_i2c_id,
+};
+
+module_i2c_driver(xrs700x_i2c_driver);
+
+MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>");
+MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/xrs700x/xrs700x_mdio.c b/drivers/net/dsa/xrs700x/xrs700x_mdio.c
new file mode 100644 (file)
index 0000000..a10ee28
--- /dev/null
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 NovaTech LLC
+ * George McCollister <george.mccollister@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/phy.h>
+#include <linux/if_vlan.h>
+#include "xrs700x.h"
+#include "xrs700x_reg.h"
+
+#define XRS_MDIO_IBA0  0x10
+#define XRS_MDIO_IBA1  0x11
+#define XRS_MDIO_IBD   0x14
+
+#define XRS_IB_READ    0x0
+#define XRS_IB_WRITE   0x1
+
+static int xrs700x_mdio_reg_read(void *context, unsigned int reg,
+                                unsigned int *val)
+{
+       struct mdio_device *mdiodev = context;
+       struct device *dev = &mdiodev->dev;
+       u16 uval;
+       int ret;
+
+       uval = (u16)FIELD_GET(GENMASK(31, 16), reg);
+
+       ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA1, uval);
+       if (ret < 0) {
+               dev_err(dev, "xrs mdiobus_write returned %d\n", ret);
+               return ret;
+       }
+
+       uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_READ);
+
+       ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA0, uval);
+       if (ret < 0) {
+               dev_err(dev, "xrs mdiobus_write returned %d\n", ret);
+               return ret;
+       }
+
+       ret = mdiobus_read(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBD);
+       if (ret < 0) {
+               dev_err(dev, "xrs mdiobus_read returned %d\n", ret);
+               return ret;
+       }
+
+       *val = (unsigned int)ret;
+
+       return 0;
+}
+
+static int xrs700x_mdio_reg_write(void *context, unsigned int reg,
+                                 unsigned int val)
+{
+       struct mdio_device *mdiodev = context;
+       struct device *dev = &mdiodev->dev;
+       u16 uval;
+       int ret;
+
+       ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBD, (u16)val);
+       if (ret < 0) {
+               dev_err(dev, "xrs mdiobus_write returned %d\n", ret);
+               return ret;
+       }
+
+       uval = (u16)FIELD_GET(GENMASK(31, 16), reg);
+
+       ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA1, uval);
+       if (ret < 0) {
+               dev_err(dev, "xrs mdiobus_write returned %d\n", ret);
+               return ret;
+       }
+
+       uval = (u16)((reg & GENMASK(15, 1)) | XRS_IB_WRITE);
+
+       ret = mdiobus_write(mdiodev->bus, mdiodev->addr, XRS_MDIO_IBA0, uval);
+       if (ret < 0) {
+               dev_err(dev, "xrs mdiobus_write returned %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct regmap_config xrs700x_mdio_regmap_config = {
+       .val_bits = 16,
+       .reg_stride = 2,
+       .reg_bits = 32,
+       .pad_bits = 0,
+       .write_flag_mask = 0,
+       .read_flag_mask = 0,
+       .reg_read = xrs700x_mdio_reg_read,
+       .reg_write = xrs700x_mdio_reg_write,
+       .max_register = XRS_VLAN(VLAN_N_VID - 1),
+       .cache_type = REGCACHE_NONE,
+       .reg_format_endian = REGMAP_ENDIAN_BIG,
+       .val_format_endian = REGMAP_ENDIAN_BIG
+};
+
+static int xrs700x_mdio_probe(struct mdio_device *mdiodev)
+{
+       struct xrs700x *priv;
+       int ret;
+
+       priv = xrs700x_switch_alloc(&mdiodev->dev, mdiodev);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->regmap = devm_regmap_init(&mdiodev->dev, NULL, mdiodev,
+                                       &xrs700x_mdio_regmap_config);
+       if (IS_ERR(priv->regmap)) {
+               ret = PTR_ERR(priv->regmap);
+               dev_err(&mdiodev->dev, "Failed to initialize regmap: %d\n", ret);
+               return ret;
+       }
+
+       dev_set_drvdata(&mdiodev->dev, priv);
+
+       ret = xrs700x_switch_register(priv);
+
+       /* Main DSA driver may not be started yet. */
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void xrs700x_mdio_remove(struct mdio_device *mdiodev)
+{
+       struct xrs700x *priv = dev_get_drvdata(&mdiodev->dev);
+
+       xrs700x_switch_remove(priv);
+}
+
+static const struct of_device_id xrs700x_mdio_dt_ids[] = {
+       { .compatible = "arrow,xrs7003e", .data = &xrs7003e_info },
+       { .compatible = "arrow,xrs7003f", .data = &xrs7003f_info },
+       { .compatible = "arrow,xrs7004e", .data = &xrs7004e_info },
+       { .compatible = "arrow,xrs7004f", .data = &xrs7004f_info },
+       {},
+};
+MODULE_DEVICE_TABLE(of, xrs700x_mdio_dt_ids);
+
+static struct mdio_driver xrs700x_mdio_driver = {
+       .mdiodrv.driver = {
+               .name   = "xrs700x-mdio",
+               .of_match_table = xrs700x_mdio_dt_ids,
+       },
+       .probe  = xrs700x_mdio_probe,
+       .remove = xrs700x_mdio_remove,
+};
+
+mdio_module_driver(xrs700x_mdio_driver);
+
+MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>");
+MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA MDIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/dsa/xrs700x/xrs700x_reg.h b/drivers/net/dsa/xrs700x/xrs700x_reg.h
new file mode 100644 (file)
index 0000000..a135d4d
--- /dev/null
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Register Base Addresses */
+#define XRS_DEVICE_ID_BASE             0x0
+#define XRS_GPIO_BASE                  0x10000
+#define XRS_PORT_OFFSET                        0x10000
+#define XRS_PORT_BASE(x)               (0x200000 + XRS_PORT_OFFSET * (x))
+#define XRS_RTC_BASE                   0x280000
+#define XRS_TS_OFFSET                  0x8000
+#define XRS_TS_BASE(x)                 (0x290000 + XRS_TS_OFFSET * (x))
+#define XRS_SWITCH_CONF_BASE           0x300000
+
+/* Device Identification Registers */
+#define XRS_DEV_ID0                    (XRS_DEVICE_ID_BASE + 0)
+#define XRS_DEV_ID1                    (XRS_DEVICE_ID_BASE + 2)
+#define XRS_INT_ID0                    (XRS_DEVICE_ID_BASE + 4)
+#define XRS_INT_ID1                    (XRS_DEVICE_ID_BASE + 6)
+#define XRS_REV_ID                     (XRS_DEVICE_ID_BASE + 8)
+
+/* GPIO Registers */
+#define XRS_CONFIG0                    (XRS_GPIO_BASE + 0x1000)
+#define XRS_INPUT_STATUS0              (XRS_GPIO_BASE + 0x1002)
+#define XRS_CONFIG1                    (XRS_GPIO_BASE + 0x1004)
+#define XRS_INPUT_STATUS1              (XRS_GPIO_BASE + 0x1006)
+#define XRS_CONFIG2                    (XRS_GPIO_BASE + 0x1008)
+#define XRS_INPUT_STATUS2              (XRS_GPIO_BASE + 0x100a)
+
+/* Port Configuration Registers */
+#define XRS_PORT_GEN_BASE(x)           (XRS_PORT_BASE(x) + 0x0)
+#define XRS_PORT_HSR_BASE(x)           (XRS_PORT_BASE(x) + 0x2000)
+#define XRS_PORT_PTP_BASE(x)           (XRS_PORT_BASE(x) + 0x4000)
+#define XRS_PORT_CNT_BASE(x)           (XRS_PORT_BASE(x) + 0x6000)
+#define XRS_PORT_IPO_BASE(x)           (XRS_PORT_BASE(x) + 0x8000)
+
+/* Port Configuration Registers - General and State */
+#define XRS_PORT_STATE(x)              (XRS_PORT_GEN_BASE(x) + 0x0)
+#define XRS_PORT_FORWARDING            0
+#define XRS_PORT_LEARNING              1
+#define XRS_PORT_DISABLED              2
+#define XRS_PORT_MODE_NORMAL           0
+#define XRS_PORT_MODE_MANAGEMENT       1
+#define XRS_PORT_SPEED_1000            0x12
+#define XRS_PORT_SPEED_100             0x20
+#define XRS_PORT_SPEED_10              0x30
+#define XRS_PORT_VLAN(x)               (XRS_PORT_GEN_BASE(x) + 0x10)
+#define XRS_PORT_VLAN0_MAPPING(x)      (XRS_PORT_GEN_BASE(x) + 0x12)
+#define XRS_PORT_FWD_MASK(x)           (XRS_PORT_GEN_BASE(x) + 0x14)
+#define XRS_PORT_VLAN_PRIO(x)          (XRS_PORT_GEN_BASE(x) + 0x16)
+
+/* Port Configuration Registers - HSR/PRP */
+#define XRS_HSR_CFG(x)                 (XRS_PORT_HSR_BASE(x) + 0x0)
+
+/* Port Configuration Registers - PTP */
+#define XRS_PTP_RX_SYNC_DELAY_NS_LO(x) (XRS_PORT_PTP_BASE(x) + 0x2)
+#define XRS_PTP_RX_SYNC_DELAY_NS_HI(x) (XRS_PORT_PTP_BASE(x) + 0x4)
+#define XRS_PTP_RX_EVENT_DELAY_NS(x)   (XRS_PORT_PTP_BASE(x) + 0xa)
+#define XRS_PTP_TX_EVENT_DELAY_NS(x)   (XRS_PORT_PTP_BASE(x) + 0x12)
+
+/* Port Configuration Registers - Counter */
+#define XRS_CNT_CTRL(x)                        (XRS_PORT_CNT_BASE(x) + 0x0)
+#define XRS_RX_GOOD_OCTETS_L           (XRS_PORT_CNT_BASE(0) + 0x200)
+#define XRS_RX_GOOD_OCTETS_H           (XRS_PORT_CNT_BASE(0) + 0x202)
+#define XRS_RX_BAD_OCTETS_L            (XRS_PORT_CNT_BASE(0) + 0x204)
+#define XRS_RX_BAD_OCTETS_H            (XRS_PORT_CNT_BASE(0) + 0x206)
+#define XRS_RX_UNICAST_L               (XRS_PORT_CNT_BASE(0) + 0x208)
+#define XRS_RX_UNICAST_H               (XRS_PORT_CNT_BASE(0) + 0x20a)
+#define XRS_RX_BROADCAST_L             (XRS_PORT_CNT_BASE(0) + 0x20c)
+#define XRS_RX_BROADCAST_H             (XRS_PORT_CNT_BASE(0) + 0x20e)
+#define XRS_RX_MULTICAST_L             (XRS_PORT_CNT_BASE(0) + 0x210)
+#define XRS_RX_MULTICAST_H             (XRS_PORT_CNT_BASE(0) + 0x212)
+#define XRS_RX_UNDERSIZE_L             (XRS_PORT_CNT_BASE(0) + 0x214)
+#define XRS_RX_UNDERSIZE_H             (XRS_PORT_CNT_BASE(0) + 0x216)
+#define XRS_RX_FRAGMENTS_L             (XRS_PORT_CNT_BASE(0) + 0x218)
+#define XRS_RX_FRAGMENTS_H             (XRS_PORT_CNT_BASE(0) + 0x21a)
+#define XRS_RX_OVERSIZE_L              (XRS_PORT_CNT_BASE(0) + 0x21c)
+#define XRS_RX_OVERSIZE_H              (XRS_PORT_CNT_BASE(0) + 0x21e)
+#define XRS_RX_JABBER_L                        (XRS_PORT_CNT_BASE(0) + 0x220)
+#define XRS_RX_JABBER_H                        (XRS_PORT_CNT_BASE(0) + 0x222)
+#define XRS_RX_ERR_L                   (XRS_PORT_CNT_BASE(0) + 0x224)
+#define XRS_RX_ERR_H                   (XRS_PORT_CNT_BASE(0) + 0x226)
+#define XRS_RX_CRC_L                   (XRS_PORT_CNT_BASE(0) + 0x228)
+#define XRS_RX_CRC_H                   (XRS_PORT_CNT_BASE(0) + 0x22a)
+#define XRS_RX_64_L                    (XRS_PORT_CNT_BASE(0) + 0x22c)
+#define XRS_RX_64_H                    (XRS_PORT_CNT_BASE(0) + 0x22e)
+#define XRS_RX_65_127_L                        (XRS_PORT_CNT_BASE(0) + 0x230)
+#define XRS_RX_65_127_H                        (XRS_PORT_CNT_BASE(0) + 0x232)
+#define XRS_RX_128_255_L               (XRS_PORT_CNT_BASE(0) + 0x234)
+#define XRS_RX_128_255_H               (XRS_PORT_CNT_BASE(0) + 0x236)
+#define XRS_RX_256_511_L               (XRS_PORT_CNT_BASE(0) + 0x238)
+#define XRS_RX_256_511_H               (XRS_PORT_CNT_BASE(0) + 0x23a)
+#define XRS_RX_512_1023_L              (XRS_PORT_CNT_BASE(0) + 0x23c)
+#define XRS_RX_512_1023_H              (XRS_PORT_CNT_BASE(0) + 0x23e)
+#define XRS_RX_1024_1536_L             (XRS_PORT_CNT_BASE(0) + 0x240)
+#define XRS_RX_1024_1536_H             (XRS_PORT_CNT_BASE(0) + 0x242)
+#define XRS_RX_HSR_PRP_L               (XRS_PORT_CNT_BASE(0) + 0x244)
+#define XRS_RX_HSR_PRP_H               (XRS_PORT_CNT_BASE(0) + 0x246)
+#define XRS_RX_WRONGLAN_L              (XRS_PORT_CNT_BASE(0) + 0x248)
+#define XRS_RX_WRONGLAN_H              (XRS_PORT_CNT_BASE(0) + 0x24a)
+#define XRS_RX_DUPLICATE_L             (XRS_PORT_CNT_BASE(0) + 0x24c)
+#define XRS_RX_DUPLICATE_H             (XRS_PORT_CNT_BASE(0) + 0x24e)
+#define XRS_TX_OCTETS_L                        (XRS_PORT_CNT_BASE(0) + 0x280)
+#define XRS_TX_OCTETS_H                        (XRS_PORT_CNT_BASE(0) + 0x282)
+#define XRS_TX_UNICAST_L               (XRS_PORT_CNT_BASE(0) + 0x284)
+#define XRS_TX_UNICAST_H               (XRS_PORT_CNT_BASE(0) + 0x286)
+#define XRS_TX_BROADCAST_L             (XRS_PORT_CNT_BASE(0) + 0x288)
+#define XRS_TX_BROADCAST_H             (XRS_PORT_CNT_BASE(0) + 0x28a)
+#define XRS_TX_MULTICAST_L             (XRS_PORT_CNT_BASE(0) + 0x28c)
+#define XRS_TX_MULTICAST_H             (XRS_PORT_CNT_BASE(0) + 0x28e)
+#define XRS_TX_HSR_PRP_L               (XRS_PORT_CNT_BASE(0) + 0x290)
+#define XRS_TX_HSR_PRP_H               (XRS_PORT_CNT_BASE(0) + 0x292)
+#define XRS_PRIQ_DROP_L                        (XRS_PORT_CNT_BASE(0) + 0x2c0)
+#define XRS_PRIQ_DROP_H                        (XRS_PORT_CNT_BASE(0) + 0x2c2)
+#define XRS_EARLY_DROP_L               (XRS_PORT_CNT_BASE(0) + 0x2c4)
+#define XRS_EARLY_DROP_H               (XRS_PORT_CNT_BASE(0) + 0x2c6)
+
+/* Port Configuration Registers - Inbound Policy 0 - 15 */
+#define XRS_ETH_ADDR_CFG(x, p)         (XRS_PORT_IPO_BASE(x) + \
+                                        (p) * 0x20 + 0x0)
+#define XRS_ETH_ADDR_FWD_ALLOW(x, p)   (XRS_PORT_IPO_BASE(x) + \
+                                        (p) * 0x20 + 0x2)
+#define XRS_ETH_ADDR_FWD_MIRROR(x, p)  (XRS_PORT_IPO_BASE(x) + \
+                                        (p) * 0x20 + 0x4)
+#define XRS_ETH_ADDR_0(x, p)           (XRS_PORT_IPO_BASE(x) + \
+                                        (p) * 0x20 + 0x8)
+#define XRS_ETH_ADDR_1(x, p)           (XRS_PORT_IPO_BASE(x) + \
+                                        (p) * 0x20 + 0xa)
+#define XRS_ETH_ADDR_2(x, p)           (XRS_PORT_IPO_BASE(x) + \
+                                        (p) * 0x20 + 0xc)
+
+/* RTC Registers */
+#define XRS_CUR_NSEC0                  (XRS_RTC_BASE + 0x1004)
+#define XRS_CUR_NSEC1                  (XRS_RTC_BASE + 0x1006)
+#define XRS_CUR_SEC0                   (XRS_RTC_BASE + 0x1008)
+#define XRS_CUR_SEC1                   (XRS_RTC_BASE + 0x100a)
+#define XRS_CUR_SEC2                   (XRS_RTC_BASE + 0x100c)
+#define XRS_TIME_CC0                   (XRS_RTC_BASE + 0x1010)
+#define XRS_TIME_CC1                   (XRS_RTC_BASE + 0x1012)
+#define XRS_TIME_CC2                   (XRS_RTC_BASE + 0x1014)
+#define XRS_STEP_SIZE0                 (XRS_RTC_BASE + 0x1020)
+#define XRS_STEP_SIZE1                 (XRS_RTC_BASE + 0x1022)
+#define XRS_STEP_SIZE2                 (XRS_RTC_BASE + 0x1024)
+#define XRS_ADJUST_NSEC0               (XRS_RTC_BASE + 0x1034)
+#define XRS_ADJUST_NSEC1               (XRS_RTC_BASE + 0x1036)
+#define XRS_ADJUST_SEC0                        (XRS_RTC_BASE + 0x1038)
+#define XRS_ADJUST_SEC1                        (XRS_RTC_BASE + 0x103a)
+#define XRS_ADJUST_SEC2                        (XRS_RTC_BASE + 0x103c)
+#define XRS_TIME_CMD                   (XRS_RTC_BASE + 0x1040)
+
+/* Time Stamper Registers */
+#define XRS_TS_CTRL(x)                 (XRS_TS_BASE(x) + 0x1000)
+#define XRS_TS_INT_MASK(x)             (XRS_TS_BASE(x) + 0x1008)
+#define XRS_TS_INT_STATUS(x)           (XRS_TS_BASE(x) + 0x1010)
+#define XRS_TS_NSEC0(x)                        (XRS_TS_BASE(x) + 0x1104)
+#define XRS_TS_NSEC1(x)                        (XRS_TS_BASE(x) + 0x1106)
+#define XRS_TS_SEC0(x)                 (XRS_TS_BASE(x) + 0x1108)
+#define XRS_TS_SEC1(x)                 (XRS_TS_BASE(x) + 0x110a)
+#define XRS_TS_SEC2(x)                 (XRS_TS_BASE(x) + 0x110c)
+#define XRS_PNCT0(x)                   (XRS_TS_BASE(x) + 0x1110)
+#define XRS_PNCT1(x)                   (XRS_TS_BASE(x) + 0x1112)
+
+/* Switch Configuration Registers */
+#define XRS_SWITCH_GEN_BASE            (XRS_SWITCH_CONF_BASE + 0x0)
+#define XRS_SWITCH_TS_BASE             (XRS_SWITCH_CONF_BASE + 0x2000)
+#define XRS_SWITCH_VLAN_BASE           (XRS_SWITCH_CONF_BASE + 0x4000)
+
+/* Switch Configuration Registers - General */
+#define XRS_GENERAL                    (XRS_SWITCH_GEN_BASE + 0x10)
+#define XRS_GENERAL_TIME_TRAILER       BIT(9)
+#define XRS_GENERAL_MOD_SYNC           BIT(10)
+#define XRS_GENERAL_CUT_THRU           BIT(13)
+#define XRS_GENERAL_CLR_MAC_TBL                BIT(14)
+#define XRS_GENERAL_RESET              BIT(15)
+#define XRS_MT_CLEAR_MASK              (XRS_SWITCH_GEN_BASE + 0x12)
+#define XRS_ADDRESS_AGING              (XRS_SWITCH_GEN_BASE + 0x20)
+#define XRS_TS_CTRL_TX                 (XRS_SWITCH_GEN_BASE + 0x28)
+#define XRS_TS_CTRL_RX                 (XRS_SWITCH_GEN_BASE + 0x2a)
+#define XRS_INT_MASK                   (XRS_SWITCH_GEN_BASE + 0x2c)
+#define XRS_INT_STATUS                 (XRS_SWITCH_GEN_BASE + 0x2e)
+#define XRS_MAC_TABLE0                 (XRS_SWITCH_GEN_BASE + 0x200)
+#define XRS_MAC_TABLE1                 (XRS_SWITCH_GEN_BASE + 0x202)
+#define XRS_MAC_TABLE2                 (XRS_SWITCH_GEN_BASE + 0x204)
+#define XRS_MAC_TABLE3                 (XRS_SWITCH_GEN_BASE + 0x206)
+
+/* Switch Configuration Registers - Frame Timestamp */
+#define XRS_TX_TS_NS_LO(t)             (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x0)
+#define XRS_TX_TS_NS_HI(t)             (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x2)
+#define XRS_TX_TS_S_LO(t)              (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x4)
+#define XRS_TX_TS_S_HI(t)              (XRS_SWITCH_TS_BASE + 0x80 * (t) + 0x6)
+#define XRS_TX_TS_HDR(t, h)            (XRS_SWITCH_TS_BASE + 0x80 * (t) + \
+                                        0x2 * (h) + 0xe)
+#define XRS_RX_TS_NS_LO(t)             (XRS_SWITCH_TS_BASE + 0x80 * (t) + \
+                                        0x200)
+#define XRS_RX_TS_NS_HI(t)             (XRS_SWITCH_TS_BASE + 0x80 * (t) + \
+                                        0x202)
+#define XRS_RX_TS_S_LO(t)              (XRS_SWITCH_TS_BASE + 0x80 * (t) + \
+                                        0x204)
+#define XRS_RX_TS_S_HI(t)              (XRS_SWITCH_TS_BASE + 0x80 * (t) + \
+                                        0x206)
+#define XRS_RX_TS_HDR(t, h)            (XRS_SWITCH_TS_BASE + 0x80 * (t) + \
+                                        0x2 * (h) + 0xe)
+
+/* Switch Configuration Registers - VLAN */
+#define XRS_VLAN(v)                    (XRS_SWITCH_VLAN_BASE + 0x2 * (v))
index de50e8b..ad04660 100644 (file)
@@ -33,7 +33,6 @@ source "drivers/net/ethernet/apple/Kconfig"
 source "drivers/net/ethernet/aquantia/Kconfig"
 source "drivers/net/ethernet/arc/Kconfig"
 source "drivers/net/ethernet/atheros/Kconfig"
-source "drivers/net/ethernet/aurora/Kconfig"
 source "drivers/net/ethernet/broadcom/Kconfig"
 source "drivers/net/ethernet/brocade/Kconfig"
 source "drivers/net/ethernet/cadence/Kconfig"
index f8f38dc..1e7dc8a 100644 (file)
@@ -19,7 +19,6 @@ obj-$(CONFIG_NET_VENDOR_APPLE) += apple/
 obj-$(CONFIG_NET_VENDOR_AQUANTIA) += aquantia/
 obj-$(CONFIG_NET_VENDOR_ARC) += arc/
 obj-$(CONFIG_NET_VENDOR_ATHEROS) += atheros/
-obj-$(CONFIG_NET_VENDOR_AURORA) += aurora/
 obj-$(CONFIG_NET_VENDOR_CADENCE) += cadence/
 obj-$(CONFIG_NET_VENDOR_BROADCOM) += broadcom/
 obj-$(CONFIG_NET_VENDOR_BROCADE) += brocade/
index 06596fa..1db6cfd 100644 (file)
@@ -1585,10 +1585,9 @@ static int ena_xdp_handle_buff(struct ena_ring *rx_ring, struct xdp_buff *xdp)
        int ret;
 
        rx_info = &rx_ring->rx_buffer_info[rx_ring->ena_bufs[0].req_id];
-       xdp->data = page_address(rx_info->page) + rx_info->page_offset;
-       xdp_set_data_meta_invalid(xdp);
-       xdp->data_hard_start = page_address(rx_info->page);
-       xdp->data_end = xdp->data + rx_ring->ena_bufs[0].len;
+       xdp_prepare_buff(xdp, page_address(rx_info->page),
+                        rx_info->page_offset,
+                        rx_ring->ena_bufs[0].len, false);
        /* If for some reason we received a bigger packet than
         * we expect, then we simply drop it
         */
@@ -1634,8 +1633,7 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi,
        netif_dbg(rx_ring->adapter, rx_status, rx_ring->netdev,
                  "%s qid %d\n", __func__, rx_ring->qid);
        res_budget = budget;
-       xdp.rxq = &rx_ring->xdp_rxq;
-       xdp.frame_sz = ENA_PAGE_SIZE;
+       xdp_init_buff(&xdp, ENA_PAGE_SIZE, &rx_ring->xdp_rxq);
 
        do {
                xdp_verdict = XDP_PASS;
index 2709a2d..99b6d5a 100644 (file)
@@ -2295,8 +2295,6 @@ static const struct net_device_ops xgbe_netdev_ops = {
        .ndo_setup_tc           = xgbe_setup_tc,
        .ndo_fix_features       = xgbe_fix_features,
        .ndo_set_features       = xgbe_set_features,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = xgbe_features_check,
 };
 
index efb33c0..cec2018 100644 (file)
@@ -19,7 +19,6 @@ if NET_VENDOR_AQUANTIA
 config AQTION
        tristate "aQuantia AQtion(tm) Support"
        depends on PCI
-       depends on X86_64 || ARM64 || COMPILE_TEST
        depends on MACSEC || MACSEC=n
        help
          This enables the support for the aQuantia AQtion(tm) Ethernet card.
diff --git a/drivers/net/ethernet/aurora/Kconfig b/drivers/net/ethernet/aurora/Kconfig
deleted file mode 100644 (file)
index 9ee30ea..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config NET_VENDOR_AURORA
-       bool "Aurora VLSI devices"
-       default y
-       help
-         If you have a network (Ethernet) device belonging to this class,
-         say Y.
-
-         Note that the answer to this question doesn't directly affect the
-         kernel: saying N will just cause the configurator to skip all
-         questions about Aurora devices. If you say Y, you will be asked
-         for your specific device in the following questions.
-
-if NET_VENDOR_AURORA
-
-config AURORA_NB8800
-       tristate "Aurora AU-NB8800 support"
-       depends on HAS_DMA
-       select PHYLIB
-       help
-        Support for the AU-NB8800 gigabit Ethernet controller.
-
-endif
diff --git a/drivers/net/ethernet/aurora/Makefile b/drivers/net/ethernet/aurora/Makefile
deleted file mode 100644 (file)
index f3d5998..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_AURORA_NB8800) += nb8800.o
diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
deleted file mode 100644 (file)
index 5b20185..0000000
+++ /dev/null
@@ -1,1520 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (C) 2015 Mans Rullgard <mans@mansr.com>
- *
- * Mostly rewritten, based on driver from Sigma Designs.  Original
- * copyright notice below.
- *
- * Driver for tangox SMP864x/SMP865x/SMP867x/SMP868x builtin Ethernet Mac.
- *
- * Copyright (C) 2005 Maxime Bizon <mbizon@freebox.fr>
- */
-
-#include <linux/module.h>
-#include <linux/etherdevice.h>
-#include <linux/delay.h>
-#include <linux/ethtool.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/of_device.h>
-#include <linux/of_mdio.h>
-#include <linux/of_net.h>
-#include <linux/dma-mapping.h>
-#include <linux/phy.h>
-#include <linux/cache.h>
-#include <linux/jiffies.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <asm/barrier.h>
-
-#include "nb8800.h"
-
-static void nb8800_tx_done(struct net_device *dev);
-static int nb8800_dma_stop(struct net_device *dev);
-
-static inline u8 nb8800_readb(struct nb8800_priv *priv, int reg)
-{
-       return readb_relaxed(priv->base + reg);
-}
-
-static inline u32 nb8800_readl(struct nb8800_priv *priv, int reg)
-{
-       return readl_relaxed(priv->base + reg);
-}
-
-static inline void nb8800_writeb(struct nb8800_priv *priv, int reg, u8 val)
-{
-       writeb_relaxed(val, priv->base + reg);
-}
-
-static inline void nb8800_writew(struct nb8800_priv *priv, int reg, u16 val)
-{
-       writew_relaxed(val, priv->base + reg);
-}
-
-static inline void nb8800_writel(struct nb8800_priv *priv, int reg, u32 val)
-{
-       writel_relaxed(val, priv->base + reg);
-}
-
-static inline void nb8800_maskb(struct nb8800_priv *priv, int reg,
-                               u32 mask, u32 val)
-{
-       u32 old = nb8800_readb(priv, reg);
-       u32 new = (old & ~mask) | (val & mask);
-
-       if (new != old)
-               nb8800_writeb(priv, reg, new);
-}
-
-static inline void nb8800_maskl(struct nb8800_priv *priv, int reg,
-                               u32 mask, u32 val)
-{
-       u32 old = nb8800_readl(priv, reg);
-       u32 new = (old & ~mask) | (val & mask);
-
-       if (new != old)
-               nb8800_writel(priv, reg, new);
-}
-
-static inline void nb8800_modb(struct nb8800_priv *priv, int reg, u8 bits,
-                              bool set)
-{
-       nb8800_maskb(priv, reg, bits, set ? bits : 0);
-}
-
-static inline void nb8800_setb(struct nb8800_priv *priv, int reg, u8 bits)
-{
-       nb8800_maskb(priv, reg, bits, bits);
-}
-
-static inline void nb8800_clearb(struct nb8800_priv *priv, int reg, u8 bits)
-{
-       nb8800_maskb(priv, reg, bits, 0);
-}
-
-static inline void nb8800_modl(struct nb8800_priv *priv, int reg, u32 bits,
-                              bool set)
-{
-       nb8800_maskl(priv, reg, bits, set ? bits : 0);
-}
-
-static inline void nb8800_setl(struct nb8800_priv *priv, int reg, u32 bits)
-{
-       nb8800_maskl(priv, reg, bits, bits);
-}
-
-static inline void nb8800_clearl(struct nb8800_priv *priv, int reg, u32 bits)
-{
-       nb8800_maskl(priv, reg, bits, 0);
-}
-
-static int nb8800_mdio_wait(struct mii_bus *bus)
-{
-       struct nb8800_priv *priv = bus->priv;
-       u32 val;
-
-       return readl_poll_timeout_atomic(priv->base + NB8800_MDIO_CMD,
-                                        val, !(val & MDIO_CMD_GO), 1, 1000);
-}
-
-static int nb8800_mdio_cmd(struct mii_bus *bus, u32 cmd)
-{
-       struct nb8800_priv *priv = bus->priv;
-       int err;
-
-       err = nb8800_mdio_wait(bus);
-       if (err)
-               return err;
-
-       nb8800_writel(priv, NB8800_MDIO_CMD, cmd);
-       udelay(10);
-       nb8800_writel(priv, NB8800_MDIO_CMD, cmd | MDIO_CMD_GO);
-
-       return nb8800_mdio_wait(bus);
-}
-
-static int nb8800_mdio_read(struct mii_bus *bus, int phy_id, int reg)
-{
-       struct nb8800_priv *priv = bus->priv;
-       u32 val;
-       int err;
-
-       err = nb8800_mdio_cmd(bus, MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg));
-       if (err)
-               return err;
-
-       val = nb8800_readl(priv, NB8800_MDIO_STS);
-       if (val & MDIO_STS_ERR)
-               return 0xffff;
-
-       return val & 0xffff;
-}
-
-static int nb8800_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
-{
-       u32 cmd = MDIO_CMD_ADDR(phy_id) | MDIO_CMD_REG(reg) |
-               MDIO_CMD_DATA(val) | MDIO_CMD_WR;
-
-       return nb8800_mdio_cmd(bus, cmd);
-}
-
-static void nb8800_mac_tx(struct net_device *dev, bool enable)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-
-       while (nb8800_readl(priv, NB8800_TXC_CR) & TCR_EN)
-               cpu_relax();
-
-       nb8800_modb(priv, NB8800_TX_CTL1, TX_EN, enable);
-}
-
-static void nb8800_mac_rx(struct net_device *dev, bool enable)
-{
-       nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_EN, enable);
-}
-
-static void nb8800_mac_af(struct net_device *dev, bool enable)
-{
-       nb8800_modb(netdev_priv(dev), NB8800_RX_CTL, RX_AF_EN, enable);
-}
-
-static void nb8800_start_rx(struct net_device *dev)
-{
-       nb8800_setl(netdev_priv(dev), NB8800_RXC_CR, RCR_EN);
-}
-
-static int nb8800_alloc_rx(struct net_device *dev, unsigned int i, bool napi)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_rx_desc *rxd = &priv->rx_descs[i];
-       struct nb8800_rx_buf *rxb = &priv->rx_bufs[i];
-       int size = L1_CACHE_ALIGN(RX_BUF_SIZE);
-       dma_addr_t dma_addr;
-       struct page *page;
-       unsigned long offset;
-       void *data;
-
-       data = napi ? napi_alloc_frag(size) : netdev_alloc_frag(size);
-       if (!data)
-               return -ENOMEM;
-
-       page = virt_to_head_page(data);
-       offset = data - page_address(page);
-
-       dma_addr = dma_map_page(&dev->dev, page, offset, RX_BUF_SIZE,
-                               DMA_FROM_DEVICE);
-
-       if (dma_mapping_error(&dev->dev, dma_addr)) {
-               skb_free_frag(data);
-               return -ENOMEM;
-       }
-
-       rxb->page = page;
-       rxb->offset = offset;
-       rxd->desc.s_addr = dma_addr;
-
-       return 0;
-}
-
-static void nb8800_receive(struct net_device *dev, unsigned int i,
-                          unsigned int len)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_rx_desc *rxd = &priv->rx_descs[i];
-       struct page *page = priv->rx_bufs[i].page;
-       int offset = priv->rx_bufs[i].offset;
-       void *data = page_address(page) + offset;
-       dma_addr_t dma = rxd->desc.s_addr;
-       struct sk_buff *skb;
-       unsigned int size;
-       int err;
-
-       size = len <= RX_COPYBREAK ? len : RX_COPYHDR;
-
-       skb = napi_alloc_skb(&priv->napi, size);
-       if (!skb) {
-               netdev_err(dev, "rx skb allocation failed\n");
-               dev->stats.rx_dropped++;
-               return;
-       }
-
-       if (len <= RX_COPYBREAK) {
-               dma_sync_single_for_cpu(&dev->dev, dma, len, DMA_FROM_DEVICE);
-               skb_put_data(skb, data, len);
-               dma_sync_single_for_device(&dev->dev, dma, len,
-                                          DMA_FROM_DEVICE);
-       } else {
-               err = nb8800_alloc_rx(dev, i, true);
-               if (err) {
-                       netdev_err(dev, "rx buffer allocation failed\n");
-                       dev->stats.rx_dropped++;
-                       dev_kfree_skb(skb);
-                       return;
-               }
-
-               dma_unmap_page(&dev->dev, dma, RX_BUF_SIZE, DMA_FROM_DEVICE);
-               skb_put_data(skb, data, RX_COPYHDR);
-               skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
-                               offset + RX_COPYHDR, len - RX_COPYHDR,
-                               RX_BUF_SIZE);
-       }
-
-       skb->protocol = eth_type_trans(skb, dev);
-       napi_gro_receive(&priv->napi, skb);
-}
-
-static void nb8800_rx_error(struct net_device *dev, u32 report)
-{
-       if (report & RX_LENGTH_ERR)
-               dev->stats.rx_length_errors++;
-
-       if (report & RX_FCS_ERR)
-               dev->stats.rx_crc_errors++;
-
-       if (report & RX_FIFO_OVERRUN)
-               dev->stats.rx_fifo_errors++;
-
-       if (report & RX_ALIGNMENT_ERROR)
-               dev->stats.rx_frame_errors++;
-
-       dev->stats.rx_errors++;
-}
-
-static int nb8800_poll(struct napi_struct *napi, int budget)
-{
-       struct net_device *dev = napi->dev;
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_rx_desc *rxd;
-       unsigned int last = priv->rx_eoc;
-       unsigned int next;
-       int work = 0;
-
-       nb8800_tx_done(dev);
-
-again:
-       do {
-               unsigned int len;
-
-               next = (last + 1) % RX_DESC_COUNT;
-
-               rxd = &priv->rx_descs[next];
-
-               if (!rxd->report)
-                       break;
-
-               len = RX_BYTES_TRANSFERRED(rxd->report);
-
-               if (IS_RX_ERROR(rxd->report))
-                       nb8800_rx_error(dev, rxd->report);
-               else
-                       nb8800_receive(dev, next, len);
-
-               dev->stats.rx_packets++;
-               dev->stats.rx_bytes += len;
-
-               if (rxd->report & RX_MULTICAST_PKT)
-                       dev->stats.multicast++;
-
-               rxd->report = 0;
-               last = next;
-               work++;
-       } while (work < budget);
-
-       if (work) {
-               priv->rx_descs[last].desc.config |= DESC_EOC;
-               wmb();  /* ensure new EOC is written before clearing old */
-               priv->rx_descs[priv->rx_eoc].desc.config &= ~DESC_EOC;
-               priv->rx_eoc = last;
-               nb8800_start_rx(dev);
-       }
-
-       if (work < budget) {
-               nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq);
-
-               /* If a packet arrived after we last checked but
-                * before writing RX_ITR, the interrupt will be
-                * delayed, so we retrieve it now.
-                */
-               if (priv->rx_descs[next].report)
-                       goto again;
-
-               napi_complete_done(napi, work);
-       }
-
-       return work;
-}
-
-static void __nb8800_tx_dma_start(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_tx_buf *txb;
-       u32 txc_cr;
-
-       txb = &priv->tx_bufs[priv->tx_queue];
-       if (!txb->ready)
-               return;
-
-       txc_cr = nb8800_readl(priv, NB8800_TXC_CR);
-       if (txc_cr & TCR_EN)
-               return;
-
-       nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc);
-       wmb();          /* ensure desc addr is written before starting DMA */
-       nb8800_writel(priv, NB8800_TXC_CR, txc_cr | TCR_EN);
-
-       priv->tx_queue = (priv->tx_queue + txb->chain_len) % TX_DESC_COUNT;
-}
-
-static void nb8800_tx_dma_start(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-
-       spin_lock_irq(&priv->tx_lock);
-       __nb8800_tx_dma_start(dev);
-       spin_unlock_irq(&priv->tx_lock);
-}
-
-static void nb8800_tx_dma_start_irq(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-
-       spin_lock(&priv->tx_lock);
-       __nb8800_tx_dma_start(dev);
-       spin_unlock(&priv->tx_lock);
-}
-
-static netdev_tx_t nb8800_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_tx_desc *txd;
-       struct nb8800_tx_buf *txb;
-       struct nb8800_dma_desc *desc;
-       dma_addr_t dma_addr;
-       unsigned int dma_len;
-       unsigned int align;
-       unsigned int next;
-       bool xmit_more;
-
-       if (atomic_read(&priv->tx_free) <= NB8800_DESC_LOW) {
-               netif_stop_queue(dev);
-               return NETDEV_TX_BUSY;
-       }
-
-       align = (8 - (uintptr_t)skb->data) & 7;
-
-       dma_len = skb->len - align;
-       dma_addr = dma_map_single(&dev->dev, skb->data + align,
-                                 dma_len, DMA_TO_DEVICE);
-
-       if (dma_mapping_error(&dev->dev, dma_addr)) {
-               netdev_err(dev, "tx dma mapping error\n");
-               kfree_skb(skb);
-               dev->stats.tx_dropped++;
-               return NETDEV_TX_OK;
-       }
-
-       xmit_more = netdev_xmit_more();
-       if (atomic_dec_return(&priv->tx_free) <= NB8800_DESC_LOW) {
-               netif_stop_queue(dev);
-               xmit_more = false;
-       }
-
-       next = priv->tx_next;
-       txb = &priv->tx_bufs[next];
-       txd = &priv->tx_descs[next];
-       desc = &txd->desc[0];
-
-       next = (next + 1) % TX_DESC_COUNT;
-
-       if (align) {
-               memcpy(txd->buf, skb->data, align);
-
-               desc->s_addr =
-                       txb->dma_desc + offsetof(struct nb8800_tx_desc, buf);
-               desc->n_addr = txb->dma_desc + sizeof(txd->desc[0]);
-               desc->config = DESC_BTS(2) | DESC_DS | align;
-
-               desc++;
-       }
-
-       desc->s_addr = dma_addr;
-       desc->n_addr = priv->tx_bufs[next].dma_desc;
-       desc->config = DESC_BTS(2) | DESC_DS | DESC_EOF | dma_len;
-
-       if (!xmit_more)
-               desc->config |= DESC_EOC;
-
-       txb->skb = skb;
-       txb->dma_addr = dma_addr;
-       txb->dma_len = dma_len;
-
-       if (!priv->tx_chain) {
-               txb->chain_len = 1;
-               priv->tx_chain = txb;
-       } else {
-               priv->tx_chain->chain_len++;
-       }
-
-       netdev_sent_queue(dev, skb->len);
-
-       priv->tx_next = next;
-
-       if (!xmit_more) {
-               smp_wmb();
-               priv->tx_chain->ready = true;
-               priv->tx_chain = NULL;
-               nb8800_tx_dma_start(dev);
-       }
-
-       return NETDEV_TX_OK;
-}
-
-static void nb8800_tx_error(struct net_device *dev, u32 report)
-{
-       if (report & TX_LATE_COLLISION)
-               dev->stats.collisions++;
-
-       if (report & TX_PACKET_DROPPED)
-               dev->stats.tx_dropped++;
-
-       if (report & TX_FIFO_UNDERRUN)
-               dev->stats.tx_fifo_errors++;
-
-       dev->stats.tx_errors++;
-}
-
-static void nb8800_tx_done(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       unsigned int limit = priv->tx_next;
-       unsigned int done = priv->tx_done;
-       unsigned int packets = 0;
-       unsigned int len = 0;
-
-       while (done != limit) {
-               struct nb8800_tx_desc *txd = &priv->tx_descs[done];
-               struct nb8800_tx_buf *txb = &priv->tx_bufs[done];
-               struct sk_buff *skb;
-
-               if (!txd->report)
-                       break;
-
-               skb = txb->skb;
-               len += skb->len;
-
-               dma_unmap_single(&dev->dev, txb->dma_addr, txb->dma_len,
-                                DMA_TO_DEVICE);
-
-               if (IS_TX_ERROR(txd->report)) {
-                       nb8800_tx_error(dev, txd->report);
-                       kfree_skb(skb);
-               } else {
-                       consume_skb(skb);
-               }
-
-               dev->stats.tx_packets++;
-               dev->stats.tx_bytes += TX_BYTES_TRANSFERRED(txd->report);
-               dev->stats.collisions += TX_EARLY_COLLISIONS(txd->report);
-
-               txb->skb = NULL;
-               txb->ready = false;
-               txd->report = 0;
-
-               done = (done + 1) % TX_DESC_COUNT;
-               packets++;
-       }
-
-       if (packets) {
-               smp_mb__before_atomic();
-               atomic_add(packets, &priv->tx_free);
-               netdev_completed_queue(dev, packets, len);
-               netif_wake_queue(dev);
-               priv->tx_done = done;
-       }
-}
-
-static irqreturn_t nb8800_irq(int irq, void *dev_id)
-{
-       struct net_device *dev = dev_id;
-       struct nb8800_priv *priv = netdev_priv(dev);
-       irqreturn_t ret = IRQ_NONE;
-       u32 val;
-
-       /* tx interrupt */
-       val = nb8800_readl(priv, NB8800_TXC_SR);
-       if (val) {
-               nb8800_writel(priv, NB8800_TXC_SR, val);
-
-               if (val & TSR_DI)
-                       nb8800_tx_dma_start_irq(dev);
-
-               if (val & TSR_TI)
-                       napi_schedule_irqoff(&priv->napi);
-
-               if (unlikely(val & TSR_DE))
-                       netdev_err(dev, "TX DMA error\n");
-
-               /* should never happen with automatic status retrieval */
-               if (unlikely(val & TSR_TO))
-                       netdev_err(dev, "TX Status FIFO overflow\n");
-
-               ret = IRQ_HANDLED;
-       }
-
-       /* rx interrupt */
-       val = nb8800_readl(priv, NB8800_RXC_SR);
-       if (val) {
-               nb8800_writel(priv, NB8800_RXC_SR, val);
-
-               if (likely(val & (RSR_RI | RSR_DI))) {
-                       nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_poll);
-                       napi_schedule_irqoff(&priv->napi);
-               }
-
-               if (unlikely(val & RSR_DE))
-                       netdev_err(dev, "RX DMA error\n");
-
-               /* should never happen with automatic status retrieval */
-               if (unlikely(val & RSR_RO))
-                       netdev_err(dev, "RX Status FIFO overflow\n");
-
-               ret = IRQ_HANDLED;
-       }
-
-       return ret;
-}
-
-static void nb8800_mac_config(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       bool gigabit = priv->speed == SPEED_1000;
-       u32 mac_mode_mask = RGMII_MODE | HALF_DUPLEX | GMAC_MODE;
-       u32 mac_mode = 0;
-       u32 slot_time;
-       u32 phy_clk;
-       u32 ict;
-
-       if (!priv->duplex)
-               mac_mode |= HALF_DUPLEX;
-
-       if (gigabit) {
-               if (phy_interface_is_rgmii(dev->phydev))
-                       mac_mode |= RGMII_MODE;
-
-               mac_mode |= GMAC_MODE;
-               phy_clk = 125000000;
-
-               /* Should be 512 but register is only 8 bits */
-               slot_time = 255;
-       } else {
-               phy_clk = 25000000;
-               slot_time = 128;
-       }
-
-       ict = DIV_ROUND_UP(phy_clk, clk_get_rate(priv->clk));
-
-       nb8800_writeb(priv, NB8800_IC_THRESHOLD, ict);
-       nb8800_writeb(priv, NB8800_SLOT_TIME, slot_time);
-       nb8800_maskb(priv, NB8800_MAC_MODE, mac_mode_mask, mac_mode);
-}
-
-static void nb8800_pause_config(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-       u32 rxcr;
-
-       if (priv->pause_aneg) {
-               if (!phydev || !phydev->link)
-                       return;
-
-               priv->pause_rx = phydev->pause;
-               priv->pause_tx = phydev->pause ^ phydev->asym_pause;
-       }
-
-       nb8800_modb(priv, NB8800_RX_CTL, RX_PAUSE_EN, priv->pause_rx);
-
-       rxcr = nb8800_readl(priv, NB8800_RXC_CR);
-       if (!!(rxcr & RCR_FL) == priv->pause_tx)
-               return;
-
-       if (netif_running(dev)) {
-               napi_disable(&priv->napi);
-               netif_tx_lock_bh(dev);
-               nb8800_dma_stop(dev);
-               nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx);
-               nb8800_start_rx(dev);
-               netif_tx_unlock_bh(dev);
-               napi_enable(&priv->napi);
-       } else {
-               nb8800_modl(priv, NB8800_RXC_CR, RCR_FL, priv->pause_tx);
-       }
-}
-
-static void nb8800_link_reconfigure(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-       int change = 0;
-
-       if (phydev->link) {
-               if (phydev->speed != priv->speed) {
-                       priv->speed = phydev->speed;
-                       change = 1;
-               }
-
-               if (phydev->duplex != priv->duplex) {
-                       priv->duplex = phydev->duplex;
-                       change = 1;
-               }
-
-               if (change)
-                       nb8800_mac_config(dev);
-
-               nb8800_pause_config(dev);
-       }
-
-       if (phydev->link != priv->link) {
-               priv->link = phydev->link;
-               change = 1;
-       }
-
-       if (change)
-               phy_print_status(phydev);
-}
-
-static void nb8800_update_mac_addr(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       int i;
-
-       for (i = 0; i < ETH_ALEN; i++)
-               nb8800_writeb(priv, NB8800_SRC_ADDR(i), dev->dev_addr[i]);
-
-       for (i = 0; i < ETH_ALEN; i++)
-               nb8800_writeb(priv, NB8800_UC_ADDR(i), dev->dev_addr[i]);
-}
-
-static int nb8800_set_mac_address(struct net_device *dev, void *addr)
-{
-       struct sockaddr *sock = addr;
-
-       if (netif_running(dev))
-               return -EBUSY;
-
-       ether_addr_copy(dev->dev_addr, sock->sa_data);
-       nb8800_update_mac_addr(dev);
-
-       return 0;
-}
-
-static void nb8800_mc_init(struct net_device *dev, int val)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-
-       nb8800_writeb(priv, NB8800_MC_INIT, val);
-       readb_poll_timeout_atomic(priv->base + NB8800_MC_INIT, val, !val,
-                                 1, 1000);
-}
-
-static void nb8800_set_rx_mode(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct netdev_hw_addr *ha;
-       int i;
-
-       if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
-               nb8800_mac_af(dev, false);
-               return;
-       }
-
-       nb8800_mac_af(dev, true);
-       nb8800_mc_init(dev, 0);
-
-       netdev_for_each_mc_addr(ha, dev) {
-               for (i = 0; i < ETH_ALEN; i++)
-                       nb8800_writeb(priv, NB8800_MC_ADDR(i), ha->addr[i]);
-
-               nb8800_mc_init(dev, 0xff);
-       }
-}
-
-#define RX_DESC_SIZE (RX_DESC_COUNT * sizeof(struct nb8800_rx_desc))
-#define TX_DESC_SIZE (TX_DESC_COUNT * sizeof(struct nb8800_tx_desc))
-
-static void nb8800_dma_free(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       unsigned int i;
-
-       if (priv->rx_bufs) {
-               for (i = 0; i < RX_DESC_COUNT; i++)
-                       if (priv->rx_bufs[i].page)
-                               put_page(priv->rx_bufs[i].page);
-
-               kfree(priv->rx_bufs);
-               priv->rx_bufs = NULL;
-       }
-
-       if (priv->tx_bufs) {
-               for (i = 0; i < TX_DESC_COUNT; i++)
-                       kfree_skb(priv->tx_bufs[i].skb);
-
-               kfree(priv->tx_bufs);
-               priv->tx_bufs = NULL;
-       }
-
-       if (priv->rx_descs) {
-               dma_free_coherent(dev->dev.parent, RX_DESC_SIZE, priv->rx_descs,
-                                 priv->rx_desc_dma);
-               priv->rx_descs = NULL;
-       }
-
-       if (priv->tx_descs) {
-               dma_free_coherent(dev->dev.parent, TX_DESC_SIZE, priv->tx_descs,
-                                 priv->tx_desc_dma);
-               priv->tx_descs = NULL;
-       }
-}
-
-static void nb8800_dma_reset(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_rx_desc *rxd;
-       struct nb8800_tx_desc *txd;
-       unsigned int i;
-
-       for (i = 0; i < RX_DESC_COUNT; i++) {
-               dma_addr_t rx_dma = priv->rx_desc_dma + i * sizeof(*rxd);
-
-               rxd = &priv->rx_descs[i];
-               rxd->desc.n_addr = rx_dma + sizeof(*rxd);
-               rxd->desc.r_addr =
-                       rx_dma + offsetof(struct nb8800_rx_desc, report);
-               rxd->desc.config = priv->rx_dma_config;
-               rxd->report = 0;
-       }
-
-       rxd->desc.n_addr = priv->rx_desc_dma;
-       rxd->desc.config |= DESC_EOC;
-
-       priv->rx_eoc = RX_DESC_COUNT - 1;
-
-       for (i = 0; i < TX_DESC_COUNT; i++) {
-               struct nb8800_tx_buf *txb = &priv->tx_bufs[i];
-               dma_addr_t r_dma = txb->dma_desc +
-                       offsetof(struct nb8800_tx_desc, report);
-
-               txd = &priv->tx_descs[i];
-               txd->desc[0].r_addr = r_dma;
-               txd->desc[1].r_addr = r_dma;
-               txd->report = 0;
-       }
-
-       priv->tx_next = 0;
-       priv->tx_queue = 0;
-       priv->tx_done = 0;
-       atomic_set(&priv->tx_free, TX_DESC_COUNT);
-
-       nb8800_writel(priv, NB8800_RX_DESC_ADDR, priv->rx_desc_dma);
-
-       wmb();          /* ensure all setup is written before starting */
-}
-
-static int nb8800_dma_init(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       unsigned int n_rx = RX_DESC_COUNT;
-       unsigned int n_tx = TX_DESC_COUNT;
-       unsigned int i;
-       int err;
-
-       priv->rx_descs = dma_alloc_coherent(dev->dev.parent, RX_DESC_SIZE,
-                                           &priv->rx_desc_dma, GFP_KERNEL);
-       if (!priv->rx_descs)
-               goto err_out;
-
-       priv->rx_bufs = kcalloc(n_rx, sizeof(*priv->rx_bufs), GFP_KERNEL);
-       if (!priv->rx_bufs)
-               goto err_out;
-
-       for (i = 0; i < n_rx; i++) {
-               err = nb8800_alloc_rx(dev, i, false);
-               if (err)
-                       goto err_out;
-       }
-
-       priv->tx_descs = dma_alloc_coherent(dev->dev.parent, TX_DESC_SIZE,
-                                           &priv->tx_desc_dma, GFP_KERNEL);
-       if (!priv->tx_descs)
-               goto err_out;
-
-       priv->tx_bufs = kcalloc(n_tx, sizeof(*priv->tx_bufs), GFP_KERNEL);
-       if (!priv->tx_bufs)
-               goto err_out;
-
-       for (i = 0; i < n_tx; i++)
-               priv->tx_bufs[i].dma_desc =
-                       priv->tx_desc_dma + i * sizeof(struct nb8800_tx_desc);
-
-       nb8800_dma_reset(dev);
-
-       return 0;
-
-err_out:
-       nb8800_dma_free(dev);
-
-       return -ENOMEM;
-}
-
-static int nb8800_dma_stop(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct nb8800_tx_buf *txb = &priv->tx_bufs[0];
-       struct nb8800_tx_desc *txd = &priv->tx_descs[0];
-       int retry = 5;
-       u32 txcr;
-       u32 rxcr;
-       int err;
-       unsigned int i;
-
-       /* wait for tx to finish */
-       err = readl_poll_timeout_atomic(priv->base + NB8800_TXC_CR, txcr,
-                                       !(txcr & TCR_EN) &&
-                                       priv->tx_done == priv->tx_next,
-                                       1000, 1000000);
-       if (err)
-               return err;
-
-       /* The rx DMA only stops if it reaches the end of chain.
-        * To make this happen, we set the EOC flag on all rx
-        * descriptors, put the device in loopback mode, and send
-        * a few dummy frames.  The interrupt handler will ignore
-        * these since NAPI is disabled and no real frames are in
-        * the tx queue.
-        */
-
-       for (i = 0; i < RX_DESC_COUNT; i++)
-               priv->rx_descs[i].desc.config |= DESC_EOC;
-
-       txd->desc[0].s_addr =
-               txb->dma_desc + offsetof(struct nb8800_tx_desc, buf);
-       txd->desc[0].config = DESC_BTS(2) | DESC_DS | DESC_EOF | DESC_EOC | 8;
-       memset(txd->buf, 0, sizeof(txd->buf));
-
-       nb8800_mac_af(dev, false);
-       nb8800_setb(priv, NB8800_MAC_MODE, LOOPBACK_EN);
-
-       do {
-               nb8800_writel(priv, NB8800_TX_DESC_ADDR, txb->dma_desc);
-               wmb();
-               nb8800_writel(priv, NB8800_TXC_CR, txcr | TCR_EN);
-
-               err = readl_poll_timeout_atomic(priv->base + NB8800_RXC_CR,
-                                               rxcr, !(rxcr & RCR_EN),
-                                               1000, 100000);
-       } while (err && --retry);
-
-       nb8800_mac_af(dev, true);
-       nb8800_clearb(priv, NB8800_MAC_MODE, LOOPBACK_EN);
-       nb8800_dma_reset(dev);
-
-       return retry ? 0 : -ETIMEDOUT;
-}
-
-static void nb8800_pause_adv(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-
-       if (!phydev)
-               return;
-
-       phy_set_asym_pause(phydev, priv->pause_rx, priv->pause_tx);
-}
-
-static int nb8800_open(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct phy_device *phydev;
-       int err;
-
-       /* clear any pending interrupts */
-       nb8800_writel(priv, NB8800_RXC_SR, 0xf);
-       nb8800_writel(priv, NB8800_TXC_SR, 0xf);
-
-       err = nb8800_dma_init(dev);
-       if (err)
-               return err;
-
-       err = request_irq(dev->irq, nb8800_irq, 0, dev_name(&dev->dev), dev);
-       if (err)
-               goto err_free_dma;
-
-       nb8800_mac_rx(dev, true);
-       nb8800_mac_tx(dev, true);
-
-       phydev = of_phy_connect(dev, priv->phy_node,
-                               nb8800_link_reconfigure, 0,
-                               priv->phy_mode);
-       if (!phydev) {
-               err = -ENODEV;
-               goto err_free_irq;
-       }
-
-       nb8800_pause_adv(dev);
-
-       netdev_reset_queue(dev);
-       napi_enable(&priv->napi);
-       netif_start_queue(dev);
-
-       nb8800_start_rx(dev);
-       phy_start(phydev);
-
-       return 0;
-
-err_free_irq:
-       free_irq(dev->irq, dev);
-err_free_dma:
-       nb8800_dma_free(dev);
-
-       return err;
-}
-
-static int nb8800_stop(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-
-       phy_stop(phydev);
-
-       netif_stop_queue(dev);
-       napi_disable(&priv->napi);
-
-       nb8800_dma_stop(dev);
-       nb8800_mac_rx(dev, false);
-       nb8800_mac_tx(dev, false);
-
-       phy_disconnect(phydev);
-
-       free_irq(dev->irq, dev);
-
-       nb8800_dma_free(dev);
-
-       return 0;
-}
-
-static const struct net_device_ops nb8800_netdev_ops = {
-       .ndo_open               = nb8800_open,
-       .ndo_stop               = nb8800_stop,
-       .ndo_start_xmit         = nb8800_xmit,
-       .ndo_set_mac_address    = nb8800_set_mac_address,
-       .ndo_set_rx_mode        = nb8800_set_rx_mode,
-       .ndo_do_ioctl           = phy_do_ioctl,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-static void nb8800_get_pauseparam(struct net_device *dev,
-                                 struct ethtool_pauseparam *pp)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-
-       pp->autoneg = priv->pause_aneg;
-       pp->rx_pause = priv->pause_rx;
-       pp->tx_pause = priv->pause_tx;
-}
-
-static int nb8800_set_pauseparam(struct net_device *dev,
-                                struct ethtool_pauseparam *pp)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-
-       priv->pause_aneg = pp->autoneg;
-       priv->pause_rx = pp->rx_pause;
-       priv->pause_tx = pp->tx_pause;
-
-       nb8800_pause_adv(dev);
-
-       if (!priv->pause_aneg)
-               nb8800_pause_config(dev);
-       else if (phydev)
-               phy_start_aneg(phydev);
-
-       return 0;
-}
-
-static const char nb8800_stats_names[][ETH_GSTRING_LEN] = {
-       "rx_bytes_ok",
-       "rx_frames_ok",
-       "rx_undersize_frames",
-       "rx_fragment_frames",
-       "rx_64_byte_frames",
-       "rx_127_byte_frames",
-       "rx_255_byte_frames",
-       "rx_511_byte_frames",
-       "rx_1023_byte_frames",
-       "rx_max_size_frames",
-       "rx_oversize_frames",
-       "rx_bad_fcs_frames",
-       "rx_broadcast_frames",
-       "rx_multicast_frames",
-       "rx_control_frames",
-       "rx_pause_frames",
-       "rx_unsup_control_frames",
-       "rx_align_error_frames",
-       "rx_overrun_frames",
-       "rx_jabber_frames",
-       "rx_bytes",
-       "rx_frames",
-
-       "tx_bytes_ok",
-       "tx_frames_ok",
-       "tx_64_byte_frames",
-       "tx_127_byte_frames",
-       "tx_255_byte_frames",
-       "tx_511_byte_frames",
-       "tx_1023_byte_frames",
-       "tx_max_size_frames",
-       "tx_oversize_frames",
-       "tx_broadcast_frames",
-       "tx_multicast_frames",
-       "tx_control_frames",
-       "tx_pause_frames",
-       "tx_underrun_frames",
-       "tx_single_collision_frames",
-       "tx_multi_collision_frames",
-       "tx_deferred_collision_frames",
-       "tx_late_collision_frames",
-       "tx_excessive_collision_frames",
-       "tx_bytes",
-       "tx_frames",
-       "tx_collisions",
-};
-
-#define NB8800_NUM_STATS ARRAY_SIZE(nb8800_stats_names)
-
-static int nb8800_get_sset_count(struct net_device *dev, int sset)
-{
-       if (sset == ETH_SS_STATS)
-               return NB8800_NUM_STATS;
-
-       return -EOPNOTSUPP;
-}
-
-static void nb8800_get_strings(struct net_device *dev, u32 sset, u8 *buf)
-{
-       if (sset == ETH_SS_STATS)
-               memcpy(buf, &nb8800_stats_names, sizeof(nb8800_stats_names));
-}
-
-static u32 nb8800_read_stat(struct net_device *dev, int index)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-
-       nb8800_writeb(priv, NB8800_STAT_INDEX, index);
-
-       return nb8800_readl(priv, NB8800_STAT_DATA);
-}
-
-static void nb8800_get_ethtool_stats(struct net_device *dev,
-                                    struct ethtool_stats *estats, u64 *st)
-{
-       unsigned int i;
-       u32 rx, tx;
-
-       for (i = 0; i < NB8800_NUM_STATS / 2; i++) {
-               rx = nb8800_read_stat(dev, i);
-               tx = nb8800_read_stat(dev, i | 0x80);
-               st[i] = rx;
-               st[i + NB8800_NUM_STATS / 2] = tx;
-       }
-}
-
-static const struct ethtool_ops nb8800_ethtool_ops = {
-       .nway_reset             = phy_ethtool_nway_reset,
-       .get_link               = ethtool_op_get_link,
-       .get_pauseparam         = nb8800_get_pauseparam,
-       .set_pauseparam         = nb8800_set_pauseparam,
-       .get_sset_count         = nb8800_get_sset_count,
-       .get_strings            = nb8800_get_strings,
-       .get_ethtool_stats      = nb8800_get_ethtool_stats,
-       .get_link_ksettings     = phy_ethtool_get_link_ksettings,
-       .set_link_ksettings     = phy_ethtool_set_link_ksettings,
-};
-
-static int nb8800_hw_init(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       u32 val;
-
-       val = TX_RETRY_EN | TX_PAD_EN | TX_APPEND_FCS;
-       nb8800_writeb(priv, NB8800_TX_CTL1, val);
-
-       /* Collision retry count */
-       nb8800_writeb(priv, NB8800_TX_CTL2, 5);
-
-       val = RX_PAD_STRIP | RX_AF_EN;
-       nb8800_writeb(priv, NB8800_RX_CTL, val);
-
-       /* Chosen by fair dice roll */
-       nb8800_writeb(priv, NB8800_RANDOM_SEED, 4);
-
-       /* TX cycles per deferral period */
-       nb8800_writeb(priv, NB8800_TX_SDP, 12);
-
-       /* The following three threshold values have been
-        * experimentally determined for good results.
-        */
-
-       /* RX/TX FIFO threshold for partial empty (64-bit entries) */
-       nb8800_writeb(priv, NB8800_PE_THRESHOLD, 0);
-
-       /* RX/TX FIFO threshold for partial full (64-bit entries) */
-       nb8800_writeb(priv, NB8800_PF_THRESHOLD, 255);
-
-       /* Buffer size for transmit (64-bit entries) */
-       nb8800_writeb(priv, NB8800_TX_BUFSIZE, 64);
-
-       /* Configure tx DMA */
-
-       val = nb8800_readl(priv, NB8800_TXC_CR);
-       val &= TCR_LE;          /* keep endian setting */
-       val |= TCR_DM;          /* DMA descriptor mode */
-       val |= TCR_RS;          /* automatically store tx status  */
-       val |= TCR_DIE;         /* interrupt on DMA chain completion */
-       val |= TCR_TFI(7);      /* interrupt after 7 frames transmitted */
-       val |= TCR_BTS(2);      /* 32-byte bus transaction size */
-       nb8800_writel(priv, NB8800_TXC_CR, val);
-
-       /* TX complete interrupt after 10 ms or 7 frames (see above) */
-       val = clk_get_rate(priv->clk) / 100;
-       nb8800_writel(priv, NB8800_TX_ITR, val);
-
-       /* Configure rx DMA */
-
-       val = nb8800_readl(priv, NB8800_RXC_CR);
-       val &= RCR_LE;          /* keep endian setting */
-       val |= RCR_DM;          /* DMA descriptor mode */
-       val |= RCR_RS;          /* automatically store rx status */
-       val |= RCR_DIE;         /* interrupt at end of DMA chain */
-       val |= RCR_RFI(7);      /* interrupt after 7 frames received */
-       val |= RCR_BTS(2);      /* 32-byte bus transaction size */
-       nb8800_writel(priv, NB8800_RXC_CR, val);
-
-       /* The rx interrupt can fire before the DMA has completed
-        * unless a small delay is added.  50 us is hopefully enough.
-        */
-       priv->rx_itr_irq = clk_get_rate(priv->clk) / 20000;
-
-       /* In NAPI poll mode we want to disable interrupts, but the
-        * hardware does not permit this.  Delay 10 ms instead.
-        */
-       priv->rx_itr_poll = clk_get_rate(priv->clk) / 100;
-
-       nb8800_writel(priv, NB8800_RX_ITR, priv->rx_itr_irq);
-
-       priv->rx_dma_config = RX_BUF_SIZE | DESC_BTS(2) | DESC_DS | DESC_EOF;
-
-       /* Flow control settings */
-
-       /* Pause time of 0.1 ms */
-       val = 100000 / 512;
-       nb8800_writeb(priv, NB8800_PQ1, val >> 8);
-       nb8800_writeb(priv, NB8800_PQ2, val & 0xff);
-
-       /* Auto-negotiate by default */
-       priv->pause_aneg = true;
-       priv->pause_rx = true;
-       priv->pause_tx = true;
-
-       nb8800_mc_init(dev, 0);
-
-       return 0;
-}
-
-static int nb8800_tangox_init(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       u32 pad_mode = PAD_MODE_MII;
-
-       switch (priv->phy_mode) {
-       case PHY_INTERFACE_MODE_MII:
-       case PHY_INTERFACE_MODE_GMII:
-               pad_mode = PAD_MODE_MII;
-               break;
-
-       case PHY_INTERFACE_MODE_RGMII:
-       case PHY_INTERFACE_MODE_RGMII_ID:
-       case PHY_INTERFACE_MODE_RGMII_RXID:
-       case PHY_INTERFACE_MODE_RGMII_TXID:
-               pad_mode = PAD_MODE_RGMII;
-               break;
-
-       default:
-               dev_err(dev->dev.parent, "unsupported phy mode %s\n",
-                       phy_modes(priv->phy_mode));
-               return -EINVAL;
-       }
-
-       nb8800_writeb(priv, NB8800_TANGOX_PAD_MODE, pad_mode);
-
-       return 0;
-}
-
-static int nb8800_tangox_reset(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       int clk_div;
-
-       nb8800_writeb(priv, NB8800_TANGOX_RESET, 0);
-       usleep_range(1000, 10000);
-       nb8800_writeb(priv, NB8800_TANGOX_RESET, 1);
-
-       wmb();          /* ensure reset is cleared before proceeding */
-
-       clk_div = DIV_ROUND_UP(clk_get_rate(priv->clk), 2 * MAX_MDC_CLOCK);
-       nb8800_writew(priv, NB8800_TANGOX_MDIO_CLKDIV, clk_div);
-
-       return 0;
-}
-
-static const struct nb8800_ops nb8800_tangox_ops = {
-       .init   = nb8800_tangox_init,
-       .reset  = nb8800_tangox_reset,
-};
-
-static int nb8800_tango4_init(struct net_device *dev)
-{
-       struct nb8800_priv *priv = netdev_priv(dev);
-       int err;
-
-       err = nb8800_tangox_init(dev);
-       if (err)
-               return err;
-
-       /* On tango4 interrupt on DMA completion per frame works and gives
-        * better performance despite generating more rx interrupts.
-        */
-
-       /* Disable unnecessary interrupt on rx completion */
-       nb8800_clearl(priv, NB8800_RXC_CR, RCR_RFI(7));
-
-       /* Request interrupt on descriptor DMA completion */
-       priv->rx_dma_config |= DESC_ID;
-
-       return 0;
-}
-
-static const struct nb8800_ops nb8800_tango4_ops = {
-       .init   = nb8800_tango4_init,
-       .reset  = nb8800_tangox_reset,
-};
-
-static const struct of_device_id nb8800_dt_ids[] = {
-       {
-               .compatible = "aurora,nb8800",
-       },
-       {
-               .compatible = "sigma,smp8642-ethernet",
-               .data = &nb8800_tangox_ops,
-       },
-       {
-               .compatible = "sigma,smp8734-ethernet",
-               .data = &nb8800_tango4_ops,
-       },
-       { }
-};
-MODULE_DEVICE_TABLE(of, nb8800_dt_ids);
-
-static int nb8800_probe(struct platform_device *pdev)
-{
-       const struct of_device_id *match;
-       const struct nb8800_ops *ops = NULL;
-       struct nb8800_priv *priv;
-       struct resource *res;
-       struct net_device *dev;
-       struct mii_bus *bus;
-       const unsigned char *mac;
-       void __iomem *base;
-       int irq;
-       int ret;
-
-       match = of_match_device(nb8800_dt_ids, &pdev->dev);
-       if (match)
-               ops = match->data;
-
-       irq = platform_get_irq(pdev, 0);
-       if (irq <= 0)
-               return -EINVAL;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
-
-       dev_dbg(&pdev->dev, "AU-NB8800 Ethernet at %pa\n", &res->start);
-
-       dev = alloc_etherdev(sizeof(*priv));
-       if (!dev)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, dev);
-       SET_NETDEV_DEV(dev, &pdev->dev);
-
-       priv = netdev_priv(dev);
-       priv->base = base;
-
-       ret = of_get_phy_mode(pdev->dev.of_node, &priv->phy_mode);
-       if (ret)
-               priv->phy_mode = PHY_INTERFACE_MODE_RGMII;
-
-       priv->clk = devm_clk_get(&pdev->dev, NULL);
-       if (IS_ERR(priv->clk)) {
-               dev_err(&pdev->dev, "failed to get clock\n");
-               ret = PTR_ERR(priv->clk);
-               goto err_free_dev;
-       }
-
-       ret = clk_prepare_enable(priv->clk);
-       if (ret)
-               goto err_free_dev;
-
-       spin_lock_init(&priv->tx_lock);
-
-       if (ops && ops->reset) {
-               ret = ops->reset(dev);
-               if (ret)
-                       goto err_disable_clk;
-       }
-
-       bus = devm_mdiobus_alloc(&pdev->dev);
-       if (!bus) {
-               ret = -ENOMEM;
-               goto err_disable_clk;
-       }
-
-       bus->name = "nb8800-mii";
-       bus->read = nb8800_mdio_read;
-       bus->write = nb8800_mdio_write;
-       bus->parent = &pdev->dev;
-       snprintf(bus->id, MII_BUS_ID_SIZE, "%lx.nb8800-mii",
-                (unsigned long)res->start);
-       bus->priv = priv;
-
-       ret = of_mdiobus_register(bus, pdev->dev.of_node);
-       if (ret) {
-               dev_err(&pdev->dev, "failed to register MII bus\n");
-               goto err_disable_clk;
-       }
-
-       if (of_phy_is_fixed_link(pdev->dev.of_node)) {
-               ret = of_phy_register_fixed_link(pdev->dev.of_node);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "bad fixed-link spec\n");
-                       goto err_free_bus;
-               }
-               priv->phy_node = of_node_get(pdev->dev.of_node);
-       }
-
-       if (!priv->phy_node)
-               priv->phy_node = of_parse_phandle(pdev->dev.of_node,
-                                                 "phy-handle", 0);
-
-       if (!priv->phy_node) {
-               dev_err(&pdev->dev, "no PHY specified\n");
-               ret = -ENODEV;
-               goto err_free_bus;
-       }
-
-       priv->mii_bus = bus;
-
-       ret = nb8800_hw_init(dev);
-       if (ret)
-               goto err_deregister_fixed_link;
-
-       if (ops && ops->init) {
-               ret = ops->init(dev);
-               if (ret)
-                       goto err_deregister_fixed_link;
-       }
-
-       dev->netdev_ops = &nb8800_netdev_ops;
-       dev->ethtool_ops = &nb8800_ethtool_ops;
-       dev->flags |= IFF_MULTICAST;
-       dev->irq = irq;
-
-       mac = of_get_mac_address(pdev->dev.of_node);
-       if (!IS_ERR(mac))
-               ether_addr_copy(dev->dev_addr, mac);
-
-       if (!is_valid_ether_addr(dev->dev_addr))
-               eth_hw_addr_random(dev);
-
-       nb8800_update_mac_addr(dev);
-
-       netif_carrier_off(dev);
-
-       ret = register_netdev(dev);
-       if (ret) {
-               netdev_err(dev, "failed to register netdev\n");
-               goto err_free_dma;
-       }
-
-       netif_napi_add(dev, &priv->napi, nb8800_poll, NAPI_POLL_WEIGHT);
-
-       netdev_info(dev, "MAC address %pM\n", dev->dev_addr);
-
-       return 0;
-
-err_free_dma:
-       nb8800_dma_free(dev);
-err_deregister_fixed_link:
-       if (of_phy_is_fixed_link(pdev->dev.of_node))
-               of_phy_deregister_fixed_link(pdev->dev.of_node);
-err_free_bus:
-       of_node_put(priv->phy_node);
-       mdiobus_unregister(bus);
-err_disable_clk:
-       clk_disable_unprepare(priv->clk);
-err_free_dev:
-       free_netdev(dev);
-
-       return ret;
-}
-
-static int nb8800_remove(struct platform_device *pdev)
-{
-       struct net_device *ndev = platform_get_drvdata(pdev);
-       struct nb8800_priv *priv = netdev_priv(ndev);
-
-       unregister_netdev(ndev);
-       if (of_phy_is_fixed_link(pdev->dev.of_node))
-               of_phy_deregister_fixed_link(pdev->dev.of_node);
-       of_node_put(priv->phy_node);
-
-       mdiobus_unregister(priv->mii_bus);
-
-       clk_disable_unprepare(priv->clk);
-
-       nb8800_dma_free(ndev);
-       free_netdev(ndev);
-
-       return 0;
-}
-
-static struct platform_driver nb8800_driver = {
-       .driver = {
-               .name           = "nb8800",
-               .of_match_table = nb8800_dt_ids,
-       },
-       .probe  = nb8800_probe,
-       .remove = nb8800_remove,
-};
-
-module_platform_driver(nb8800_driver);
-
-MODULE_DESCRIPTION("Aurora AU-NB8800 Ethernet driver");
-MODULE_AUTHOR("Mans Rullgard <mans@mansr.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/aurora/nb8800.h b/drivers/net/ethernet/aurora/nb8800.h
deleted file mode 100644 (file)
index 40941fb..0000000
+++ /dev/null
@@ -1,316 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _NB8800_H_
-#define _NB8800_H_
-
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/phy.h>
-#include <linux/clk.h>
-#include <linux/bitops.h>
-
-#define RX_DESC_COUNT                  256
-#define TX_DESC_COUNT                  256
-
-#define NB8800_DESC_LOW                        4
-
-#define RX_BUF_SIZE                    1552
-
-#define RX_COPYBREAK                   256
-#define RX_COPYHDR                     128
-
-#define MAX_MDC_CLOCK                  2500000
-
-/* Stargate Solutions SSN8800 core registers */
-#define NB8800_TX_CTL1                 0x000
-#define TX_TPD                         BIT(5)
-#define TX_APPEND_FCS                  BIT(4)
-#define TX_PAD_EN                      BIT(3)
-#define TX_RETRY_EN                    BIT(2)
-#define TX_EN                          BIT(0)
-
-#define NB8800_TX_CTL2                 0x001
-
-#define NB8800_RX_CTL                  0x004
-#define RX_BC_DISABLE                  BIT(7)
-#define RX_RUNT                                BIT(6)
-#define RX_AF_EN                       BIT(5)
-#define RX_PAUSE_EN                    BIT(3)
-#define RX_SEND_CRC                    BIT(2)
-#define RX_PAD_STRIP                   BIT(1)
-#define RX_EN                          BIT(0)
-
-#define NB8800_RANDOM_SEED             0x008
-#define NB8800_TX_SDP                  0x14
-#define NB8800_TX_TPDP1                        0x18
-#define NB8800_TX_TPDP2                        0x19
-#define NB8800_SLOT_TIME               0x1c
-
-#define NB8800_MDIO_CMD                        0x020
-#define MDIO_CMD_GO                    BIT(31)
-#define MDIO_CMD_WR                    BIT(26)
-#define MDIO_CMD_ADDR(x)               ((x) << 21)
-#define MDIO_CMD_REG(x)                        ((x) << 16)
-#define MDIO_CMD_DATA(x)               ((x) <<  0)
-
-#define NB8800_MDIO_STS                        0x024
-#define MDIO_STS_ERR                   BIT(31)
-
-#define NB8800_MC_ADDR(i)              (0x028 + (i))
-#define NB8800_MC_INIT                 0x02e
-#define NB8800_UC_ADDR(i)              (0x03c + (i))
-
-#define NB8800_MAC_MODE                        0x044
-#define RGMII_MODE                     BIT(7)
-#define HALF_DUPLEX                    BIT(4)
-#define BURST_EN                       BIT(3)
-#define LOOPBACK_EN                    BIT(2)
-#define GMAC_MODE                      BIT(0)
-
-#define NB8800_IC_THRESHOLD            0x050
-#define NB8800_PE_THRESHOLD            0x051
-#define NB8800_PF_THRESHOLD            0x052
-#define NB8800_TX_BUFSIZE              0x054
-#define NB8800_FIFO_CTL                        0x056
-#define NB8800_PQ1                     0x060
-#define NB8800_PQ2                     0x061
-#define NB8800_SRC_ADDR(i)             (0x06a + (i))
-#define NB8800_STAT_DATA               0x078
-#define NB8800_STAT_INDEX              0x07c
-#define NB8800_STAT_CLEAR              0x07d
-
-#define NB8800_SLEEP_MODE              0x07e
-#define SLEEP_MODE                     BIT(0)
-
-#define NB8800_WAKEUP                  0x07f
-#define WAKEUP                         BIT(0)
-
-/* Aurora NB8800 host interface registers */
-#define NB8800_TXC_CR                  0x100
-#define TCR_LK                         BIT(12)
-#define TCR_DS                         BIT(11)
-#define TCR_BTS(x)                     (((x) & 0x7) << 8)
-#define TCR_DIE                                BIT(7)
-#define TCR_TFI(x)                     (((x) & 0x7) << 4)
-#define TCR_LE                         BIT(3)
-#define TCR_RS                         BIT(2)
-#define TCR_DM                         BIT(1)
-#define TCR_EN                         BIT(0)
-
-#define NB8800_TXC_SR                  0x104
-#define TSR_DE                         BIT(3)
-#define TSR_DI                         BIT(2)
-#define TSR_TO                         BIT(1)
-#define TSR_TI                         BIT(0)
-
-#define NB8800_TX_SAR                  0x108
-#define NB8800_TX_DESC_ADDR            0x10c
-
-#define NB8800_TX_REPORT_ADDR          0x110
-#define TX_BYTES_TRANSFERRED(x)                (((x) >> 16) & 0xffff)
-#define TX_FIRST_DEFERRAL              BIT(7)
-#define TX_EARLY_COLLISIONS(x)         (((x) >> 3) & 0xf)
-#define TX_LATE_COLLISION              BIT(2)
-#define TX_PACKET_DROPPED              BIT(1)
-#define TX_FIFO_UNDERRUN               BIT(0)
-#define IS_TX_ERROR(r)                 ((r) & 0x07)
-
-#define NB8800_TX_FIFO_SR              0x114
-#define NB8800_TX_ITR                  0x118
-
-#define NB8800_RXC_CR                  0x200
-#define RCR_FL                         BIT(13)
-#define RCR_LK                         BIT(12)
-#define RCR_DS                         BIT(11)
-#define RCR_BTS(x)                     (((x) & 7) << 8)
-#define RCR_DIE                                BIT(7)
-#define RCR_RFI(x)                     (((x) & 7) << 4)
-#define RCR_LE                         BIT(3)
-#define RCR_RS                         BIT(2)
-#define RCR_DM                         BIT(1)
-#define RCR_EN                         BIT(0)
-
-#define NB8800_RXC_SR                  0x204
-#define RSR_DE                         BIT(3)
-#define RSR_DI                         BIT(2)
-#define RSR_RO                         BIT(1)
-#define RSR_RI                         BIT(0)
-
-#define NB8800_RX_SAR                  0x208
-#define NB8800_RX_DESC_ADDR            0x20c
-
-#define NB8800_RX_REPORT_ADDR          0x210
-#define RX_BYTES_TRANSFERRED(x)                (((x) >> 16) & 0xFFFF)
-#define RX_MULTICAST_PKT               BIT(9)
-#define RX_BROADCAST_PKT               BIT(8)
-#define RX_LENGTH_ERR                  BIT(7)
-#define RX_FCS_ERR                     BIT(6)
-#define RX_RUNT_PKT                    BIT(5)
-#define RX_FIFO_OVERRUN                        BIT(4)
-#define RX_LATE_COLLISION              BIT(3)
-#define RX_ALIGNMENT_ERROR             BIT(2)
-#define RX_ERROR_MASK                  0xfc
-#define IS_RX_ERROR(r)                 ((r) & RX_ERROR_MASK)
-
-#define NB8800_RX_FIFO_SR              0x214
-#define NB8800_RX_ITR                  0x218
-
-/* Sigma Designs SMP86xx additional registers */
-#define NB8800_TANGOX_PAD_MODE         0x400
-#define PAD_MODE_MASK                  0x7
-#define PAD_MODE_MII                   0x0
-#define PAD_MODE_RGMII                 0x1
-#define PAD_MODE_GTX_CLK_INV           BIT(3)
-#define PAD_MODE_GTX_CLK_DELAY         BIT(4)
-
-#define NB8800_TANGOX_MDIO_CLKDIV      0x420
-#define NB8800_TANGOX_RESET            0x424
-
-/* Hardware DMA descriptor */
-struct nb8800_dma_desc {
-       u32                             s_addr; /* start address */
-       u32                             n_addr; /* next descriptor address */
-       u32                             r_addr; /* report address */
-       u32                             config;
-} __aligned(8);
-
-#define DESC_ID                                BIT(23)
-#define DESC_EOC                       BIT(22)
-#define DESC_EOF                       BIT(21)
-#define DESC_LK                                BIT(20)
-#define DESC_DS                                BIT(19)
-#define DESC_BTS(x)                    (((x) & 0x7) << 16)
-
-/* DMA descriptor and associated data for rx.
- * Allocated from coherent memory.
- */
-struct nb8800_rx_desc {
-       /* DMA descriptor */
-       struct nb8800_dma_desc          desc;
-
-       /* Status report filled in by hardware */
-       u32                             report;
-};
-
-/* Address of buffer on rx ring */
-struct nb8800_rx_buf {
-       struct page                     *page;
-       unsigned long                   offset;
-};
-
-/* DMA descriptors and associated data for tx.
- * Allocated from coherent memory.
- */
-struct nb8800_tx_desc {
-       /* DMA descriptor.  The second descriptor is used if packet
-        * data is unaligned.
-        */
-       struct nb8800_dma_desc          desc[2];
-
-       /* Status report filled in by hardware */
-       u32                             report;
-
-       /* Bounce buffer for initial unaligned part of packet */
-       u8                              buf[8] __aligned(8);
-};
-
-/* Packet in tx queue */
-struct nb8800_tx_buf {
-       /* Currently queued skb */
-       struct sk_buff                  *skb;
-
-       /* DMA address of the first descriptor */
-       dma_addr_t                      dma_desc;
-
-       /* DMA address of packet data */
-       dma_addr_t                      dma_addr;
-
-       /* Length of DMA mapping, less than skb->len if alignment
-        * buffer is used.
-        */
-       unsigned int                    dma_len;
-
-       /* Number of packets in chain starting here */
-       unsigned int                    chain_len;
-
-       /* Packet chain ready to be submitted to hardware */
-       bool                            ready;
-};
-
-struct nb8800_priv {
-       struct napi_struct              napi;
-
-       void __iomem                    *base;
-
-       /* RX DMA descriptors */
-       struct nb8800_rx_desc           *rx_descs;
-
-       /* RX buffers referenced by DMA descriptors */
-       struct nb8800_rx_buf            *rx_bufs;
-
-       /* Current end of chain */
-       u32                             rx_eoc;
-
-       /* Value for rx interrupt time register in NAPI interrupt mode */
-       u32                             rx_itr_irq;
-
-       /* Value for rx interrupt time register in NAPI poll mode */
-       u32                             rx_itr_poll;
-
-       /* Value for config field of rx DMA descriptors */
-       u32                             rx_dma_config;
-
-       /* TX DMA descriptors */
-       struct nb8800_tx_desc           *tx_descs;
-
-       /* TX packet queue */
-       struct nb8800_tx_buf            *tx_bufs;
-
-       /* Number of free tx queue entries */
-       atomic_t                        tx_free;
-
-       /* First free tx queue entry */
-       u32                             tx_next;
-
-       /* Next buffer to transmit */
-       u32                             tx_queue;
-
-       /* Start of current packet chain */
-       struct nb8800_tx_buf            *tx_chain;
-
-       /* Next buffer to reclaim */
-       u32                             tx_done;
-
-       /* Lock for DMA activation */
-       spinlock_t                      tx_lock;
-
-       struct mii_bus                  *mii_bus;
-       struct device_node              *phy_node;
-
-       /* PHY connection type from DT */
-       phy_interface_t                 phy_mode;
-
-       /* Current link status */
-       int                             speed;
-       int                             duplex;
-       int                             link;
-
-       /* Pause settings */
-       bool                            pause_aneg;
-       bool                            pause_rx;
-       bool                            pause_tx;
-
-       /* DMA base address of rx descriptors, see rx_descs above */
-       dma_addr_t                      rx_desc_dma;
-
-       /* DMA base address of tx descriptors, see tx_descs above */
-       dma_addr_t                      tx_desc_dma;
-
-       struct clk                      *clk;
-};
-
-struct nb8800_ops {
-       int                             (*init)(struct net_device *dev);
-       int                             (*reset)(struct net_device *dev);
-};
-
-#endif /* _NB8800_H_ */
index 7b79528..4bdf8fb 100644 (file)
@@ -174,7 +174,6 @@ config BGMAC_BCMA
 config BGMAC_PLATFORM
        tristate "Broadcom iProc GBit platform support"
        depends on ARCH_BCM_IPROC || COMPILE_TEST
-       depends on OF
        select BGMAC
        select PHYLIB
        select FIXED_PHY
index 916824c..fd87672 100644 (file)
@@ -220,7 +220,7 @@ static void bcm_enet_mdio_write_mii(struct net_device *dev, int mii_id,
 /*
  * refill rx queue
  */
-static int bcm_enet_refill_rx(struct net_device *dev)
+static int bcm_enet_refill_rx(struct net_device *dev, bool napi_mode)
 {
        struct bcm_enet_priv *priv;
 
@@ -228,26 +228,29 @@ static int bcm_enet_refill_rx(struct net_device *dev)
 
        while (priv->rx_desc_count < priv->rx_ring_size) {
                struct bcm_enet_desc *desc;
-               struct sk_buff *skb;
-               dma_addr_t p;
                int desc_idx;
                u32 len_stat;
 
                desc_idx = priv->rx_dirty_desc;
                desc = &priv->rx_desc_cpu[desc_idx];
 
-               if (!priv->rx_skb[desc_idx]) {
-                       skb = netdev_alloc_skb(dev, priv->rx_skb_size);
-                       if (!skb)
+               if (!priv->rx_buf[desc_idx]) {
+                       void *buf;
+
+                       if (likely(napi_mode))
+                               buf = napi_alloc_frag(priv->rx_frag_size);
+                       else
+                               buf = netdev_alloc_frag(priv->rx_frag_size);
+                       if (unlikely(!buf))
                                break;
-                       priv->rx_skb[desc_idx] = skb;
-                       p = dma_map_single(&priv->pdev->dev, skb->data,
-                                          priv->rx_skb_size,
-                                          DMA_FROM_DEVICE);
-                       desc->address = p;
+                       priv->rx_buf[desc_idx] = buf;
+                       desc->address = dma_map_single(&priv->pdev->dev,
+                                                      buf + priv->rx_buf_offset,
+                                                      priv->rx_buf_size,
+                                                      DMA_FROM_DEVICE);
                }
 
-               len_stat = priv->rx_skb_size << DMADESC_LENGTH_SHIFT;
+               len_stat = priv->rx_buf_size << DMADESC_LENGTH_SHIFT;
                len_stat |= DMADESC_OWNER_MASK;
                if (priv->rx_dirty_desc == priv->rx_ring_size - 1) {
                        len_stat |= (DMADESC_WRAP_MASK >> priv->dma_desc_shift);
@@ -287,7 +290,7 @@ static void bcm_enet_refill_rx_timer(struct timer_list *t)
        struct net_device *dev = priv->net_dev;
 
        spin_lock(&priv->rx_lock);
-       bcm_enet_refill_rx(dev);
+       bcm_enet_refill_rx(dev, false);
        spin_unlock(&priv->rx_lock);
 }
 
@@ -297,10 +300,12 @@ static void bcm_enet_refill_rx_timer(struct timer_list *t)
 static int bcm_enet_receive_queue(struct net_device *dev, int budget)
 {
        struct bcm_enet_priv *priv;
+       struct list_head rx_list;
        struct device *kdev;
        int processed;
 
        priv = netdev_priv(dev);
+       INIT_LIST_HEAD(&rx_list);
        kdev = &priv->pdev->dev;
        processed = 0;
 
@@ -315,6 +320,7 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
                int desc_idx;
                u32 len_stat;
                unsigned int len;
+               void *buf;
 
                desc_idx = priv->rx_curr_desc;
                desc = &priv->rx_desc_cpu[desc_idx];
@@ -333,7 +339,6 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
                priv->rx_curr_desc++;
                if (priv->rx_curr_desc == priv->rx_ring_size)
                        priv->rx_curr_desc = 0;
-               priv->rx_desc_count--;
 
                /* if the packet does not have start of packet _and_
                 * end of packet flag set, then just recycle it */
@@ -360,16 +365,14 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
                }
 
                /* valid packet */
-               skb = priv->rx_skb[desc_idx];
+               buf = priv->rx_buf[desc_idx];
                len = (len_stat & DMADESC_LENGTH_MASK) >> DMADESC_LENGTH_SHIFT;
                /* don't include FCS */
                len -= 4;
 
                if (len < copybreak) {
-                       struct sk_buff *nskb;
-
-                       nskb = napi_alloc_skb(&priv->napi, len);
-                       if (!nskb) {
+                       skb = napi_alloc_skb(&priv->napi, len);
+                       if (unlikely(!skb)) {
                                /* forget packet, just rearm desc */
                                dev->stats.rx_dropped++;
                                continue;
@@ -377,26 +380,36 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
 
                        dma_sync_single_for_cpu(kdev, desc->address,
                                                len, DMA_FROM_DEVICE);
-                       memcpy(nskb->data, skb->data, len);
+                       memcpy(skb->data, buf + priv->rx_buf_offset, len);
                        dma_sync_single_for_device(kdev, desc->address,
                                                   len, DMA_FROM_DEVICE);
-                       skb = nskb;
                } else {
-                       dma_unmap_single(&priv->pdev->dev, desc->address,
-                                        priv->rx_skb_size, DMA_FROM_DEVICE);
-                       priv->rx_skb[desc_idx] = NULL;
+                       dma_unmap_single(kdev, desc->address,
+                                        priv->rx_buf_size, DMA_FROM_DEVICE);
+                       priv->rx_buf[desc_idx] = NULL;
+
+                       skb = build_skb(buf, priv->rx_frag_size);
+                       if (unlikely(!skb)) {
+                               skb_free_frag(buf);
+                               dev->stats.rx_dropped++;
+                               continue;
+                       }
+                       skb_reserve(skb, priv->rx_buf_offset);
                }
 
                skb_put(skb, len);
                skb->protocol = eth_type_trans(skb, dev);
                dev->stats.rx_packets++;
                dev->stats.rx_bytes += len;
-               netif_receive_skb(skb);
+               list_add_tail(&skb->list, &rx_list);
 
-       } while (--budget > 0);
+       } while (processed < budget);
+
+       netif_receive_skb_list(&rx_list);
+       priv->rx_desc_count -= processed;
 
        if (processed || !priv->rx_desc_count) {
-               bcm_enet_refill_rx(dev);
+               bcm_enet_refill_rx(dev, true);
 
                /* kick rx dma */
                enet_dmac_writel(priv, priv->dma_chan_en_mask,
@@ -413,9 +426,11 @@ static int bcm_enet_receive_queue(struct net_device *dev, int budget)
 static int bcm_enet_tx_reclaim(struct net_device *dev, int force)
 {
        struct bcm_enet_priv *priv;
+       unsigned int bytes;
        int released;
 
        priv = netdev_priv(dev);
+       bytes = 0;
        released = 0;
 
        while (priv->tx_desc_count < priv->tx_ring_size) {
@@ -452,10 +467,13 @@ static int bcm_enet_tx_reclaim(struct net_device *dev, int force)
                if (desc->len_stat & DMADESC_UNDER_MASK)
                        dev->stats.tx_errors++;
 
+               bytes += skb->len;
                dev_kfree_skb(skb);
                released++;
        }
 
+       netdev_completed_queue(dev, released, bytes);
+
        if (netif_queue_stopped(dev) && released)
                netif_wake_queue(dev);
 
@@ -622,8 +640,11 @@ bcm_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
        desc->len_stat = len_stat;
        wmb();
 
+       netdev_sent_queue(dev, skb->len);
+
        /* kick tx dma */
-       enet_dmac_writel(priv, priv->dma_chan_en_mask,
+       if (!netdev_xmit_more() || !priv->tx_desc_count)
+               enet_dmac_writel(priv, priv->dma_chan_en_mask,
                                 ENETDMAC_CHANCFG, priv->tx_chan);
 
        /* stop queue if no more desc available */
@@ -845,6 +866,24 @@ static void bcm_enet_adjust_link(struct net_device *dev)
                priv->pause_tx ? "tx" : "off");
 }
 
+static void bcm_enet_free_rx_buf_ring(struct device *kdev, struct bcm_enet_priv *priv)
+{
+       int i;
+
+       for (i = 0; i < priv->rx_ring_size; i++) {
+               struct bcm_enet_desc *desc;
+
+               if (!priv->rx_buf[i])
+                       continue;
+
+               desc = &priv->rx_desc_cpu[i];
+               dma_unmap_single(kdev, desc->address, priv->rx_buf_size,
+                                DMA_FROM_DEVICE);
+               skb_free_frag(priv->rx_buf[i]);
+       }
+       kfree(priv->rx_buf);
+}
+
 /*
  * open callback, allocate dma rings & buffers and start rx operation
  */
@@ -954,10 +993,10 @@ static int bcm_enet_open(struct net_device *dev)
        priv->tx_curr_desc = 0;
        spin_lock_init(&priv->tx_lock);
 
-       /* init & fill rx ring with skbs */
-       priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *),
+       /* init & fill rx ring with buffers */
+       priv->rx_buf = kcalloc(priv->rx_ring_size, sizeof(void *),
                               GFP_KERNEL);
-       if (!priv->rx_skb) {
+       if (!priv->rx_buf) {
                ret = -ENOMEM;
                goto out_free_tx_skb;
        }
@@ -974,8 +1013,8 @@ static int bcm_enet_open(struct net_device *dev)
                enet_dmac_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0,
                                ENETDMAC_BUFALLOC, priv->rx_chan);
 
-       if (bcm_enet_refill_rx(dev)) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+       if (bcm_enet_refill_rx(dev, false)) {
+               dev_err(kdev, "cannot allocate rx buffer queue\n");
                ret = -ENOMEM;
                goto out;
        }
@@ -1069,18 +1108,7 @@ static int bcm_enet_open(struct net_device *dev)
        return 0;
 
 out:
-       for (i = 0; i < priv->rx_ring_size; i++) {
-               struct bcm_enet_desc *desc;
-
-               if (!priv->rx_skb[i])
-                       continue;
-
-               desc = &priv->rx_desc_cpu[i];
-               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
-                                DMA_FROM_DEVICE);
-               kfree_skb(priv->rx_skb[i]);
-       }
-       kfree(priv->rx_skb);
+       bcm_enet_free_rx_buf_ring(kdev, priv);
 
 out_free_tx_skb:
        kfree(priv->tx_skb);
@@ -1159,12 +1187,12 @@ static int bcm_enet_stop(struct net_device *dev)
 {
        struct bcm_enet_priv *priv;
        struct device *kdev;
-       int i;
 
        priv = netdev_priv(dev);
        kdev = &priv->pdev->dev;
 
        netif_stop_queue(dev);
+       netdev_reset_queue(dev);
        napi_disable(&priv->napi);
        if (priv->has_phy)
                phy_stop(dev->phydev);
@@ -1186,21 +1214,10 @@ static int bcm_enet_stop(struct net_device *dev)
        /* force reclaim of all tx buffers */
        bcm_enet_tx_reclaim(dev, 1);
 
-       /* free the rx skb ring */
-       for (i = 0; i < priv->rx_ring_size; i++) {
-               struct bcm_enet_desc *desc;
-
-               if (!priv->rx_skb[i])
-                       continue;
-
-               desc = &priv->rx_desc_cpu[i];
-               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
-                                DMA_FROM_DEVICE);
-               kfree_skb(priv->rx_skb[i]);
-       }
+       /* free the rx buffer ring */
+       bcm_enet_free_rx_buf_ring(kdev, priv);
 
        /* free remaining allocated memory */
-       kfree(priv->rx_skb);
        kfree(priv->tx_skb);
        dma_free_coherent(kdev, priv->rx_desc_alloc_size,
                          priv->rx_desc_cpu, priv->rx_desc_dma);
@@ -1622,9 +1639,12 @@ static int bcm_enet_change_mtu(struct net_device *dev, int new_mtu)
         * align rx buffer size to dma burst len, account FCS since
         * it's appended
         */
-       priv->rx_skb_size = ALIGN(actual_mtu + ETH_FCS_LEN,
+       priv->rx_buf_size = ALIGN(actual_mtu + ETH_FCS_LEN,
                                  priv->dma_maxburst * 4);
 
+       priv->rx_frag_size = SKB_DATA_ALIGN(priv->rx_buf_offset + priv->rx_buf_size) +
+                                           SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
        dev->mtu = new_mtu;
        return 0;
 }
@@ -1709,6 +1729,7 @@ static int bcm_enet_probe(struct platform_device *pdev)
 
        priv->enet_is_sw = false;
        priv->dma_maxburst = BCMENET_DMA_MAXBURST;
+       priv->rx_buf_offset = NET_SKB_PAD;
 
        ret = bcm_enet_change_mtu(dev, dev->mtu);
        if (ret)
@@ -2126,7 +2147,7 @@ static int bcm_enetsw_open(struct net_device *dev)
        priv->tx_skb = kcalloc(priv->tx_ring_size, sizeof(struct sk_buff *),
                               GFP_KERNEL);
        if (!priv->tx_skb) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+               dev_err(kdev, "cannot allocate tx skb queue\n");
                ret = -ENOMEM;
                goto out_free_tx_ring;
        }
@@ -2136,11 +2157,11 @@ static int bcm_enetsw_open(struct net_device *dev)
        priv->tx_curr_desc = 0;
        spin_lock_init(&priv->tx_lock);
 
-       /* init & fill rx ring with skbs */
-       priv->rx_skb = kcalloc(priv->rx_ring_size, sizeof(struct sk_buff *),
+       /* init & fill rx ring with buffers */
+       priv->rx_buf = kcalloc(priv->rx_ring_size, sizeof(void *),
                               GFP_KERNEL);
-       if (!priv->rx_skb) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+       if (!priv->rx_buf) {
+               dev_err(kdev, "cannot allocate rx buffer queue\n");
                ret = -ENOMEM;
                goto out_free_tx_skb;
        }
@@ -2187,8 +2208,8 @@ static int bcm_enetsw_open(struct net_device *dev)
        enet_dma_writel(priv, ENETDMA_BUFALLOC_FORCE_MASK | 0,
                        ENETDMA_BUFALLOC_REG(priv->rx_chan));
 
-       if (bcm_enet_refill_rx(dev)) {
-               dev_err(kdev, "cannot allocate rx skb queue\n");
+       if (bcm_enet_refill_rx(dev, false)) {
+               dev_err(kdev, "cannot allocate rx buffer queue\n");
                ret = -ENOMEM;
                goto out;
        }
@@ -2287,18 +2308,7 @@ static int bcm_enetsw_open(struct net_device *dev)
        return 0;
 
 out:
-       for (i = 0; i < priv->rx_ring_size; i++) {
-               struct bcm_enet_desc *desc;
-
-               if (!priv->rx_skb[i])
-                       continue;
-
-               desc = &priv->rx_desc_cpu[i];
-               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
-                                DMA_FROM_DEVICE);
-               kfree_skb(priv->rx_skb[i]);
-       }
-       kfree(priv->rx_skb);
+       bcm_enet_free_rx_buf_ring(kdev, priv);
 
 out_free_tx_skb:
        kfree(priv->tx_skb);
@@ -2327,13 +2337,13 @@ static int bcm_enetsw_stop(struct net_device *dev)
 {
        struct bcm_enet_priv *priv;
        struct device *kdev;
-       int i;
 
        priv = netdev_priv(dev);
        kdev = &priv->pdev->dev;
 
        del_timer_sync(&priv->swphy_poll);
        netif_stop_queue(dev);
+       netdev_reset_queue(dev);
        napi_disable(&priv->napi);
        del_timer_sync(&priv->rx_timeout);
 
@@ -2348,21 +2358,10 @@ static int bcm_enetsw_stop(struct net_device *dev)
        /* force reclaim of all tx buffers */
        bcm_enet_tx_reclaim(dev, 1);
 
-       /* free the rx skb ring */
-       for (i = 0; i < priv->rx_ring_size; i++) {
-               struct bcm_enet_desc *desc;
-
-               if (!priv->rx_skb[i])
-                       continue;
-
-               desc = &priv->rx_desc_cpu[i];
-               dma_unmap_single(kdev, desc->address, priv->rx_skb_size,
-                                DMA_FROM_DEVICE);
-               kfree_skb(priv->rx_skb[i]);
-       }
+       /* free the rx buffer ring */
+       bcm_enet_free_rx_buf_ring(kdev, priv);
 
        /* free remaining allocated memory */
-       kfree(priv->rx_skb);
        kfree(priv->tx_skb);
        dma_free_coherent(kdev, priv->rx_desc_alloc_size,
                          priv->rx_desc_cpu, priv->rx_desc_dma);
@@ -2659,6 +2658,7 @@ static int bcm_enetsw_probe(struct platform_device *pdev)
        priv->rx_ring_size = BCMENET_DEF_RX_DESC;
        priv->tx_ring_size = BCMENET_DEF_TX_DESC;
        priv->dma_maxburst = BCMENETSW_DMA_MAXBURST;
+       priv->rx_buf_offset = NET_SKB_PAD + NET_IP_ALIGN;
 
        pd = dev_get_platdata(&pdev->dev);
        if (pd) {
index 1d3c917..78f1830 100644 (file)
@@ -230,11 +230,17 @@ struct bcm_enet_priv {
        /* next dirty rx descriptor to refill */
        int rx_dirty_desc;
 
-       /* size of allocated rx skbs */
-       unsigned int rx_skb_size;
+       /* size of allocated rx buffers */
+       unsigned int rx_buf_size;
 
-       /* list of skb given to hw for rx */
-       struct sk_buff **rx_skb;
+       /* allocated rx buffer offset */
+       unsigned int rx_buf_offset;
+
+       /* size of allocated rx frag */
+       unsigned int rx_frag_size;
+
+       /* list of buffer given to hw for rx */
+       void **rx_buf;
 
        /* used when rx skb allocation failed, so we defer rx queue
         * refill */
index 0fdd19d..777bbf6 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/netdevice.h>
+#include <linux/dsa/brcm.h>
 #include <linux/etherdevice.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
@@ -2310,33 +2311,22 @@ static const struct net_device_ops bcm_sysport_netdev_ops = {
        .ndo_select_queue       = bcm_sysport_select_queue,
 };
 
-static int bcm_sysport_map_queues(struct notifier_block *nb,
-                                 struct dsa_notifier_register_info *info)
+static int bcm_sysport_map_queues(struct net_device *dev,
+                                 struct net_device *slave_dev)
 {
+       struct dsa_port *dp = dsa_port_from_netdev(slave_dev);
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
        struct bcm_sysport_tx_ring *ring;
-       struct bcm_sysport_priv *priv;
-       struct net_device *slave_dev;
        unsigned int num_tx_queues;
        unsigned int q, qp, port;
-       struct net_device *dev;
-
-       priv = container_of(nb, struct bcm_sysport_priv, dsa_notifier);
-       if (priv->netdev != info->master)
-               return 0;
-
-       dev = info->master;
 
        /* We can't be setting up queue inspection for non directly attached
         * switches
         */
-       if (info->switch_number)
+       if (dp->ds->index)
                return 0;
 
-       if (dev->netdev_ops != &bcm_sysport_netdev_ops)
-               return 0;
-
-       port = info->port_number;
-       slave_dev = info->info.dev;
+       port = dp->index;
 
        /* On SYSTEMPORT Lite we have twice as less queues, so we cannot do a
         * 1:1 mapping, we can only do a 2:1 mapping. By reducing the number of
@@ -2376,27 +2366,16 @@ static int bcm_sysport_map_queues(struct notifier_block *nb,
        return 0;
 }
 
-static int bcm_sysport_unmap_queues(struct notifier_block *nb,
-                                   struct dsa_notifier_register_info *info)
+static int bcm_sysport_unmap_queues(struct net_device *dev,
+                                   struct net_device *slave_dev)
 {
+       struct dsa_port *dp = dsa_port_from_netdev(slave_dev);
+       struct bcm_sysport_priv *priv = netdev_priv(dev);
        struct bcm_sysport_tx_ring *ring;
-       struct bcm_sysport_priv *priv;
-       struct net_device *slave_dev;
        unsigned int num_tx_queues;
-       struct net_device *dev;
        unsigned int q, qp, port;
 
-       priv = container_of(nb, struct bcm_sysport_priv, dsa_notifier);
-       if (priv->netdev != info->master)
-               return 0;
-
-       dev = info->master;
-
-       if (dev->netdev_ops != &bcm_sysport_netdev_ops)
-               return 0;
-
-       port = info->port_number;
-       slave_dev = info->info.dev;
+       port = dp->index;
 
        num_tx_queues = slave_dev->real_num_tx_queues;
 
@@ -2417,17 +2396,30 @@ static int bcm_sysport_unmap_queues(struct notifier_block *nb,
        return 0;
 }
 
-static int bcm_sysport_dsa_notifier(struct notifier_block *nb,
-                                   unsigned long event, void *ptr)
+static int bcm_sysport_netdevice_event(struct notifier_block *nb,
+                                      unsigned long event, void *ptr)
 {
-       int ret = NOTIFY_DONE;
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       struct netdev_notifier_changeupper_info *info = ptr;
+       struct bcm_sysport_priv *priv;
+       int ret = 0;
+
+       priv = container_of(nb, struct bcm_sysport_priv, netdev_notifier);
+       if (priv->netdev != dev)
+               return NOTIFY_DONE;
 
        switch (event) {
-       case DSA_PORT_REGISTER:
-               ret = bcm_sysport_map_queues(nb, ptr);
-               break;
-       case DSA_PORT_UNREGISTER:
-               ret = bcm_sysport_unmap_queues(nb, ptr);
+       case NETDEV_CHANGEUPPER:
+               if (dev->netdev_ops != &bcm_sysport_netdev_ops)
+                       return NOTIFY_DONE;
+
+               if (!dsa_slave_dev_check(info->upper_dev))
+                       return NOTIFY_DONE;
+
+               if (info->linking)
+                       ret = bcm_sysport_map_queues(dev, info->upper_dev);
+               else
+                       ret = bcm_sysport_unmap_queues(dev, info->upper_dev);
                break;
        }
 
@@ -2503,8 +2495,10 @@ static int bcm_sysport_probe(struct platform_device *pdev)
        priv = netdev_priv(dev);
 
        priv->clk = devm_clk_get_optional(&pdev->dev, "sw_sysport");
-       if (IS_ERR(priv->clk))
-               return PTR_ERR(priv->clk);
+       if (IS_ERR(priv->clk)) {
+               ret = PTR_ERR(priv->clk);
+               goto err_free_netdev;
+       }
 
        /* Allocate number of TX rings */
        priv->tx_rings = devm_kcalloc(&pdev->dev, txq,
@@ -2577,6 +2571,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
                         NETIF_F_HW_VLAN_CTAG_TX;
        dev->hw_features |= dev->features;
        dev->vlan_features |= dev->features;
+       dev->max_mtu = UMAC_MAX_MTU_SIZE;
 
        /* Request the WOL interrupt and advertise suspend if available */
        priv->wol_irq_disabled = 1;
@@ -2599,9 +2594,9 @@ static int bcm_sysport_probe(struct platform_device *pdev)
        priv->rx_max_coalesced_frames = 1;
        u64_stats_init(&priv->syncp);
 
-       priv->dsa_notifier.notifier_call = bcm_sysport_dsa_notifier;
+       priv->netdev_notifier.notifier_call = bcm_sysport_netdevice_event;
 
-       ret = register_dsa_notifier(&priv->dsa_notifier);
+       ret = register_netdevice_notifier(&priv->netdev_notifier);
        if (ret) {
                dev_err(&pdev->dev, "failed to register DSA notifier\n");
                goto err_deregister_fixed_link;
@@ -2628,7 +2623,7 @@ static int bcm_sysport_probe(struct platform_device *pdev)
        return 0;
 
 err_deregister_notifier:
-       unregister_dsa_notifier(&priv->dsa_notifier);
+       unregister_netdevice_notifier(&priv->netdev_notifier);
 err_deregister_fixed_link:
        if (of_phy_is_fixed_link(dn))
                of_phy_deregister_fixed_link(dn);
@@ -2646,7 +2641,7 @@ static int bcm_sysport_remove(struct platform_device *pdev)
        /* Not much to do, ndo_close has been called
         * and we use managed allocations
         */
-       unregister_dsa_notifier(&priv->dsa_notifier);
+       unregister_netdevice_notifier(&priv->netdev_notifier);
        unregister_netdev(dev);
        if (of_phy_is_fixed_link(dn))
                of_phy_deregister_fixed_link(dn);
index 3a5cb6f..984f76e 100644 (file)
@@ -13,6 +13,8 @@
 #include <linux/if_vlan.h>
 #include <linux/dim.h>
 
+#include "unimac.h"
+
 /* Receive/transmit descriptor format */
 #define DESC_ADDR_HI_STATUS_LEN        0x00
 #define  DESC_ADDR_HI_SHIFT    0
@@ -213,39 +215,6 @@ struct bcm_rsb {
 /* UniMAC offset and defines */
 #define SYS_PORT_UMAC_OFFSET           0x800
 
-#define UMAC_CMD                       0x008
-#define  CMD_TX_EN                     (1 << 0)
-#define  CMD_RX_EN                     (1 << 1)
-#define  CMD_SPEED_SHIFT               2
-#define  CMD_SPEED_10                  0
-#define  CMD_SPEED_100                 1
-#define  CMD_SPEED_1000                        2
-#define  CMD_SPEED_2500                        3
-#define  CMD_SPEED_MASK                        3
-#define  CMD_PROMISC                   (1 << 4)
-#define  CMD_PAD_EN                    (1 << 5)
-#define  CMD_CRC_FWD                   (1 << 6)
-#define  CMD_PAUSE_FWD                 (1 << 7)
-#define  CMD_RX_PAUSE_IGNORE           (1 << 8)
-#define  CMD_TX_ADDR_INS               (1 << 9)
-#define  CMD_HD_EN                     (1 << 10)
-#define  CMD_SW_RESET                  (1 << 13)
-#define  CMD_LCL_LOOP_EN               (1 << 15)
-#define  CMD_AUTO_CONFIG               (1 << 22)
-#define  CMD_CNTL_FRM_EN               (1 << 23)
-#define  CMD_NO_LEN_CHK                        (1 << 24)
-#define  CMD_RMT_LOOP_EN               (1 << 25)
-#define  CMD_PRBL_EN                   (1 << 27)
-#define  CMD_TX_PAUSE_IGNORE           (1 << 28)
-#define  CMD_TX_RX_EN                  (1 << 29)
-#define  CMD_RUNT_FILTER_DIS           (1 << 30)
-
-#define UMAC_MAC0                      0x00c
-#define UMAC_MAC1                      0x010
-#define UMAC_MAX_FRAME_LEN             0x014
-
-#define UMAC_TX_FLUSH                  0x334
-
 #define UMAC_MIB_START                 0x400
 
 /* There is a 0xC gap between the end of RX and beginning of TX stats and then
@@ -787,7 +756,7 @@ struct bcm_sysport_priv {
        struct u64_stats_sync   syncp;
 
        /* map information between switch port queues and local queues */
-       struct notifier_block   dsa_notifier;
+       struct notifier_block   netdev_notifier;
        unsigned int            per_port_num_tx_queues;
        struct bcm_sysport_tx_ring *ring_map[DSA_MAX_PORTS * 8];
 
index 98ec1b8..075f6e1 100644 (file)
@@ -746,25 +746,25 @@ error:
 /* TODO: can we just drop @force? Can we don't reset MAC at all if there is
  * nothing to change? Try if after stabilizng driver.
  */
-static void bgmac_cmdcfg_maskset(struct bgmac *bgmac, u32 mask, u32 set,
-                                bool force)
+static void bgmac_umac_cmd_maskset(struct bgmac *bgmac, u32 mask, u32 set,
+                                  bool force)
 {
-       u32 cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG);
+       u32 cmdcfg = bgmac_umac_read(bgmac, UMAC_CMD);
        u32 new_val = (cmdcfg & mask) | set;
        u32 cmdcfg_sr;
 
        if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4)
-               cmdcfg_sr = BGMAC_CMDCFG_SR_REV4;
+               cmdcfg_sr = CMD_SW_RESET;
        else
-               cmdcfg_sr = BGMAC_CMDCFG_SR_REV0;
+               cmdcfg_sr = CMD_SW_RESET_OLD;
 
-       bgmac_set(bgmac, BGMAC_CMDCFG, cmdcfg_sr);
+       bgmac_umac_maskset(bgmac, UMAC_CMD, ~0, cmdcfg_sr);
        udelay(2);
 
        if (new_val != cmdcfg || force)
-               bgmac_write(bgmac, BGMAC_CMDCFG, new_val);
+               bgmac_umac_write(bgmac, UMAC_CMD, new_val);
 
-       bgmac_mask(bgmac, BGMAC_CMDCFG, ~cmdcfg_sr);
+       bgmac_umac_maskset(bgmac, UMAC_CMD, ~cmdcfg_sr, 0);
        udelay(2);
 }
 
@@ -773,9 +773,9 @@ static void bgmac_write_mac_address(struct bgmac *bgmac, u8 *addr)
        u32 tmp;
 
        tmp = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
-       bgmac_write(bgmac, BGMAC_MACADDR_HIGH, tmp);
+       bgmac_umac_write(bgmac, UMAC_MAC0, tmp);
        tmp = (addr[4] << 8) | addr[5];
-       bgmac_write(bgmac, BGMAC_MACADDR_LOW, tmp);
+       bgmac_umac_write(bgmac, UMAC_MAC1, tmp);
 }
 
 static void bgmac_set_rx_mode(struct net_device *net_dev)
@@ -783,9 +783,9 @@ static void bgmac_set_rx_mode(struct net_device *net_dev)
        struct bgmac *bgmac = netdev_priv(net_dev);
 
        if (net_dev->flags & IFF_PROMISC)
-               bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_PROM, true);
+               bgmac_umac_cmd_maskset(bgmac, ~0, CMD_PROMISC, true);
        else
-               bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_PROM, 0, true);
+               bgmac_umac_cmd_maskset(bgmac, ~CMD_PROMISC, 0, true);
 }
 
 #if 0 /* We don't use that regs yet */
@@ -825,21 +825,21 @@ static void bgmac_clear_mib(struct bgmac *bgmac)
 /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_speed */
 static void bgmac_mac_speed(struct bgmac *bgmac)
 {
-       u32 mask = ~(BGMAC_CMDCFG_ES_MASK | BGMAC_CMDCFG_HD);
+       u32 mask = ~(CMD_SPEED_MASK << CMD_SPEED_SHIFT | CMD_HD_EN);
        u32 set = 0;
 
        switch (bgmac->mac_speed) {
        case SPEED_10:
-               set |= BGMAC_CMDCFG_ES_10;
+               set |= CMD_SPEED_10 << CMD_SPEED_SHIFT;
                break;
        case SPEED_100:
-               set |= BGMAC_CMDCFG_ES_100;
+               set |= CMD_SPEED_100 << CMD_SPEED_SHIFT;
                break;
        case SPEED_1000:
-               set |= BGMAC_CMDCFG_ES_1000;
+               set |= CMD_SPEED_1000 << CMD_SPEED_SHIFT;
                break;
        case SPEED_2500:
-               set |= BGMAC_CMDCFG_ES_2500;
+               set |= CMD_SPEED_2500 << CMD_SPEED_SHIFT;
                break;
        default:
                dev_err(bgmac->dev, "Unsupported speed: %d\n",
@@ -847,9 +847,9 @@ static void bgmac_mac_speed(struct bgmac *bgmac)
        }
 
        if (bgmac->mac_duplex == DUPLEX_HALF)
-               set |= BGMAC_CMDCFG_HD;
+               set |= CMD_HD_EN;
 
-       bgmac_cmdcfg_maskset(bgmac, mask, set, true);
+       bgmac_umac_cmd_maskset(bgmac, mask, set, true);
 }
 
 static void bgmac_miiconfig(struct bgmac *bgmac)
@@ -917,7 +917,7 @@ static void bgmac_chip_reset(struct bgmac *bgmac)
                for (i = 0; i < BGMAC_MAX_TX_RINGS; i++)
                        bgmac_dma_tx_reset(bgmac, &bgmac->tx_ring[i]);
 
-               bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false);
+               bgmac_umac_cmd_maskset(bgmac, ~0, CMD_LCL_LOOP_EN, false);
                udelay(1);
 
                for (i = 0; i < BGMAC_MAX_RX_RINGS; i++)
@@ -986,34 +986,34 @@ static void bgmac_chip_reset(struct bgmac *bgmac)
        }
 
        /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_reset
-        * Specs don't say about using BGMAC_CMDCFG_SR, but in this routine
-        * BGMAC_CMDCFG is read _after_ putting chip in a reset. So it has to
+        * Specs don't say about using UMAC_CMD_SR, but in this routine
+        * UMAC_CMD is read _after_ putting chip in a reset. So it has to
         * be keps until taking MAC out of the reset.
         */
        if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4)
-               cmdcfg_sr = BGMAC_CMDCFG_SR_REV4;
+               cmdcfg_sr = CMD_SW_RESET;
        else
-               cmdcfg_sr = BGMAC_CMDCFG_SR_REV0;
-
-       bgmac_cmdcfg_maskset(bgmac,
-                            ~(BGMAC_CMDCFG_TE |
-                              BGMAC_CMDCFG_RE |
-                              BGMAC_CMDCFG_RPI |
-                              BGMAC_CMDCFG_TAI |
-                              BGMAC_CMDCFG_HD |
-                              BGMAC_CMDCFG_ML |
-                              BGMAC_CMDCFG_CFE |
-                              BGMAC_CMDCFG_RL |
-                              BGMAC_CMDCFG_RED |
-                              BGMAC_CMDCFG_PE |
-                              BGMAC_CMDCFG_TPI |
-                              BGMAC_CMDCFG_PAD_EN |
-                              BGMAC_CMDCFG_PF),
-                            BGMAC_CMDCFG_PROM |
-                            BGMAC_CMDCFG_NLC |
-                            BGMAC_CMDCFG_CFE |
-                            cmdcfg_sr,
-                            false);
+               cmdcfg_sr = CMD_SW_RESET_OLD;
+
+       bgmac_umac_cmd_maskset(bgmac,
+                              ~(CMD_TX_EN |
+                                CMD_RX_EN |
+                                CMD_RX_PAUSE_IGNORE |
+                                CMD_TX_ADDR_INS |
+                                CMD_HD_EN |
+                                CMD_LCL_LOOP_EN |
+                                CMD_CNTL_FRM_EN |
+                                CMD_RMT_LOOP_EN |
+                                CMD_RX_ERR_DISC |
+                                CMD_PRBL_EN |
+                                CMD_TX_PAUSE_IGNORE |
+                                CMD_PAD_EN |
+                                CMD_PAUSE_FWD),
+                              CMD_PROMISC |
+                              CMD_NO_LEN_CHK |
+                              CMD_CNTL_FRM_EN |
+                              cmdcfg_sr,
+                              false);
        bgmac->mac_speed = SPEED_UNKNOWN;
        bgmac->mac_duplex = DUPLEX_UNKNOWN;
 
@@ -1049,16 +1049,16 @@ static void bgmac_enable(struct bgmac *bgmac)
        u32 mode;
 
        if (bgmac->feature_flags & BGMAC_FEAT_CMDCFG_SR_REV4)
-               cmdcfg_sr = BGMAC_CMDCFG_SR_REV4;
+               cmdcfg_sr = CMD_SW_RESET;
        else
-               cmdcfg_sr = BGMAC_CMDCFG_SR_REV0;
+               cmdcfg_sr = CMD_SW_RESET_OLD;
 
-       cmdcfg = bgmac_read(bgmac, BGMAC_CMDCFG);
-       bgmac_cmdcfg_maskset(bgmac, ~(BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE),
-                            cmdcfg_sr, true);
+       cmdcfg = bgmac_umac_read(bgmac, UMAC_CMD);
+       bgmac_umac_cmd_maskset(bgmac, ~(CMD_TX_EN | CMD_RX_EN),
+                              cmdcfg_sr, true);
        udelay(2);
-       cmdcfg |= BGMAC_CMDCFG_TE | BGMAC_CMDCFG_RE;
-       bgmac_write(bgmac, BGMAC_CMDCFG, cmdcfg);
+       cmdcfg |= CMD_TX_EN | CMD_RX_EN;
+       bgmac_umac_write(bgmac, UMAC_CMD, cmdcfg);
 
        mode = (bgmac_read(bgmac, BGMAC_DEV_STATUS) & BGMAC_DS_MM_MASK) >>
                BGMAC_DS_MM_SHIFT;
@@ -1078,7 +1078,7 @@ static void bgmac_enable(struct bgmac *bgmac)
                        fl_ctl = 0x03cb04cb;
 
                bgmac_write(bgmac, BGMAC_FLOW_CTL_THRESH, fl_ctl);
-               bgmac_write(bgmac, BGMAC_PAUSE_CTL, 0x27fff);
+               bgmac_umac_write(bgmac, UMAC_PAUSE_CTRL, 0x27fff);
        }
 
        if (bgmac->feature_flags & BGMAC_FEAT_SET_RXQ_CLK) {
@@ -1105,18 +1105,18 @@ static void bgmac_chip_init(struct bgmac *bgmac)
        bgmac_write(bgmac, BGMAC_INT_RECV_LAZY, 1 << BGMAC_IRL_FC_SHIFT);
 
        /* Enable 802.3x tx flow control (honor received PAUSE frames) */
-       bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_RPI, 0, true);
+       bgmac_umac_cmd_maskset(bgmac, ~CMD_RX_PAUSE_IGNORE, 0, true);
 
        bgmac_set_rx_mode(bgmac->net_dev);
 
        bgmac_write_mac_address(bgmac, bgmac->net_dev->dev_addr);
 
        if (bgmac->loopback)
-               bgmac_cmdcfg_maskset(bgmac, ~0, BGMAC_CMDCFG_ML, false);
+               bgmac_umac_cmd_maskset(bgmac, ~0, CMD_LCL_LOOP_EN, false);
        else
-               bgmac_cmdcfg_maskset(bgmac, ~BGMAC_CMDCFG_ML, 0, false);
+               bgmac_umac_cmd_maskset(bgmac, ~CMD_LCL_LOOP_EN, 0, false);
 
-       bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + ETHER_MAX_LEN);
+       bgmac_umac_write(bgmac, UMAC_MAX_FRAME_LEN, 32 + ETHER_MAX_LEN);
 
        bgmac_chip_intrs_on(bgmac);
 
@@ -1252,7 +1252,7 @@ static int bgmac_change_mtu(struct net_device *net_dev, int mtu)
 {
        struct bgmac *bgmac = netdev_priv(net_dev);
 
-       bgmac_write(bgmac, BGMAC_RXMAX_LENGTH, 32 + mtu);
+       bgmac_umac_write(bgmac, UMAC_MAX_FRAME_LEN, 32 + mtu);
        return 0;
 }
 
index 351c598..110088e 100644 (file)
@@ -4,6 +4,8 @@
 
 #include <linux/netdevice.h>
 
+#include "unimac.h"
+
 #define BGMAC_DEV_CTL                          0x000
 #define  BGMAC_DC_TSM                          0x00000002
 #define  BGMAC_DC_CFCO                         0x00000004
 #define BGMAC_RX_NONPAUSE_PKTS                 0x420
 #define BGMAC_RX_SACHANGES                     0x424
 #define BGMAC_RX_UNI_PKTS                      0x428
-#define BGMAC_UNIMAC_VERSION                   0x800
-#define BGMAC_HDBKP_CTL                                0x804
-#define BGMAC_CMDCFG                           0x808           /* Configuration */
-#define  BGMAC_CMDCFG_TE                       0x00000001      /* Set to activate TX */
-#define  BGMAC_CMDCFG_RE                       0x00000002      /* Set to activate RX */
-#define  BGMAC_CMDCFG_ES_MASK                  0x0000000c      /* Ethernet speed see gmac_speed */
-#define   BGMAC_CMDCFG_ES_10                   0x00000000
-#define   BGMAC_CMDCFG_ES_100                  0x00000004
-#define   BGMAC_CMDCFG_ES_1000                 0x00000008
-#define   BGMAC_CMDCFG_ES_2500                 0x0000000C
-#define  BGMAC_CMDCFG_PROM                     0x00000010      /* Set to activate promiscuous mode */
-#define  BGMAC_CMDCFG_PAD_EN                   0x00000020
-#define  BGMAC_CMDCFG_CF                       0x00000040
-#define  BGMAC_CMDCFG_PF                       0x00000080
-#define  BGMAC_CMDCFG_RPI                      0x00000100      /* Unset to enable 802.3x tx flow control */
-#define  BGMAC_CMDCFG_TAI                      0x00000200
-#define  BGMAC_CMDCFG_HD                       0x00000400      /* Set if in half duplex mode */
-#define  BGMAC_CMDCFG_HD_SHIFT                 10
-#define  BGMAC_CMDCFG_SR_REV0                  0x00000800      /* Set to reset mode, for core rev 0-3 */
-#define  BGMAC_CMDCFG_SR_REV4                  0x00002000      /* Set to reset mode, for core rev >= 4 */
-#define  BGMAC_CMDCFG_ML                       0x00008000      /* Set to activate mac loopback mode */
-#define  BGMAC_CMDCFG_AE                       0x00400000
-#define  BGMAC_CMDCFG_CFE                      0x00800000
-#define  BGMAC_CMDCFG_NLC                      0x01000000
-#define  BGMAC_CMDCFG_RL                       0x02000000
-#define  BGMAC_CMDCFG_RED                      0x04000000
-#define  BGMAC_CMDCFG_PE                       0x08000000
-#define  BGMAC_CMDCFG_TPI                      0x10000000
-#define  BGMAC_CMDCFG_AT                       0x20000000
-#define BGMAC_MACADDR_HIGH                     0x80c           /* High 4 octets of own mac address */
-#define BGMAC_MACADDR_LOW                      0x810           /* Low 2 octets of own mac address */
-#define BGMAC_RXMAX_LENGTH                     0x814           /* Max receive frame length with vlan tag */
-#define BGMAC_PAUSEQUANTA                      0x818
-#define BGMAC_MAC_MODE                         0x844
-#define BGMAC_OUTERTAG                         0x848
-#define BGMAC_INNERTAG                         0x84c
-#define BGMAC_TXIPG                            0x85c
-#define BGMAC_PAUSE_CTL                                0xb30
-#define BGMAC_TX_FLUSH                         0xb34
-#define BGMAC_RX_STATUS                                0xb38
-#define BGMAC_TX_STATUS                                0xb3c
+#define BGMAC_UNIMAC                           0x800
 
 /* BCMA GMAC core specific IO Control (BCMA_IOCTL) flags */
 #define BGMAC_BCMA_IOCTL_SW_CLKEN              0x00000004      /* PHY Clock Enable */
@@ -556,6 +518,16 @@ static inline void bgmac_write(struct bgmac *bgmac, u16 offset, u32 value)
        bgmac->write(bgmac, offset, value);
 }
 
+static inline u32 bgmac_umac_read(struct bgmac *bgmac, u16 offset)
+{
+       return bgmac_read(bgmac, BGMAC_UNIMAC + offset);
+}
+
+static inline void bgmac_umac_write(struct bgmac *bgmac, u16 offset, u32 value)
+{
+       bgmac_write(bgmac, BGMAC_UNIMAC + offset, value);
+}
+
 static inline u32 bgmac_idm_read(struct bgmac *bgmac, u16 offset)
 {
        return bgmac->idm_read(bgmac, offset);
@@ -609,6 +581,11 @@ static inline void bgmac_set(struct bgmac *bgmac, u16 offset, u32 set)
        bgmac_maskset(bgmac, offset, ~0, set);
 }
 
+static inline void bgmac_umac_maskset(struct bgmac *bgmac, u16 offset, u32 mask, u32 set)
+{
+       bgmac_maskset(bgmac, BGMAC_UNIMAC + offset, mask, set);
+}
+
 static inline int bgmac_phy_connect(struct bgmac *bgmac)
 {
        return bgmac->phy_connect(bgmac);
index 28069b2..b652ed7 100644 (file)
@@ -13071,8 +13071,6 @@ static const struct net_device_ops bnx2x_netdev_ops = {
        .ndo_get_phys_port_id   = bnx2x_get_phys_port_id,
        .ndo_set_vf_link_state  = bnx2x_set_vf_link_state,
        .ndo_features_check     = bnx2x_features_check,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
 };
 
 static int bnx2x_set_coherency_mask(struct bnx2x *bp)
index 4edd6f8..f508c5c 100644 (file)
@@ -255,6 +255,7 @@ static const u16 bnxt_async_events_arr[] = {
        ASYNC_EVENT_CMPL_EVENT_ID_PORT_PHY_CFG_CHANGE,
        ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY,
        ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY,
+       ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION,
        ASYNC_EVENT_CMPL_EVENT_ID_RING_MONITOR_MSG,
 };
 
@@ -1265,8 +1266,7 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
        } else {
                tpa_info->hash_type = PKT_HASH_TYPE_NONE;
                tpa_info->gso_type = 0;
-               if (netif_msg_rx_err(bp))
-                       netdev_warn(bp->dev, "TPA packet without valid hash\n");
+               netif_warn(bp, rx_err, bp->dev, "TPA packet without valid hash\n");
        }
        tpa_info->flags2 = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_flags2);
        tpa_info->metadata = le32_to_cpu(tpa_start1->rx_tpa_start_cmp_metadata);
@@ -2021,10 +2021,9 @@ static int bnxt_async_event_process(struct bnxt *bp,
                        goto async_event_process_exit;
                set_bit(BNXT_RESET_TASK_SILENT_SP_EVENT, &bp->sp_event);
                break;
-       case ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY:
-               if (netif_msg_hw(bp))
-                       netdev_warn(bp->dev, "Received RESET_NOTIFY event, data1: 0x%x, data2: 0x%x\n",
-                                   data1, data2);
+       case ASYNC_EVENT_CMPL_EVENT_ID_RESET_NOTIFY: {
+               char *fatal_str = "non-fatal";
+
                if (!bp->fw_health)
                        goto async_event_process_exit;
 
@@ -2036,14 +2035,17 @@ static int bnxt_async_event_process(struct bnxt *bp,
                if (!bp->fw_reset_max_dsecs)
                        bp->fw_reset_max_dsecs = BNXT_DFLT_FW_RST_MAX_DSECS;
                if (EVENT_DATA1_RESET_NOTIFY_FATAL(data1)) {
-                       netdev_warn(bp->dev, "Firmware fatal reset event received\n");
+                       fatal_str = "fatal";
                        set_bit(BNXT_STATE_FW_FATAL_COND, &bp->state);
-               } else {
-                       netdev_warn(bp->dev, "Firmware non-fatal reset event received, max wait time %d msec\n",
-                                   bp->fw_reset_max_dsecs * 100);
                }
+               netif_warn(bp, hw, bp->dev,
+                          "Firmware %s reset event, data1: 0x%x, data2: 0x%x, min wait %u ms, max wait %u ms\n",
+                          fatal_str, data1, data2,
+                          bp->fw_reset_min_dsecs * 100,
+                          bp->fw_reset_max_dsecs * 100);
                set_bit(BNXT_FW_RESET_NOTIFY_SP_EVENT, &bp->sp_event);
                break;
+       }
        case ASYNC_EVENT_CMPL_EVENT_ID_ERROR_RECOVERY: {
                struct bnxt_fw_health *fw_health = bp->fw_health;
 
@@ -2055,13 +2057,11 @@ static int bnxt_async_event_process(struct bnxt *bp,
                if (!fw_health->enabled)
                        break;
 
-               if (netif_msg_drv(bp))
-                       netdev_info(bp->dev, "Error recovery info: error recovery[%d], master[%d], reset count[0x%x], health status: 0x%x\n",
-                                   fw_health->enabled, fw_health->master,
-                                   bnxt_fw_health_readl(bp,
-                                                        BNXT_FW_RESET_CNT_REG),
-                                   bnxt_fw_health_readl(bp,
-                                                        BNXT_FW_HEALTH_REG));
+               netif_info(bp, drv, bp->dev,
+                          "Error recovery info: error recovery[%d], master[%d], reset count[0x%x], health status: 0x%x\n",
+                          fw_health->enabled, fw_health->master,
+                          bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG),
+                          bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG));
                fw_health->tmr_multiplier =
                        DIV_ROUND_UP(fw_health->polling_dsecs * HZ,
                                     bp->current_interval * 10);
@@ -2072,6 +2072,11 @@ static int bnxt_async_event_process(struct bnxt *bp,
                        bnxt_fw_health_readl(bp, BNXT_FW_RESET_CNT_REG);
                goto async_event_process_exit;
        }
+       case ASYNC_EVENT_CMPL_EVENT_ID_DEBUG_NOTIFICATION:
+               netif_notice(bp, hw, bp->dev,
+                            "Received firmware debug notification, data1: 0x%x, data2: 0x%x\n",
+                            data1, data2);
+               goto async_event_process_exit;
        case ASYNC_EVENT_CMPL_EVENT_ID_RING_MONITOR_MSG: {
                struct bnxt_rx_ring_info *rxr;
                u16 grp_idx;
@@ -2394,6 +2399,10 @@ static int bnxt_poll(struct napi_struct *napi, int budget)
        struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
        int work_done = 0;
 
+       if (unlikely(test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))) {
+               napi_complete(napi);
+               return 0;
+       }
        while (1) {
                work_done += bnxt_poll_work(bp, cpr, budget - work_done);
 
@@ -2468,6 +2477,10 @@ static int bnxt_poll_p5(struct napi_struct *napi, int budget)
        int work_done = 0;
        u32 cons;
 
+       if (unlikely(test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))) {
+               napi_complete(napi);
+               return 0;
+       }
        if (cpr->has_more_work) {
                cpr->has_more_work = 0;
                work_done = __bnxt_poll_cqs(bp, bnapi, budget);
@@ -4272,6 +4285,9 @@ static void bnxt_disable_int_sync(struct bnxt *bp)
 {
        int i;
 
+       if (!bp->irq_tbl)
+               return;
+
        atomic_inc(&bp->intr_sem);
 
        bnxt_disable_int(bp);
@@ -4425,6 +4441,8 @@ static int bnxt_hwrm_do_send_msg(struct bnxt *bp, void *msg, u32 msg_len,
 
        if (!timeout)
                timeout = DFLT_HWRM_CMD_TIMEOUT;
+       /* Limit timeout to an upper limit */
+       timeout = min(timeout, HWRM_CMD_MAX_TIMEOUT);
        /* convert timeout to usec */
        timeout *= 1000;
 
@@ -6790,8 +6808,10 @@ static int bnxt_hwrm_func_backing_store_qcaps(struct bnxt *bp)
                ctx->tqm_fp_rings_count = resp->tqm_fp_rings_count;
                if (!ctx->tqm_fp_rings_count)
                        ctx->tqm_fp_rings_count = bp->max_q;
+               else if (ctx->tqm_fp_rings_count > BNXT_MAX_TQM_FP_RINGS)
+                       ctx->tqm_fp_rings_count = BNXT_MAX_TQM_FP_RINGS;
 
-               tqm_rings = ctx->tqm_fp_rings_count + 1;
+               tqm_rings = ctx->tqm_fp_rings_count + BNXT_MAX_TQM_SP_RINGS;
                ctx_pg = kcalloc(tqm_rings, sizeof(*ctx_pg), GFP_KERNEL);
                if (!ctx_pg) {
                        kfree(ctx);
@@ -6843,6 +6863,7 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables)
        struct hwrm_func_backing_store_cfg_input req = {0};
        struct bnxt_ctx_mem_info *ctx = bp->ctx;
        struct bnxt_ctx_pg_info *ctx_pg;
+       u32 req_len = sizeof(req);
        __le32 *num_entries;
        __le64 *pg_dir;
        u32 flags = 0;
@@ -6853,6 +6874,8 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables)
        if (!ctx)
                return 0;
 
+       if (req_len > bp->hwrm_max_ext_req_len)
+               req_len = BNXT_BACKING_STORE_CFG_LEGACY_LEN;
        bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_BACKING_STORE_CFG, -1, -1);
        req.enables = cpu_to_le32(enables);
 
@@ -6925,7 +6948,8 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables)
             pg_attr = &req.tqm_sp_pg_size_tqm_sp_lvl,
             pg_dir = &req.tqm_sp_page_dir,
             ena = FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP;
-            i < 9; i++, num_entries++, pg_attr++, pg_dir++, ena <<= 1) {
+            i < BNXT_MAX_TQM_RINGS;
+            i++, num_entries++, pg_attr++, pg_dir++, ena <<= 1) {
                if (!(enables & ena))
                        continue;
 
@@ -6935,7 +6959,7 @@ static int bnxt_hwrm_func_backing_store_cfg(struct bnxt *bp, u32 enables)
                bnxt_hwrm_set_pg_attr(&ctx_pg->ring_mem, pg_attr, pg_dir);
        }
        req.flags = cpu_to_le32(flags);
-       return hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       return hwrm_send_message(bp, &req, req_len, HWRM_CMD_TIMEOUT);
 }
 
 static int bnxt_alloc_ctx_mem_blk(struct bnxt *bp,
@@ -7435,9 +7459,22 @@ static void bnxt_try_map_fw_health_reg(struct bnxt *bp)
 
        sig = readl(hs + offsetof(struct hcomm_status, sig_ver));
        if ((sig & HCOMM_STATUS_SIGNATURE_MASK) != HCOMM_STATUS_SIGNATURE_VAL) {
-               if (bp->fw_health)
-                       bp->fw_health->status_reliable = false;
-               return;
+               if (!bp->chip_num) {
+                       __bnxt_map_fw_health_reg(bp, BNXT_GRC_REG_BASE);
+                       bp->chip_num = readl(bp->bar0 +
+                                            BNXT_FW_HEALTH_WIN_BASE +
+                                            BNXT_GRC_REG_CHIP_NUM);
+               }
+               if (!BNXT_CHIP_P5(bp)) {
+                       if (bp->fw_health)
+                               bp->fw_health->status_reliable = false;
+                       return;
+               }
+               status_loc = BNXT_GRC_REG_STATUS_P5 |
+                            BNXT_FW_HEALTH_REG_TYPE_BAR0;
+       } else {
+               status_loc = readl(hs + offsetof(struct hcomm_status,
+                                                fw_status_loc));
        }
 
        if (__bnxt_alloc_fw_health(bp)) {
@@ -7445,7 +7482,6 @@ static void bnxt_try_map_fw_health_reg(struct bnxt *bp)
                return;
        }
 
-       status_loc = readl(hs + offsetof(struct hcomm_status, fw_status_loc));
        bp->fw_health->regs[BNXT_FW_HEALTH_REG] = status_loc;
        reg_type = BNXT_FW_HEALTH_REG_TYPE(status_loc);
        if (reg_type == BNXT_FW_HEALTH_REG_TYPE_GRC) {
@@ -8600,7 +8636,7 @@ msix_setup_exit:
 
 static int bnxt_init_inta(struct bnxt *bp)
 {
-       bp->irq_tbl = kcalloc(1, sizeof(struct bnxt_irq), GFP_KERNEL);
+       bp->irq_tbl = kzalloc(sizeof(struct bnxt_irq), GFP_KERNEL);
        if (!bp->irq_tbl)
                return -ENOMEM;
 
@@ -8808,7 +8844,8 @@ static void bnxt_disable_napi(struct bnxt *bp)
 {
        int i;
 
-       if (!bp->bnapi)
+       if (!bp->bnapi ||
+           test_and_set_bit(BNXT_STATE_NAPI_DISABLED, &bp->state))
                return;
 
        for (i = 0; i < bp->cp_nr_rings; i++) {
@@ -8825,6 +8862,7 @@ static void bnxt_enable_napi(struct bnxt *bp)
 {
        int i;
 
+       clear_bit(BNXT_STATE_NAPI_DISABLED, &bp->state);
        for (i = 0; i < bp->cp_nr_rings; i++) {
                struct bnxt_napi *bnapi = bp->bnapi[i];
                struct bnxt_cp_ring_info *cpr;
@@ -9331,13 +9369,60 @@ static int bnxt_hwrm_shutdown_link(struct bnxt *bp)
 
 static int bnxt_fw_init_one(struct bnxt *bp);
 
+static int bnxt_fw_reset_via_optee(struct bnxt *bp)
+{
+#ifdef CONFIG_TEE_BNXT_FW
+       int rc = tee_bnxt_fw_load();
+
+       if (rc)
+               netdev_err(bp->dev, "Failed FW reset via OP-TEE, rc=%d\n", rc);
+
+       return rc;
+#else
+       netdev_err(bp->dev, "OP-TEE not supported\n");
+       return -ENODEV;
+#endif
+}
+
+static int bnxt_try_recover_fw(struct bnxt *bp)
+{
+       if (bp->fw_health && bp->fw_health->status_reliable) {
+               int retry = 0, rc;
+               u32 sts;
+
+               mutex_lock(&bp->hwrm_cmd_lock);
+               do {
+                       rc = __bnxt_hwrm_ver_get(bp, true);
+                       sts = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
+                       if (!sts || !BNXT_FW_IS_BOOTING(sts))
+                               break;
+                       retry++;
+               } while (rc == -EBUSY && retry < BNXT_FW_RETRY);
+               mutex_unlock(&bp->hwrm_cmd_lock);
+
+               if (!BNXT_FW_IS_HEALTHY(sts)) {
+                       netdev_err(bp->dev,
+                                  "Firmware not responding, status: 0x%x\n",
+                                  sts);
+                       rc = -ENODEV;
+               }
+               if (sts & FW_STATUS_REG_CRASHED_NO_MASTER) {
+                       netdev_warn(bp->dev, "Firmware recover via OP-TEE requested\n");
+                       return bnxt_fw_reset_via_optee(bp);
+               }
+               return rc;
+       }
+
+       return -ENODEV;
+}
+
 static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
 {
        struct hwrm_func_drv_if_change_output *resp = bp->hwrm_cmd_resp_addr;
        struct hwrm_func_drv_if_change_input req = {0};
        bool resc_reinit = false, fw_reset = false;
+       int rc, retry = 0;
        u32 flags = 0;
-       int rc;
 
        if (!(bp->fw_cap & BNXT_FW_CAP_IF_CHANGE))
                return 0;
@@ -9346,10 +9431,25 @@ static int bnxt_hwrm_if_change(struct bnxt *bp, bool up)
        if (up)
                req.flags = cpu_to_le32(FUNC_DRV_IF_CHANGE_REQ_FLAGS_UP);
        mutex_lock(&bp->hwrm_cmd_lock);
-       rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT);
+       while (retry < BNXT_FW_IF_RETRY) {
+               rc = _hwrm_send_message(bp, &req, sizeof(req),
+                                       HWRM_CMD_TIMEOUT);
+               if (rc != -EAGAIN)
+                       break;
+
+               msleep(50);
+               retry++;
+       }
        if (!rc)
                flags = le32_to_cpu(resp->flags);
        mutex_unlock(&bp->hwrm_cmd_lock);
+
+       if (rc == -EAGAIN)
+               return rc;
+       if (rc && up) {
+               rc = bnxt_try_recover_fw(bp);
+               fw_reset = true;
+       }
        if (rc)
                return rc;
 
@@ -9689,6 +9789,25 @@ static void bnxt_preset_reg_win(struct bnxt *bp)
 
 static int bnxt_init_dflt_ring_mode(struct bnxt *bp);
 
+static int bnxt_reinit_after_abort(struct bnxt *bp)
+{
+       int rc;
+
+       if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
+               return -EBUSY;
+
+       rc = bnxt_fw_init_one(bp);
+       if (!rc) {
+               bnxt_clear_int_mode(bp);
+               rc = bnxt_init_int_mode(bp);
+               if (!rc) {
+                       clear_bit(BNXT_STATE_ABORT_ERR, &bp->state);
+                       set_bit(BNXT_STATE_FW_RESET_DET, &bp->state);
+               }
+       }
+       return rc;
+}
+
 static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init)
 {
        int rc = 0;
@@ -9847,8 +9966,14 @@ static int bnxt_open(struct net_device *dev)
        int rc;
 
        if (test_bit(BNXT_STATE_ABORT_ERR, &bp->state)) {
-               netdev_err(bp->dev, "A previous firmware reset did not complete, aborting\n");
-               return -ENODEV;
+               rc = bnxt_reinit_after_abort(bp);
+               if (rc) {
+                       if (rc == -EBUSY)
+                               netdev_err(bp->dev, "A previous firmware reset has not completed, aborting\n");
+                       else
+                               netdev_err(bp->dev, "Failed to reinitialize after aborted firmware reset\n");
+                       return -ENODEV;
+               }
        }
 
        rc = bnxt_hwrm_if_change(bp, true);
@@ -10785,11 +10910,18 @@ static void bnxt_rx_ring_reset(struct bnxt *bp)
 static void bnxt_fw_reset_close(struct bnxt *bp)
 {
        bnxt_ulp_stop(bp);
-       /* When firmware is fatal state, disable PCI device to prevent
-        * any potential bad DMAs before freeing kernel memory.
+       /* When firmware is in fatal state, quiesce device and disable
+        * bus master to prevent any potential bad DMAs before freeing
+        * kernel memory.
         */
-       if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state))
+       if (test_bit(BNXT_STATE_FW_FATAL_COND, &bp->state)) {
+               bnxt_tx_disable(bp);
+               bnxt_disable_napi(bp);
+               bnxt_disable_int_sync(bp);
+               bnxt_free_irq(bp);
+               bnxt_clear_int_mode(bp);
                pci_disable_device(bp->pdev);
+       }
        __bnxt_close_nic(bp, true, false);
        bnxt_clear_int_mode(bp);
        bnxt_hwrm_func_drv_unrgtr(bp);
@@ -11177,21 +11309,6 @@ static void bnxt_init_dflt_coal(struct bnxt *bp)
        bp->stats_coal_ticks = BNXT_DEF_STATS_COAL_TICKS;
 }
 
-static int bnxt_fw_reset_via_optee(struct bnxt *bp)
-{
-#ifdef CONFIG_TEE_BNXT_FW
-       int rc = tee_bnxt_fw_load();
-
-       if (rc)
-               netdev_err(bp->dev, "Failed FW reset via OP-TEE, rc=%d\n", rc);
-
-       return rc;
-#else
-       netdev_err(bp->dev, "OP-TEE not supported\n");
-       return -ENODEV;
-#endif
-}
-
 static int bnxt_fw_init_one_p1(struct bnxt *bp)
 {
        int rc;
@@ -11200,19 +11317,10 @@ static int bnxt_fw_init_one_p1(struct bnxt *bp)
        rc = bnxt_hwrm_ver_get(bp);
        bnxt_try_map_fw_health_reg(bp);
        if (rc) {
-               if (bp->fw_health && bp->fw_health->status_reliable) {
-                       u32 sts = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
-
-                       netdev_err(bp->dev,
-                                  "Firmware not responding, status: 0x%x\n",
-                                  sts);
-                       if (sts & FW_STATUS_REG_CRASHED_NO_MASTER) {
-                               netdev_warn(bp->dev, "Firmware recover via OP-TEE requested\n");
-                               rc = bnxt_fw_reset_via_optee(bp);
-                               if (!rc)
-                                       rc = bnxt_hwrm_ver_get(bp);
-                       }
-               }
+               rc = bnxt_try_recover_fw(bp);
+               if (rc)
+                       return rc;
+               rc = bnxt_hwrm_ver_get(bp);
                if (rc)
                        return rc;
        }
@@ -11412,6 +11520,12 @@ static void bnxt_reset_all(struct bnxt *bp)
        bp->fw_reset_timestamp = jiffies;
 }
 
+static bool bnxt_fw_reset_timeout(struct bnxt *bp)
+{
+       return time_after(jiffies, bp->fw_reset_timestamp +
+                         (bp->fw_reset_max_dsecs * HZ / 10));
+}
+
 static void bnxt_fw_reset_task(struct work_struct *work)
 {
        struct bnxt *bp = container_of(work, struct bnxt, fw_reset_task.work);
@@ -11433,8 +11547,7 @@ static void bnxt_fw_reset_task(struct work_struct *work)
                                   bp->fw_reset_timestamp));
                        goto fw_reset_abort;
                } else if (n > 0) {
-                       if (time_after(jiffies, bp->fw_reset_timestamp +
-                                      (bp->fw_reset_max_dsecs * HZ / 10))) {
+                       if (bnxt_fw_reset_timeout(bp)) {
                                clear_bit(BNXT_STATE_IN_FW_RESET, &bp->state);
                                bp->fw_reset_state = 0;
                                netdev_err(bp->dev, "Firmware reset aborted, bnxt_get_registered_vfs() returns %d\n",
@@ -11463,8 +11576,7 @@ static void bnxt_fw_reset_task(struct work_struct *work)
 
                val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
                if (!(val & BNXT_FW_STATUS_SHUTDOWN) &&
-                   !time_after(jiffies, bp->fw_reset_timestamp +
-                   (bp->fw_reset_max_dsecs * HZ / 10))) {
+                   !bnxt_fw_reset_timeout(bp)) {
                        bnxt_queue_fw_reset_work(bp, HZ / 5);
                        return;
                }
@@ -11506,8 +11618,7 @@ static void bnxt_fw_reset_task(struct work_struct *work)
                bp->hwrm_cmd_timeout = SHORT_HWRM_CMD_TIMEOUT;
                rc = __bnxt_hwrm_ver_get(bp, true);
                if (rc) {
-                       if (time_after(jiffies, bp->fw_reset_timestamp +
-                                      (bp->fw_reset_max_dsecs * HZ / 10))) {
+                       if (bnxt_fw_reset_timeout(bp)) {
                                netdev_err(bp->dev, "Firmware reset aborted\n");
                                goto fw_reset_abort_status;
                        }
@@ -12088,8 +12199,6 @@ static const struct net_device_ops bnxt_netdev_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = bnxt_rx_flow_steer,
 #endif
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_bpf                = bnxt_xdp,
        .ndo_xdp_xmit           = bnxt_xdp_xmit,
        .ndo_bridge_getlink     = bnxt_bridge_getlink,
@@ -12541,9 +12650,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        dev->ethtool_ops = &bnxt_ethtool_ops;
        pci_set_drvdata(pdev, dev);
 
-       if (BNXT_PF(bp))
-               bnxt_vpd_read_info(bp);
-
        rc = bnxt_alloc_hwrm_resources(bp);
        if (rc)
                goto init_err_pci_clean;
@@ -12555,6 +12661,9 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                goto init_err_pci_clean;
 
+       if (BNXT_PF(bp))
+               bnxt_vpd_read_info(bp);
+
        if (BNXT_CHIP_P5(bp)) {
                bp->flags |= BNXT_FLAG_CHIP_P5;
                if (BNXT_CHIP_SR2(bp))
@@ -12887,10 +12996,10 @@ static pci_ers_result_t bnxt_io_error_detected(struct pci_dev *pdev,
  */
 static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
 {
+       pci_ers_result_t result = PCI_ERS_RESULT_DISCONNECT;
        struct net_device *netdev = pci_get_drvdata(pdev);
        struct bnxt *bp = netdev_priv(netdev);
        int err = 0, off;
-       pci_ers_result_t result = PCI_ERS_RESULT_DISCONNECT;
 
        netdev_info(bp->dev, "PCI Slot Reset\n");
 
@@ -12919,22 +13028,8 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
                pci_save_state(pdev);
 
                err = bnxt_hwrm_func_reset(bp);
-               if (!err) {
-                       err = bnxt_hwrm_func_qcaps(bp);
-                       if (!err && netif_running(netdev))
-                               err = bnxt_open(netdev);
-               }
-               bnxt_ulp_start(bp, err);
-               if (!err) {
-                       bnxt_reenable_sriov(bp);
+               if (!err)
                        result = PCI_ERS_RESULT_RECOVERED;
-               }
-       }
-
-       if (result != PCI_ERS_RESULT_RECOVERED) {
-               if (netif_running(netdev))
-                       dev_close(netdev);
-               pci_disable_device(pdev);
        }
 
        rtnl_unlock();
@@ -12952,10 +13047,21 @@ static pci_ers_result_t bnxt_io_slot_reset(struct pci_dev *pdev)
 static void bnxt_io_resume(struct pci_dev *pdev)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
+       struct bnxt *bp = netdev_priv(netdev);
+       int err;
 
+       netdev_info(bp->dev, "PCI Slot Resume\n");
        rtnl_lock();
 
-       netif_device_attach(netdev);
+       err = bnxt_hwrm_func_qcaps(bp);
+       if (!err && netif_running(netdev))
+               err = bnxt_open(netdev);
+
+       bnxt_ulp_start(bp, err);
+       if (!err) {
+               bnxt_reenable_sriov(bp);
+               netif_device_attach(netdev);
+       }
 
        rtnl_unlock();
 }
index 950ea26..4ef6888 100644 (file)
@@ -656,6 +656,7 @@ struct nqe_cn {
 #define BNXT_HWRM_MAX_REQ_LEN          (bp->hwrm_max_req_len)
 #define BNXT_HWRM_SHORT_REQ_LEN                sizeof(struct hwrm_short_input)
 #define DFLT_HWRM_CMD_TIMEOUT          500
+#define HWRM_CMD_MAX_TIMEOUT           40000
 #define SHORT_HWRM_CMD_TIMEOUT         20
 #define HWRM_CMD_TIMEOUT               (bp->hwrm_cmd_timeout)
 #define HWRM_RESET_TIMEOUT             ((HWRM_CMD_TIMEOUT) * 4)
@@ -1345,9 +1346,14 @@ struct bnxt_test_info {
 #define BNXT_CAG_REG_LEGACY_INT_STATUS         0x4014
 #define BNXT_CAG_REG_BASE                      0x300000
 
+#define BNXT_GRC_REG_STATUS_P5                 0x520
+
 #define BNXT_GRCPF_REG_KONG_COMM               0xA00
 #define BNXT_GRCPF_REG_KONG_COMM_TRIGGER       0xB00
 
+#define BNXT_GRC_REG_CHIP_NUM                  0x48
+#define BNXT_GRC_REG_BASE                      0x260000
+
 #define BNXT_GRC_BASE_MASK                     0xfffff000
 #define BNXT_GRC_OFFSET_MASK                   0x00000ffc
 
@@ -1436,6 +1442,13 @@ struct bnxt_ctx_pg_info {
        struct bnxt_ctx_pg_info **ctx_pg_tbl;
 };
 
+#define BNXT_MAX_TQM_SP_RINGS          1
+#define BNXT_MAX_TQM_FP_RINGS          8
+#define BNXT_MAX_TQM_RINGS             \
+       (BNXT_MAX_TQM_SP_RINGS + BNXT_MAX_TQM_FP_RINGS)
+
+#define BNXT_BACKING_STORE_CFG_LEGACY_LEN      256
+
 struct bnxt_ctx_mem_info {
        u32     qp_max_entries;
        u16     qp_min_qp1_entries;
@@ -1474,7 +1487,7 @@ struct bnxt_ctx_mem_info {
        struct bnxt_ctx_pg_info stat_mem;
        struct bnxt_ctx_pg_info mrav_mem;
        struct bnxt_ctx_pg_info tim_mem;
-       struct bnxt_ctx_pg_info *tqm_mem[9];
+       struct bnxt_ctx_pg_info *tqm_mem[BNXT_MAX_TQM_RINGS];
 };
 
 struct bnxt_fw_health {
@@ -1527,9 +1540,22 @@ struct bnxt_fw_reporter_ctx {
 #define BNXT_FW_HEALTH_WIN_OFF(reg)    (BNXT_FW_HEALTH_WIN_BASE +      \
                                         ((reg) & BNXT_GRC_OFFSET_MASK))
 
+#define BNXT_FW_STATUS_HEALTH_MSK      0xffff
 #define BNXT_FW_STATUS_HEALTHY         0x8000
 #define BNXT_FW_STATUS_SHUTDOWN                0x100000
 
+#define BNXT_FW_IS_HEALTHY(sts)                (((sts) & BNXT_FW_STATUS_HEALTH_MSK) ==\
+                                        BNXT_FW_STATUS_HEALTHY)
+
+#define BNXT_FW_IS_BOOTING(sts)                (((sts) & BNXT_FW_STATUS_HEALTH_MSK) < \
+                                        BNXT_FW_STATUS_HEALTHY)
+
+#define BNXT_FW_IS_ERR(sts)            (((sts) & BNXT_FW_STATUS_HEALTH_MSK) > \
+                                        BNXT_FW_STATUS_HEALTHY)
+
+#define BNXT_FW_RETRY                  5
+#define BNXT_FW_IF_RETRY               10
+
 struct bnxt {
        void __iomem            *bar0;
        void __iomem            *bar1;
@@ -1783,6 +1809,7 @@ struct bnxt {
 #define BNXT_STATE_FW_FATAL_COND       6
 #define BNXT_STATE_DRV_REGISTERED      7
 #define BNXT_STATE_PCI_CHANNEL_IO_FROZEN       8
+#define BNXT_STATE_NAPI_DISABLED       9
 
 #define BNXT_NO_FW_ACCESS(bp)                                  \
        (test_bit(BNXT_STATE_FW_FATAL_COND, &(bp)->state) ||    \
index 6b7b69e..90a31b4 100644 (file)
@@ -44,21 +44,20 @@ static int bnxt_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
                                     struct netlink_ext_ack *extack)
 {
        struct bnxt *bp = devlink_health_reporter_priv(reporter);
-       u32 val, health_status;
+       u32 val;
        int rc;
 
        if (test_bit(BNXT_STATE_IN_FW_RESET, &bp->state))
                return 0;
 
        val = bnxt_fw_health_readl(bp, BNXT_FW_HEALTH_REG);
-       health_status = val & 0xffff;
 
-       if (health_status < BNXT_FW_STATUS_HEALTHY) {
+       if (BNXT_FW_IS_BOOTING(val)) {
                rc = devlink_fmsg_string_pair_put(fmsg, "Description",
                                                  "Not yet completed initialization");
                if (rc)
                        return rc;
-       } else if (health_status > BNXT_FW_STATUS_HEALTHY) {
+       } else if (BNXT_FW_IS_ERR(val)) {
                rc = devlink_fmsg_string_pair_put(fmsg, "Description",
                                                  "Encountered fatal error and cannot recover");
                if (rc)
index 9ff79d5..2f8b193 100644 (file)
@@ -2532,7 +2532,7 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware
 
                if (rc && ((struct hwrm_err_output *)&resp)->cmd_err ==
                    NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) {
-                       install.flags |=
+                       install.flags =
                                cpu_to_le16(NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG);
 
                        rc = _hwrm_send_message_silent(bp, &install,
@@ -2546,6 +2546,7 @@ int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware
                                 * UPDATE directory and try the flash again
                                 */
                                defrag_attempted = true;
+                               install.flags = 0;
                                rc = __bnxt_flash_nvram(bp->dev,
                                                        BNX_DIR_TYPE_UPDATE,
                                                        BNX_DIR_ORDINAL_FIRST,
index 2d3e962..d5c6e6a 100644 (file)
@@ -2,7 +2,7 @@
  *
  * Copyright (c) 2014-2016 Broadcom Corporation
  * Copyright (c) 2014-2018 Broadcom Limited
- * Copyright (c) 2018-2020 Broadcom Inc.
+ * Copyright (c) 2018-2021 Broadcom Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -164,6 +164,7 @@ struct cmd_nums {
        #define HWRM_VNIC_PLCMODES_CFG                    0x48UL
        #define HWRM_VNIC_PLCMODES_QCFG                   0x49UL
        #define HWRM_VNIC_QCAPS                           0x4aUL
+       #define HWRM_VNIC_UPDATE                          0x4bUL
        #define HWRM_RING_ALLOC                           0x50UL
        #define HWRM_RING_FREE                            0x51UL
        #define HWRM_RING_CMPL_RING_QAGGINT_PARAMS        0x52UL
@@ -184,6 +185,9 @@ struct cmd_nums {
        #define HWRM_QUEUE_MPLS_QCAPS                     0x80UL
        #define HWRM_QUEUE_MPLSTC2PRI_QCFG                0x81UL
        #define HWRM_QUEUE_MPLSTC2PRI_CFG                 0x82UL
+       #define HWRM_QUEUE_VLANPRI_QCAPS                  0x83UL
+       #define HWRM_QUEUE_VLANPRI2PRI_QCFG               0x84UL
+       #define HWRM_QUEUE_VLANPRI2PRI_CFG                0x85UL
        #define HWRM_CFA_L2_FILTER_ALLOC                  0x90UL
        #define HWRM_CFA_L2_FILTER_FREE                   0x91UL
        #define HWRM_CFA_L2_FILTER_CFG                    0x92UL
@@ -217,6 +221,8 @@ struct cmd_nums {
        #define HWRM_PORT_TX_FIR_CFG                      0xbbUL
        #define HWRM_PORT_TX_FIR_QCFG                     0xbcUL
        #define HWRM_PORT_ECN_QSTATS                      0xbdUL
+       #define HWRM_FW_LIVEPATCH_QUERY                   0xbeUL
+       #define HWRM_FW_LIVEPATCH                         0xbfUL
        #define HWRM_FW_RESET                             0xc0UL
        #define HWRM_FW_QSTATUS                           0xc1UL
        #define HWRM_FW_HEALTH_CHECK                      0xc2UL
@@ -347,6 +353,8 @@ struct cmd_nums {
        #define HWRM_FUNC_HOST_PF_IDS_QUERY               0x197UL
        #define HWRM_FUNC_QSTATS_EXT                      0x198UL
        #define HWRM_STAT_EXT_CTX_QUERY                   0x199UL
+       #define HWRM_FUNC_SPD_CFG                         0x19aUL
+       #define HWRM_FUNC_SPD_QCFG                        0x19bUL
        #define HWRM_SELFTEST_QLIST                       0x200UL
        #define HWRM_SELFTEST_EXEC                        0x201UL
        #define HWRM_SELFTEST_IRQ                         0x202UL
@@ -359,6 +367,11 @@ struct cmd_nums {
        #define HWRM_MFG_HDMA_TEST                        0x209UL
        #define HWRM_MFG_FRU_EEPROM_WRITE                 0x20aUL
        #define HWRM_MFG_FRU_EEPROM_READ                  0x20bUL
+       #define HWRM_MFG_SOC_IMAGE                        0x20cUL
+       #define HWRM_MFG_SOC_QSTATUS                      0x20dUL
+       #define HWRM_MFG_PARAM_SEEPROM_SYNC               0x20eUL
+       #define HWRM_MFG_PARAM_SEEPROM_READ               0x20fUL
+       #define HWRM_MFG_PARAM_SEEPROM_HEALTH             0x210UL
        #define HWRM_TF                                   0x2bcUL
        #define HWRM_TF_VERSION_GET                       0x2bdUL
        #define HWRM_TF_SESSION_OPEN                      0x2c6UL
@@ -384,6 +397,7 @@ struct cmd_nums {
        #define HWRM_TF_EXT_EM_QCFG                       0x2e9UL
        #define HWRM_TF_EM_INSERT                         0x2eaUL
        #define HWRM_TF_EM_DELETE                         0x2ebUL
+       #define HWRM_TF_EM_HASH_INSERT                    0x2ecUL
        #define HWRM_TF_TCAM_SET                          0x2f8UL
        #define HWRM_TF_TCAM_GET                          0x2f9UL
        #define HWRM_TF_TCAM_MOVE                         0x2faUL
@@ -486,9 +500,9 @@ struct hwrm_err_output {
 #define HWRM_TARGET_ID_TOOLS 0xFFFD
 #define HWRM_VERSION_MAJOR 1
 #define HWRM_VERSION_MINOR 10
-#define HWRM_VERSION_UPDATE 1
-#define HWRM_VERSION_RSVD 68
-#define HWRM_VERSION_STR "1.10.1.68"
+#define HWRM_VERSION_UPDATE 2
+#define HWRM_VERSION_RSVD 11
+#define HWRM_VERSION_STR "1.10.2.11"
 
 /* hwrm_ver_get_input (size:192b/24B) */
 struct hwrm_ver_get_input {
@@ -563,8 +577,9 @@ struct hwrm_ver_get_output {
        __le16  max_resp_len;
        __le16  def_req_timeout;
        u8      flags;
-       #define VER_GET_RESP_FLAGS_DEV_NOT_RDY       0x1UL
-       #define VER_GET_RESP_FLAGS_EXT_VER_AVAIL     0x2UL
+       #define VER_GET_RESP_FLAGS_DEV_NOT_RDY                   0x1UL
+       #define VER_GET_RESP_FLAGS_EXT_VER_AVAIL                 0x2UL
+       #define VER_GET_RESP_FLAGS_DEV_NOT_RDY_BACKING_STORE     0x4UL
        u8      unused_0[2];
        u8      always_1;
        __le16  hwrm_intf_major;
@@ -708,6 +723,7 @@ struct hwrm_async_event_cmpl {
        #define ASYNC_EVENT_CMPL_EVENT_ID_QUIESCE_DONE               0x3fUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_DEFERRED_RESPONSE          0x40UL
        #define ASYNC_EVENT_CMPL_EVENT_ID_PFC_WATCHDOG_CFG_CHANGE    0x41UL
+       #define ASYNC_EVENT_CMPL_EVENT_ID_MAX_RGTR_EVENT_ID          0x42UL
        #define ASYNC_EVENT_CMPL_EVENT_ID_FW_TRACE_MSG               0xfeUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR                 0xffUL
        #define ASYNC_EVENT_CMPL_EVENT_ID_LAST                      ASYNC_EVENT_CMPL_EVENT_ID_HWRM_ERROR
@@ -815,6 +831,8 @@ struct hwrm_async_event_cmpl_reset_notify {
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_RESET_NOTIFY 0x8UL
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_LAST        ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_ID_RESET_NOTIFY
        __le32  event_data2;
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA2_FW_STATUS_CODE_MASK 0xffffUL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA2_FW_STATUS_CODE_SFT 0
        u8      opaque_v;
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_V          0x1UL
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_OPAQUE_MASK 0xfeUL
@@ -832,7 +850,8 @@ struct hwrm_async_event_cmpl_reset_notify {
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_MANAGEMENT_RESET_REQUEST  (0x1UL << 8)
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_FATAL        (0x2UL << 8)
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_NON_FATAL    (0x3UL << 8)
-       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_LAST                     ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FW_EXCEPTION_NON_FATAL
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FAST_RESET                (0x4UL << 8)
+       #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_LAST                     ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_REASON_CODE_FAST_RESET
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DELAY_IN_100MS_TICKS_MASK           0xffff0000UL
        #define ASYNC_EVENT_CMPL_RESET_NOTIFY_EVENT_DATA1_DELAY_IN_100MS_TICKS_SFT            16
 };
@@ -1271,6 +1290,10 @@ struct hwrm_func_qcaps_output {
        #define FUNC_QCAPS_RESP_FLAGS_EXT_TX_PROXY_SRC_INTF_OVERRIDE_SUPPORT     0x20UL
        #define FUNC_QCAPS_RESP_FLAGS_EXT_SCHQ_SUPPORTED                         0x40UL
        #define FUNC_QCAPS_RESP_FLAGS_EXT_PPP_PUSH_MODE_SUPPORTED                0x80UL
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_EVB_MODE_CFG_NOT_SUPPORTED             0x100UL
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_SOC_SPD_SUPPORTED                      0x200UL
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_FW_LIVEPATCH_SUPPORTED                 0x400UL
+       #define FUNC_QCAPS_RESP_FLAGS_EXT_FAST_RESET_CAPABLE                     0x800UL
        u8      max_schqs;
        u8      mpc_chnls_cap;
        #define FUNC_QCAPS_RESP_MPC_CHNLS_CAP_TCE         0x1UL
@@ -1315,6 +1338,7 @@ struct hwrm_func_qcfg_output {
        #define FUNC_QCFG_RESP_FLAGS_HOT_RESET_ALLOWED            0x200UL
        #define FUNC_QCFG_RESP_FLAGS_PPP_PUSH_MODE_ENABLED        0x400UL
        #define FUNC_QCFG_RESP_FLAGS_RING_MONITOR_ENABLED         0x800UL
+       #define FUNC_QCFG_RESP_FLAGS_FAST_RESET_ALLOWED           0x1000UL
        u8      mac_address[6];
        __le16  pci_id;
        __le16  alloc_rsscos_ctx;
@@ -1731,6 +1755,7 @@ struct hwrm_func_drv_rgtr_input {
        #define FUNC_DRV_RGTR_REQ_FLAGS_HOT_RESET_SUPPORT          0x10UL
        #define FUNC_DRV_RGTR_REQ_FLAGS_ERROR_RECOVERY_SUPPORT     0x20UL
        #define FUNC_DRV_RGTR_REQ_FLAGS_MASTER_SUPPORT             0x40UL
+       #define FUNC_DRV_RGTR_REQ_FLAGS_FAST_RESET_SUPPORT         0x80UL
        __le32  enables;
        #define FUNC_DRV_RGTR_REQ_ENABLES_OS_TYPE             0x1UL
        #define FUNC_DRV_RGTR_REQ_ENABLES_VER                 0x2UL
@@ -1993,7 +2018,7 @@ struct hwrm_func_backing_store_qcaps_input {
        __le64  resp_addr;
 };
 
-/* hwrm_func_backing_store_qcaps_output (size:640b/80B) */
+/* hwrm_func_backing_store_qcaps_output (size:704b/88B) */
 struct hwrm_func_backing_store_qcaps_output {
        __le16  error_code;
        __le16  req_type;
@@ -2024,13 +2049,25 @@ struct hwrm_func_backing_store_qcaps_output {
        __le16  mrav_num_entries_units;
        u8      tqm_entries_multiple;
        u8      ctx_kind_initializer;
-       __le32  rsvd;
-       __le16  rsvd1;
+       __le16  ctx_init_mask;
+       #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_QP       0x1UL
+       #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_SRQ      0x2UL
+       #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_CQ       0x4UL
+       #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_VNIC     0x8UL
+       #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_STAT     0x10UL
+       #define FUNC_BACKING_STORE_QCAPS_RESP_CTX_INIT_MASK_MRAV     0x20UL
+       u8      qp_init_offset;
+       u8      srq_init_offset;
+       u8      cq_init_offset;
+       u8      vnic_init_offset;
        u8      tqm_fp_rings_count;
+       u8      stat_init_offset;
+       u8      mrav_init_offset;
+       u8      rsvd[6];
        u8      valid;
 };
 
-/* hwrm_func_backing_store_cfg_input (size:2048b/256B) */
+/* hwrm_func_backing_store_cfg_input (size:2432b/304B) */
 struct hwrm_func_backing_store_cfg_input {
        __le16  req_type;
        __le16  cmpl_ring;
@@ -2041,22 +2078,25 @@ struct hwrm_func_backing_store_cfg_input {
        #define FUNC_BACKING_STORE_CFG_REQ_FLAGS_PREBOOT_MODE               0x1UL
        #define FUNC_BACKING_STORE_CFG_REQ_FLAGS_MRAV_RESERVATION_SPLIT     0x2UL
        __le32  enables;
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP            0x1UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_SRQ           0x2UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_CQ            0x4UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_VNIC          0x8UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_STAT          0x10UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP        0x20UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING0     0x40UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING1     0x80UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING2     0x100UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING3     0x200UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING4     0x400UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING5     0x800UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING6     0x1000UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING7     0x2000UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV          0x4000UL
-       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM           0x8000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_QP             0x1UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_SRQ            0x2UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_CQ             0x4UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_VNIC           0x8UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_STAT           0x10UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_SP         0x20UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING0      0x40UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING1      0x80UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING2      0x100UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING3      0x200UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING4      0x400UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING5      0x800UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING6      0x1000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING7      0x2000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_MRAV           0x4000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TIM            0x8000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING8      0x10000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING9      0x20000UL
+       #define FUNC_BACKING_STORE_CFG_REQ_ENABLES_TQM_RING10     0x40000UL
        u8      qpc_pg_size_qpc_lvl;
        #define FUNC_BACKING_STORE_CFG_REQ_QPC_LVL_MASK      0xfUL
        #define FUNC_BACKING_STORE_CFG_REQ_QPC_LVL_SFT       0
@@ -2358,6 +2398,63 @@ struct hwrm_func_backing_store_cfg_input {
        __le16  tqm_entry_size;
        __le16  mrav_entry_size;
        __le16  tim_entry_size;
+       u8      tqm_ring8_pg_size_tqm_ring_lvl;
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_MASK      0xfUL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_SFT       0
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_0       0x0UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_1       0x1UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_2       0x2UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LAST       FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_LVL_LVL_2
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_MASK  0xf0UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_SFT   4
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_4K   (0x0UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_8K   (0x1UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_64K  (0x2UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_2M   (0x3UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_8M   (0x4UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_1G   (0x5UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_LAST   FUNC_BACKING_STORE_CFG_REQ_RING8_TQM_RING_PG_SIZE_PG_1G
+       u8      ring8_unused[3];
+       __le32  tqm_ring8_num_entries;
+       __le64  tqm_ring8_page_dir;
+       u8      tqm_ring9_pg_size_tqm_ring_lvl;
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_MASK      0xfUL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_SFT       0
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_0       0x0UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_1       0x1UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_2       0x2UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LAST       FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_LVL_LVL_2
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_MASK  0xf0UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_SFT   4
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_4K   (0x0UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_8K   (0x1UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_64K  (0x2UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_2M   (0x3UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_8M   (0x4UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_1G   (0x5UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_LAST   FUNC_BACKING_STORE_CFG_REQ_RING9_TQM_RING_PG_SIZE_PG_1G
+       u8      ring9_unused[3];
+       __le32  tqm_ring9_num_entries;
+       __le64  tqm_ring9_page_dir;
+       u8      tqm_ring10_pg_size_tqm_ring_lvl;
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_MASK      0xfUL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_SFT       0
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_0       0x0UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_1       0x1UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_2       0x2UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LAST       FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_LVL_LVL_2
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_MASK  0xf0UL
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_SFT   4
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_4K   (0x0UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_8K   (0x1UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_64K  (0x2UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_2M   (0x3UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_8M   (0x4UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_1G   (0x5UL << 4)
+       #define FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_LAST   FUNC_BACKING_STORE_CFG_REQ_RING10_TQM_RING_PG_SIZE_PG_1G
+       u8      ring10_unused[3];
+       __le32  tqm_ring10_num_entries;
+       __le64  tqm_ring10_page_dir;
 };
 
 /* hwrm_func_backing_store_cfg_output (size:128b/16B) */
@@ -2930,6 +3027,7 @@ struct hwrm_port_phy_qcfg_output {
        #define PORT_PHY_QCFG_RESP_DUPLEX_STATE_LAST PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL
        u8      option_flags;
        #define PORT_PHY_QCFG_RESP_OPTION_FLAGS_MEDIA_AUTO_DETECT     0x1UL
+       #define PORT_PHY_QCFG_RESP_OPTION_FLAGS_SIGNAL_MODE_KNOWN     0x2UL
        char    phy_vendor_name[16];
        char    phy_vendor_partnumber[16];
        __le16  support_pam4_speeds;
@@ -3528,8 +3626,8 @@ struct hwrm_port_phy_qcaps_output {
        #define PORT_PHY_QCAPS_RESP_FLAGS_SHARED_PHY_CFG_SUPPORTED         0x8UL
        #define PORT_PHY_QCAPS_RESP_FLAGS_CUMULATIVE_COUNTERS_ON_RESET     0x10UL
        #define PORT_PHY_QCAPS_RESP_FLAGS_LOCAL_LPBK_NOT_SUPPORTED         0x20UL
-       #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_MASK                       0xc0UL
-       #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_SFT                        6
+       #define PORT_PHY_QCAPS_RESP_FLAGS_FW_MANAGED_LINK_DOWN             0x40UL
+       #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1                            0x80UL
        u8      port_cnt;
        #define PORT_PHY_QCAPS_RESP_PORT_CNT_UNKNOWN 0x0UL
        #define PORT_PHY_QCAPS_RESP_PORT_CNT_1       0x1UL
@@ -4119,7 +4217,10 @@ struct hwrm_queue_qportcfg_output {
        #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LOSSLESS_NIC   0x3UL
        #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN        0xffUL
        #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_LAST          QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_UNKNOWN
-       u8      unused_0;
+       u8      queue_id0_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID0_SERVICE_PROFILE_TYPE_CNP      0x4UL
        char    qid0_name[16];
        char    qid1_name[16];
        char    qid2_name[16];
@@ -4128,7 +4229,34 @@ struct hwrm_queue_qportcfg_output {
        char    qid5_name[16];
        char    qid6_name[16];
        char    qid7_name[16];
-       u8      unused_1[7];
+       u8      queue_id1_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID1_SERVICE_PROFILE_TYPE_CNP      0x4UL
+       u8      queue_id2_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID2_SERVICE_PROFILE_TYPE_CNP      0x4UL
+       u8      queue_id3_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID3_SERVICE_PROFILE_TYPE_CNP      0x4UL
+       u8      queue_id4_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID4_SERVICE_PROFILE_TYPE_CNP      0x4UL
+       u8      queue_id5_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID5_SERVICE_PROFILE_TYPE_CNP      0x4UL
+       u8      queue_id6_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID6_SERVICE_PROFILE_TYPE_CNP      0x4UL
+       u8      queue_id7_service_profile_type;
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_TYPE_ROCE     0x1UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_TYPE_NIC      0x2UL
+       #define QUEUE_QPORTCFG_RESP_QUEUE_ID7_SERVICE_PROFILE_TYPE_CNP      0x4UL
        u8      valid;
 };
 
@@ -5142,8 +5270,10 @@ struct hwrm_vnic_alloc_input {
        __le16  target_id;
        __le64  resp_addr;
        __le32  flags;
-       #define VNIC_ALLOC_REQ_FLAGS_DEFAULT     0x1UL
-       u8      unused_0[4];
+       #define VNIC_ALLOC_REQ_FLAGS_DEFAULT                  0x1UL
+       #define VNIC_ALLOC_REQ_FLAGS_VIRTIO_NET_FID_VALID     0x2UL
+       __le16  virtio_net_fid;
+       u8      unused_0[2];
 };
 
 /* hwrm_vnic_alloc_output (size:128b/16B) */
@@ -5260,6 +5390,8 @@ struct hwrm_vnic_qcaps_output {
        #define VNIC_QCAPS_RESP_FLAGS_OUTERMOST_RSS_CAP                   0x80UL
        #define VNIC_QCAPS_RESP_FLAGS_COS_ASSIGNMENT_CAP                  0x100UL
        #define VNIC_QCAPS_RESP_FLAGS_RX_CMPL_V2_CAP                      0x200UL
+       #define VNIC_QCAPS_RESP_FLAGS_VNIC_STATE_CAP                      0x400UL
+       #define VNIC_QCAPS_RESP_FLAGS_VIRTIO_NET_VNIC_ALLOC_CAP           0x800UL
        __le16  max_aggs_supported;
        u8      unused_1[5];
        u8      valid;
@@ -5585,7 +5717,11 @@ struct hwrm_ring_alloc_output {
        __le16  resp_len;
        __le16  ring_id;
        __le16  logical_ring_id;
-       u8      unused_0[3];
+       u8      push_buffer_index;
+       #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL
+       #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL
+       #define RING_ALLOC_RESP_PUSH_BUFFER_INDEX_LAST       RING_ALLOC_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER
+       u8      unused_0[2];
        u8      valid;
 };
 
@@ -5644,7 +5780,11 @@ struct hwrm_ring_reset_output {
        __le16  req_type;
        __le16  seq_id;
        __le16  resp_len;
-       u8      unused_0[4];
+       u8      push_buffer_index;
+       #define RING_RESET_RESP_PUSH_BUFFER_INDEX_PING_BUFFER 0x0UL
+       #define RING_RESET_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER 0x1UL
+       #define RING_RESET_RESP_PUSH_BUFFER_INDEX_LAST       RING_RESET_RESP_PUSH_BUFFER_INDEX_PONG_BUFFER
+       u8      unused_0[3];
        u8      consumer_idx[3];
        u8      valid;
 };
@@ -6988,21 +7128,23 @@ struct hwrm_cfa_adv_flow_mgnt_qcaps_output {
        __le16  seq_id;
        __le16  resp_len;
        __le32  flags;
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_16BIT_SUPPORTED                  0x1UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_64BIT_SUPPORTED                  0x2UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_BATCH_DELETE_SUPPORTED               0x4UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_RESET_ALL_SUPPORTED                  0x8UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_DEST_FUNC_SUPPORTED           0x10UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TX_EEM_FLOW_SUPPORTED                     0x20UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RX_EEM_FLOW_SUPPORTED                     0x40UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_COUNTER_ALLOC_SUPPORTED              0x80UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_SUPPORTED                0x100UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_UNTAGGED_VLAN_SUPPORTED                   0x200UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_XDP_SUPPORTED                             0x400UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_HEADER_SOURCE_FIELDS_SUPPORTED         0x800UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ARP_SUPPORTED              0x1000UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED             0x2000UL
-       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ETHERTYPE_IP_SUPPORTED     0x4000UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_16BIT_SUPPORTED                     0x1UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_HND_64BIT_SUPPORTED                     0x2UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_BATCH_DELETE_SUPPORTED                  0x4UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_RESET_ALL_SUPPORTED                     0x8UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_DEST_FUNC_SUPPORTED              0x10UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TX_EEM_FLOW_SUPPORTED                        0x20UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RX_EEM_FLOW_SUPPORTED                        0x40UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_FLOW_COUNTER_ALLOC_SUPPORTED                 0x80UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_SUPPORTED                   0x100UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_UNTAGGED_VLAN_SUPPORTED                      0x200UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_XDP_SUPPORTED                                0x400UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_HEADER_SOURCE_FIELDS_SUPPORTED            0x800UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ARP_SUPPORTED                 0x1000UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_RFS_RING_TBL_IDX_V2_SUPPORTED                0x2000UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_NTUPLE_FLOW_RX_ETHERTYPE_IP_SUPPORTED        0x4000UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_TRUFLOW_CAPABLE                              0x8000UL
+       #define CFA_ADV_FLOW_MGNT_QCAPS_RESP_FLAGS_L2_FILTER_TRAFFIC_TYPE_L2_ROCE_SUPPORTED     0x10000UL
        u8      unused_0[3];
        u8      valid;
 };
@@ -7472,7 +7614,8 @@ struct hwrm_struct_hdr {
        #define STRUCT_HDR_STRUCT_ID_AFM_OPAQUE         0x1UL
        #define STRUCT_HDR_STRUCT_ID_PORT_DESCRIPTION   0xaUL
        #define STRUCT_HDR_STRUCT_ID_RSS_V2             0x64UL
-       #define STRUCT_HDR_STRUCT_ID_LAST              STRUCT_HDR_STRUCT_ID_RSS_V2
+       #define STRUCT_HDR_STRUCT_ID_MSIX_PER_VF        0xc8UL
+       #define STRUCT_HDR_STRUCT_ID_LAST              STRUCT_HDR_STRUCT_ID_MSIX_PER_VF
        __le16  len;
        u8      version;
        u8      count;
@@ -8000,6 +8143,9 @@ struct hwrm_dbg_coredump_initiate_output {
 struct coredump_data_hdr {
        __le32  address;
        __le32  flags_length;
+       #define COREDUMP_DATA_HDR_FLAGS_LENGTH_ACTUAL_LEN_MASK     0xffffffUL
+       #define COREDUMP_DATA_HDR_FLAGS_LENGTH_ACTUAL_LEN_SFT      0
+       #define COREDUMP_DATA_HDR_FLAGS_LENGTH_INDIRECT_ACCESS     0x1000000UL
        __le32  instance;
        __le32  next_offset;
 };
@@ -8669,7 +8815,6 @@ struct hcomm_status {
        #define HCOMM_STATUS_TRUE_OFFSET_MASK        0xfffffffcUL
        #define HCOMM_STATUS_TRUE_OFFSET_SFT         2
 };
-
 #define HCOMM_STATUS_STRUCT_LOC 0x31001F0UL
 
 #endif /* _BNXT_HSI_H_ */
index 8c8368c..64dbbb0 100644 (file)
@@ -222,8 +222,12 @@ int bnxt_get_ulp_msix_base(struct bnxt *bp)
 
 int bnxt_get_ulp_stat_ctxs(struct bnxt *bp)
 {
-       if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP))
-               return BNXT_MIN_ROCE_STAT_CTXS;
+       if (bnxt_ulp_registered(bp->edev, BNXT_ROCE_ULP)) {
+               struct bnxt_en_dev *edev = bp->edev;
+
+               if (edev->ulp_tbl[BNXT_ROCE_ULP].msix_requested)
+                       return BNXT_MIN_ROCE_STAT_CTXS;
+       }
 
        return 0;
 }
index fcc2620..6413038 100644 (file)
@@ -133,12 +133,9 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
        dma_sync_single_for_cpu(&pdev->dev, mapping + offset, *len, bp->rx_dir);
 
        txr = rxr->bnapi->tx_ring;
-       xdp.data_hard_start = *data_ptr - offset;
-       xdp.data = *data_ptr;
-       xdp_set_data_meta_invalid(&xdp);
-       xdp.data_end = *data_ptr + *len;
-       xdp.rxq = &rxr->xdp_rxq;
-       xdp.frame_sz = PAGE_SIZE; /* BNXT_RX_PAGE_MODE(bp) when XDP enabled */
+       /* BNXT_RX_PAGE_MODE(bp) when XDP enabled */
+       xdp_init_buff(&xdp, PAGE_SIZE, &rxr->xdp_rxq);
+       xdp_prepare_buff(&xdp, *data_ptr - offset, offset, *len, false);
        orig_data = xdp.data;
 
        rcu_read_lock();
index f6ca01d..0a6d91b 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/dim.h>
 #include <linux/ethtool.h>
 
+#include "../unimac.h"
+
 /* total number of Buffer Descriptors, same for Rx/Tx */
 #define TOTAL_DESC                             256
 
@@ -150,63 +152,6 @@ struct bcmgenet_mib_counters {
        u32     tx_realloc_tsb_failed;
 };
 
-#define UMAC_HD_BKP_CTRL               0x004
-#define         HD_FC_EN                       (1 << 0)
-#define  HD_FC_BKOFF_OK                        (1 << 1)
-#define  IPG_CONFIG_RX_SHIFT           2
-#define  IPG_CONFIG_RX_MASK            0x1F
-
-#define UMAC_CMD                       0x008
-#define  CMD_TX_EN                     (1 << 0)
-#define  CMD_RX_EN                     (1 << 1)
-#define  UMAC_SPEED_10                 0
-#define  UMAC_SPEED_100                        1
-#define  UMAC_SPEED_1000               2
-#define  UMAC_SPEED_2500               3
-#define  CMD_SPEED_SHIFT               2
-#define  CMD_SPEED_MASK                        3
-#define  CMD_PROMISC                   (1 << 4)
-#define  CMD_PAD_EN                    (1 << 5)
-#define  CMD_CRC_FWD                   (1 << 6)
-#define  CMD_PAUSE_FWD                 (1 << 7)
-#define  CMD_RX_PAUSE_IGNORE           (1 << 8)
-#define  CMD_TX_ADDR_INS               (1 << 9)
-#define  CMD_HD_EN                     (1 << 10)
-#define  CMD_SW_RESET                  (1 << 13)
-#define  CMD_LCL_LOOP_EN               (1 << 15)
-#define  CMD_AUTO_CONFIG               (1 << 22)
-#define  CMD_CNTL_FRM_EN               (1 << 23)
-#define  CMD_NO_LEN_CHK                        (1 << 24)
-#define  CMD_RMT_LOOP_EN               (1 << 25)
-#define  CMD_PRBL_EN                   (1 << 27)
-#define  CMD_TX_PAUSE_IGNORE           (1 << 28)
-#define  CMD_TX_RX_EN                  (1 << 29)
-#define  CMD_RUNT_FILTER_DIS           (1 << 30)
-
-#define UMAC_MAC0                      0x00C
-#define UMAC_MAC1                      0x010
-#define UMAC_MAX_FRAME_LEN             0x014
-
-#define UMAC_MODE                      0x44
-#define  MODE_LINK_STATUS              (1 << 5)
-
-#define UMAC_EEE_CTRL                  0x064
-#define  EN_LPI_RX_PAUSE               (1 << 0)
-#define  EN_LPI_TX_PFC                 (1 << 1)
-#define  EN_LPI_TX_PAUSE               (1 << 2)
-#define  EEE_EN                                (1 << 3)
-#define  RX_FIFO_CHECK                 (1 << 4)
-#define  EEE_TX_CLK_DIS                        (1 << 5)
-#define  DIS_EEE_10M                   (1 << 6)
-#define  LP_IDLE_PREDICTION_MODE       (1 << 7)
-
-#define UMAC_EEE_LPI_TIMER             0x068
-#define UMAC_EEE_WAKE_TIMER            0x06C
-#define UMAC_EEE_REF_COUNT             0x070
-#define  EEE_REFERENCE_COUNT_MASK      0xffff
-
-#define UMAC_TX_FLUSH                  0x334
-
 #define UMAC_MIB_START                 0x400
 
 #define UMAC_MDIO_CMD                  0x614
index 6fb6c35..17f997e 100644 (file)
@@ -63,11 +63,11 @@ void bcmgenet_mii_setup(struct net_device *dev)
 
                /* speed */
                if (phydev->speed == SPEED_1000)
-                       cmd_bits = UMAC_SPEED_1000;
+                       cmd_bits = CMD_SPEED_1000;
                else if (phydev->speed == SPEED_100)
-                       cmd_bits = UMAC_SPEED_100;
+                       cmd_bits = CMD_SPEED_100;
                else
-                       cmd_bits = UMAC_SPEED_10;
+                       cmd_bits = CMD_SPEED_10;
                cmd_bits <<= CMD_SPEED_SHIFT;
 
                /* duplex */
index 5143cdd..8936c2b 100644 (file)
@@ -12826,11 +12826,13 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
 
                        offset = tg3_nvram_logical_addr(tp, offset);
                }
-       }
 
-       if (!offset || !len) {
-               offset = TG3_NVM_VPD_OFF;
-               len = TG3_NVM_VPD_LEN;
+               if (!offset || !len) {
+                       offset = TG3_NVM_VPD_OFF;
+                       len = TG3_NVM_VPD_LEN;
+               }
+       } else {
+               len = TG3_NVM_PCI_VPD_MAX_LEN;
        }
 
        buf = kmalloc(len, GFP_KERNEL);
@@ -12846,26 +12848,16 @@ static __be32 *tg3_vpd_readblock(struct tg3 *tp, u32 *vpdlen)
                        if (tg3_nvram_read_be32(tp, offset + i, &buf[i/4]))
                                goto error;
                }
+               *vpdlen = len;
        } else {
-               u8 *ptr;
                ssize_t cnt;
-               unsigned int pos = 0;
-
-               ptr = (u8 *)&buf[0];
-               for (i = 0; pos < len && i < 3; i++, pos += cnt, ptr += cnt) {
-                       cnt = pci_read_vpd(tp->pdev, pos,
-                                          len - pos, ptr);
-                       if (cnt == -ETIMEDOUT || cnt == -EINTR)
-                               cnt = 0;
-                       else if (cnt < 0)
-                               goto error;
-               }
-               if (pos != len)
+
+               cnt = pci_read_vpd(tp->pdev, 0, len, (u8 *)buf);
+               if (cnt < 0)
                        goto error;
+               *vpdlen = cnt;
        }
 
-       *vpdlen = len;
-
        return buf;
 
 error:
index 1000c89..46ec4fd 100644 (file)
 /* Hardware Legacy NVRAM layout */
 #define TG3_NVM_VPD_OFF                        0x100
 #define TG3_NVM_VPD_LEN                        256
+#define TG3_NVM_PCI_VPD_MAX_LEN                512
 
 /* Hardware Selfboot NVRAM layout */
 #define TG3_NVM_HWSB_CFG1              0x00000004
diff --git a/drivers/net/ethernet/broadcom/unimac.h b/drivers/net/ethernet/broadcom/unimac.h
new file mode 100644 (file)
index 0000000..585a852
--- /dev/null
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __UNIMAC_H
+#define __UNIMAC_H
+
+#define UMAC_HD_BKP_CTRL               0x004
+#define  HD_FC_EN                      (1 << 0)
+#define  HD_FC_BKOFF_OK                        (1 << 1)
+#define  IPG_CONFIG_RX_SHIFT           2
+#define  IPG_CONFIG_RX_MASK            0x1F
+#define UMAC_CMD                       0x008
+#define  CMD_TX_EN                     (1 << 0)
+#define  CMD_RX_EN                     (1 << 1)
+#define  CMD_SPEED_10                  0
+#define  CMD_SPEED_100                 1
+#define  CMD_SPEED_1000                        2
+#define  CMD_SPEED_2500                        3
+#define  CMD_SPEED_SHIFT               2
+#define  CMD_SPEED_MASK                        3
+#define  CMD_PROMISC                   (1 << 4)
+#define  CMD_PAD_EN                    (1 << 5)
+#define  CMD_CRC_FWD                   (1 << 6)
+#define  CMD_PAUSE_FWD                 (1 << 7)
+#define  CMD_RX_PAUSE_IGNORE           (1 << 8)
+#define  CMD_TX_ADDR_INS               (1 << 9)
+#define  CMD_HD_EN                     (1 << 10)
+#define  CMD_SW_RESET_OLD              (1 << 11)
+#define  CMD_SW_RESET                  (1 << 13)
+#define  CMD_LCL_LOOP_EN               (1 << 15)
+#define  CMD_AUTO_CONFIG               (1 << 22)
+#define  CMD_CNTL_FRM_EN               (1 << 23)
+#define  CMD_NO_LEN_CHK                        (1 << 24)
+#define  CMD_RMT_LOOP_EN               (1 << 25)
+#define  CMD_RX_ERR_DISC               (1 << 26)
+#define  CMD_PRBL_EN                   (1 << 27)
+#define  CMD_TX_PAUSE_IGNORE           (1 << 28)
+#define  CMD_TX_RX_EN                  (1 << 29)
+#define  CMD_RUNT_FILTER_DIS           (1 << 30)
+#define UMAC_MAC0                      0x00c
+#define UMAC_MAC1                      0x010
+#define UMAC_MAX_FRAME_LEN             0x014
+#define UMAC_PAUSE_QUANTA              0x018
+#define UMAC_MODE                      0x044
+#define  MODE_LINK_STATUS              (1 << 5)
+#define UMAC_FRM_TAG0                  0x048           /* outer tag */
+#define UMAC_FRM_TAG1                  0x04c           /* inner tag */
+#define UMAC_TX_IPG_LEN                        0x05c
+#define UMAC_EEE_CTRL                  0x064
+#define  EN_LPI_RX_PAUSE               (1 << 0)
+#define  EN_LPI_TX_PFC                 (1 << 1)
+#define  EN_LPI_TX_PAUSE               (1 << 2)
+#define  EEE_EN                                (1 << 3)
+#define  RX_FIFO_CHECK                 (1 << 4)
+#define  EEE_TX_CLK_DIS                        (1 << 5)
+#define  DIS_EEE_10M                   (1 << 6)
+#define  LP_IDLE_PREDICTION_MODE       (1 << 7)
+#define UMAC_EEE_LPI_TIMER             0x068
+#define UMAC_EEE_WAKE_TIMER            0x06C
+#define UMAC_EEE_REF_COUNT             0x070
+#define  EEE_REFERENCE_COUNT_MASK      0xffff
+#define UMAC_RX_IPG_INV                        0x078
+#define UMAC_MACSEC_PROG_TX_CRC                0x310
+#define UMAC_MACSEC_CTRL               0x314
+#define UMAC_PAUSE_CTRL                        0x330
+#define UMAC_TX_FLUSH                  0x334
+#define UMAC_RX_FIFO_STATUS            0x338
+#define UMAC_TX_FIFO_STATUS            0x33c
+
+#endif
index d5d9109..472bf8f 100644 (file)
@@ -467,7 +467,11 @@ static void macb_set_tx_clk(struct macb *bp, int speed)
 {
        long ferr, rate, rate_rounded;
 
-       if (!bp->tx_clk || !(bp->caps & MACB_CAPS_CLK_HW_CHG))
+       if (!bp->tx_clk || (bp->caps & MACB_CAPS_CLK_HW_CHG))
+               return;
+
+       /* In case of MII the PHY is the clock master */
+       if (bp->phy_interface == PHY_INTERFACE_MODE_MII)
                return;
 
        switch (speed) {
index 37d0641..2a0d64e 100644 (file)
@@ -1163,7 +1163,7 @@ int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs)
                        oct->flags |= LIO_FLAG_MSI_ENABLED;
 
                /* allocate storage for the names assigned to the irq */
-               oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL);
+               oct->irq_name_storage = kzalloc(INTRNAMSIZ, GFP_KERNEL);
                if (!oct->irq_name_storage)
                        return -ENOMEM;
 
index 7d00d3a..7c5af4b 100644 (file)
@@ -3219,8 +3219,6 @@ static const struct net_device_ops lionetdevops = {
        .ndo_do_ioctl           = liquidio_ioctl,
        .ndo_fix_features       = liquidio_fix_features,
        .ndo_set_features       = liquidio_set_features,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_set_vf_mac         = liquidio_set_vf_mac,
        .ndo_set_vf_vlan        = liquidio_set_vf_vlan,
        .ndo_get_vf_config      = liquidio_get_vf_config,
index 103440f..516f166 100644 (file)
@@ -1879,8 +1879,6 @@ static const struct net_device_ops lionetdevops = {
        .ndo_do_ioctl           = liquidio_ioctl,
        .ndo_fix_features       = liquidio_fix_features,
        .ndo_set_features       = liquidio_set_features,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
 };
 
 static int lio_nic_info(struct octeon_recv_info *recv_info, void *buf)
index 387a57c..e159194 100644 (file)
@@ -545,7 +545,7 @@ static atomic_t adapter_fw_states[MAX_OCTEON_DEVICES];
 
 static u32 octeon_device_count;
 /* locks device array (i.e. octeon_device[]) */
-static spinlock_t octeon_devices_lock;
+static DEFINE_SPINLOCK(octeon_devices_lock);
 
 static struct octeon_core_setup core_setup[MAX_OCTEON_DEVICES];
 
@@ -563,7 +563,6 @@ void octeon_init_device_list(int conf_type)
        memset(octeon_device, 0, (sizeof(void *) * MAX_OCTEON_DEVICES));
        for (i = 0; i <  MAX_OCTEON_DEVICES; i++)
                oct_set_config_info(i, conf_type);
-       spin_lock_init(&octeon_devices_lock);
 }
 
 static void *__retrieve_octeon_config_info(struct octeon_device *oct,
index 5e50bb1..ecffebd 100644 (file)
@@ -1556,18 +1556,7 @@ static struct platform_driver octeon_mgmt_driver = {
        .remove         = octeon_mgmt_remove,
 };
 
-static int __init octeon_mgmt_mod_init(void)
-{
-       return platform_driver_register(&octeon_mgmt_driver);
-}
-
-static void __exit octeon_mgmt_mod_exit(void)
-{
-       platform_driver_unregister(&octeon_mgmt_driver);
-}
-
-module_init(octeon_mgmt_mod_init);
-module_exit(octeon_mgmt_mod_exit);
+module_platform_driver(octeon_mgmt_driver);
 
 MODULE_SOFTDEP("pre: mdio-cavium");
 MODULE_DESCRIPTION(DRV_DESCRIPTION);
index f3b7b44..c33b4e8 100644 (file)
@@ -530,6 +530,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog,
                                struct cqe_rx_t *cqe_rx, struct snd_queue *sq,
                                struct rcv_queue *rq, struct sk_buff **skb)
 {
+       unsigned char *hard_start, *data;
        struct xdp_buff xdp;
        struct page *page;
        u32 action;
@@ -547,12 +548,11 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog,
        cpu_addr = (u64)phys_to_virt(cpu_addr);
        page = virt_to_page((void *)cpu_addr);
 
-       xdp.data_hard_start = page_address(page);
-       xdp.data = (void *)cpu_addr;
-       xdp_set_data_meta_invalid(&xdp);
-       xdp.data_end = xdp.data + len;
-       xdp.rxq = &rq->xdp_rxq;
-       xdp.frame_sz = RCV_FRAG_LEN + XDP_PACKET_HEADROOM;
+       xdp_init_buff(&xdp, RCV_FRAG_LEN + XDP_PACKET_HEADROOM,
+                     &rq->xdp_rxq);
+       hard_start = page_address(page);
+       data = (unsigned char *)cpu_addr;
+       xdp_prepare_buff(&xdp, hard_start, data - hard_start, len, false);
        orig_data = xdp.data;
 
        rcu_read_lock();
index 7fd264a..9f1965c 100644 (file)
@@ -3882,8 +3882,6 @@ static const struct net_device_ops cxgb4_netdev_ops = {
 #endif /* CONFIG_CHELSIO_T4_FCOE */
        .ndo_set_tx_maxrate   = cxgb_set_tx_maxrate,
        .ndo_setup_tc         = cxgb_setup_tc,
-       .ndo_udp_tunnel_add   = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del   = udp_tunnel_nic_del_port,
        .ndo_features_check   = cxgb_features_check,
        .ndo_fix_features     = cxgb_fix_features,
 };
@@ -5139,7 +5137,7 @@ static int adap_init0(struct adapter *adap, int vpd_skip)
 
        /* See if FW supports FW_FILTER2 work request */
        if (is_t4(adap->params.chip)) {
-               adap->params.filter2_wr_support = 0;
+               adap->params.filter2_wr_support = false;
        } else {
                params[0] = FW_PARAM_DEV(FILTER2_WR);
                ret = t4_query_params(adap, adap->mbox, adap->pf, 0,
index 196652a..550cc06 100644 (file)
@@ -1600,7 +1600,8 @@ static netdev_tx_t cxgb4_eth_xmit(struct sk_buff *skb, struct net_device *dev)
                 * has opened up.
                 */
                eth_txq_stop(q);
-               wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
+               if (chip_ver > CHELSIO_T5)
+                       wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
        }
 
        wr = (void *)&q->q.desc[q->q.pidx];
@@ -1832,6 +1833,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb,
        struct adapter *adapter;
        int qidx, credits, ret;
        size_t fw_hdr_copy_len;
+       unsigned int chip_ver;
        u64 cntrl, *end;
        u32 wr_mid;
 
@@ -1896,6 +1898,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb,
                goto out_free;
        }
 
+       chip_ver = CHELSIO_CHIP_VERSION(adapter->params.chip);
        wr_mid = FW_WR_LEN16_V(DIV_ROUND_UP(flits, 2));
        if (unlikely(credits < ETHTXQ_STOP_THRES)) {
                /* After we're done injecting the Work Request for this
@@ -1907,7 +1910,8 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb,
                 * has opened up.
                 */
                eth_txq_stop(txq);
-               wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
+               if (chip_ver > CHELSIO_T5)
+                       wr_mid |= FW_WR_EQUEQ_F | FW_WR_EQUIQ_F;
        }
 
        /* Start filling in our Work Request.  Note that we do _not_ handle
@@ -1960,7 +1964,7 @@ static netdev_tx_t cxgb4_vf_eth_xmit(struct sk_buff *skb,
                 */
                cpl = (void *)(lso + 1);
 
-               if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5)
+               if (chip_ver <= CHELSIO_T5)
                        cntrl = TXPKT_ETHHDR_LEN_V(eth_xtra_len);
                else
                        cntrl = T6_TXPKT_ETHHDR_LEN_V(eth_xtra_len);
@@ -3598,6 +3602,25 @@ static void t4_tx_completion_handler(struct sge_rspq *rspq,
        }
 
        txq = &s->ethtxq[pi->first_qset + rspq->idx];
+
+       /* We've got the Hardware Consumer Index Update in the Egress Update
+        * message. These Egress Update messages will be our sole CIDX Updates
+        * we get since we don't want to chew up PCIe bandwidth for both Ingress
+        * Messages and Status Page writes.  However, The code which manages
+        * reclaiming successfully DMA'ed TX Work Requests uses the CIDX value
+        * stored in the Status Page at the end of the TX Queue.  It's easiest
+        * to simply copy the CIDX Update value from the Egress Update message
+        * to the Status Page.  Also note that no Endian issues need to be
+        * considered here since both are Big Endian and we're just copying
+        * bytes consistently ...
+        */
+       if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) {
+               struct cpl_sge_egr_update *egr;
+
+               egr = (struct cpl_sge_egr_update *)rsp;
+               WRITE_ONCE(txq->q.stat->cidx, egr->cidx);
+       }
+
        t4_sge_eth_txq_egress_update(adapter, txq, -1);
 }
 
@@ -4583,11 +4606,15 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
         * write the CIDX Updates into the Status Page at the end of the
         * TX Queue.
         */
-       c.autoequiqe_to_viid = htonl(FW_EQ_ETH_CMD_AUTOEQUEQE_F |
+       c.autoequiqe_to_viid = htonl(((chip_ver <= CHELSIO_T5) ?
+                                     FW_EQ_ETH_CMD_AUTOEQUIQE_F :
+                                     FW_EQ_ETH_CMD_AUTOEQUEQE_F) |
                                     FW_EQ_ETH_CMD_VIID_V(pi->viid));
 
        c.fetchszm_to_iqid =
-               htonl(FW_EQ_ETH_CMD_HOSTFCMODE_V(HOSTFCMODE_STATUS_PAGE_X) |
+               htonl(FW_EQ_ETH_CMD_HOSTFCMODE_V((chip_ver <= CHELSIO_T5) ?
+                                                HOSTFCMODE_INGRESS_QUEUE_X :
+                                                HOSTFCMODE_STATUS_PAGE_X) |
                      FW_EQ_ETH_CMD_PCIECHN_V(pi->tx_chan) |
                      FW_EQ_ETH_CMD_FETCHRO_F | FW_EQ_ETH_CMD_IQID_V(iqid));
 
@@ -4598,6 +4625,7 @@ int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
                                            : FETCHBURSTMIN_64B_T6_X) |
                      FW_EQ_ETH_CMD_FBMAX_V(FETCHBURSTMAX_512B_X) |
                      FW_EQ_ETH_CMD_CIDXFTHRESH_V(CIDXFLUSHTHRESH_32_X) |
+                     FW_EQ_ETH_CMD_CIDXFTHRESHO_V(chip_ver == CHELSIO_T5) |
                      FW_EQ_ETH_CMD_EQSIZE_V(nentries));
 
        c.eqaddr = cpu_to_be64(txq->q.phys_addr);
index 98d01a7..98829e4 100644 (file)
@@ -2689,7 +2689,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size)
 #define VPD_BASE           0x400
 #define VPD_BASE_OLD       0
 #define VPD_LEN            1024
-#define CHELSIO_VPD_UNIQUE_ID 0x82
 
 /**
  * t4_eeprom_ptov - translate a physical EEPROM address to virtual
@@ -2745,7 +2744,7 @@ int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p)
 {
        int i, ret = 0, addr;
        int ec, sn, pn, na;
-       u8 *vpd, csum;
+       u8 *vpd, csum, base_val = 0;
        unsigned int vpdr_len, kw_offset, id_len;
 
        vpd = vmalloc(VPD_LEN);
@@ -2755,17 +2754,11 @@ int t4_get_raw_vpd_params(struct adapter *adapter, struct vpd_params *p)
        /* Card information normally starts at VPD_BASE but early cards had
         * it at 0.
         */
-       ret = pci_read_vpd(adapter->pdev, VPD_BASE, sizeof(u32), vpd);
+       ret = pci_read_vpd(adapter->pdev, VPD_BASE, 1, &base_val);
        if (ret < 0)
                goto out;
 
-       /* The VPD shall have a unique identifier specified by the PCI SIG.
-        * For chelsio adapters, the identifier is 0x82. The first byte of a VPD
-        * shall be CHELSIO_VPD_UNIQUE_ID (0x82). The VPD programming software
-        * is expected to automatically put this entry at the
-        * beginning of the VPD.
-        */
-       addr = *vpd == CHELSIO_VPD_UNIQUE_ID ? VPD_BASE : VPD_BASE_OLD;
+       addr = base_val == PCI_VPD_LRDT_ID_STRING ? VPD_BASE : VPD_BASE_OLD;
 
        ret = pci_read_vpd(adapter->pdev, addr, VPD_LEN, vpd);
        if (ret < 0)
index 92473dd..22a0220 100644 (file)
 #define TCB_L2T_IX_M           0xfffULL
 #define TCB_L2T_IX_V(x)                ((x) << TCB_L2T_IX_S)
 
+#define TCB_T_FLAGS_W           1
+#define TCB_T_FLAGS_S           0
+#define TCB_T_FLAGS_M           0xffffffffffffffffULL
+#define TCB_T_FLAGS_V(x)        ((__u64)(x) << TCB_T_FLAGS_S)
+
+#define TCB_FIELD_COOKIE_TFLAG 1
+
 #define TCB_SMAC_SEL_W         0
 #define TCB_SMAC_SEL_S         24
 #define TCB_SMAC_SEL_M         0xffULL
index 47d9268..5855905 100644 (file)
@@ -92,9 +92,6 @@ static const struct xfrmdev_ops ch_ipsec_xfrmdev_ops = {
 
 static struct cxgb4_uld_info ch_ipsec_uld_info = {
        .name = CHIPSEC_DRV_MODULE_NAME,
-       .nrxq = MAX_ULD_QSETS,
-       /* Max ntxq will be derived from fw config file*/
-       .rxq_size = 1024,
        .add = ch_ipsec_uld_add,
        .state_change = ch_ipsec_uld_state_change,
        .tx_handler = ch_ipsec_xmit,
index 72bb123..9e23780 100644 (file)
@@ -575,7 +575,11 @@ int send_tx_flowc_wr(struct sock *sk, int compl,
 void chtls_tcp_push(struct sock *sk, int flags);
 int chtls_push_frames(struct chtls_sock *csk, int comp);
 int chtls_set_tcb_tflag(struct sock *sk, unsigned int bit_pos, int val);
+void chtls_set_tcb_field_rpl_skb(struct sock *sk, u16 word,
+                                u64 mask, u64 val, u8 cookie,
+                                int through_l2t);
 int chtls_setkey(struct chtls_sock *csk, u32 keylen, u32 mode, int cipher_type);
+void chtls_set_quiesce_ctrl(struct sock *sk, int val);
 void skb_entail(struct sock *sk, struct sk_buff *skb, int flags);
 unsigned int keyid_to_addr(int start_addr, int keyid);
 void free_tls_keyid(struct sock *sk);
index a0e0d8a..19dc7dc 100644 (file)
@@ -32,6 +32,7 @@
 #include "chtls.h"
 #include "chtls_cm.h"
 #include "clip_tbl.h"
+#include "t4_tcb.h"
 
 /*
  * State transitions and actions for close.  Note that if we are in SYN_SENT
@@ -267,7 +268,9 @@ static void chtls_send_reset(struct sock *sk, int mode, struct sk_buff *skb)
        if (sk->sk_state != TCP_SYN_RECV)
                chtls_send_abort(sk, mode, skb);
        else
-               goto out;
+               chtls_set_tcb_field_rpl_skb(sk, TCB_T_FLAGS_W,
+                                           TCB_T_FLAGS_V(TCB_T_FLAGS_M), 0,
+                                           TCB_FIELD_COOKIE_TFLAG, 1);
 
        return;
 out:
@@ -621,7 +624,7 @@ static void chtls_reset_synq(struct listen_ctx *listen_ctx)
 
        while (!skb_queue_empty(&listen_ctx->synq)) {
                struct chtls_sock *csk =
-                       container_of((struct synq *)__skb_dequeue
+                       container_of((struct synq *)skb_peek
                                (&listen_ctx->synq), struct chtls_sock, synq);
                struct sock *child = csk->sk;
 
@@ -1109,6 +1112,7 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
                                    const struct cpl_pass_accept_req *req,
                                    struct chtls_dev *cdev)
 {
+       struct adapter *adap = pci_get_drvdata(cdev->pdev);
        struct neighbour *n = NULL;
        struct inet_sock *newinet;
        const struct iphdr *iph;
@@ -1118,9 +1122,10 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
        struct dst_entry *dst;
        struct tcp_sock *tp;
        struct sock *newsk;
+       bool found = false;
        u16 port_id;
        int rxq_idx;
-       int step;
+       int step, i;
 
        iph = (const struct iphdr *)network_hdr;
        newsk = tcp_create_openreq_child(lsk, oreq, cdev->askb);
@@ -1152,15 +1157,20 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
                n = dst_neigh_lookup(dst, &ip6h->saddr);
 #endif
        }
-       if (!n)
-               goto free_sk;
+       if (!n || !n->dev)
+               goto free_dst;
 
        ndev = n->dev;
-       if (!ndev)
-               goto free_dst;
        if (is_vlan_dev(ndev))
                ndev = vlan_dev_real_dev(ndev);
 
+       for_each_port(adap, i)
+               if (cdev->ports[i] == ndev)
+                       found = true;
+
+       if (!found)
+               goto free_dst;
+
        port_id = cxgb4_port_idx(ndev);
 
        csk = chtls_sock_create(cdev);
@@ -1238,6 +1248,8 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
 free_csk:
        chtls_sock_release(&csk->kref);
 free_dst:
+       if (n)
+               neigh_release(n);
        dst_release(dst);
 free_sk:
        inet_csk_prepare_forced_close(newsk);
@@ -1387,7 +1399,7 @@ static void chtls_pass_accept_request(struct sock *sk,
 
        newsk = chtls_recv_sock(sk, oreq, network_hdr, req, cdev);
        if (!newsk)
-               goto free_oreq;
+               goto reject;
 
        if (chtls_get_module(newsk))
                goto reject;
@@ -1403,8 +1415,6 @@ static void chtls_pass_accept_request(struct sock *sk,
        kfree_skb(skb);
        return;
 
-free_oreq:
-       chtls_reqsk_free(oreq);
 reject:
        mk_tid_release(reply_skb, 0, tid);
        cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb);
@@ -1589,6 +1599,11 @@ static int chtls_pass_establish(struct chtls_dev *cdev, struct sk_buff *skb)
                        sk_wake_async(sk, 0, POLL_OUT);
 
                data = lookup_stid(cdev->tids, stid);
+               if (!data) {
+                       /* listening server close */
+                       kfree_skb(skb);
+                       goto unlock;
+               }
                lsk = ((struct listen_ctx *)data)->lsk;
 
                bh_lock_sock(lsk);
@@ -1936,6 +1951,8 @@ static void chtls_close_con_rpl(struct sock *sk, struct sk_buff *skb)
                else if (tcp_sk(sk)->linger2 < 0 &&
                         !csk_flag_nochk(csk, CSK_ABORT_SHUTDOWN))
                        chtls_abort_conn(sk, skb);
+               else if (csk_flag_nochk(csk, CSK_TX_DATA_SENT))
+                       chtls_set_quiesce_ctrl(sk, 0);
                break;
        default:
                pr_info("close_con_rpl in bad state %d\n", sk->sk_state);
@@ -1997,39 +2014,6 @@ static void t4_defer_reply(struct sk_buff *skb, struct chtls_dev *cdev,
        spin_unlock_bh(&cdev->deferq.lock);
 }
 
-static void send_abort_rpl(struct sock *sk, struct sk_buff *skb,
-                          struct chtls_dev *cdev, int status, int queue)
-{
-       struct cpl_abort_req_rss *req = cplhdr(skb);
-       struct sk_buff *reply_skb;
-       struct chtls_sock *csk;
-
-       csk = rcu_dereference_sk_user_data(sk);
-
-       reply_skb = alloc_skb(sizeof(struct cpl_abort_rpl),
-                             GFP_KERNEL);
-
-       if (!reply_skb) {
-               req->status = (queue << 1);
-               t4_defer_reply(skb, cdev, send_defer_abort_rpl);
-               return;
-       }
-
-       set_abort_rpl_wr(reply_skb, GET_TID(req), status);
-       kfree_skb(skb);
-
-       set_wr_txq(reply_skb, CPL_PRIORITY_DATA, queue);
-       if (csk_conn_inline(csk)) {
-               struct l2t_entry *e = csk->l2t_entry;
-
-               if (e && sk->sk_state != TCP_SYN_RECV) {
-                       cxgb4_l2t_send(csk->egress_dev, reply_skb, e);
-                       return;
-               }
-       }
-       cxgb4_ofld_send(cdev->lldi->ports[0], reply_skb);
-}
-
 static void chtls_send_abort_rpl(struct sock *sk, struct sk_buff *skb,
                                 struct chtls_dev *cdev,
                                 int status, int queue)
@@ -2078,9 +2062,9 @@ static void bl_abort_syn_rcv(struct sock *lsk, struct sk_buff *skb)
        queue = csk->txq_idx;
 
        skb->sk = NULL;
+       chtls_send_abort_rpl(child, skb, BLOG_SKB_CB(skb)->cdev,
+                            CPL_ABORT_NO_RST, queue);
        do_abort_syn_rcv(child, lsk);
-       send_abort_rpl(child, skb, BLOG_SKB_CB(skb)->cdev,
-                      CPL_ABORT_NO_RST, queue);
 }
 
 static int abort_syn_rcv(struct sock *sk, struct sk_buff *skb)
@@ -2110,8 +2094,8 @@ static int abort_syn_rcv(struct sock *sk, struct sk_buff *skb)
        if (!sock_owned_by_user(psk)) {
                int queue = csk->txq_idx;
 
+               chtls_send_abort_rpl(sk, skb, cdev, CPL_ABORT_NO_RST, queue);
                do_abort_syn_rcv(sk, psk);
-               send_abort_rpl(sk, skb, cdev, CPL_ABORT_NO_RST, queue);
        } else {
                skb->sk = sk;
                BLOG_SKB_CB(skb)->backlog_rcv = bl_abort_syn_rcv;
@@ -2129,9 +2113,6 @@ static void chtls_abort_req_rss(struct sock *sk, struct sk_buff *skb)
        int queue = csk->txq_idx;
 
        if (is_neg_adv(req->status)) {
-               if (sk->sk_state == TCP_SYN_RECV)
-                       chtls_set_tcb_tflag(sk, 0, 0);
-
                kfree_skb(skb);
                return;
        }
@@ -2158,12 +2139,12 @@ static void chtls_abort_req_rss(struct sock *sk, struct sk_buff *skb)
                if (sk->sk_state == TCP_SYN_RECV && !abort_syn_rcv(sk, skb))
                        return;
 
-               chtls_release_resources(sk);
-               chtls_conn_done(sk);
        }
 
        chtls_send_abort_rpl(sk, skb, BLOG_SKB_CB(skb)->cdev,
                             rst_status, queue);
+       chtls_release_resources(sk);
+       chtls_conn_done(sk);
 }
 
 static void chtls_abort_rpl_rss(struct sock *sk, struct sk_buff *skb)
@@ -2315,6 +2296,28 @@ static int chtls_wr_ack(struct chtls_dev *cdev, struct sk_buff *skb)
        return 0;
 }
 
+static int chtls_set_tcb_rpl(struct chtls_dev *cdev, struct sk_buff *skb)
+{
+       struct cpl_set_tcb_rpl *rpl = cplhdr(skb) + RSS_HDR;
+       unsigned int hwtid = GET_TID(rpl);
+       struct sock *sk;
+
+       sk = lookup_tid(cdev->tids, hwtid);
+
+       /* return EINVAL if socket doesn't exist */
+       if (!sk)
+               return -EINVAL;
+
+       /* Reusing the skb as size of cpl_set_tcb_field structure
+        * is greater than cpl_abort_req
+        */
+       if (TCB_COOKIE_G(rpl->cookie) == TCB_FIELD_COOKIE_TFLAG)
+               chtls_send_abort(sk, CPL_ABORT_SEND_RST, NULL);
+
+       kfree_skb(skb);
+       return 0;
+}
+
 chtls_handler_func chtls_handlers[NUM_CPL_CMDS] = {
        [CPL_PASS_OPEN_RPL]     = chtls_pass_open_rpl,
        [CPL_CLOSE_LISTSRV_RPL] = chtls_close_listsrv_rpl,
@@ -2327,5 +2330,6 @@ chtls_handler_func chtls_handlers[NUM_CPL_CMDS] = {
        [CPL_CLOSE_CON_RPL]     = chtls_conn_cpl,
        [CPL_ABORT_REQ_RSS]     = chtls_conn_cpl,
        [CPL_ABORT_RPL_RSS]     = chtls_conn_cpl,
-       [CPL_FW4_ACK]           = chtls_wr_ack,
+       [CPL_FW4_ACK]           = chtls_wr_ack,
+       [CPL_SET_TCB_RPL]       = chtls_set_tcb_rpl,
 };
index a4fb463..1e67140 100644 (file)
@@ -88,6 +88,24 @@ static int chtls_set_tcb_field(struct sock *sk, u16 word, u64 mask, u64 val)
        return ret < 0 ? ret : 0;
 }
 
+void chtls_set_tcb_field_rpl_skb(struct sock *sk, u16 word,
+                                u64 mask, u64 val, u8 cookie,
+                                int through_l2t)
+{
+       struct sk_buff *skb;
+       unsigned int wrlen;
+
+       wrlen = sizeof(struct cpl_set_tcb_field) + sizeof(struct ulptx_idata);
+       wrlen = roundup(wrlen, 16);
+
+       skb = alloc_skb(wrlen, GFP_KERNEL | __GFP_NOFAIL);
+       if (!skb)
+               return;
+
+       __set_tcb_field(sk, skb, word, mask, val, cookie, 0);
+       send_or_defer(sk, tcp_sk(sk), skb, through_l2t);
+}
+
 /*
  * Set one of the t_flags bits in the TCB.
  */
@@ -113,6 +131,29 @@ static int chtls_set_tcb_quiesce(struct sock *sk, int val)
                                   TF_RX_QUIESCE_V(val));
 }
 
+void chtls_set_quiesce_ctrl(struct sock *sk, int val)
+{
+       struct chtls_sock *csk;
+       struct sk_buff *skb;
+       unsigned int wrlen;
+       int ret;
+
+       wrlen = sizeof(struct cpl_set_tcb_field) + sizeof(struct ulptx_idata);
+       wrlen = roundup(wrlen, 16);
+
+       skb = alloc_skb(wrlen, GFP_ATOMIC);
+       if (!skb)
+               return;
+
+       csk = rcu_dereference_sk_user_data(sk);
+
+       __set_tcb_field(sk, skb, 1, TF_RX_QUIESCE_V(1), 0, 0, 1);
+       set_wr_txq(skb, CPL_PRIORITY_CONTROL, csk->port_id);
+       ret = cxgb4_ofld_send(csk->egress_dev, skb);
+       if (ret < 0)
+               kfree_skb(skb);
+}
+
 /* TLS Key bitmap processing */
 int chtls_init_kmap(struct chtls_dev *cdev, struct cxgb4_lld_info *lldi)
 {
index fb269d5..f04ec53 100644 (file)
@@ -2509,8 +2509,6 @@ static const struct net_device_ops enic_netdev_dynamic_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = enic_rx_flow_steer,
 #endif
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = enic_features_check,
 };
 
@@ -2535,8 +2533,6 @@ static const struct net_device_ops enic_netdev_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = enic_rx_flow_steer,
 #endif
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = enic_features_check,
 };
 
index d402d83..b6eba29 100644 (file)
@@ -5179,8 +5179,6 @@ static const struct net_device_ops be_netdev_ops = {
 #endif
        .ndo_bridge_setlink     = be_ndo_bridge_setlink,
        .ndo_bridge_getlink     = be_ndo_bridge_getlink,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = be_features_check,
        .ndo_get_phys_port_id   = be_get_phys_port_id,
 };
index 0981fe9..3d9b0b1 100644 (file)
@@ -1211,7 +1211,7 @@ static int ethoc_probe(struct platform_device *pdev)
        ret = mdiobus_register(priv->mdio);
        if (ret) {
                dev_err(&netdev->dev, "failed to register MDIO bus\n");
-               goto free2;
+               goto free3;
        }
 
        ret = ethoc_mdio_probe(netdev);
@@ -1243,6 +1243,7 @@ error2:
        netif_napi_del(&priv->napi);
 error:
        mdiobus_unregister(priv->mdio);
+free3:
        mdiobus_free(priv->mdio);
 free2:
        clk_disable_unprepare(priv->clk);
index 4360ce4..d8e568f 100644 (file)
@@ -2532,12 +2532,10 @@ static u32 dpaa_run_xdp(struct dpaa_priv *priv, struct qm_fd *fd, void *vaddr,
                return XDP_PASS;
        }
 
-       xdp.data = vaddr + fd_off;
-       xdp.data_meta = xdp.data;
-       xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
-       xdp.data_end = xdp.data + qm_fd_get_length(fd);
-       xdp.frame_sz = DPAA_BP_RAW_SIZE - DPAA_TX_PRIV_DATA_SIZE;
-       xdp.rxq = &dpaa_fq->xdp_rxq;
+       xdp_init_buff(&xdp, DPAA_BP_RAW_SIZE - DPAA_TX_PRIV_DATA_SIZE,
+                     &dpaa_fq->xdp_rxq);
+       xdp_prepare_buff(&xdp, vaddr + fd_off - XDP_PACKET_HEADROOM,
+                        XDP_PACKET_HEADROOM, qm_fd_get_length(fd), true);
 
        /* We reserve a fixed headroom of 256 bytes under the erratum and we
         * offer it all to XDP programs to use. If no room is left for the
index fb0bcd1..41e225b 100644 (file)
@@ -350,7 +350,7 @@ static u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv,
        struct bpf_prog *xdp_prog;
        struct xdp_buff xdp;
        u32 xdp_act = XDP_PASS;
-       int err;
+       int err, offset;
 
        rcu_read_lock();
 
@@ -358,14 +358,10 @@ static u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv,
        if (!xdp_prog)
                goto out;
 
-       xdp.data = vaddr + dpaa2_fd_get_offset(fd);
-       xdp.data_end = xdp.data + dpaa2_fd_get_len(fd);
-       xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM;
-       xdp_set_data_meta_invalid(&xdp);
-       xdp.rxq = &ch->xdp_rxq;
-
-       xdp.frame_sz = DPAA2_ETH_RX_BUF_RAW_SIZE -
-               (dpaa2_fd_get_offset(fd) - XDP_PACKET_HEADROOM);
+       offset = dpaa2_fd_get_offset(fd) - XDP_PACKET_HEADROOM;
+       xdp_init_buff(&xdp, DPAA2_ETH_RX_BUF_RAW_SIZE - offset, &ch->xdp_rxq);
+       xdp_prepare_buff(&xdp, vaddr + offset, XDP_PACKET_HEADROOM,
+                        dpaa2_fd_get_len(fd), false);
 
        xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
 
@@ -1262,6 +1258,22 @@ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv,
        percpu_stats->tx_errors++;
 }
 
+static int dpaa2_eth_set_rx_vlan_filtering(struct dpaa2_eth_priv *priv,
+                                          bool enable)
+{
+       int err;
+
+       err = dpni_enable_vlan_filter(priv->mc_io, 0, priv->mc_token, enable);
+
+       if (err) {
+               netdev_err(priv->net_dev,
+                          "dpni_enable_vlan_filter failed\n");
+               return err;
+       }
+
+       return 0;
+}
+
 static int dpaa2_eth_set_rx_csum(struct dpaa2_eth_priv *priv, bool enable)
 {
        int err;
@@ -1691,7 +1703,7 @@ static int dpaa2_eth_link_state_update(struct dpaa2_eth_priv *priv)
        /* When we manage the MAC/PHY using phylink there is no need
         * to manually update the netif_carrier.
         */
-       if (priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
                goto out;
 
        /* Chech link state; speed / duplex changes are not treated yet */
@@ -1730,7 +1742,7 @@ static int dpaa2_eth_open(struct net_device *net_dev)
                           priv->dpbp_dev->obj_desc.id, priv->bpid);
        }
 
-       if (!priv->mac) {
+       if (!dpaa2_eth_is_type_phy(priv)) {
                /* We'll only start the txqs when the link is actually ready;
                 * make sure we don't race against the link up notification,
                 * which may come immediately after dpni_enable();
@@ -1752,7 +1764,7 @@ static int dpaa2_eth_open(struct net_device *net_dev)
                goto enable_err;
        }
 
-       if (priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
                phylink_start(priv->mac->phylink);
 
        return 0;
@@ -1826,11 +1838,11 @@ static int dpaa2_eth_stop(struct net_device *net_dev)
        int dpni_enabled = 0;
        int retries = 10;
 
-       if (!priv->mac) {
+       if (dpaa2_eth_is_type_phy(priv)) {
+               phylink_stop(priv->mac->phylink);
+       } else {
                netif_tx_stop_all_queues(net_dev);
                netif_carrier_off(net_dev);
-       } else {
-               phylink_stop(priv->mac->phylink);
        }
 
        /* On dpni_disable(), the MC firmware will:
@@ -1952,6 +1964,43 @@ static void dpaa2_eth_add_mc_hw_addr(const struct net_device *net_dev,
        }
 }
 
+static int dpaa2_eth_rx_add_vid(struct net_device *net_dev,
+                               __be16 vlan_proto, u16 vid)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       int err;
+
+       err = dpni_add_vlan_id(priv->mc_io, 0, priv->mc_token,
+                              vid, 0, 0, 0);
+
+       if (err) {
+               netdev_warn(priv->net_dev,
+                           "Could not add the vlan id %u\n",
+                           vid);
+               return err;
+       }
+
+       return 0;
+}
+
+static int dpaa2_eth_rx_kill_vid(struct net_device *net_dev,
+                                __be16 vlan_proto, u16 vid)
+{
+       struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+       int err;
+
+       err = dpni_remove_vlan_id(priv->mc_io, 0, priv->mc_token, vid);
+
+       if (err) {
+               netdev_warn(priv->net_dev,
+                           "Could not remove the vlan id %u\n",
+                           vid);
+               return err;
+       }
+
+       return 0;
+}
+
 static void dpaa2_eth_set_rx_mode(struct net_device *net_dev)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
@@ -2058,6 +2107,13 @@ static int dpaa2_eth_set_features(struct net_device *net_dev,
        bool enable;
        int err;
 
+       if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+               enable = !!(features & NETIF_F_HW_VLAN_CTAG_FILTER);
+               err = dpaa2_eth_set_rx_vlan_filtering(priv, enable);
+               if (err)
+                       return err;
+       }
+
        if (changed & NETIF_F_RXCSUM) {
                enable = !!(features & NETIF_F_RXCSUM);
                err = dpaa2_eth_set_rx_csum(priv, enable);
@@ -2115,7 +2171,7 @@ static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        if (cmd == SIOCSHWTSTAMP)
                return dpaa2_eth_ts_ioctl(dev, rq, cmd);
 
-       if (priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
                return phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
 
        return -EOPNOTSUPP;
@@ -2507,6 +2563,8 @@ static const struct net_device_ops dpaa2_eth_ops = {
        .ndo_bpf = dpaa2_eth_xdp,
        .ndo_xdp_xmit = dpaa2_eth_xdp_xmit,
        .ndo_setup_tc = dpaa2_eth_setup_tc,
+       .ndo_vlan_rx_add_vid = dpaa2_eth_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = dpaa2_eth_rx_kill_vid
 };
 
 static void dpaa2_eth_cdan_cb(struct dpaa2_io_notification_ctx *ctx)
@@ -4015,6 +4073,9 @@ static int dpaa2_eth_netdev_init(struct net_device *net_dev)
                            NETIF_F_LLTX | NETIF_F_HW_TC;
        net_dev->hw_features = net_dev->features;
 
+       if (priv->dpni_attrs.vlan_filter_entries)
+               net_dev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER;
+
        return 0;
 }
 
@@ -4042,10 +4103,11 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
 
        dpni_dev = to_fsl_mc_device(priv->net_dev->dev.parent);
        dpmac_dev = fsl_mc_get_endpoint(dpni_dev);
-       if (IS_ERR_OR_NULL(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type)
-               return 0;
 
-       if (dpaa2_mac_is_type_fixed(dpmac_dev, priv->mc_io))
+       if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER)
+               return PTR_ERR(dpmac_dev);
+
+       if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type)
                return 0;
 
        mac = kzalloc(sizeof(struct dpaa2_mac), GFP_KERNEL);
@@ -4056,23 +4118,38 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
        mac->mc_io = priv->mc_io;
        mac->net_dev = priv->net_dev;
 
-       err = dpaa2_mac_connect(mac);
-       if (err) {
-               netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n");
-               kfree(mac);
-               return err;
-       }
+       err = dpaa2_mac_open(mac);
+       if (err)
+               goto err_free_mac;
        priv->mac = mac;
 
+       if (dpaa2_eth_is_type_phy(priv)) {
+               err = dpaa2_mac_connect(mac);
+               if (err) {
+                       netdev_err(priv->net_dev, "Error connecting to the MAC endpoint\n");
+                       goto err_close_mac;
+               }
+       }
+
        return 0;
+
+err_close_mac:
+       dpaa2_mac_close(mac);
+       priv->mac = NULL;
+err_free_mac:
+       kfree(mac);
+       return err;
 }
 
 static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
 {
-       if (!priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
+               dpaa2_mac_disconnect(priv->mac);
+
+       if (!dpaa2_eth_has_mac(priv))
                return;
 
-       dpaa2_mac_disconnect(priv->mac);
+       dpaa2_mac_close(priv->mac);
        kfree(priv->mac);
        priv->mac = NULL;
 }
@@ -4101,7 +4178,7 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
                dpaa2_eth_update_tx_fqids(priv);
 
                rtnl_lock();
-               if (priv->mac)
+               if (dpaa2_eth_has_mac(priv))
                        dpaa2_eth_disconnect_mac(priv);
                else
                        dpaa2_eth_connect_mac(priv);
index d236b86..c3d456c 100644 (file)
@@ -693,6 +693,19 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
        return priv->tx_data_offset - DPAA2_ETH_RX_HWA_SIZE;
 }
 
+static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv)
+{
+       if (priv->mac && priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY)
+               return true;
+
+       return false;
+}
+
+static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
+{
+       return priv->mac ? true : false;
+}
+
 int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags);
 int dpaa2_eth_set_cls(struct net_device *net_dev, u64 key);
 int dpaa2_eth_cls_key_size(u64 key);
index f981a52..bf59708 100644 (file)
@@ -85,7 +85,7 @@ static int dpaa2_eth_nway_reset(struct net_device *net_dev)
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
 
-       if (priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
                return phylink_ethtool_nway_reset(priv->mac->phylink);
 
        return -EOPNOTSUPP;
@@ -97,7 +97,7 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev,
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
 
-       if (priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
                return phylink_ethtool_ksettings_get(priv->mac->phylink,
                                                     link_settings);
 
@@ -115,7 +115,7 @@ dpaa2_eth_set_link_ksettings(struct net_device *net_dev,
 {
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
 
-       if (!priv->mac)
+       if (!dpaa2_eth_is_type_phy(priv))
                return -ENOTSUPP;
 
        return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
@@ -127,7 +127,7 @@ static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
        struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
        u64 link_options = priv->link_state.options;
 
-       if (priv->mac) {
+       if (dpaa2_eth_is_type_phy(priv)) {
                phylink_ethtool_get_pauseparam(priv->mac->phylink, pause);
                return;
        }
@@ -150,7 +150,7 @@ static int dpaa2_eth_set_pauseparam(struct net_device *net_dev,
                return -EOPNOTSUPP;
        }
 
-       if (priv->mac)
+       if (dpaa2_eth_is_type_phy(priv))
                return phylink_ethtool_set_pauseparam(priv->mac->phylink,
                                                      pause);
        if (pause->autoneg)
@@ -198,7 +198,7 @@ static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset,
                        strlcpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN);
                        p += ETH_GSTRING_LEN;
                }
-               if (priv->mac)
+               if (dpaa2_eth_has_mac(priv))
                        dpaa2_mac_get_strings(p);
                break;
        }
@@ -211,7 +211,7 @@ static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset)
 
        switch (sset) {
        case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */
-               if (priv->mac)
+               if (dpaa2_eth_has_mac(priv))
                        num_ss_stats += dpaa2_mac_get_sset_count();
                return num_ss_stats;
        default:
@@ -313,7 +313,7 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
        }
        *(data + i++) = buf_cnt;
 
-       if (priv->mac)
+       if (dpaa2_eth_has_mac(priv))
                dpaa2_mac_get_ethtool_stats(priv->mac, data + i);
 }
 
index 828c177..69ad869 100644 (file)
@@ -174,30 +174,22 @@ static void dpaa2_mac_link_up(struct phylink_config *config,
 
        dpmac_state->up = 1;
 
-       if (mac->if_link_type == DPMAC_LINK_TYPE_PHY) {
-               /* If the DPMAC is configured for PHY mode, we need
-                * to pass the link parameters to the MC firmware.
-                */
-               dpmac_state->rate = speed;
-
-               if (duplex == DUPLEX_HALF)
-                       dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
-               else if (duplex == DUPLEX_FULL)
-                       dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
-
-               /* This is lossy; the firmware really should take the pause
-                * enablement status rather than pause/asym pause status.
-                */
-               if (rx_pause)
-                       dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
-               else
-                       dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
-
-               if (rx_pause ^ tx_pause)
-                       dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
-               else
-                       dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
-       }
+       dpmac_state->rate = speed;
+
+       if (duplex == DUPLEX_HALF)
+               dpmac_state->options |= DPMAC_LINK_OPT_HALF_DUPLEX;
+       else if (duplex == DUPLEX_FULL)
+               dpmac_state->options &= ~DPMAC_LINK_OPT_HALF_DUPLEX;
+
+       if (rx_pause)
+               dpmac_state->options |= DPMAC_LINK_OPT_PAUSE;
+       else
+               dpmac_state->options &= ~DPMAC_LINK_OPT_PAUSE;
+
+       if (rx_pause ^ tx_pause)
+               dpmac_state->options |= DPMAC_LINK_OPT_ASYM_PAUSE;
+       else
+               dpmac_state->options &= ~DPMAC_LINK_OPT_ASYM_PAUSE;
 
        err = dpmac_set_link_state(mac->mc_io, 0,
                                   mac->mc_dev->mc_handle, dpmac_state);
@@ -228,32 +220,6 @@ static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
        .mac_link_down = dpaa2_mac_link_down,
 };
 
-bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
-                            struct fsl_mc_io *mc_io)
-{
-       struct dpmac_attr attr;
-       bool fixed = false;
-       u16 mc_handle = 0;
-       int err;
-
-       err = dpmac_open(mc_io, 0, dpmac_dev->obj_desc.id,
-                        &mc_handle);
-       if (err || !mc_handle)
-               return false;
-
-       err = dpmac_get_attributes(mc_io, 0, mc_handle, &attr);
-       if (err)
-               goto out;
-
-       if (attr.link_type == DPMAC_LINK_TYPE_FIXED)
-               fixed = true;
-
-out:
-       dpmac_close(mc_io, 0, mc_handle);
-
-       return fixed;
-}
-
 static int dpaa2_pcs_create(struct dpaa2_mac *mac,
                            struct device_node *dpmac_node, int id)
 {
@@ -302,36 +268,20 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
 
 int dpaa2_mac_connect(struct dpaa2_mac *mac)
 {
-       struct fsl_mc_device *dpmac_dev = mac->mc_dev;
        struct net_device *net_dev = mac->net_dev;
        struct device_node *dpmac_node;
        struct phylink *phylink;
-       struct dpmac_attr attr;
        int err;
 
-       err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id,
-                        &dpmac_dev->mc_handle);
-       if (err || !dpmac_dev->mc_handle) {
-               netdev_err(net_dev, "dpmac_open() = %d\n", err);
-               return -ENODEV;
-       }
-
-       err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle, &attr);
-       if (err) {
-               netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err);
-               goto err_close_dpmac;
-       }
+       mac->if_link_type = mac->attr.link_type;
 
-       mac->if_link_type = attr.link_type;
-
-       dpmac_node = dpaa2_mac_get_node(attr.id);
+       dpmac_node = dpaa2_mac_get_node(mac->attr.id);
        if (!dpmac_node) {
-               netdev_err(net_dev, "No dpmac@%d node found.\n", attr.id);
-               err = -ENODEV;
-               goto err_close_dpmac;
+               netdev_err(net_dev, "No dpmac@%d node found.\n", mac->attr.id);
+               return -ENODEV;
        }
 
-       err = dpaa2_mac_get_if_mode(dpmac_node, attr);
+       err = dpaa2_mac_get_if_mode(dpmac_node, mac->attr);
        if (err < 0) {
                err = -EINVAL;
                goto err_put_node;
@@ -351,9 +301,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
                goto err_put_node;
        }
 
-       if (attr.link_type == DPMAC_LINK_TYPE_PHY &&
-           attr.eth_if != DPMAC_ETH_IF_RGMII) {
-               err = dpaa2_pcs_create(mac, dpmac_node, attr.id);
+       if (mac->attr.link_type == DPMAC_LINK_TYPE_PHY &&
+           mac->attr.eth_if != DPMAC_ETH_IF_RGMII) {
+               err = dpaa2_pcs_create(mac, dpmac_node, mac->attr.id);
                if (err)
                        goto err_put_node;
        }
@@ -389,8 +339,7 @@ err_pcs_destroy:
        dpaa2_pcs_destroy(mac);
 err_put_node:
        of_node_put(dpmac_node);
-err_close_dpmac:
-       dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
+
        return err;
 }
 
@@ -402,8 +351,40 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
        phylink_disconnect_phy(mac->phylink);
        phylink_destroy(mac->phylink);
        dpaa2_pcs_destroy(mac);
+}
+
+int dpaa2_mac_open(struct dpaa2_mac *mac)
+{
+       struct fsl_mc_device *dpmac_dev = mac->mc_dev;
+       struct net_device *net_dev = mac->net_dev;
+       int err;
 
-       dpmac_close(mac->mc_io, 0, mac->mc_dev->mc_handle);
+       err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id,
+                        &dpmac_dev->mc_handle);
+       if (err || !dpmac_dev->mc_handle) {
+               netdev_err(net_dev, "dpmac_open() = %d\n", err);
+               return -ENODEV;
+       }
+
+       err = dpmac_get_attributes(mac->mc_io, 0, dpmac_dev->mc_handle,
+                                  &mac->attr);
+       if (err) {
+               netdev_err(net_dev, "dpmac_get_attributes() = %d\n", err);
+               goto err_close_dpmac;
+       }
+
+       return 0;
+
+err_close_dpmac:
+       dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
+       return err;
+}
+
+void dpaa2_mac_close(struct dpaa2_mac *mac)
+{
+       struct fsl_mc_device *dpmac_dev = mac->mc_dev;
+
+       dpmac_close(mac->mc_io, 0, dpmac_dev->mc_handle);
 }
 
 static char dpaa2_mac_ethtool_stats[][ETH_GSTRING_LEN] = {
index 955a528..13d42dd 100644 (file)
@@ -17,6 +17,7 @@ struct dpaa2_mac {
        struct dpmac_link_state state;
        struct net_device *net_dev;
        struct fsl_mc_io *mc_io;
+       struct dpmac_attr attr;
 
        struct phylink_config phylink_config;
        struct phylink *phylink;
@@ -28,6 +29,10 @@ struct dpaa2_mac {
 bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
                             struct fsl_mc_io *mc_io);
 
+int dpaa2_mac_open(struct dpaa2_mac *mac);
+
+void dpaa2_mac_close(struct dpaa2_mac *mac);
+
 int dpaa2_mac_connect(struct dpaa2_mac *mac);
 
 void dpaa2_mac_disconnect(struct dpaa2_mac *mac);
index 90453dc..9f80bdf 100644 (file)
 
 #define DPNI_CMDID_SET_RX_TC_DIST                      DPNI_CMD(0x235)
 
+#define DPNI_CMDID_ENABLE_VLAN_FILTER                  DPNI_CMD(0x230)
+#define DPNI_CMDID_ADD_VLAN_ID                         DPNI_CMD_V2(0x231)
+#define DPNI_CMDID_REMOVE_VLAN_ID                      DPNI_CMD(0x232)
+
 #define DPNI_CMDID_SET_QOS_TBL                         DPNI_CMD(0x240)
 #define DPNI_CMDID_ADD_QOS_ENT                         DPNI_CMD(0x241)
 #define DPNI_CMDID_REMOVE_QOS_ENT                      DPNI_CMD(0x242)
@@ -662,4 +666,17 @@ struct dpni_rsp_single_step_cfg {
        __le32 peer_delay;
 };
 
+struct dpni_cmd_enable_vlan_filter {
+       /* only the LSB */
+       u8 en;
+};
+
+struct dpni_cmd_vlan_id {
+       u8 flags;
+       u8 tc_id;
+       u8 flow_id;
+       u8 pad;
+       __le16 vlan_id;
+};
+
 #endif /* _FSL_DPNI_CMD_H */
index 6ea7db6..aa429c1 100644 (file)
@@ -1224,6 +1224,99 @@ int dpni_get_port_mac_addr(struct fsl_mc_io *mc_io,
        return 0;
 }
 
+/**
+ * dpni_enable_vlan_filter() - Enable/disable VLAN filtering mode
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPNI object
+ * @en:                Set to '1' to enable; '0' to disable
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpni_enable_vlan_filter(struct fsl_mc_io *mc_io,
+                           u32 cmd_flags,
+                           u16 token,
+                           u32 en)
+{
+       struct dpni_cmd_enable_vlan_filter *cmd_params;
+       struct fsl_mc_command cmd = { 0 };
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE_VLAN_FILTER,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpni_cmd_enable_vlan_filter *)cmd.params;
+       dpni_set_field(cmd_params->en, ENABLE, en);
+
+       /* send command to mc*/
+       return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpni_add_vlan_id() - Add VLAN ID filter
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPNI object
+ * @vlan_id:   VLAN ID to add
+ * @flags:   0 - tc_id and flow_id will be ignored.
+ * Pkt with this vlan_id will be passed to the next
+ * classification stages
+ * DPNI_VLAN_SET_QUEUE_ACTION
+ * Pkt with this vlan_id will be forward directly to
+ * queue defined by the tc_id and flow_id
+ *
+ * @tc_id: Traffic class selection (0-7)
+ * @flow_id: Selects the specific queue out of the set allocated for the
+ *           same as tc_id. Value must be in range 0 to NUM_QUEUES - 1
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpni_add_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+                    u16 vlan_id, u8 flags, u8 tc_id, u8 flow_id)
+{
+       struct dpni_cmd_vlan_id *cmd_params;
+       struct fsl_mc_command cmd = { 0 };
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_VLAN_ID,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpni_cmd_vlan_id *)cmd.params;
+       cmd_params->flags = flags;
+       cmd_params->tc_id = tc_id;
+       cmd_params->flow_id =  flow_id;
+       cmd_params->vlan_id = cpu_to_le16(vlan_id);
+
+       /* send command to mc*/
+       return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpni_remove_vlan_id() - Remove VLAN ID filter
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPNI object
+ * @vlan_id:   VLAN ID to remove
+ *
+ * Return:     '0' on Success; Error code otherwise.
+ */
+int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+                       u16 vlan_id)
+{
+       struct dpni_cmd_vlan_id *cmd_params;
+       struct fsl_mc_command cmd = { 0 };
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_VLAN_ID,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpni_cmd_vlan_id *)cmd.params;
+       cmd_params->vlan_id = cpu_to_le16(vlan_id);
+
+       /* send command to mc*/
+       return mc_send_command(mc_io, &cmd);
+}
+
 /**
  * dpni_add_mac_addr() - Add MAC address filter
  * @mc_io:     Pointer to MC portal's I/O object
index e7b9e19..4e96d93 100644 (file)
@@ -1114,4 +1114,13 @@ int dpni_get_single_step_cfg(struct fsl_mc_io *mc_io,
                             u16 token,
                             struct dpni_single_step_cfg *ptp_cfg);
 
+int dpni_enable_vlan_filter(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+                           u32 en);
+
+int dpni_add_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+                    u16 vlan_id, u8 flags, u8 tc_id, u8 flow_id);
+
+int dpni_remove_vlan_id(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+                       u16 vlan_id);
+
 #endif /* __FSL_DPNI_H */
index ee0116e..70e6d97 100644 (file)
 #define        ENETC_MDIO_DATA 0x8     /* MDIO data */
 #define        ENETC_MDIO_ADDR 0xc     /* MDIO address */
 
-static inline u32 _enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
-{
-       return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off);
-}
-
-static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
-                                 u32 val)
-{
-       enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val);
-}
-
-#define enetc_mdio_rd(mdio_priv, off) \
-       _enetc_mdio_rd(mdio_priv, ENETC_##off)
-#define enetc_mdio_wr(mdio_priv, off, val) \
-       _enetc_mdio_wr(mdio_priv, ENETC_##off, val)
-#define enetc_mdio_rd_reg(off) enetc_mdio_rd(mdio_priv, off)
-
 #define MDIO_CFG_CLKDIV(x)     ((((x) >> 1) & 0xff) << 8)
 #define MDIO_CFG_BSY           BIT(0)
 #define MDIO_CFG_RD_ER         BIT(1)
@@ -47,15 +30,29 @@ static inline void _enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
 #define MDIO_CTL_DEV_ADDR(x)   ((x) & 0x1f)
 #define MDIO_CTL_PORT_ADDR(x)  (((x) & 0x1f) << 5)
 #define MDIO_CTL_READ          BIT(15)
-#define MDIO_DATA(x)           ((x) & 0xffff)
 
-#define TIMEOUT        1000
+static inline u32 enetc_mdio_rd(struct enetc_mdio_priv *mdio_priv, int off)
+{
+       return enetc_port_rd_mdio(mdio_priv->hw, mdio_priv->mdio_base + off);
+}
+
+static inline void enetc_mdio_wr(struct enetc_mdio_priv *mdio_priv, int off,
+                                u32 val)
+{
+       enetc_port_wr_mdio(mdio_priv->hw, mdio_priv->mdio_base + off, val);
+}
+
+static bool enetc_mdio_is_busy(struct enetc_mdio_priv *mdio_priv)
+{
+       return enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_BSY;
+}
+
 static int enetc_mdio_wait_complete(struct enetc_mdio_priv *mdio_priv)
 {
-       u32 val;
+       bool is_busy;
 
-       return readx_poll_timeout(enetc_mdio_rd_reg, MDIO_CFG, val,
-                                 !(val & MDIO_CFG_BSY), 10, 10 * TIMEOUT);
+       return readx_poll_timeout(enetc_mdio_is_busy, mdio_priv,
+                                 is_busy, !is_busy, 10, 10 * 1000);
 }
 
 int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
@@ -75,7 +72,7 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
                mdio_cfg &= ~MDIO_CFG_ENC45;
        }
 
-       enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
+       enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
 
        ret = enetc_mdio_wait_complete(mdio_priv);
        if (ret)
@@ -83,11 +80,11 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
 
        /* set port and dev addr */
        mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
-       enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
+       enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
 
        /* set the register address */
        if (regnum & MII_ADDR_C45) {
-               enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
+               enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
 
                ret = enetc_mdio_wait_complete(mdio_priv);
                if (ret)
@@ -95,7 +92,7 @@ int enetc_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value)
        }
 
        /* write the value */
-       enetc_mdio_wr(mdio_priv, MDIO_DATA, MDIO_DATA(value));
+       enetc_mdio_wr(mdio_priv, ENETC_MDIO_DATA, value);
 
        ret = enetc_mdio_wait_complete(mdio_priv);
        if (ret)
@@ -121,7 +118,7 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
                mdio_cfg &= ~MDIO_CFG_ENC45;
        }
 
-       enetc_mdio_wr(mdio_priv, MDIO_CFG, mdio_cfg);
+       enetc_mdio_wr(mdio_priv, ENETC_MDIO_CFG, mdio_cfg);
 
        ret = enetc_mdio_wait_complete(mdio_priv);
        if (ret)
@@ -129,11 +126,11 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 
        /* set port and device addr */
        mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr);
-       enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl);
+       enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl);
 
        /* set the register address */
        if (regnum & MII_ADDR_C45) {
-               enetc_mdio_wr(mdio_priv, MDIO_ADDR, regnum & 0xffff);
+               enetc_mdio_wr(mdio_priv, ENETC_MDIO_ADDR, regnum & 0xffff);
 
                ret = enetc_mdio_wait_complete(mdio_priv);
                if (ret)
@@ -141,21 +138,21 @@ int enetc_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
        }
 
        /* initiate the read */
-       enetc_mdio_wr(mdio_priv, MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
+       enetc_mdio_wr(mdio_priv, ENETC_MDIO_CTL, mdio_ctl | MDIO_CTL_READ);
 
        ret = enetc_mdio_wait_complete(mdio_priv);
        if (ret)
                return ret;
 
        /* return all Fs if nothing was there */
-       if (enetc_mdio_rd(mdio_priv, MDIO_CFG) & MDIO_CFG_RD_ER) {
+       if (enetc_mdio_rd(mdio_priv, ENETC_MDIO_CFG) & MDIO_CFG_RD_ER) {
                dev_dbg(&bus->dev,
                        "Error while reading PHY%d reg at %d.%hhu\n",
                        phy_id, dev_addr, regnum);
                return 0xffff;
        }
 
-       value = enetc_mdio_rd(mdio_priv, MDIO_DATA) & 0xffff;
+       value = enetc_mdio_rd(mdio_priv, ENETC_MDIO_DATA) & 0xffff;
 
        return value;
 }
index c527f4e..0602d5d 100644 (file)
@@ -462,6 +462,11 @@ struct bufdesc_ex {
  */
 #define FEC_QUIRK_CLEAR_SETUP_MII      (1 << 17)
 
+/* Some link partners do not tolerate the momentary reset of the REF_CLK
+ * frequency when the RNCTL register is cleared by hardware reset.
+ */
+#define FEC_QUIRK_NO_HARD_RESET                (1 << 18)
+
 struct bufdesc_prop {
        int qid;
        /* Address of Rx and Tx buffers */
index 04f24c6..9ebdb0e 100644 (file)
@@ -100,7 +100,8 @@ static const struct fec_devinfo fec_imx27_info = {
 static const struct fec_devinfo fec_imx28_info = {
        .quirks = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME |
                  FEC_QUIRK_SINGLE_MDIO | FEC_QUIRK_HAS_RACC |
-                 FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII,
+                 FEC_QUIRK_HAS_FRREG | FEC_QUIRK_CLEAR_SETUP_MII |
+                 FEC_QUIRK_NO_HARD_RESET,
 };
 
 static const struct fec_devinfo fec_imx6q_info = {
@@ -953,7 +954,8 @@ fec_restart(struct net_device *ndev)
         * For i.MX6SX SOC, enet use AXI bus, we use disable MAC
         * instead of reset MAC itself.
         */
-       if (fep->quirks & FEC_QUIRK_HAS_AVB) {
+       if (fep->quirks & FEC_QUIRK_HAS_AVB ||
+           ((fep->quirks & FEC_QUIRK_NO_HARD_RESET) && fep->link)) {
                writel(0, fep->hwp + FEC_ECNTRL);
        } else {
                writel(1, fep->hwp + FEC_ECNTRL);
@@ -2165,9 +2167,9 @@ static int fec_enet_mii_init(struct platform_device *pdev)
        fep->mii_bus->parent = &pdev->dev;
 
        err = of_mdiobus_register(fep->mii_bus, node);
-       of_node_put(node);
        if (err)
                goto err_out_free_mdiobus;
+       of_node_put(node);
 
        mii_cnt++;
 
@@ -2180,6 +2182,7 @@ static int fec_enet_mii_init(struct platform_device *pdev)
 err_out_free_mdiobus:
        mdiobus_free(fep->mii_bus);
 err_out:
+       of_node_put(node);
        return err;
 }
 
index bb9887f..62f4292 100644 (file)
@@ -111,6 +111,7 @@ do {                                                                        \
 
 #define IF_MODE_MASK           0x00000003 /* 30-31 Mask on i/f mode bits */
 #define IF_MODE_10G            0x00000000 /* 30-31 10G interface */
+#define IF_MODE_MII            0x00000001 /* 30-31 MII interface */
 #define IF_MODE_GMII           0x00000002 /* 30-31 GMII (1G) interface */
 #define IF_MODE_RGMII          0x00000004
 #define IF_MODE_RGMII_AUTO     0x00008000
@@ -442,6 +443,9 @@ static int init(struct memac_regs __iomem *regs, struct memac_cfg *cfg,
        case PHY_INTERFACE_MODE_XGMII:
                tmp |= IF_MODE_10G;
                break;
+       case PHY_INTERFACE_MODE_MII:
+               tmp |= IF_MODE_MII;
+               break;
        default:
                tmp |= IF_MODE_GMII;
                if (phy_if == PHY_INTERFACE_MODE_RGMII ||
index c8e5d88..21de563 100644 (file)
@@ -223,3 +223,4 @@ static struct platform_driver fs_enet_bb_mdio_driver = {
 };
 
 module_platform_driver(fs_enet_bb_mdio_driver);
+MODULE_LICENSE("GPL");
index 8b51ee1..152f4d8 100644 (file)
@@ -224,3 +224,4 @@ static struct platform_driver fs_enet_fec_mdio_driver = {
 };
 
 module_platform_driver(fs_enet_fec_mdio_driver);
+MODULE_LICENSE("GPL");
index d391a45..541de32 100644 (file)
@@ -58,7 +58,6 @@
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define DEBUG
 
 #include <linux/kernel.h>
 #include <linux/string.h>
index ba8869c..ef4e2fe 100644 (file)
@@ -70,9 +70,32 @@ static struct {
 module_param_named(debug, debug.msg_enable, int, 0);
 MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., 0xffff=all)");
 
-static struct ucc_geth_info ugeth_primary_info = {
+static int ucc_geth_thread_count(enum ucc_geth_num_of_threads idx)
+{
+       static const u8 count[] = {
+               [UCC_GETH_NUM_OF_THREADS_1] = 1,
+               [UCC_GETH_NUM_OF_THREADS_2] = 2,
+               [UCC_GETH_NUM_OF_THREADS_4] = 4,
+               [UCC_GETH_NUM_OF_THREADS_6] = 6,
+               [UCC_GETH_NUM_OF_THREADS_8] = 8,
+       };
+       if (idx >= ARRAY_SIZE(count))
+               return 0;
+       return count[idx];
+}
+
+static inline int ucc_geth_tx_queues(const struct ucc_geth_info *info)
+{
+       return 1;
+}
+
+static inline int ucc_geth_rx_queues(const struct ucc_geth_info *info)
+{
+       return 1;
+}
+
+static const struct ucc_geth_info ugeth_primary_info = {
        .uf_info = {
-                   .bd_mem_part = MEM_PART_SYSTEM,
                    .rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES,
                    .max_rx_buf_length = 1536,
                    /* adjusted at startup if max-speed 1000 */
@@ -90,8 +113,6 @@ static struct ucc_geth_info ugeth_primary_info = {
                    .tcrc = UCC_FAST_16_BIT_CRC,
                    .synl = UCC_FAST_SYNC_LEN_NOT_USED,
                    },
-       .numQueuesTx = 1,
-       .numQueuesRx = 1,
        .extendedFilteringChainPointer = ((uint32_t) NULL),
        .typeorlen = 3072 /*1536 */ ,
        .nonBackToBackIfgPart1 = 0x40,
@@ -157,8 +178,6 @@ static struct ucc_geth_info ugeth_primary_info = {
        .riscRx = QE_RISC_ALLOCATION_RISC1_AND_RISC2,
 };
 
-static struct ucc_geth_info ugeth_info[8];
-
 #ifdef DEBUG
 static void mem_disp(u8 *addr, int size)
 {
@@ -558,7 +577,7 @@ static void dump_bds(struct ucc_geth_private *ugeth)
        int i;
        int length;
 
-       for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) {
+       for (i = 0; i < ucc_geth_tx_queues(ugeth->ug_info); i++) {
                if (ugeth->p_tx_bd_ring[i]) {
                        length =
                            (ugeth->ug_info->bdRingLenTx[i] *
@@ -567,7 +586,7 @@ static void dump_bds(struct ucc_geth_private *ugeth)
                        mem_disp(ugeth->p_tx_bd_ring[i], length);
                }
        }
-       for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) {
+       for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) {
                if (ugeth->p_rx_bd_ring[i]) {
                        length =
                            (ugeth->ug_info->bdRingLenRx[i] *
@@ -671,32 +690,12 @@ static void dump_regs(struct ucc_geth_private *ugeth)
                in_be32(&ugeth->ug_regs->scam));
 
        if (ugeth->p_thread_data_tx) {
-               int numThreadsTxNumerical;
-               switch (ugeth->ug_info->numThreadsTx) {
-               case UCC_GETH_NUM_OF_THREADS_1:
-                       numThreadsTxNumerical = 1;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_2:
-                       numThreadsTxNumerical = 2;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_4:
-                       numThreadsTxNumerical = 4;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_6:
-                       numThreadsTxNumerical = 6;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_8:
-                       numThreadsTxNumerical = 8;
-                       break;
-               default:
-                       numThreadsTxNumerical = 0;
-                       break;
-               }
+               int count = ucc_geth_thread_count(ugeth->ug_info->numThreadsTx);
 
                pr_info("Thread data TXs:\n");
                pr_info("Base address: 0x%08x\n",
                        (u32)ugeth->p_thread_data_tx);
-               for (i = 0; i < numThreadsTxNumerical; i++) {
+               for (i = 0; i < count; i++) {
                        pr_info("Thread data TX[%d]:\n", i);
                        pr_info("Base address: 0x%08x\n",
                                (u32)&ugeth->p_thread_data_tx[i]);
@@ -705,32 +704,12 @@ static void dump_regs(struct ucc_geth_private *ugeth)
                }
        }
        if (ugeth->p_thread_data_rx) {
-               int numThreadsRxNumerical;
-               switch (ugeth->ug_info->numThreadsRx) {
-               case UCC_GETH_NUM_OF_THREADS_1:
-                       numThreadsRxNumerical = 1;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_2:
-                       numThreadsRxNumerical = 2;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_4:
-                       numThreadsRxNumerical = 4;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_6:
-                       numThreadsRxNumerical = 6;
-                       break;
-               case UCC_GETH_NUM_OF_THREADS_8:
-                       numThreadsRxNumerical = 8;
-                       break;
-               default:
-                       numThreadsRxNumerical = 0;
-                       break;
-               }
+               int count = ucc_geth_thread_count(ugeth->ug_info->numThreadsRx);
 
                pr_info("Thread data RX:\n");
                pr_info("Base address: 0x%08x\n",
                        (u32)ugeth->p_thread_data_rx);
-               for (i = 0; i < numThreadsRxNumerical; i++) {
+               for (i = 0; i < count; i++) {
                        pr_info("Thread data RX[%d]:\n", i);
                        pr_info("Base address: 0x%08x\n",
                                (u32)&ugeth->p_thread_data_rx[i]);
@@ -905,7 +884,7 @@ static void dump_regs(struct ucc_geth_private *ugeth)
        if (ugeth->p_send_q_mem_reg) {
                pr_info("Send Q memory registers:\n");
                pr_info("Base address: 0x%08x\n", (u32)ugeth->p_send_q_mem_reg);
-               for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) {
+               for (i = 0; i < ucc_geth_tx_queues(ugeth->ug_info); i++) {
                        pr_info("SQQD[%d]:\n", i);
                        pr_info("Base address: 0x%08x\n",
                                (u32)&ugeth->p_send_q_mem_reg->sqqd[i]);
@@ -937,7 +916,7 @@ static void dump_regs(struct ucc_geth_private *ugeth)
                pr_info("RX IRQ coalescing tables:\n");
                pr_info("Base address: 0x%08x\n",
                        (u32)ugeth->p_rx_irq_coalescing_tbl);
-               for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) {
+               for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) {
                        pr_info("RX IRQ coalescing table entry[%d]:\n", i);
                        pr_info("Base address: 0x%08x\n",
                                (u32)&ugeth->p_rx_irq_coalescing_tbl->
@@ -959,7 +938,7 @@ static void dump_regs(struct ucc_geth_private *ugeth)
        if (ugeth->p_rx_bd_qs_tbl) {
                pr_info("RX BD QS tables:\n");
                pr_info("Base address: 0x%08x\n", (u32)ugeth->p_rx_bd_qs_tbl);
-               for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) {
+               for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) {
                        pr_info("RX BD QS table[%d]:\n", i);
                        pr_info("Base address: 0x%08x\n",
                                (u32)&ugeth->p_rx_bd_qs_tbl[i]);
@@ -1835,7 +1814,7 @@ static void ucc_geth_free_rx(struct ucc_geth_private *ugeth)
        ug_info = ugeth->ug_info;
        uf_info = &ug_info->uf_info;
 
-       for (i = 0; i < ugeth->ug_info->numQueuesRx; i++) {
+       for (i = 0; i < ucc_geth_rx_queues(ugeth->ug_info); i++) {
                if (ugeth->p_rx_bd_ring[i]) {
                        /* Return existing data buffers in ring */
                        bd = ugeth->p_rx_bd_ring[i];
@@ -1856,12 +1835,7 @@ static void ucc_geth_free_rx(struct ucc_geth_private *ugeth)
 
                        kfree(ugeth->rx_skbuff[i]);
 
-                       if (ugeth->ug_info->uf_info.bd_mem_part ==
-                           MEM_PART_SYSTEM)
-                               kfree((void *)ugeth->rx_bd_ring_offset[i]);
-                       else if (ugeth->ug_info->uf_info.bd_mem_part ==
-                                MEM_PART_MURAM)
-                               qe_muram_free(ugeth->rx_bd_ring_offset[i]);
+                       kfree(ugeth->p_rx_bd_ring[i]);
                        ugeth->p_rx_bd_ring[i] = NULL;
                }
        }
@@ -1880,7 +1854,7 @@ static void ucc_geth_free_tx(struct ucc_geth_private *ugeth)
        ug_info = ugeth->ug_info;
        uf_info = &ug_info->uf_info;
 
-       for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) {
+       for (i = 0; i < ucc_geth_tx_queues(ugeth->ug_info); i++) {
                bd = ugeth->p_tx_bd_ring[i];
                if (!bd)
                        continue;
@@ -1898,15 +1872,8 @@ static void ucc_geth_free_tx(struct ucc_geth_private *ugeth)
 
                kfree(ugeth->tx_skbuff[i]);
 
-               if (ugeth->p_tx_bd_ring[i]) {
-                       if (ugeth->ug_info->uf_info.bd_mem_part ==
-                           MEM_PART_SYSTEM)
-                               kfree((void *)ugeth->tx_bd_ring_offset[i]);
-                       else if (ugeth->ug_info->uf_info.bd_mem_part ==
-                                MEM_PART_MURAM)
-                               qe_muram_free(ugeth->tx_bd_ring_offset[i]);
-                       ugeth->p_tx_bd_ring[i] = NULL;
-               }
+               kfree(ugeth->p_tx_bd_ring[i]);
+               ugeth->p_tx_bd_ring[i] = NULL;
        }
 
 }
@@ -1921,50 +1888,39 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth)
                ugeth->uccf = NULL;
        }
 
-       if (ugeth->p_thread_data_tx) {
-               qe_muram_free(ugeth->thread_dat_tx_offset);
-               ugeth->p_thread_data_tx = NULL;
-       }
-       if (ugeth->p_thread_data_rx) {
-               qe_muram_free(ugeth->thread_dat_rx_offset);
-               ugeth->p_thread_data_rx = NULL;
-       }
-       if (ugeth->p_exf_glbl_param) {
-               qe_muram_free(ugeth->exf_glbl_param_offset);
-               ugeth->p_exf_glbl_param = NULL;
-       }
-       if (ugeth->p_rx_glbl_pram) {
-               qe_muram_free(ugeth->rx_glbl_pram_offset);
-               ugeth->p_rx_glbl_pram = NULL;
-       }
-       if (ugeth->p_tx_glbl_pram) {
-               qe_muram_free(ugeth->tx_glbl_pram_offset);
-               ugeth->p_tx_glbl_pram = NULL;
-       }
-       if (ugeth->p_send_q_mem_reg) {
-               qe_muram_free(ugeth->send_q_mem_reg_offset);
-               ugeth->p_send_q_mem_reg = NULL;
-       }
-       if (ugeth->p_scheduler) {
-               qe_muram_free(ugeth->scheduler_offset);
-               ugeth->p_scheduler = NULL;
-       }
-       if (ugeth->p_tx_fw_statistics_pram) {
-               qe_muram_free(ugeth->tx_fw_statistics_pram_offset);
-               ugeth->p_tx_fw_statistics_pram = NULL;
-       }
-       if (ugeth->p_rx_fw_statistics_pram) {
-               qe_muram_free(ugeth->rx_fw_statistics_pram_offset);
-               ugeth->p_rx_fw_statistics_pram = NULL;
-       }
-       if (ugeth->p_rx_irq_coalescing_tbl) {
-               qe_muram_free(ugeth->rx_irq_coalescing_tbl_offset);
-               ugeth->p_rx_irq_coalescing_tbl = NULL;
-       }
-       if (ugeth->p_rx_bd_qs_tbl) {
-               qe_muram_free(ugeth->rx_bd_qs_tbl_offset);
-               ugeth->p_rx_bd_qs_tbl = NULL;
-       }
+       qe_muram_free_addr(ugeth->p_thread_data_tx);
+       ugeth->p_thread_data_tx = NULL;
+
+       qe_muram_free_addr(ugeth->p_thread_data_rx);
+       ugeth->p_thread_data_rx = NULL;
+
+       qe_muram_free_addr(ugeth->p_exf_glbl_param);
+       ugeth->p_exf_glbl_param = NULL;
+
+       qe_muram_free_addr(ugeth->p_rx_glbl_pram);
+       ugeth->p_rx_glbl_pram = NULL;
+
+       qe_muram_free_addr(ugeth->p_tx_glbl_pram);
+       ugeth->p_tx_glbl_pram = NULL;
+
+       qe_muram_free_addr(ugeth->p_send_q_mem_reg);
+       ugeth->p_send_q_mem_reg = NULL;
+
+       qe_muram_free_addr(ugeth->p_scheduler);
+       ugeth->p_scheduler = NULL;
+
+       qe_muram_free_addr(ugeth->p_tx_fw_statistics_pram);
+       ugeth->p_tx_fw_statistics_pram = NULL;
+
+       qe_muram_free_addr(ugeth->p_rx_fw_statistics_pram);
+       ugeth->p_rx_fw_statistics_pram = NULL;
+
+       qe_muram_free_addr(ugeth->p_rx_irq_coalescing_tbl);
+       ugeth->p_rx_irq_coalescing_tbl = NULL;
+
+       qe_muram_free_addr(ugeth->p_rx_bd_qs_tbl);
+       ugeth->p_rx_bd_qs_tbl = NULL;
+
        if (ugeth->p_init_enet_param_shadow) {
                return_init_enet_entries(ugeth,
                                         &(ugeth->p_init_enet_param_shadow->
@@ -2073,15 +2029,8 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        ug_info = ugeth->ug_info;
        uf_info = &ug_info->uf_info;
 
-       if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) ||
-             (uf_info->bd_mem_part == MEM_PART_MURAM))) {
-               if (netif_msg_probe(ugeth))
-                       pr_err("Bad memory partition value\n");
-               return -EINVAL;
-       }
-
        /* Rx BD lengths */
-       for (i = 0; i < ug_info->numQueuesRx; i++) {
+       for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) {
                if ((ug_info->bdRingLenRx[i] < UCC_GETH_RX_BD_RING_SIZE_MIN) ||
                    (ug_info->bdRingLenRx[i] %
                     UCC_GETH_RX_BD_RING_SIZE_ALIGNMENT)) {
@@ -2092,7 +2041,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        }
 
        /* Tx BD lengths */
-       for (i = 0; i < ug_info->numQueuesTx; i++) {
+       for (i = 0; i < ucc_geth_tx_queues(ug_info); i++) {
                if (ug_info->bdRingLenTx[i] < UCC_GETH_TX_BD_RING_SIZE_MIN) {
                        if (netif_msg_probe(ugeth))
                                pr_err("Tx BD ring length must be no smaller than 2\n");
@@ -2109,14 +2058,14 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
        }
 
        /* num Tx queues */
-       if (ug_info->numQueuesTx > NUM_TX_QUEUES) {
+       if (ucc_geth_tx_queues(ug_info) > NUM_TX_QUEUES) {
                if (netif_msg_probe(ugeth))
                        pr_err("number of tx queues too large\n");
                return -EINVAL;
        }
 
        /* num Rx queues */
-       if (ug_info->numQueuesRx > NUM_RX_QUEUES) {
+       if (ucc_geth_rx_queues(ug_info) > NUM_RX_QUEUES) {
                if (netif_msg_probe(ugeth))
                        pr_err("number of rx queues too large\n");
                return -EINVAL;
@@ -2124,7 +2073,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
 
        /* l2qt */
        for (i = 0; i < UCC_GETH_VLAN_PRIORITY_MAX; i++) {
-               if (ug_info->l2qt[i] >= ug_info->numQueuesRx) {
+               if (ug_info->l2qt[i] >= ucc_geth_rx_queues(ug_info)) {
                        if (netif_msg_probe(ugeth))
                                pr_err("VLAN priority table entry must not be larger than number of Rx queues\n");
                        return -EINVAL;
@@ -2133,7 +2082,7 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
 
        /* l3qt */
        for (i = 0; i < UCC_GETH_IP_PRIORITY_MAX; i++) {
-               if (ug_info->l3qt[i] >= ug_info->numQueuesRx) {
+               if (ug_info->l3qt[i] >= ucc_geth_rx_queues(ug_info)) {
                        if (netif_msg_probe(ugeth))
                                pr_err("IP priority table entry must not be larger than number of Rx queues\n");
                        return -EINVAL;
@@ -2156,10 +2105,10 @@ static int ucc_struct_init(struct ucc_geth_private *ugeth)
 
        /* Generate uccm_mask for receive */
        uf_info->uccm_mask = ug_info->eventRegMask & UCCE_OTHER;/* Errors */
-       for (i = 0; i < ug_info->numQueuesRx; i++)
+       for (i = 0; i < ucc_geth_rx_queues(ug_info); i++)
                uf_info->uccm_mask |= (UCC_GETH_UCCE_RXF0 << i);
 
-       for (i = 0; i < ug_info->numQueuesTx; i++)
+       for (i = 0; i < ucc_geth_tx_queues(ug_info); i++)
                uf_info->uccm_mask |= (UCC_GETH_UCCE_TXB0 << i);
        /* Initialize the general fast UCC block. */
        if (ucc_fast_init(uf_info, &ugeth->uccf)) {
@@ -2198,53 +2147,32 @@ static int ucc_geth_alloc_tx(struct ucc_geth_private *ugeth)
        uf_info = &ug_info->uf_info;
 
        /* Allocate Tx bds */
-       for (j = 0; j < ug_info->numQueuesTx; j++) {
-               /* Allocate in multiple of
-                  UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT,
-                  according to spec */
-               length = ((ug_info->bdRingLenTx[j] * sizeof(struct qe_bd))
-                         / UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT)
-                   * UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT;
-               if ((ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)) %
-                   UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT)
-                       length += UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT;
-               if (uf_info->bd_mem_part == MEM_PART_SYSTEM) {
-                       u32 align = 4;
-                       if (UCC_GETH_TX_BD_RING_ALIGNMENT > 4)
-                               align = UCC_GETH_TX_BD_RING_ALIGNMENT;
-                       ugeth->tx_bd_ring_offset[j] =
-                               (u32) kmalloc((u32) (length + align), GFP_KERNEL);
-
-                       if (ugeth->tx_bd_ring_offset[j] != 0)
-                               ugeth->p_tx_bd_ring[j] =
-                                       (u8 __iomem *)((ugeth->tx_bd_ring_offset[j] +
-                                       align) & ~(align - 1));
-               } else if (uf_info->bd_mem_part == MEM_PART_MURAM) {
-                       ugeth->tx_bd_ring_offset[j] =
-                           qe_muram_alloc(length,
-                                          UCC_GETH_TX_BD_RING_ALIGNMENT);
-                       if (!IS_ERR_VALUE(ugeth->tx_bd_ring_offset[j]))
-                               ugeth->p_tx_bd_ring[j] =
-                                   (u8 __iomem *) qe_muram_addr(ugeth->
-                                                        tx_bd_ring_offset[j]);
-               }
+       for (j = 0; j < ucc_geth_tx_queues(ug_info); j++) {
+               u32 align = max(UCC_GETH_TX_BD_RING_ALIGNMENT,
+                               UCC_GETH_TX_BD_RING_SIZE_MEMORY_ALIGNMENT);
+               u32 alloc;
+
+               length = ug_info->bdRingLenTx[j] * sizeof(struct qe_bd);
+               alloc = round_up(length, align);
+               alloc = roundup_pow_of_two(alloc);
+
+               ugeth->p_tx_bd_ring[j] = kmalloc(alloc, GFP_KERNEL);
+
                if (!ugeth->p_tx_bd_ring[j]) {
                        if (netif_msg_ifup(ugeth))
                                pr_err("Can not allocate memory for Tx bd rings\n");
                        return -ENOMEM;
                }
                /* Zero unused end of bd ring, according to spec */
-               memset_io((void __iomem *)(ugeth->p_tx_bd_ring[j] +
-                      ug_info->bdRingLenTx[j] * sizeof(struct qe_bd)), 0,
-                      length - ug_info->bdRingLenTx[j] * sizeof(struct qe_bd));
+               memset(ugeth->p_tx_bd_ring[j] + length, 0, alloc - length);
        }
 
        /* Init Tx bds */
-       for (j = 0; j < ug_info->numQueuesTx; j++) {
+       for (j = 0; j < ucc_geth_tx_queues(ug_info); j++) {
                /* Setup the skbuff rings */
                ugeth->tx_skbuff[j] =
-                       kmalloc_array(ugeth->ug_info->bdRingLenTx[j],
-                                     sizeof(struct sk_buff *), GFP_KERNEL);
+                       kcalloc(ugeth->ug_info->bdRingLenTx[j],
+                               sizeof(struct sk_buff *), GFP_KERNEL);
 
                if (ugeth->tx_skbuff[j] == NULL) {
                        if (netif_msg_ifup(ugeth))
@@ -2252,9 +2180,6 @@ static int ucc_geth_alloc_tx(struct ucc_geth_private *ugeth)
                        return -ENOMEM;
                }
 
-               for (i = 0; i < ugeth->ug_info->bdRingLenTx[j]; i++)
-                       ugeth->tx_skbuff[j][i] = NULL;
-
                ugeth->skb_curtx[j] = ugeth->skb_dirtytx[j] = 0;
                bd = ugeth->confBd[j] = ugeth->txBd[j] = ugeth->p_tx_bd_ring[j];
                for (i = 0; i < ug_info->bdRingLenTx[j]; i++) {
@@ -2284,27 +2209,15 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth)
        uf_info = &ug_info->uf_info;
 
        /* Allocate Rx bds */
-       for (j = 0; j < ug_info->numQueuesRx; j++) {
+       for (j = 0; j < ucc_geth_rx_queues(ug_info); j++) {
+               u32 align = UCC_GETH_RX_BD_RING_ALIGNMENT;
+               u32 alloc;
+
                length = ug_info->bdRingLenRx[j] * sizeof(struct qe_bd);
-               if (uf_info->bd_mem_part == MEM_PART_SYSTEM) {
-                       u32 align = 4;
-                       if (UCC_GETH_RX_BD_RING_ALIGNMENT > 4)
-                               align = UCC_GETH_RX_BD_RING_ALIGNMENT;
-                       ugeth->rx_bd_ring_offset[j] =
-                               (u32) kmalloc((u32) (length + align), GFP_KERNEL);
-                       if (ugeth->rx_bd_ring_offset[j] != 0)
-                               ugeth->p_rx_bd_ring[j] =
-                                       (u8 __iomem *)((ugeth->rx_bd_ring_offset[j] +
-                                       align) & ~(align - 1));
-               } else if (uf_info->bd_mem_part == MEM_PART_MURAM) {
-                       ugeth->rx_bd_ring_offset[j] =
-                           qe_muram_alloc(length,
-                                          UCC_GETH_RX_BD_RING_ALIGNMENT);
-                       if (!IS_ERR_VALUE(ugeth->rx_bd_ring_offset[j]))
-                               ugeth->p_rx_bd_ring[j] =
-                                   (u8 __iomem *) qe_muram_addr(ugeth->
-                                                        rx_bd_ring_offset[j]);
-               }
+               alloc = round_up(length, align);
+               alloc = roundup_pow_of_two(alloc);
+
+               ugeth->p_rx_bd_ring[j] = kmalloc(alloc, GFP_KERNEL);
                if (!ugeth->p_rx_bd_ring[j]) {
                        if (netif_msg_ifup(ugeth))
                                pr_err("Can not allocate memory for Rx bd rings\n");
@@ -2313,11 +2226,11 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth)
        }
 
        /* Init Rx bds */
-       for (j = 0; j < ug_info->numQueuesRx; j++) {
+       for (j = 0; j < ucc_geth_rx_queues(ug_info); j++) {
                /* Setup the skbuff rings */
                ugeth->rx_skbuff[j] =
-                       kmalloc_array(ugeth->ug_info->bdRingLenRx[j],
-                                     sizeof(struct sk_buff *), GFP_KERNEL);
+                       kcalloc(ugeth->ug_info->bdRingLenRx[j],
+                               sizeof(struct sk_buff *), GFP_KERNEL);
 
                if (ugeth->rx_skbuff[j] == NULL) {
                        if (netif_msg_ifup(ugeth))
@@ -2325,9 +2238,6 @@ static int ucc_geth_alloc_rx(struct ucc_geth_private *ugeth)
                        return -ENOMEM;
                }
 
-               for (i = 0; i < ugeth->ug_info->bdRingLenRx[j]; i++)
-                       ugeth->rx_skbuff[j][i] = NULL;
-
                ugeth->skb_currx[j] = 0;
                bd = ugeth->rxBd[j] = ugeth->p_rx_bd_ring[j];
                for (i = 0; i < ug_info->bdRingLenRx[j]; i++) {
@@ -2359,10 +2269,10 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        u32 init_enet_pram_offset, cecr_subblock, command;
        u32 ifstat, i, j, size, l2qt, l3qt;
        u16 temoder = UCC_GETH_TEMODER_INIT;
-       u16 test;
        u8 function_code = 0;
        u8 __iomem *endOfRing;
        u8 numThreadsRxNumerical, numThreadsTxNumerical;
+       s32 rx_glbl_pram_offset, tx_glbl_pram_offset;
 
        ugeth_vdbg("%s: IN", __func__);
        uccf = ugeth->uccf;
@@ -2371,45 +2281,15 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        uf_regs = uccf->uf_regs;
        ug_regs = ugeth->ug_regs;
 
-       switch (ug_info->numThreadsRx) {
-       case UCC_GETH_NUM_OF_THREADS_1:
-               numThreadsRxNumerical = 1;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_2:
-               numThreadsRxNumerical = 2;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_4:
-               numThreadsRxNumerical = 4;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_6:
-               numThreadsRxNumerical = 6;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_8:
-               numThreadsRxNumerical = 8;
-               break;
-       default:
+       numThreadsRxNumerical = ucc_geth_thread_count(ug_info->numThreadsRx);
+       if (!numThreadsRxNumerical) {
                if (netif_msg_ifup(ugeth))
                        pr_err("Bad number of Rx threads value\n");
                return -EINVAL;
        }
 
-       switch (ug_info->numThreadsTx) {
-       case UCC_GETH_NUM_OF_THREADS_1:
-               numThreadsTxNumerical = 1;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_2:
-               numThreadsTxNumerical = 2;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_4:
-               numThreadsTxNumerical = 4;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_6:
-               numThreadsTxNumerical = 6;
-               break;
-       case UCC_GETH_NUM_OF_THREADS_8:
-               numThreadsTxNumerical = 8;
-               break;
-       default:
+       numThreadsTxNumerical = ucc_geth_thread_count(ug_info->numThreadsTx);
+       if (!numThreadsTxNumerical) {
                if (netif_msg_ifup(ugeth))
                        pr_err("Bad number of Tx threads value\n");
                return -EINVAL;
@@ -2507,20 +2387,15 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
         */
        /* Tx global PRAM */
        /* Allocate global tx parameter RAM page */
-       ugeth->tx_glbl_pram_offset =
+       tx_glbl_pram_offset =
            qe_muram_alloc(sizeof(struct ucc_geth_tx_global_pram),
                           UCC_GETH_TX_GLOBAL_PRAM_ALIGNMENT);
-       if (IS_ERR_VALUE(ugeth->tx_glbl_pram_offset)) {
+       if (tx_glbl_pram_offset < 0) {
                if (netif_msg_ifup(ugeth))
                        pr_err("Can not allocate DPRAM memory for p_tx_glbl_pram\n");
                return -ENOMEM;
        }
-       ugeth->p_tx_glbl_pram =
-           (struct ucc_geth_tx_global_pram __iomem *) qe_muram_addr(ugeth->
-                                                       tx_glbl_pram_offset);
-       /* Zero out p_tx_glbl_pram */
-       memset_io((void __iomem *)ugeth->p_tx_glbl_pram, 0, sizeof(struct ucc_geth_tx_global_pram));
-
+       ugeth->p_tx_glbl_pram = qe_muram_addr(tx_glbl_pram_offset);
        /* Fill global PRAM */
 
        /* TQPTR */
@@ -2554,7 +2429,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        /* SQPTR */
        /* Size varies with number of Tx queues */
        ugeth->send_q_mem_reg_offset =
-           qe_muram_alloc(ug_info->numQueuesTx *
+           qe_muram_alloc(ucc_geth_tx_queues(ug_info) *
                           sizeof(struct ucc_geth_send_queue_qd),
                           UCC_GETH_SEND_QUEUE_QUEUE_DESCRIPTOR_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->send_q_mem_reg_offset)) {
@@ -2570,29 +2445,20 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
 
        /* Setup the table */
        /* Assume BD rings are already established */
-       for (i = 0; i < ug_info->numQueuesTx; i++) {
+       for (i = 0; i < ucc_geth_tx_queues(ug_info); i++) {
                endOfRing =
                    ugeth->p_tx_bd_ring[i] + (ug_info->bdRingLenTx[i] -
                                              1) * sizeof(struct qe_bd);
-               if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) {
-                       out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base,
-                                (u32) virt_to_phys(ugeth->p_tx_bd_ring[i]));
-                       out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].
-                                last_bd_completed_address,
-                                (u32) virt_to_phys(endOfRing));
-               } else if (ugeth->ug_info->uf_info.bd_mem_part ==
-                          MEM_PART_MURAM) {
-                       out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base,
-                                (u32)qe_muram_dma(ugeth->p_tx_bd_ring[i]));
-                       out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].
-                                last_bd_completed_address,
-                                (u32)qe_muram_dma(endOfRing));
-               }
+               out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].bd_ring_base,
+                        (u32) virt_to_phys(ugeth->p_tx_bd_ring[i]));
+               out_be32(&ugeth->p_send_q_mem_reg->sqqd[i].
+                        last_bd_completed_address,
+                        (u32) virt_to_phys(endOfRing));
        }
 
        /* schedulerbasepointer */
 
-       if (ug_info->numQueuesTx > 1) {
+       if (ucc_geth_tx_queues(ug_info) > 1) {
        /* scheduler exists only if more than 1 tx queue */
                ugeth->scheduler_offset =
                    qe_muram_alloc(sizeof(struct ucc_geth_scheduler),
@@ -2608,8 +2474,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                                                           scheduler_offset);
                out_be32(&ugeth->p_tx_glbl_pram->schedulerbasepointer,
                         ugeth->scheduler_offset);
-               /* Zero out p_scheduler */
-               memset_io((void __iomem *)ugeth->p_scheduler, 0, sizeof(struct ucc_geth_scheduler));
 
                /* Set values in scheduler */
                out_be32(&ugeth->p_scheduler->mblinterval,
@@ -2652,23 +2516,18 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                ugeth->p_tx_fw_statistics_pram =
                    (struct ucc_geth_tx_firmware_statistics_pram __iomem *)
                    qe_muram_addr(ugeth->tx_fw_statistics_pram_offset);
-               /* Zero out p_tx_fw_statistics_pram */
-               memset_io((void __iomem *)ugeth->p_tx_fw_statistics_pram,
-                      0, sizeof(struct ucc_geth_tx_firmware_statistics_pram));
        }
 
        /* temoder */
        /* Already has speed set */
 
-       if (ug_info->numQueuesTx > 1)
+       if (ucc_geth_tx_queues(ug_info) > 1)
                temoder |= TEMODER_SCHEDULER_ENABLE;
        if (ug_info->ipCheckSumGenerate)
                temoder |= TEMODER_IP_CHECKSUM_GENERATE;
-       temoder |= ((ug_info->numQueuesTx - 1) << TEMODER_NUM_OF_QUEUES_SHIFT);
+       temoder |= ((ucc_geth_tx_queues(ug_info) - 1) << TEMODER_NUM_OF_QUEUES_SHIFT);
        out_be16(&ugeth->p_tx_glbl_pram->temoder, temoder);
 
-       test = in_be16(&ugeth->p_tx_glbl_pram->temoder);
-
        /* Function code register value to be used later */
        function_code = UCC_BMR_BO_BE | UCC_BMR_GBL;
        /* Required for QE */
@@ -2678,20 +2537,15 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
 
        /* Rx global PRAM */
        /* Allocate global rx parameter RAM page */
-       ugeth->rx_glbl_pram_offset =
+       rx_glbl_pram_offset =
            qe_muram_alloc(sizeof(struct ucc_geth_rx_global_pram),
                           UCC_GETH_RX_GLOBAL_PRAM_ALIGNMENT);
-       if (IS_ERR_VALUE(ugeth->rx_glbl_pram_offset)) {
+       if (rx_glbl_pram_offset < 0) {
                if (netif_msg_ifup(ugeth))
                        pr_err("Can not allocate DPRAM memory for p_rx_glbl_pram\n");
                return -ENOMEM;
        }
-       ugeth->p_rx_glbl_pram =
-           (struct ucc_geth_rx_global_pram __iomem *) qe_muram_addr(ugeth->
-                                                       rx_glbl_pram_offset);
-       /* Zero out p_rx_glbl_pram */
-       memset_io((void __iomem *)ugeth->p_rx_glbl_pram, 0, sizeof(struct ucc_geth_rx_global_pram));
-
+       ugeth->p_rx_glbl_pram = qe_muram_addr(rx_glbl_pram_offset);
        /* Fill global PRAM */
 
        /* RQPTR */
@@ -2729,16 +2583,13 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                ugeth->p_rx_fw_statistics_pram =
                    (struct ucc_geth_rx_firmware_statistics_pram __iomem *)
                    qe_muram_addr(ugeth->rx_fw_statistics_pram_offset);
-               /* Zero out p_rx_fw_statistics_pram */
-               memset_io((void __iomem *)ugeth->p_rx_fw_statistics_pram, 0,
-                      sizeof(struct ucc_geth_rx_firmware_statistics_pram));
        }
 
        /* intCoalescingPtr */
 
        /* Size varies with number of Rx queues */
        ugeth->rx_irq_coalescing_tbl_offset =
-           qe_muram_alloc(ug_info->numQueuesRx *
+           qe_muram_alloc(ucc_geth_rx_queues(ug_info) *
                           sizeof(struct ucc_geth_rx_interrupt_coalescing_entry)
                           + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT);
        if (IS_ERR_VALUE(ugeth->rx_irq_coalescing_tbl_offset)) {
@@ -2754,7 +2605,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
                 ugeth->rx_irq_coalescing_tbl_offset);
 
        /* Fill interrupt coalescing table */
-       for (i = 0; i < ug_info->numQueuesRx; i++) {
+       for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) {
                out_be32(&ugeth->p_rx_irq_coalescing_tbl->coalescingentry[i].
                         interruptcoalescingmaxvalue,
                         ug_info->interruptcoalescingmaxvalue[i]);
@@ -2803,7 +2654,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        /* RBDQPTR */
        /* Size varies with number of Rx queues */
        ugeth->rx_bd_qs_tbl_offset =
-           qe_muram_alloc(ug_info->numQueuesRx *
+           qe_muram_alloc(ucc_geth_rx_queues(ug_info) *
                           (sizeof(struct ucc_geth_rx_bd_queues_entry) +
                            sizeof(struct ucc_geth_rx_prefetched_bds)),
                           UCC_GETH_RX_BD_QUEUES_ALIGNMENT);
@@ -2817,23 +2668,12 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
            (struct ucc_geth_rx_bd_queues_entry __iomem *) qe_muram_addr(ugeth->
                                    rx_bd_qs_tbl_offset);
        out_be32(&ugeth->p_rx_glbl_pram->rbdqptr, ugeth->rx_bd_qs_tbl_offset);
-       /* Zero out p_rx_bd_qs_tbl */
-       memset_io((void __iomem *)ugeth->p_rx_bd_qs_tbl,
-              0,
-              ug_info->numQueuesRx * (sizeof(struct ucc_geth_rx_bd_queues_entry) +
-                                      sizeof(struct ucc_geth_rx_prefetched_bds)));
 
        /* Setup the table */
        /* Assume BD rings are already established */
-       for (i = 0; i < ug_info->numQueuesRx; i++) {
-               if (ugeth->ug_info->uf_info.bd_mem_part == MEM_PART_SYSTEM) {
-                       out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
-                                (u32) virt_to_phys(ugeth->p_rx_bd_ring[i]));
-               } else if (ugeth->ug_info->uf_info.bd_mem_part ==
-                          MEM_PART_MURAM) {
-                       out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
-                                (u32)qe_muram_dma(ugeth->p_rx_bd_ring[i]));
-               }
+       for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) {
+               out_be32(&ugeth->p_rx_bd_qs_tbl[i].externalbdbaseptr,
+                        (u32) virt_to_phys(ugeth->p_rx_bd_ring[i]));
                /* rest of fields handled by QE */
        }
 
@@ -2854,7 +2694,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
            ug_info->
            vlanOperationNonTagged << REMODER_VLAN_OPERATION_NON_TAGGED_SHIFT;
        remoder |= ug_info->rxQoSMode << REMODER_RX_QOS_MODE_SHIFT;
-       remoder |= ((ug_info->numQueuesRx - 1) << REMODER_NUM_OF_QUEUES_SHIFT);
+       remoder |= ((ucc_geth_rx_queues(ug_info) - 1) << REMODER_NUM_OF_QUEUES_SHIFT);
        if (ug_info->ipCheckSumCheck)
                remoder |= REMODER_IP_CHECKSUM_CHECK;
        if (ug_info->ipAddressAlignment)
@@ -2937,14 +2777,11 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
         * allocated resources can be released when the channel is freed.
         */
        if (!(ugeth->p_init_enet_param_shadow =
-             kmalloc(sizeof(struct ucc_geth_init_pram), GFP_KERNEL))) {
+             kzalloc(sizeof(struct ucc_geth_init_pram), GFP_KERNEL))) {
                if (netif_msg_ifup(ugeth))
                        pr_err("Can not allocate memory for p_UccInitEnetParamShadows\n");
                return -ENOMEM;
        }
-       /* Zero out *p_init_enet_param_shadow */
-       memset((char *)ugeth->p_init_enet_param_shadow,
-              0, sizeof(struct ucc_geth_init_pram));
 
        /* Fill shadow InitEnet command parameter structure */
 
@@ -2964,7 +2801,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
            ((u32) ug_info->numThreadsTx) << ENET_INIT_PARAM_TGF_SHIFT;
 
        ugeth->p_init_enet_param_shadow->rgftgfrxglobal |=
-           ugeth->rx_glbl_pram_offset | ug_info->riscRx;
+           rx_glbl_pram_offset | ug_info->riscRx;
        if ((ug_info->largestexternallookupkeysize !=
             QE_FLTR_LARGEST_EXTERNAL_TABLE_LOOKUP_KEY_SIZE_NONE) &&
            (ug_info->largestexternallookupkeysize !=
@@ -3002,7 +2839,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        }
 
        ugeth->p_init_enet_param_shadow->txglobal =
-           ugeth->tx_glbl_pram_offset | ug_info->riscTx;
+           tx_glbl_pram_offset | ug_info->riscTx;
        if ((ret_val =
             fill_init_enet_entries(ugeth,
                                    &(ugeth->p_init_enet_param_shadow->
@@ -3016,7 +2853,7 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth)
        }
 
        /* Load Rx bds with buffers */
-       for (i = 0; i < ug_info->numQueuesRx; i++) {
+       for (i = 0; i < ucc_geth_rx_queues(ug_info); i++) {
                if ((ret_val = rx_bd_buffer_set(ugeth, (u8) i)) != 0) {
                        if (netif_msg_ifup(ugeth))
                                pr_err("Can not fill Rx bds with buffers\n");
@@ -3287,12 +3124,12 @@ static int ucc_geth_poll(struct napi_struct *napi, int budget)
 
        /* Tx event processing */
        spin_lock(&ugeth->lock);
-       for (i = 0; i < ug_info->numQueuesTx; i++)
+       for (i = 0; i < ucc_geth_tx_queues(ug_info); i++)
                ucc_geth_tx(ugeth->ndev, i);
        spin_unlock(&ugeth->lock);
 
        howmany = 0;
-       for (i = 0; i < ug_info->numQueuesRx; i++)
+       for (i = 0; i < ucc_geth_rx_queues(ug_info); i++)
                howmany += ucc_geth_rx(ugeth, i, budget - howmany);
 
        if (howmany < budget) {
@@ -3685,6 +3522,36 @@ static const struct net_device_ops ucc_geth_netdev_ops = {
 #endif
 };
 
+static int ucc_geth_parse_clock(struct device_node *np, const char *which,
+                               enum qe_clock *out)
+{
+       const char *sprop;
+       char buf[24];
+
+       snprintf(buf, sizeof(buf), "%s-clock-name", which);
+       sprop = of_get_property(np, buf, NULL);
+       if (sprop) {
+               *out = qe_clock_source(sprop);
+       } else {
+               u32 val;
+
+               snprintf(buf, sizeof(buf), "%s-clock", which);
+               if (of_property_read_u32(np, buf, &val)) {
+                       /* If both *-clock-name and *-clock are missing,
+                        * we want to tell people to use *-clock-name.
+                        */
+                       pr_err("missing %s-clock-name property\n", buf);
+                       return -EINVAL;
+               }
+               *out = val;
+       }
+       if (*out < QE_CLK_NONE || *out > QE_CLK24) {
+               pr_err("invalid %s property\n", buf);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int ucc_geth_probe(struct platform_device* ofdev)
 {
        struct device *device = &ofdev->dev;
@@ -3695,7 +3562,6 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        struct resource res;
        int err, ucc_num, max_speed = 0;
        const unsigned int *prop;
-       const char *sprop;
        const void *mac_addr;
        phy_interface_t phy_interface;
        static const int enet_to_speed[] = {
@@ -3725,62 +3591,23 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        if ((ucc_num < 0) || (ucc_num > 7))
                return -ENODEV;
 
-       ug_info = &ugeth_info[ucc_num];
-       if (ug_info == NULL) {
-               if (netif_msg_probe(&debug))
-                       pr_err("[%d] Missing additional data!\n", ucc_num);
-               return -ENODEV;
-       }
+       ug_info = kmalloc(sizeof(*ug_info), GFP_KERNEL);
+       if (ug_info == NULL)
+               return -ENOMEM;
+       memcpy(ug_info, &ugeth_primary_info, sizeof(*ug_info));
 
        ug_info->uf_info.ucc_num = ucc_num;
 
-       sprop = of_get_property(np, "rx-clock-name", NULL);
-       if (sprop) {
-               ug_info->uf_info.rx_clock = qe_clock_source(sprop);
-               if ((ug_info->uf_info.rx_clock < QE_CLK_NONE) ||
-                   (ug_info->uf_info.rx_clock > QE_CLK24)) {
-                       pr_err("invalid rx-clock-name property\n");
-                       return -EINVAL;
-               }
-       } else {
-               prop = of_get_property(np, "rx-clock", NULL);
-               if (!prop) {
-                       /* If both rx-clock-name and rx-clock are missing,
-                          we want to tell people to use rx-clock-name. */
-                       pr_err("missing rx-clock-name property\n");
-                       return -EINVAL;
-               }
-               if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
-                       pr_err("invalid rx-clock property\n");
-                       return -EINVAL;
-               }
-               ug_info->uf_info.rx_clock = *prop;
-       }
-
-       sprop = of_get_property(np, "tx-clock-name", NULL);
-       if (sprop) {
-               ug_info->uf_info.tx_clock = qe_clock_source(sprop);
-               if ((ug_info->uf_info.tx_clock < QE_CLK_NONE) ||
-                   (ug_info->uf_info.tx_clock > QE_CLK24)) {
-                       pr_err("invalid tx-clock-name property\n");
-                       return -EINVAL;
-               }
-       } else {
-               prop = of_get_property(np, "tx-clock", NULL);
-               if (!prop) {
-                       pr_err("missing tx-clock-name property\n");
-                       return -EINVAL;
-               }
-               if ((*prop < QE_CLK_NONE) || (*prop > QE_CLK24)) {
-                       pr_err("invalid tx-clock property\n");
-                       return -EINVAL;
-               }
-               ug_info->uf_info.tx_clock = *prop;
-       }
+       err = ucc_geth_parse_clock(np, "rx", &ug_info->uf_info.rx_clock);
+       if (err)
+               goto err_free_info;
+       err = ucc_geth_parse_clock(np, "tx", &ug_info->uf_info.tx_clock);
+       if (err)
+               goto err_free_info;
 
        err = of_address_to_resource(np, 0, &res);
        if (err)
-               return -EINVAL;
+               goto err_free_info;
 
        ug_info->uf_info.regs = res.start;
        ug_info->uf_info.irq = irq_of_parse_and_map(np, 0);
@@ -3793,7 +3620,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
                 */
                err = of_phy_register_fixed_link(np);
                if (err)
-                       return err;
+                       goto err_free_info;
                ug_info->phy_node = of_node_get(np);
        }
 
@@ -3889,6 +3716,7 @@ static int ucc_geth_probe(struct platform_device* ofdev)
        INIT_WORK(&ugeth->timeout_work, ucc_geth_timeout_work);
        netif_napi_add(dev, &ugeth->napi, ucc_geth_poll, 64);
        dev->mtu = 1500;
+       dev->max_mtu = 1518;
 
        ugeth->msg_enable = netif_msg_init(debug.msg_enable, UGETH_MSG_DEFAULT);
        ugeth->phy_interface = phy_interface;
@@ -3923,6 +3751,8 @@ err_deregister_fixed_link:
                of_phy_deregister_fixed_link(np);
        of_node_put(ug_info->tbi_node);
        of_node_put(ug_info->phy_node);
+err_free_info:
+       kfree(ug_info);
 
        return err;
 }
@@ -3934,12 +3764,13 @@ static int ucc_geth_remove(struct platform_device* ofdev)
        struct device_node *np = ofdev->dev.of_node;
 
        unregister_netdev(dev);
-       free_netdev(dev);
        ucc_geth_memclean(ugeth);
        if (of_phy_is_fixed_link(np))
                of_phy_deregister_fixed_link(np);
        of_node_put(ugeth->ug_info->tbi_node);
        of_node_put(ugeth->ug_info->phy_node);
+       kfree(ugeth->ug_info);
+       free_netdev(dev);
 
        return 0;
 }
@@ -3967,17 +3798,10 @@ static struct platform_driver ucc_geth_driver = {
 
 static int __init ucc_geth_init(void)
 {
-       int i, ret;
-
        if (netif_msg_drv(&debug))
                pr_info(DRV_DESC "\n");
-       for (i = 0; i < 8; i++)
-               memcpy(&(ugeth_info[i]), &ugeth_primary_info,
-                      sizeof(ugeth_primary_info));
-
-       ret = platform_driver_register(&ucc_geth_driver);
 
-       return ret;
+       return platform_driver_register(&ucc_geth_driver);
 }
 
 static void __exit ucc_geth_exit(void)
index 1a9bdf6..4294ed0 100644 (file)
@@ -575,7 +575,14 @@ struct ucc_geth_tx_global_pram {
        u32 vtagtable[0x8];     /* 8 4-byte VLAN tags */
        u32 tqptr;              /* a base pointer to the Tx Queues Memory
                                   Region */
-       u8 res2[0x80 - 0x74];
+       u8 res2[0x78 - 0x74];
+       u64 snums_en;
+       u32 l2l3baseptr;        /* top byte consists of a few other bit fields */
+
+       u16 mtu[8];
+       u8 res3[0xa8 - 0x94];
+       u32 wrrtablebase;       /* top byte is reserved */
+       u8 res4[0xc0 - 0xac];
 } __packed;
 
 /* structure representing Extended Filtering Global Parameters in PRAM */
@@ -1069,8 +1076,6 @@ struct ucc_geth_tad_params {
 /* GETH protocol initialization structure */
 struct ucc_geth_info {
        struct ucc_fast_info uf_info;
-       u8 numQueuesTx;
-       u8 numQueuesRx;
        int ipCheckSumCheck;
        int ipCheckSumGenerate;
        int rxExtendedFiltering;
@@ -1158,9 +1163,7 @@ struct ucc_geth_private {
        struct ucc_geth_exf_global_pram __iomem *p_exf_glbl_param;
        u32 exf_glbl_param_offset;
        struct ucc_geth_rx_global_pram __iomem *p_rx_glbl_pram;
-       u32 rx_glbl_pram_offset;
        struct ucc_geth_tx_global_pram __iomem *p_tx_glbl_pram;
-       u32 tx_glbl_pram_offset;
        struct ucc_geth_send_queue_mem_region __iomem *p_send_q_mem_reg;
        u32 send_q_mem_reg_offset;
        struct ucc_geth_thread_data_tx __iomem *p_thread_data_tx;
@@ -1178,9 +1181,7 @@ struct ucc_geth_private {
        struct ucc_geth_rx_bd_queues_entry __iomem *p_rx_bd_qs_tbl;
        u32 rx_bd_qs_tbl_offset;
        u8 __iomem *p_tx_bd_ring[NUM_TX_QUEUES];
-       u32 tx_bd_ring_offset[NUM_TX_QUEUES];
        u8 __iomem *p_rx_bd_ring[NUM_RX_QUEUES];
-       u32 rx_bd_ring_offset[NUM_RX_QUEUES];
        u8 __iomem *confBd[NUM_TX_QUEUES];
        u8 __iomem *txBd[NUM_TX_QUEUES];
        u8 __iomem *rxBd[NUM_RX_QUEUES];
index 858cb29..5d7824d 100644 (file)
@@ -1502,7 +1502,7 @@ static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb,
 {
        struct hns_nic_priv *priv = netdev_priv(ndev);
 
-       assert(skb->queue_mapping < ndev->ae_handle->q_num);
+       assert(skb->queue_mapping < priv->ae_handle->q_num);
 
        return hns_nic_net_xmit_hw(ndev, skb,
                                   &tx_ring_data(priv, skb->queue_mapping));
index 7165da0..a6e3f07 100644 (file)
@@ -415,6 +415,10 @@ static void __lb_other_process(struct hns_nic_ring_data *ring_data,
        /* for mutl buffer*/
        new_skb = skb_copy(skb, GFP_ATOMIC);
        dev_kfree_skb_any(skb);
+       if (!new_skb) {
+               netdev_err(ndev, "skb alloc failed\n");
+               return;
+       }
        skb = new_skb;
 
        check_ok = 0;
index fb5e884..33defa4 100644 (file)
@@ -169,7 +169,7 @@ struct hclgevf_mbx_arq_ring {
 #define hclge_mbx_ring_ptr_move_crq(crq) \
        (crq->next_to_use = (crq->next_to_use + 1) % crq->desc_num)
 #define hclge_mbx_tail_ptr_move_arq(arq) \
-       (arq.tail = (arq.tail + 1) % HCLGE_MBX_MAX_ARQ_MSG_SIZE)
+               (arq.tail = (arq.tail + 1) % HCLGE_MBX_MAX_ARQ_MSG_NUM)
 #define hclge_mbx_head_ptr_move_arq(arq) \
-               (arq.head = (arq.head + 1) % HCLGE_MBX_MAX_ARQ_MSG_SIZE)
+               (arq.head = (arq.head + 1) % HCLGE_MBX_MAX_ARQ_MSG_NUM)
 #endif
index 405e490..5120806 100644 (file)
@@ -1070,7 +1070,7 @@ static bool hns3_check_hw_tx_csum(struct sk_buff *skb)
         * HW checksum of the non-IP packets and GSO packets is handled at
         * different place in the following code
         */
-       if (skb->csum_not_inet || skb_is_gso(skb) ||
+       if (skb_csum_is_sctp(skb) || skb_is_gso(skb) ||
            !test_bit(HNS3_NIC_STATE_HW_TX_CSUM_ENABLE, &priv->state))
                return false;
 
index e6f37f9..c242883 100644 (file)
@@ -752,7 +752,8 @@ static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset)
                handle->flags |= HNAE3_SUPPORT_SERDES_SERIAL_LOOPBACK;
                handle->flags |= HNAE3_SUPPORT_SERDES_PARALLEL_LOOPBACK;
 
-               if (hdev->hw.mac.phydev) {
+               if (hdev->hw.mac.phydev && hdev->hw.mac.phydev->drv &&
+                   hdev->hw.mac.phydev->drv->set_loopback) {
                        count += 1;
                        handle->flags |= HNAE3_SUPPORT_PHY_LOOPBACK;
                }
@@ -4537,8 +4538,8 @@ static int hclge_set_rss_tuple(struct hnae3_handle *handle,
                req->ipv4_sctp_en = tuple_sets;
                break;
        case SCTP_V6_FLOW:
-               if ((nfc->data & RXH_L4_B_0_1) ||
-                   (nfc->data & RXH_L4_B_2_3))
+               if (hdev->ae_dev->dev_version <= HNAE3_DEVICE_VERSION_V2 &&
+                   (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)))
                        return -EINVAL;
 
                req->ipv6_sctp_en = tuple_sets;
@@ -4730,6 +4731,8 @@ static void hclge_rss_init_cfg(struct hclge_dev *hdev)
                vport[i].rss_tuple_sets.ipv6_udp_en =
                        HCLGE_RSS_INPUT_TUPLE_OTHER;
                vport[i].rss_tuple_sets.ipv6_sctp_en =
+                       hdev->ae_dev->dev_version <= HNAE3_DEVICE_VERSION_V2 ?
+                       HCLGE_RSS_INPUT_TUPLE_SCTP_NO_PORT :
                        HCLGE_RSS_INPUT_TUPLE_SCTP;
                vport[i].rss_tuple_sets.ipv6_fragment_en =
                        HCLGE_RSS_INPUT_TUPLE_OTHER;
index 50a294d..ca46bc9 100644 (file)
 #define HCLGE_D_IP_BIT                 BIT(2)
 #define HCLGE_S_IP_BIT                 BIT(3)
 #define HCLGE_V_TAG_BIT                        BIT(4)
+#define HCLGE_RSS_INPUT_TUPLE_SCTP_NO_PORT     \
+               (HCLGE_D_IP_BIT | HCLGE_S_IP_BIT | HCLGE_V_TAG_BIT)
 
 #define HCLGE_RSS_TC_SIZE_0            1
 #define HCLGE_RSS_TC_SIZE_1            2
index 145757c..674b3a2 100644 (file)
@@ -917,8 +917,8 @@ static int hclgevf_set_rss_tuple(struct hnae3_handle *handle,
                req->ipv4_sctp_en = tuple_sets;
                break;
        case SCTP_V6_FLOW:
-               if ((nfc->data & RXH_L4_B_0_1) ||
-                   (nfc->data & RXH_L4_B_2_3))
+               if (hdev->ae_dev->dev_version <= HNAE3_DEVICE_VERSION_V2 &&
+                   (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)))
                        return -EINVAL;
 
                req->ipv6_sctp_en = tuple_sets;
@@ -2502,7 +2502,10 @@ static void hclgevf_rss_init_cfg(struct hclgevf_dev *hdev)
                tuple_sets->ipv4_fragment_en = HCLGEVF_RSS_INPUT_TUPLE_OTHER;
                tuple_sets->ipv6_tcp_en = HCLGEVF_RSS_INPUT_TUPLE_OTHER;
                tuple_sets->ipv6_udp_en = HCLGEVF_RSS_INPUT_TUPLE_OTHER;
-               tuple_sets->ipv6_sctp_en = HCLGEVF_RSS_INPUT_TUPLE_SCTP;
+               tuple_sets->ipv6_sctp_en =
+                       hdev->ae_dev->dev_version <= HNAE3_DEVICE_VERSION_V2 ?
+                                       HCLGEVF_RSS_INPUT_TUPLE_SCTP_NO_PORT :
+                                       HCLGEVF_RSS_INPUT_TUPLE_SCTP;
                tuple_sets->ipv6_fragment_en = HCLGEVF_RSS_INPUT_TUPLE_OTHER;
        }
 
index 1b183bc..f6d817a 100644 (file)
 #define HCLGEVF_D_IP_BIT               BIT(2)
 #define HCLGEVF_S_IP_BIT               BIT(3)
 #define HCLGEVF_V_TAG_BIT              BIT(4)
+#define HCLGEVF_RSS_INPUT_TUPLE_SCTP_NO_PORT   \
+       (HCLGEVF_D_IP_BIT | HCLGEVF_S_IP_BIT | HCLGEVF_V_TAG_BIT)
 
 #define HCLGEVF_STATS_TIMER_INTERVAL   36U
 
index f302504..a40af51 100644 (file)
@@ -955,6 +955,7 @@ static void release_resources(struct ibmvnic_adapter *adapter)
        release_rx_pools(adapter);
 
        release_napi(adapter);
+       release_login_buffer(adapter);
        release_login_rsp_buffer(adapter);
 }
 
@@ -1383,10 +1384,10 @@ static int ibmvnic_close(struct net_device *netdev)
 
 /**
  * build_hdr_data - creates L2/L3/L4 header data buffer
- * @hdr_field - bitfield determining needed headers
- * @skb - socket buffer
- * @hdr_len - array of header lengths
- * @tot_len - total length of data
+ * @hdr_field: bitfield determining needed headers
+ * @skb: socket buffer
+ * @hdr_len: array of header lengths
+ * @hdr_data: buffer to write the header to
  *
  * Reads hdr_field to determine which headers are needed by firmware.
  * Builds a buffer containing these headers.  Saves individual header
@@ -1443,11 +1444,11 @@ static int build_hdr_data(u8 hdr_field, struct sk_buff *skb,
 
 /**
  * create_hdr_descs - create header and header extension descriptors
- * @hdr_field - bitfield determining needed headers
- * @data - buffer containing header data
- * @len - length of data buffer
- * @hdr_len - array of individual header lengths
- * @scrq_arr - descriptor array
+ * @hdr_field: bitfield determining needed headers
+ * @hdr_data: buffer containing header data
+ * @len: length of data buffer
+ * @hdr_len: array of individual header lengths
+ * @scrq_arr: descriptor array
  *
  * Creates header and, if needed, header extension descriptors and
  * places them in a descriptor array, scrq_arr
@@ -1495,10 +1496,9 @@ static int create_hdr_descs(u8 hdr_field, u8 *hdr_data, int len, int *hdr_len,
 
 /**
  * build_hdr_descs_arr - build a header descriptor array
- * @skb - socket buffer
- * @num_entries - number of descriptors to be sent
- * @subcrq - first TX descriptor
- * @hdr_field - bit field determining which headers will be sent
+ * @txbuff: tx buffer
+ * @num_entries: number of descriptors to be sent
+ * @hdr_field: bit field determining which headers will be sent
  *
  * This function will build a TX descriptor array with applicable
  * L2/L3/L4 packet header descriptors to be sent by send_subcrq_indirect.
@@ -1924,93 +1924,7 @@ static int ibmvnic_set_mac(struct net_device *netdev, void *p)
        return rc;
 }
 
-/**
- * do_change_param_reset returns zero if we are able to keep processing reset
- * events, or non-zero if we hit a fatal error and must halt.
- */
-static int do_change_param_reset(struct ibmvnic_adapter *adapter,
-                                struct ibmvnic_rwi *rwi,
-                                u32 reset_state)
-{
-       struct net_device *netdev = adapter->netdev;
-       int i, rc;
-
-       netdev_dbg(adapter->netdev, "Change param resetting driver (%d)\n",
-                  rwi->reset_reason);
-
-       netif_carrier_off(netdev);
-       adapter->reset_reason = rwi->reset_reason;
-
-       ibmvnic_cleanup(netdev);
-
-       if (reset_state == VNIC_OPEN) {
-               rc = __ibmvnic_close(netdev);
-               if (rc)
-                       goto out;
-       }
-
-       release_resources(adapter);
-       release_sub_crqs(adapter, 1);
-       release_crq_queue(adapter);
-
-       adapter->state = VNIC_PROBED;
-
-       rc = init_crq_queue(adapter);
-
-       if (rc) {
-               netdev_err(adapter->netdev,
-                          "Couldn't initialize crq. rc=%d\n", rc);
-               return rc;
-       }
-
-       rc = ibmvnic_reset_init(adapter, true);
-       if (rc) {
-               rc = IBMVNIC_INIT_FAILED;
-               goto out;
-       }
-
-       /* If the adapter was in PROBE state prior to the reset,
-        * exit here.
-        */
-       if (reset_state == VNIC_PROBED)
-               goto out;
-
-       rc = ibmvnic_login(netdev);
-       if (rc) {
-               goto out;
-       }
-
-       rc = init_resources(adapter);
-       if (rc)
-               goto out;
-
-       ibmvnic_disable_irqs(adapter);
-
-       adapter->state = VNIC_CLOSED;
-
-       if (reset_state == VNIC_CLOSED)
-               return 0;
-
-       rc = __ibmvnic_open(netdev);
-       if (rc) {
-               rc = IBMVNIC_OPEN_FAILED;
-               goto out;
-       }
-
-       /* refresh device's multicast list */
-       ibmvnic_set_multi(netdev);
-
-       /* kick napi */
-       for (i = 0; i < adapter->req_rx_queues; i++)
-               napi_schedule(&adapter->napi[i]);
-
-out:
-       if (rc)
-               adapter->state = reset_state;
-       return rc;
-}
-
-/**
+/*
  * do_reset returns zero if we are able to keep processing reset events, or
  * non-zero if we hit a fatal error and must halt.
  */
@@ -2027,7 +1941,11 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                   adapter->state, adapter->failover_pending,
                   rwi->reset_reason, reset_state);
 
-       rtnl_lock();
+       adapter->reset_reason = rwi->reset_reason;
+       /* requestor of VNIC_RESET_CHANGE_PARAM already has the rtnl lock */
+       if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM))
+               rtnl_lock();
+
        /*
         * Now that we have the rtnl lock, clear any pending failover.
         * This will ensure ibmvnic_open() has either completed or will
@@ -2037,7 +1955,6 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                adapter->failover_pending = false;
 
        netif_carrier_off(netdev);
-       adapter->reset_reason = rwi->reset_reason;
 
        old_num_rx_queues = adapter->req_rx_queues;
        old_num_tx_queues = adapter->req_tx_queues;
@@ -2049,25 +1966,37 @@ static int do_reset(struct ibmvnic_adapter *adapter,
        if (reset_state == VNIC_OPEN &&
            adapter->reset_reason != VNIC_RESET_MOBILITY &&
            adapter->reset_reason != VNIC_RESET_FAILOVER) {
-               adapter->state = VNIC_CLOSING;
+               if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) {
+                       rc = __ibmvnic_close(netdev);
+                       if (rc)
+                               goto out;
+               } else {
+                       adapter->state = VNIC_CLOSING;
 
-               /* Release the RTNL lock before link state change and
-                * re-acquire after the link state change to allow
-                * linkwatch_event to grab the RTNL lock and run during
-                * a reset.
-                */
-               rtnl_unlock();
-               rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN);
-               rtnl_lock();
-               if (rc)
-                       goto out;
+                       /* Release the RTNL lock before link state change and
+                        * re-acquire after the link state change to allow
+                        * linkwatch_event to grab the RTNL lock and run during
+                        * a reset.
+                        */
+                       rtnl_unlock();
+                       rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN);
+                       rtnl_lock();
+                       if (rc)
+                               goto out;
 
-               if (adapter->state != VNIC_CLOSING) {
-                       rc = -1;
-                       goto out;
+                       if (adapter->state != VNIC_CLOSING) {
+                               rc = -1;
+                               goto out;
+                       }
+
+                       adapter->state = VNIC_CLOSED;
                }
+       }
 
-               adapter->state = VNIC_CLOSED;
+       if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) {
+               release_resources(adapter);
+               release_sub_crqs(adapter, 1);
+               release_crq_queue(adapter);
        }
 
        if (adapter->reset_reason != VNIC_RESET_NON_FATAL) {
@@ -2076,7 +2005,9 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                 */
                adapter->state = VNIC_PROBED;
 
-               if (adapter->reset_reason == VNIC_RESET_MOBILITY) {
+               if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) {
+                       rc = init_crq_queue(adapter);
+               } else if (adapter->reset_reason == VNIC_RESET_MOBILITY) {
                        rc = ibmvnic_reenable_crq_queue(adapter);
                        release_sub_crqs(adapter, 1);
                } else {
@@ -2115,7 +2046,11 @@ static int do_reset(struct ibmvnic_adapter *adapter,
                        goto out;
                }
 
-               if (adapter->req_rx_queues != old_num_rx_queues ||
+               if (adapter->reset_reason == VNIC_RESET_CHANGE_PARAM) {
+                       rc = init_resources(adapter);
+                       if (rc)
+                               goto out;
+               } else if (adapter->req_rx_queues != old_num_rx_queues ||
                    adapter->req_tx_queues != old_num_tx_queues ||
                    adapter->req_rx_add_entries_per_subcrq !=
                    old_num_rx_slots ||
@@ -2180,7 +2115,9 @@ out:
        /* restore the adapter state if reset failed */
        if (rc)
                adapter->state = reset_state;
-       rtnl_unlock();
+       /* requestor of VNIC_RESET_CHANGE_PARAM should still hold the rtnl lock */
+       if (!(adapter->reset_reason == VNIC_RESET_CHANGE_PARAM))
+               rtnl_unlock();
 
        netdev_dbg(adapter->netdev, "[S:%d FOP:%d] Reset done, rc %d\n",
                   adapter->state, adapter->failover_pending, rc);
@@ -2311,10 +2248,7 @@ static void __ibmvnic_reset(struct work_struct *work)
                }
                spin_unlock_irqrestore(&adapter->state_lock, flags);
 
-               if (rwi->reset_reason == VNIC_RESET_CHANGE_PARAM) {
-                       /* CHANGE_PARAM requestor holds rtnl_lock */
-                       rc = do_change_param_reset(adapter, rwi, reset_state);
-               } else if (adapter->force_reset_recovery) {
+               if (adapter->force_reset_recovery) {
                        /*
                         * Since we are doing a hard reset now, clear the
                         * failover_pending flag so we don't ignore any
@@ -2341,8 +2275,7 @@ static void __ibmvnic_reset(struct work_struct *work)
                                set_current_state(TASK_UNINTERRUPTIBLE);
                                schedule_timeout(60 * HZ);
                        }
-               } else if (!(rwi->reset_reason == VNIC_RESET_FATAL &&
-                               adapter->from_passive_init)) {
+               } else {
                        rc = do_reset(adapter, rwi, reset_state);
                }
                kfree(rwi);
@@ -2981,9 +2914,7 @@ static int reset_one_sub_crq_queue(struct ibmvnic_adapter *adapter,
        int rc;
 
        if (!scrq) {
-               netdev_dbg(adapter->netdev,
-                          "Invalid scrq reset. irq (%d) or msgs (%p).\n",
-                          scrq->irq, scrq->msgs);
+               netdev_dbg(adapter->netdev, "Invalid scrq reset.\n");
                return -EINVAL;
        }
 
@@ -3873,7 +3804,9 @@ static int send_login(struct ibmvnic_adapter *adapter)
                return -1;
        }
 
+       release_login_buffer(adapter);
        release_login_rsp_buffer(adapter);
+
        client_data_len = vnic_client_data_len(adapter);
 
        buffer_size =
@@ -5084,6 +5017,12 @@ static void ibmvnic_tasklet(struct tasklet_struct *t)
        while (!done) {
                /* Pull all the valid messages off the CRQ */
                while ((crq = ibmvnic_next_crq(adapter)) != NULL) {
+                       /* This barrier makes sure ibmvnic_next_crq()'s
+                        * crq->generic.first & IBMVNIC_CRQ_CMD_RSP is loaded
+                        * before ibmvnic_handle_crq()'s
+                        * switch(gen_crq->first) and switch(gen_crq->cmd).
+                        */
+                       dma_rmb();
                        ibmvnic_handle_crq(crq, adapter);
                        crq->generic.first = 0;
                }
index 8cc651d..f8d78af 100644 (file)
@@ -1739,10 +1739,10 @@ static int e100_xmit_prepare(struct nic *nic, struct cb *cb,
        dma_addr_t dma_addr;
        cb->command = nic->tx_command;
 
-       dma_addr = pci_map_single(nic->pdev,
-                                 skb->data, skb->len, PCI_DMA_TODEVICE);
+       dma_addr = dma_map_single(&nic->pdev->dev, skb->data, skb->len,
+                                 DMA_TO_DEVICE);
        /* If we can't map the skb, have the upper layer try later */
-       if (pci_dma_mapping_error(nic->pdev, dma_addr)) {
+       if (dma_mapping_error(&nic->pdev->dev, dma_addr)) {
                dev_kfree_skb_any(skb);
                skb = NULL;
                return -ENOMEM;
@@ -1828,10 +1828,10 @@ static int e100_tx_clean(struct nic *nic)
                        dev->stats.tx_packets++;
                        dev->stats.tx_bytes += cb->skb->len;
 
-                       pci_unmap_single(nic->pdev,
-                               le32_to_cpu(cb->u.tcb.tbd.buf_addr),
-                               le16_to_cpu(cb->u.tcb.tbd.size),
-                               PCI_DMA_TODEVICE);
+                       dma_unmap_single(&nic->pdev->dev,
+                                        le32_to_cpu(cb->u.tcb.tbd.buf_addr),
+                                        le16_to_cpu(cb->u.tcb.tbd.size),
+                                        DMA_TO_DEVICE);
                        dev_kfree_skb_any(cb->skb);
                        cb->skb = NULL;
                        tx_cleaned = 1;
@@ -1855,10 +1855,10 @@ static void e100_clean_cbs(struct nic *nic)
                while (nic->cbs_avail != nic->params.cbs.count) {
                        struct cb *cb = nic->cb_to_clean;
                        if (cb->skb) {
-                               pci_unmap_single(nic->pdev,
-                                       le32_to_cpu(cb->u.tcb.tbd.buf_addr),
-                                       le16_to_cpu(cb->u.tcb.tbd.size),
-                                       PCI_DMA_TODEVICE);
+                               dma_unmap_single(&nic->pdev->dev,
+                                                le32_to_cpu(cb->u.tcb.tbd.buf_addr),
+                                                le16_to_cpu(cb->u.tcb.tbd.size),
+                                                DMA_TO_DEVICE);
                                dev_kfree_skb(cb->skb);
                        }
                        nic->cb_to_clean = nic->cb_to_clean->next;
@@ -1925,10 +1925,10 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
 
        /* Init, and map the RFD. */
        skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd));
-       rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data,
-               RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
+       rx->dma_addr = dma_map_single(&nic->pdev->dev, rx->skb->data,
+                                     RFD_BUF_LEN, DMA_BIDIRECTIONAL);
 
-       if (pci_dma_mapping_error(nic->pdev, rx->dma_addr)) {
+       if (dma_mapping_error(&nic->pdev->dev, rx->dma_addr)) {
                dev_kfree_skb_any(rx->skb);
                rx->skb = NULL;
                rx->dma_addr = 0;
@@ -1941,8 +1941,10 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx)
        if (rx->prev->skb) {
                struct rfd *prev_rfd = (struct rfd *)rx->prev->skb->data;
                put_unaligned_le32(rx->dma_addr, &prev_rfd->link);
-               pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr,
-                       sizeof(struct rfd), PCI_DMA_BIDIRECTIONAL);
+               dma_sync_single_for_device(&nic->pdev->dev,
+                                          rx->prev->dma_addr,
+                                          sizeof(struct rfd),
+                                          DMA_BIDIRECTIONAL);
        }
 
        return 0;
@@ -1961,8 +1963,8 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
                return -EAGAIN;
 
        /* Need to sync before taking a peek at cb_complete bit */
-       pci_dma_sync_single_for_cpu(nic->pdev, rx->dma_addr,
-               sizeof(struct rfd), PCI_DMA_BIDIRECTIONAL);
+       dma_sync_single_for_cpu(&nic->pdev->dev, rx->dma_addr,
+                               sizeof(struct rfd), DMA_BIDIRECTIONAL);
        rfd_status = le16_to_cpu(rfd->status);
 
        netif_printk(nic, rx_status, KERN_DEBUG, nic->netdev,
@@ -1981,9 +1983,9 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
 
                        if (ioread8(&nic->csr->scb.status) & rus_no_res)
                                nic->ru_running = RU_SUSPENDED;
-               pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr,
-                                              sizeof(struct rfd),
-                                              PCI_DMA_FROMDEVICE);
+               dma_sync_single_for_device(&nic->pdev->dev, rx->dma_addr,
+                                          sizeof(struct rfd),
+                                          DMA_FROM_DEVICE);
                return -ENODATA;
        }
 
@@ -1995,8 +1997,8 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
                actual_size = RFD_BUF_LEN - sizeof(struct rfd);
 
        /* Get data */
-       pci_unmap_single(nic->pdev, rx->dma_addr,
-               RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
+       dma_unmap_single(&nic->pdev->dev, rx->dma_addr, RFD_BUF_LEN,
+                        DMA_BIDIRECTIONAL);
 
        /* If this buffer has the el bit, but we think the receiver
         * is still running, check to see if it really stopped while
@@ -2097,22 +2099,25 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done,
                        (struct rfd *)new_before_last_rx->skb->data;
                new_before_last_rfd->size = 0;
                new_before_last_rfd->command |= cpu_to_le16(cb_el);
-               pci_dma_sync_single_for_device(nic->pdev,
-                       new_before_last_rx->dma_addr, sizeof(struct rfd),
-                       PCI_DMA_BIDIRECTIONAL);
+               dma_sync_single_for_device(&nic->pdev->dev,
+                                          new_before_last_rx->dma_addr,
+                                          sizeof(struct rfd),
+                                          DMA_BIDIRECTIONAL);
 
                /* Now that we have a new stopping point, we can clear the old
                 * stopping point.  We must sync twice to get the proper
                 * ordering on the hardware side of things. */
                old_before_last_rfd->command &= ~cpu_to_le16(cb_el);
-               pci_dma_sync_single_for_device(nic->pdev,
-                       old_before_last_rx->dma_addr, sizeof(struct rfd),
-                       PCI_DMA_BIDIRECTIONAL);
+               dma_sync_single_for_device(&nic->pdev->dev,
+                                          old_before_last_rx->dma_addr,
+                                          sizeof(struct rfd),
+                                          DMA_BIDIRECTIONAL);
                old_before_last_rfd->size = cpu_to_le16(VLAN_ETH_FRAME_LEN
                                                        + ETH_FCS_LEN);
-               pci_dma_sync_single_for_device(nic->pdev,
-                       old_before_last_rx->dma_addr, sizeof(struct rfd),
-                       PCI_DMA_BIDIRECTIONAL);
+               dma_sync_single_for_device(&nic->pdev->dev,
+                                          old_before_last_rx->dma_addr,
+                                          sizeof(struct rfd),
+                                          DMA_BIDIRECTIONAL);
        }
 
        if (restart_required) {
@@ -2134,8 +2139,9 @@ static void e100_rx_clean_list(struct nic *nic)
        if (nic->rxs) {
                for (rx = nic->rxs, i = 0; i < count; rx++, i++) {
                        if (rx->skb) {
-                               pci_unmap_single(nic->pdev, rx->dma_addr,
-                                       RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
+                               dma_unmap_single(&nic->pdev->dev,
+                                                rx->dma_addr, RFD_BUF_LEN,
+                                                DMA_BIDIRECTIONAL);
                                dev_kfree_skb(rx->skb);
                        }
                }
@@ -2177,8 +2183,8 @@ static int e100_rx_alloc_list(struct nic *nic)
        before_last = (struct rfd *)rx->skb->data;
        before_last->command |= cpu_to_le16(cb_el);
        before_last->size = 0;
-       pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr,
-               sizeof(struct rfd), PCI_DMA_BIDIRECTIONAL);
+       dma_sync_single_for_device(&nic->pdev->dev, rx->dma_addr,
+                                  sizeof(struct rfd), DMA_BIDIRECTIONAL);
 
        nic->rx_to_use = nic->rx_to_clean = nic->rxs;
        nic->ru_running = RU_SUSPENDED;
@@ -2377,8 +2383,8 @@ static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode)
 
        msleep(10);
 
-       pci_dma_sync_single_for_cpu(nic->pdev, nic->rx_to_clean->dma_addr,
-                       RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL);
+       dma_sync_single_for_cpu(&nic->pdev->dev, nic->rx_to_clean->dma_addr,
+                               RFD_BUF_LEN, DMA_BIDIRECTIONAL);
 
        if (memcmp(nic->rx_to_clean->skb->data + sizeof(struct rfd),
           skb->data, ETH_DATA_LEN))
@@ -2751,16 +2757,16 @@ static int e100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
 
 static int e100_alloc(struct nic *nic)
 {
-       nic->mem = pci_alloc_consistent(nic->pdev, sizeof(struct mem),
-               &nic->dma_addr);
+       nic->mem = dma_alloc_coherent(&nic->pdev->dev, sizeof(struct mem),
+                                     &nic->dma_addr, GFP_KERNEL);
        return nic->mem ? 0 : -ENOMEM;
 }
 
 static void e100_free(struct nic *nic)
 {
        if (nic->mem) {
-               pci_free_consistent(nic->pdev, sizeof(struct mem),
-                       nic->mem, nic->dma_addr);
+               dma_free_coherent(&nic->pdev->dev, sizeof(struct mem),
+                                 nic->mem, nic->dma_addr);
                nic->mem = NULL;
        }
 }
@@ -2853,7 +2859,7 @@ static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_out_disable_pdev;
        }
 
-       if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
+       if ((err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)))) {
                netif_err(nic, probe, nic->netdev, "No usable DMA configuration, aborting\n");
                goto err_out_free_res;
        }
index ba7a0f8..5b2143f 100644 (file)
@@ -436,6 +436,7 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca);
 #define FLAG2_DFLT_CRC_STRIPPING          BIT(12)
 #define FLAG2_CHECK_RX_HWTSTAMP           BIT(13)
 #define FLAG2_CHECK_SYSTIM_OVERFLOW       BIT(14)
+#define FLAG2_ENABLE_S0IX_FLOWS           BIT(15)
 
 #define E1000_RX_DESC_PS(R, i)     \
        (&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
index 03215b0..06442e6 100644 (file)
@@ -23,6 +23,13 @@ struct e1000_stats {
        int stat_offset;
 };
 
+static const char e1000e_priv_flags_strings[][ETH_GSTRING_LEN] = {
+#define E1000E_PRIV_FLAGS_S0IX_ENABLED BIT(0)
+       "s0ix-enabled",
+};
+
+#define E1000E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(e1000e_priv_flags_strings)
+
 #define E1000_STAT(str, m) { \
                .stat_string = str, \
                .type = E1000_STATS, \
@@ -1776,6 +1783,8 @@ static int e1000e_get_sset_count(struct net_device __always_unused *netdev,
                return E1000_TEST_LEN;
        case ETH_SS_STATS:
                return E1000_STATS_LEN;
+       case ETH_SS_PRIV_FLAGS:
+               return E1000E_PRIV_FLAGS_STR_LEN;
        default:
                return -EOPNOTSUPP;
        }
@@ -2097,6 +2106,10 @@ static void e1000_get_strings(struct net_device __always_unused *netdev,
                        p += ETH_GSTRING_LEN;
                }
                break;
+       case ETH_SS_PRIV_FLAGS:
+               memcpy(data, e1000e_priv_flags_strings,
+                      E1000E_PRIV_FLAGS_STR_LEN * ETH_GSTRING_LEN);
+               break;
        }
 }
 
@@ -2305,6 +2318,37 @@ static int e1000e_get_ts_info(struct net_device *netdev,
        return 0;
 }
 
+static u32 e1000e_get_priv_flags(struct net_device *netdev)
+{
+       struct e1000_adapter *adapter = netdev_priv(netdev);
+       u32 priv_flags = 0;
+
+       if (adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS)
+               priv_flags |= E1000E_PRIV_FLAGS_S0IX_ENABLED;
+
+       return priv_flags;
+}
+
+static int e1000e_set_priv_flags(struct net_device *netdev, u32 priv_flags)
+{
+       struct e1000_adapter *adapter = netdev_priv(netdev);
+       unsigned int flags2 = adapter->flags2;
+
+       flags2 &= ~FLAG2_ENABLE_S0IX_FLOWS;
+       if (priv_flags & E1000E_PRIV_FLAGS_S0IX_ENABLED) {
+               struct e1000_hw *hw = &adapter->hw;
+
+               if (hw->mac.type < e1000_pch_cnp)
+                       return -EINVAL;
+               flags2 |= FLAG2_ENABLE_S0IX_FLOWS;
+       }
+
+       if (flags2 != adapter->flags2)
+               adapter->flags2 = flags2;
+
+       return 0;
+}
+
 static const struct ethtool_ops e1000_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS,
        .get_drvinfo            = e1000_get_drvinfo,
@@ -2336,6 +2380,8 @@ static const struct ethtool_ops e1000_ethtool_ops = {
        .set_eee                = e1000e_set_eee,
        .get_link_ksettings     = e1000_get_link_ksettings,
        .set_link_ksettings     = e1000_set_link_ksettings,
+       .get_priv_flags         = e1000e_get_priv_flags,
+       .set_priv_flags         = e1000e_set_priv_flags,
 };
 
 void e1000e_set_ethtool_ops(struct net_device *netdev)
index 9aa6fad..6fb4668 100644 (file)
@@ -1240,6 +1240,9 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
                return 0;
 
        if (er32(FWSM) & E1000_ICH_FWSM_FW_VALID) {
+               struct e1000_adapter *adapter = hw->adapter;
+               bool firmware_bug = false;
+
                if (force) {
                        /* Request ME un-configure ULP mode in the PHY */
                        mac_reg = er32(H2ME);
@@ -1248,16 +1251,24 @@ static s32 e1000_disable_ulp_lpt_lp(struct e1000_hw *hw, bool force)
                        ew32(H2ME, mac_reg);
                }
 
-               /* Poll up to 300msec for ME to clear ULP_CFG_DONE. */
+               /* Poll up to 2.5 seconds for ME to clear ULP_CFG_DONE.
+                * If this takes more than 1 second, show a warning indicating a
+                * firmware bug
+                */
                while (er32(FWSM) & E1000_FWSM_ULP_CFG_DONE) {
-                       if (i++ == 30) {
+                       if (i++ == 250) {
                                ret_val = -E1000_ERR_PHY;
                                goto out;
                        }
+                       if (i > 100 && !firmware_bug)
+                               firmware_bug = true;
 
                        usleep_range(10000, 11000);
                }
-               e_dbg("ULP_CONFIG_DONE cleared after %dmsec\n", i * 10);
+               if (firmware_bug)
+                       e_warn("ULP_CONFIG_DONE took %dmsec.  This is a firmware bug\n", i * 10);
+               else
+                       e_dbg("ULP_CONFIG_DONE cleared after %dmsec\n", i * 10);
 
                if (force) {
                        mac_reg = er32(H2ME);
index 128ab68..e9b82c2 100644 (file)
@@ -103,45 +103,6 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = {
        {0, NULL}
 };
 
-struct e1000e_me_supported {
-       u16 device_id;          /* supported device ID */
-};
-
-static const struct e1000e_me_supported me_supported[] = {
-       {E1000_DEV_ID_PCH_LPT_I217_LM},
-       {E1000_DEV_ID_PCH_LPTLP_I218_LM},
-       {E1000_DEV_ID_PCH_I218_LM2},
-       {E1000_DEV_ID_PCH_I218_LM3},
-       {E1000_DEV_ID_PCH_SPT_I219_LM},
-       {E1000_DEV_ID_PCH_SPT_I219_LM2},
-       {E1000_DEV_ID_PCH_LBG_I219_LM3},
-       {E1000_DEV_ID_PCH_SPT_I219_LM4},
-       {E1000_DEV_ID_PCH_SPT_I219_LM5},
-       {E1000_DEV_ID_PCH_CNP_I219_LM6},
-       {E1000_DEV_ID_PCH_CNP_I219_LM7},
-       {E1000_DEV_ID_PCH_ICP_I219_LM8},
-       {E1000_DEV_ID_PCH_ICP_I219_LM9},
-       {E1000_DEV_ID_PCH_CMP_I219_LM10},
-       {E1000_DEV_ID_PCH_CMP_I219_LM11},
-       {E1000_DEV_ID_PCH_CMP_I219_LM12},
-       {E1000_DEV_ID_PCH_TGP_I219_LM13},
-       {E1000_DEV_ID_PCH_TGP_I219_LM14},
-       {E1000_DEV_ID_PCH_TGP_I219_LM15},
-       {0}
-};
-
-static bool e1000e_check_me(u16 device_id)
-{
-       struct e1000e_me_supported *id;
-
-       for (id = (struct e1000e_me_supported *)me_supported;
-            id->device_id; id++)
-               if (device_id == id->device_id)
-                       return true;
-
-       return false;
-}
-
 /**
  * __ew32_prepare - prepare to write to MAC CSR register on certain parts
  * @hw: pointer to the HW structure
@@ -6962,7 +6923,6 @@ static __maybe_unused int e1000e_pm_suspend(struct device *dev)
        struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev));
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct pci_dev *pdev = to_pci_dev(dev);
-       struct e1000_hw *hw = &adapter->hw;
        int rc;
 
        e1000e_flush_lpic(pdev);
@@ -6970,13 +6930,13 @@ static __maybe_unused int e1000e_pm_suspend(struct device *dev)
        e1000e_pm_freeze(dev);
 
        rc = __e1000_shutdown(pdev, false);
-       if (rc)
+       if (rc) {
                e1000e_pm_thaw(dev);
-
-       /* Introduce S0ix implementation */
-       if (hw->mac.type >= e1000_pch_cnp &&
-           !e1000e_check_me(hw->adapter->pdev->device))
-               e1000e_s0ix_entry_flow(adapter);
+       } else {
+               /* Introduce S0ix implementation */
+               if (adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS)
+                       e1000e_s0ix_entry_flow(adapter);
+       }
 
        return rc;
 }
@@ -6986,12 +6946,10 @@ static __maybe_unused int e1000e_pm_resume(struct device *dev)
        struct net_device *netdev = pci_get_drvdata(to_pci_dev(dev));
        struct e1000_adapter *adapter = netdev_priv(netdev);
        struct pci_dev *pdev = to_pci_dev(dev);
-       struct e1000_hw *hw = &adapter->hw;
        int rc;
 
        /* Introduce S0ix implementation */
-       if (hw->mac.type >= e1000_pch_cnp &&
-           !e1000e_check_me(hw->adapter->pdev->device))
+       if (adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS)
                e1000e_s0ix_exit_flow(adapter);
 
        rc = __e1000_resume(pdev);
@@ -7655,6 +7613,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!(adapter->flags & FLAG_HAS_AMT))
                e1000e_get_hw_control(adapter);
 
+       if (hw->mac.type >= e1000_pch_cnp)
+               adapter->flags2 |= FLAG2_ENABLE_S0IX_FLOWS;
+
        strlcpy(netdev->name, "eth%d", sizeof(netdev->name));
        err = register_netdev(netdev);
        if (err)
index 5c19ff4..2fb52bd 100644 (file)
@@ -1531,8 +1531,6 @@ static const struct net_device_ops fm10k_netdev_ops = {
        .ndo_set_vf_rate        = fm10k_ndo_set_vf_bw,
        .ndo_get_vf_config      = fm10k_ndo_get_vf_config,
        .ndo_get_vf_stats       = fm10k_ndo_get_vf_stats,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_dfwd_add_station   = fm10k_dfwd_add_station,
        .ndo_dfwd_del_station   = fm10k_dfwd_del_station,
        .ndo_features_check     = fm10k_features_check,
index d231a2c..118473d 100644 (file)
@@ -120,6 +120,7 @@ enum i40e_state_t {
        __I40E_RESET_INTR_RECEIVED,
        __I40E_REINIT_REQUESTED,
        __I40E_PF_RESET_REQUESTED,
+       __I40E_PF_RESET_AND_REBUILD_REQUESTED,
        __I40E_CORE_RESET_REQUESTED,
        __I40E_GLOBAL_RESET_REQUESTED,
        __I40E_EMP_RESET_INTR_RECEIVED,
@@ -146,6 +147,8 @@ enum i40e_state_t {
 };
 
 #define I40E_PF_RESET_FLAG     BIT_ULL(__I40E_PF_RESET_REQUESTED)
+#define I40E_PF_RESET_AND_REBUILD_FLAG \
+       BIT_ULL(__I40E_PF_RESET_AND_REBUILD_REQUESTED)
 
 /* VSI state flags */
 enum i40e_vsi_state_t {
index 1337686..521ea9d 100644 (file)
@@ -36,6 +36,8 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf);
 static void i40e_determine_queue_usage(struct i40e_pf *pf);
 static int i40e_setup_pf_filter_control(struct i40e_pf *pf);
 static void i40e_prep_for_reset(struct i40e_pf *pf, bool lock_acquired);
+static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit,
+                                  bool lock_acquired);
 static int i40e_reset(struct i40e_pf *pf);
 static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired);
 static int i40e_setup_misc_vector_for_recovery_mode(struct i40e_pf *pf);
@@ -8536,6 +8538,14 @@ void i40e_do_reset(struct i40e_pf *pf, u32 reset_flags, bool lock_acquired)
                         "FW LLDP is disabled\n" :
                         "FW LLDP is enabled\n");
 
+       } else if (reset_flags & I40E_PF_RESET_AND_REBUILD_FLAG) {
+               /* Request a PF Reset
+                *
+                * Resets PF and reinitializes PFs VSI.
+                */
+               i40e_prep_for_reset(pf, lock_acquired);
+               i40e_reset_and_rebuild(pf, true, lock_acquired);
+
        } else if (reset_flags & BIT_ULL(__I40E_REINIT_REQUESTED)) {
                int v;
 
@@ -12794,8 +12804,6 @@ static const struct net_device_ops i40e_netdev_ops = {
        .ndo_set_vf_link_state  = i40e_ndo_set_vf_link_state,
        .ndo_set_vf_spoofchk    = i40e_ndo_set_vf_spoofchk,
        .ndo_set_vf_trust       = i40e_ndo_set_vf_trust,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_get_phys_port_id   = i40e_get_phys_port_id,
        .ndo_fdb_add            = i40e_ndo_fdb_add,
        .ndo_features_check     = i40e_features_check,
index 4aca637..2574e78 100644 (file)
@@ -2344,7 +2344,7 @@ static void i40e_inc_ntc(struct i40e_ring *rx_ring)
  **/
 static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
 {
-       unsigned int total_rx_bytes = 0, total_rx_packets = 0;
+       unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0;
        struct sk_buff *skb = rx_ring->skb;
        u16 cleaned_count = I40E_DESC_UNUSED(rx_ring);
        unsigned int xdp_xmit = 0;
@@ -2352,9 +2352,9 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
        struct xdp_buff xdp;
 
 #if (PAGE_SIZE < 8192)
-       xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0);
+       frame_sz = i40e_rx_frame_truesize(rx_ring, 0);
 #endif
-       xdp.rxq = &rx_ring->xdp_rxq;
+       xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
 
        while (likely(total_rx_packets < (unsigned int)budget)) {
                struct i40e_rx_buffer *rx_buffer;
@@ -2406,12 +2406,12 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
 
                /* retrieve a buffer from the ring */
                if (!skb) {
-                       xdp.data = page_address(rx_buffer->page) +
-                                  rx_buffer->page_offset;
-                       xdp.data_meta = xdp.data;
-                       xdp.data_hard_start = xdp.data -
-                                             i40e_rx_offset(rx_ring);
-                       xdp.data_end = xdp.data + size;
+                       unsigned int offset = i40e_rx_offset(rx_ring);
+                       unsigned char *hard_start;
+
+                       hard_start = page_address(rx_buffer->page) +
+                                    rx_buffer->page_offset - offset;
+                       xdp_prepare_buff(&xdp, hard_start, offset, size, true);
 #if (PAGE_SIZE > 4096)
                        /* At larger PAGE_SIZE, frame_sz depend on len size */
                        xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, size);
index 729c4f0..7efc61a 100644 (file)
@@ -1772,7 +1772,7 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
        if (num_vfs) {
                if (!(pf->flags & I40E_FLAG_VEB_MODE_ENABLED)) {
                        pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
-                       i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
+                       i40e_do_reset_safe(pf, I40E_PF_RESET_AND_REBUILD_FLAG);
                }
                ret = i40e_pci_sriov_enable(pdev, num_vfs);
                goto sriov_configure_out;
@@ -1781,7 +1781,7 @@ int i40e_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
        if (!pci_vfs_assigned(pf->pdev)) {
                i40e_free_vfs(pf);
                pf->flags &= ~I40E_FLAG_VEB_MODE_ENABLED;
-               i40e_do_reset_safe(pf, I40E_PF_RESET_FLAG);
+               i40e_do_reset_safe(pf, I40E_PF_RESET_AND_REBUILD_FLAG);
        } else {
                dev_warn(&pdev->dev, "Unable to free VFs because some are assigned to VMs.\n");
                ret = -EINVAL;
@@ -4046,20 +4046,16 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                goto error_param;
 
        vf = &pf->vf[vf_id];
-       vsi = pf->vsi[vf->lan_vsi_idx];
 
        /* When the VF is resetting wait until it is done.
         * It can take up to 200 milliseconds,
         * but wait for up to 300 milliseconds to be safe.
-        * If the VF is indeed in reset, the vsi pointer has
-        * to show on the newly loaded vsi under pf->vsi[id].
+        * Acquire the VSI pointer only after the VF has been
+        * properly initialized.
         */
        for (i = 0; i < 15; i++) {
-               if (test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) {
-                       if (i > 0)
-                               vsi = pf->vsi[vf->lan_vsi_idx];
+               if (test_bit(I40E_VF_STATE_INIT, &vf->vf_states))
                        break;
-               }
                msleep(20);
        }
        if (!test_bit(I40E_VF_STATE_INIT, &vf->vf_states)) {
@@ -4068,6 +4064,7 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac)
                ret = -EAGAIN;
                goto error_param;
        }
+       vsi = pf->vsi[vf->lan_vsi_idx];
 
        if (is_multicast_ether_addr(mac)) {
                dev_err(&pf->pdev->dev,
index 47eb9c5..492ce21 100644 (file)
@@ -348,12 +348,12 @@ int i40e_clean_rx_irq_zc(struct i40e_ring *rx_ring, int budget)
                 * SBP is *not* set in PRT_SBPVSI (default not set).
                 */
                skb = i40e_construct_skb_zc(rx_ring, *bi);
-               *bi = NULL;
                if (!skb) {
                        rx_ring->rx_stats.alloc_buff_failed++;
                        break;
                }
 
+               *bi = NULL;
                cleaned_count++;
                i40e_inc_ntc(rx_ring);
 
index 95543df..0a867d6 100644 (file)
@@ -1834,11 +1834,9 @@ static int iavf_init_get_resources(struct iavf_adapter *adapter)
        netif_tx_stop_all_queues(netdev);
        if (CLIENT_ALLOWED(adapter)) {
                err = iavf_lan_add_device(adapter);
-               if (err) {
-                       rtnl_unlock();
+               if (err)
                        dev_info(&pdev->dev, "Failed to add VF to client API service list: %d\n",
                                 err);
-               }
        }
        dev_info(&pdev->dev, "MAC address: %pM\n", adapter->hw.mac.addr);
        if (netdev->features & NETIF_F_GRO)
index ed08ace..647e7fd 100644 (file)
@@ -911,7 +911,7 @@ static void iavf_print_link_message(struct iavf_adapter *adapter)
                return;
        }
 
-       speed = kcalloc(1, IAVF_MAX_SPEED_STRLEN, GFP_KERNEL);
+       speed = kzalloc(IAVF_MAX_SPEED_STRLEN, GFP_KERNEL);
        if (!speed)
                return;
 
index 5672535..fa1e128 100644 (file)
@@ -68,7 +68,9 @@
 #define ICE_INT_NAME_STR_LEN   (IFNAMSIZ + 16)
 #define ICE_AQ_LEN             64
 #define ICE_MBXSQ_LEN          64
-#define ICE_MIN_MSIX           2
+#define ICE_MIN_LAN_TXRX_MSIX  1
+#define ICE_MIN_LAN_OICR_MSIX  1
+#define ICE_MIN_MSIX           (ICE_MIN_LAN_TXRX_MSIX + ICE_MIN_LAN_OICR_MSIX)
 #define ICE_FDIR_MSIX          1
 #define ICE_NO_VSI             0xffff
 #define ICE_VSI_MAP_CONTIG     0
index 9e8e953..69c113a 100644 (file)
@@ -3258,8 +3258,8 @@ ice_set_rxfh(struct net_device *netdev, const u32 *indir, const u8 *key,
  */
 static int ice_get_max_txq(struct ice_pf *pf)
 {
-       return min_t(int, num_online_cpus(),
-                    pf->hw.func_caps.common_cap.num_txq);
+       return min3(pf->num_lan_msix, (u16)num_online_cpus(),
+                   (u16)pf->hw.func_caps.common_cap.num_txq);
 }
 
 /**
@@ -3268,8 +3268,8 @@ static int ice_get_max_txq(struct ice_pf *pf)
  */
 static int ice_get_max_rxq(struct ice_pf *pf)
 {
-       return min_t(int, num_online_cpus(),
-                    pf->hw.func_caps.common_cap.num_rxq);
+       return min3(pf->num_lan_msix, (u16)num_online_cpus(),
+                   (u16)pf->hw.func_caps.common_cap.num_rxq);
 }
 
 /**
index 2d27f66..1927295 100644 (file)
@@ -1576,7 +1576,13 @@ ice_set_fdir_input_set(struct ice_vsi *vsi, struct ethtool_rx_flow_spec *fsp,
                       sizeof(struct in6_addr));
                input->ip.v6.l4_header = fsp->h_u.usr_ip6_spec.l4_4_bytes;
                input->ip.v6.tc = fsp->h_u.usr_ip6_spec.tclass;
-               input->ip.v6.proto = fsp->h_u.usr_ip6_spec.l4_proto;
+
+               /* if no protocol requested, use IPPROTO_NONE */
+               if (!fsp->m_u.usr_ip6_spec.l4_proto)
+                       input->ip.v6.proto = IPPROTO_NONE;
+               else
+                       input->ip.v6.proto = fsp->h_u.usr_ip6_spec.l4_proto;
+
                memcpy(input->mask.v6.dst_ip, fsp->m_u.usr_ip6_spec.ip6dst,
                       sizeof(struct in6_addr));
                memcpy(input->mask.v6.src_ip, fsp->m_u.usr_ip6_spec.ip6src,
index 3df6748..ad9c22a 100644 (file)
@@ -161,8 +161,9 @@ static void ice_vsi_set_num_qs(struct ice_vsi *vsi, u16 vf_id)
 
        switch (vsi->type) {
        case ICE_VSI_PF:
-               vsi->alloc_txq = min_t(int, ice_get_avail_txq_count(pf),
-                                      num_online_cpus());
+               vsi->alloc_txq = min3(pf->num_lan_msix,
+                                     ice_get_avail_txq_count(pf),
+                                     (u16)num_online_cpus());
                if (vsi->req_txq) {
                        vsi->alloc_txq = vsi->req_txq;
                        vsi->num_txq = vsi->req_txq;
@@ -174,8 +175,9 @@ static void ice_vsi_set_num_qs(struct ice_vsi *vsi, u16 vf_id)
                if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
                        vsi->alloc_rxq = 1;
                } else {
-                       vsi->alloc_rxq = min_t(int, ice_get_avail_rxq_count(pf),
-                                              num_online_cpus());
+                       vsi->alloc_rxq = min3(pf->num_lan_msix,
+                                             ice_get_avail_rxq_count(pf),
+                                             (u16)num_online_cpus());
                        if (vsi->req_rxq) {
                                vsi->alloc_rxq = vsi->req_rxq;
                                vsi->num_rxq = vsi->req_rxq;
@@ -184,7 +186,9 @@ static void ice_vsi_set_num_qs(struct ice_vsi *vsi, u16 vf_id)
 
                pf->num_lan_rx = vsi->alloc_rxq;
 
-               vsi->num_q_vectors = max_t(int, vsi->alloc_rxq, vsi->alloc_txq);
+               vsi->num_q_vectors = min_t(int, pf->num_lan_msix,
+                                          max_t(int, vsi->alloc_rxq,
+                                                vsi->alloc_txq));
                break;
        case ICE_VSI_VF:
                vf = &pf->vf[vsi->vf_id];
index c52b9bb..06aa375 100644 (file)
@@ -3430,18 +3430,14 @@ static int ice_ena_msix_range(struct ice_pf *pf)
        if (v_actual < v_budget) {
                dev_warn(dev, "not enough OS MSI-X vectors. requested = %d, obtained = %d\n",
                         v_budget, v_actual);
-/* 2 vectors each for LAN and RDMA (traffic + OICR), one for flow director */
-#define ICE_MIN_LAN_VECS 2
-#define ICE_MIN_RDMA_VECS 2
-#define ICE_MIN_VECS (ICE_MIN_LAN_VECS + ICE_MIN_RDMA_VECS + 1)
 
-               if (v_actual < ICE_MIN_LAN_VECS) {
+               if (v_actual < ICE_MIN_MSIX) {
                        /* error if we can't get minimum vectors */
                        pci_disable_msix(pf->pdev);
                        err = -ERANGE;
                        goto msix_err;
                } else {
-                       pf->num_lan_msix = ICE_MIN_LAN_VECS;
+                       pf->num_lan_msix = ICE_MIN_LAN_TXRX_MSIX;
                }
        }
 
@@ -4884,9 +4880,15 @@ static int ice_set_mac_address(struct net_device *netdev, void *pi)
                goto err_update_filters;
        }
 
-       /* Add filter for new MAC. If filter exists, just return success */
+       /* Add filter for new MAC. If filter exists, return success */
        status = ice_fltr_add_mac(vsi, mac, ICE_FWD_TO_VSI);
        if (status == ICE_ERR_ALREADY_EXISTS) {
+               /* Although this MAC filter is already present in hardware it's
+                * possible in some cases (e.g. bonding) that dev_addr was
+                * modified outside of the driver and needs to be restored back
+                * to this value.
+                */
+               memcpy(netdev->dev_addr, mac, netdev->addr_len);
                netdev_dbg(netdev, "filter for MAC %pM already exists\n", mac);
                return 0;
        }
@@ -6790,6 +6792,4 @@ static const struct net_device_ops ice_netdev_ops = {
        .ndo_bpf = ice_xdp,
        .ndo_xdp_xmit = ice_xdp_xmit,
        .ndo_xsk_wakeup = ice_xsk_wakeup,
-       .ndo_udp_tunnel_add = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del = udp_tunnel_nic_del_port,
 };
index a2d0aad..2c2de56 100644 (file)
@@ -1089,23 +1089,25 @@ ice_is_non_eop(struct ice_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc,
  */
 int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget)
 {
-       unsigned int total_rx_bytes = 0, total_rx_pkts = 0;
+       unsigned int total_rx_bytes = 0, total_rx_pkts = 0, frame_sz = 0;
        u16 cleaned_count = ICE_DESC_UNUSED(rx_ring);
        unsigned int xdp_res, xdp_xmit = 0;
        struct bpf_prog *xdp_prog = NULL;
        struct xdp_buff xdp;
        bool failure;
 
-       xdp.rxq = &rx_ring->xdp_rxq;
        /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */
 #if (PAGE_SIZE < 8192)
-       xdp.frame_sz = ice_rx_frame_truesize(rx_ring, 0);
+       frame_sz = ice_rx_frame_truesize(rx_ring, 0);
 #endif
+       xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
 
        /* start the loop to process Rx packets bounded by 'budget' */
        while (likely(total_rx_pkts < (unsigned int)budget)) {
+               unsigned int offset = ice_rx_offset(rx_ring);
                union ice_32b_rx_flex_desc *rx_desc;
                struct ice_rx_buf *rx_buf;
+               unsigned char *hard_start;
                struct sk_buff *skb;
                unsigned int size;
                u16 stat_err_bits;
@@ -1151,10 +1153,9 @@ int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget)
                        goto construct_skb;
                }
 
-               xdp.data = page_address(rx_buf->page) + rx_buf->page_offset;
-               xdp.data_hard_start = xdp.data - ice_rx_offset(rx_ring);
-               xdp.data_meta = xdp.data;
-               xdp.data_end = xdp.data + size;
+               hard_start = page_address(rx_buf->page) + rx_buf->page_offset -
+                            offset;
+               xdp_prepare_buff(&xdp, hard_start, offset, size, true);
 #if (PAGE_SIZE > 4096)
                /* At larger PAGE_SIZE, frame_sz depend on len size */
                xdp.frame_sz = ice_rx_frame_truesize(rx_ring, size);
@@ -1923,12 +1924,15 @@ int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
                                  ICE_TX_CTX_EIPT_IPV4_NO_CSUM;
                        l4_proto = ip.v4->protocol;
                } else if (first->tx_flags & ICE_TX_FLAGS_IPV6) {
+                       int ret;
+
                        tunnel |= ICE_TX_CTX_EIPT_IPV6;
                        exthdr = ip.hdr + sizeof(*ip.v6);
                        l4_proto = ip.v6->nexthdr;
-                       if (l4.hdr != exthdr)
-                               ipv6_skip_exthdr(skb, exthdr - skb->data,
-                                                &l4_proto, &frag_off);
+                       ret = ipv6_skip_exthdr(skb, exthdr - skb->data,
+                                              &l4_proto, &frag_off);
+                       if (ret < 0)
+                               return -1;
                }
 
                /* define outer transport */
index bc2f439..02b1273 100644 (file)
@@ -191,12 +191,7 @@ ice_receive_skb(struct ice_ring *rx_ring, struct sk_buff *skb, u16 vlan_tag)
        if ((rx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_RX) &&
            (vlan_tag & VLAN_VID_MASK))
                __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
-       if (napi_gro_receive(&rx_ring->q_vector->napi, skb) == GRO_DROP) {
-               /* this is tracked separately to help us debug stack drops */
-               rx_ring->rx_stats.gro_dropped++;
-               netdev_dbg(rx_ring->netdev, "Receive Queue %d: Dropped packet from GRO\n",
-                          rx_ring->q_index);
-       }
+       napi_gro_receive(&rx_ring->q_vector->napi, skb);
 }
 
 /**
index 03f78fd..84d4284 100644 (file)
@@ -5959,15 +5959,6 @@ static int igb_tso(struct igb_ring *tx_ring,
        return 1;
 }
 
-static inline bool igb_ipv6_csum_is_sctp(struct sk_buff *skb)
-{
-       unsigned int offset = 0;
-
-       ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL);
-
-       return offset == skb_checksum_start_offset(skb);
-}
-
 static void igb_tx_csum(struct igb_ring *tx_ring, struct igb_tx_buffer *first)
 {
        struct sk_buff *skb = first->skb;
@@ -5990,10 +5981,7 @@ csum_failed:
                break;
        case offsetof(struct sctphdr, checksum):
                /* validate that this is actually an SCTP request */
-               if (((first->protocol == htons(ETH_P_IP)) &&
-                    (ip_hdr(skb)->protocol == IPPROTO_SCTP)) ||
-                   ((first->protocol == htons(ETH_P_IPV6)) &&
-                    igb_ipv6_csum_is_sctp(skb))) {
+               if (skb_csum_is_sctp(skb)) {
                        type_tucmd = E1000_ADVTXD_TUCMD_L4T_SCTP;
                        break;
                }
@@ -8681,13 +8669,13 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
        u16 cleaned_count = igb_desc_unused(rx_ring);
        unsigned int xdp_xmit = 0;
        struct xdp_buff xdp;
-
-       xdp.rxq = &rx_ring->xdp_rxq;
+       u32 frame_sz = 0;
 
        /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */
 #if (PAGE_SIZE < 8192)
-       xdp.frame_sz = igb_rx_frame_truesize(rx_ring, 0);
+       frame_sz = igb_rx_frame_truesize(rx_ring, 0);
 #endif
+       xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
 
        while (likely(total_packets < budget)) {
                union e1000_adv_rx_desc *rx_desc;
@@ -8715,12 +8703,12 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget)
 
                /* retrieve a buffer from the ring */
                if (!skb) {
-                       xdp.data = page_address(rx_buffer->page) +
-                                  rx_buffer->page_offset;
-                       xdp.data_meta = xdp.data;
-                       xdp.data_hard_start = xdp.data -
-                                             igb_rx_offset(rx_ring);
-                       xdp.data_end = xdp.data + size;
+                       unsigned int offset = igb_rx_offset(rx_ring);
+                       unsigned char *hard_start;
+
+                       hard_start = page_address(rx_buffer->page) +
+                                    rx_buffer->page_offset - offset;
+                       xdp_prepare_buff(&xdp, hard_start, offset, size, true);
 #if (PAGE_SIZE > 4096)
                        /* At larger PAGE_SIZE, frame_sz depend on len size */
                        xdp.frame_sz = igb_rx_frame_truesize(rx_ring, size);
index 30fdea2..fb3fbcb 100644 (file)
@@ -2072,15 +2072,6 @@ static int igbvf_tso(struct igbvf_ring *tx_ring,
        return 1;
 }
 
-static inline bool igbvf_ipv6_csum_is_sctp(struct sk_buff *skb)
-{
-       unsigned int offset = 0;
-
-       ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL);
-
-       return offset == skb_checksum_start_offset(skb);
-}
-
 static bool igbvf_tx_csum(struct igbvf_ring *tx_ring, struct sk_buff *skb,
                          u32 tx_flags, __be16 protocol)
 {
@@ -2102,10 +2093,7 @@ csum_failed:
                break;
        case offsetof(struct sctphdr, checksum):
                /* validate that this is actually an SCTP request */
-               if (((protocol == htons(ETH_P_IP)) &&
-                    (ip_hdr(skb)->protocol == IPPROTO_SCTP)) ||
-                   ((protocol == htons(ETH_P_IPV6)) &&
-                    igbvf_ipv6_csum_is_sctp(skb))) {
+               if (skb_csum_is_sctp(skb)) {
                        type_tucmd = E1000_ADVTXD_TUCMD_L4T_SCTP;
                        break;
                }
index 61d331c..831f2f0 100644 (file)
@@ -1675,12 +1675,18 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev,
        cmd->base.phy_address = hw->phy.addr;
 
        /* advertising link modes */
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
-       ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full);
+       if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Half);
+       if (hw->phy.autoneg_advertised & ADVERTISE_10_FULL)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, 10baseT_Full);
+       if (hw->phy.autoneg_advertised & ADVERTISE_100_HALF)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Half);
+       if (hw->phy.autoneg_advertised & ADVERTISE_100_FULL)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, 100baseT_Full);
+       if (hw->phy.autoneg_advertised & ADVERTISE_1000_FULL)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, 1000baseT_Full);
+       if (hw->phy.autoneg_advertised & ADVERTISE_2500_FULL)
+               ethtool_link_ksettings_add_link_mode(cmd, advertising, 2500baseT_Full);
 
        /* set autoneg settings */
        if (hw->mac.autoneg == 1) {
@@ -1792,6 +1798,12 @@ igc_ethtool_set_link_ksettings(struct net_device *netdev,
 
        ethtool_convert_link_mode_to_legacy_u32(&advertising,
                                                cmd->link_modes.advertising);
+       /* Converting to legacy u32 drops ETHTOOL_LINK_MODE_2500baseT_Full_BIT.
+        * We have to check this and convert it to ADVERTISE_2500_FULL
+        * (aka ETHTOOL_LINK_MODE_2500baseX_Full_BIT) explicitly.
+        */
+       if (ethtool_link_ksettings_test_link_mode(cmd, advertising, 2500baseT_Full))
+               advertising |= ADVERTISE_2500_FULL;
 
        if (cmd->base.autoneg == AUTONEG_ENABLE) {
                hw->mac.autoneg = 1;
index afd6a62..43aec42 100644 (file)
@@ -949,15 +949,6 @@ static void igc_tx_ctxtdesc(struct igc_ring *tx_ring,
        }
 }
 
-static inline bool igc_ipv6_csum_is_sctp(struct sk_buff *skb)
-{
-       unsigned int offset = 0;
-
-       ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL);
-
-       return offset == skb_checksum_start_offset(skb);
-}
-
 static void igc_tx_csum(struct igc_ring *tx_ring, struct igc_tx_buffer *first)
 {
        struct sk_buff *skb = first->skb;
@@ -980,10 +971,7 @@ csum_failed:
                break;
        case offsetof(struct sctphdr, checksum):
                /* validate that this is actually an SCTP request */
-               if ((first->protocol == htons(ETH_P_IP) &&
-                    (ip_hdr(skb)->protocol == IPPROTO_SCTP)) ||
-                   (first->protocol == htons(ETH_P_IPV6) &&
-                    igc_ipv6_csum_is_sctp(skb))) {
+               if (skb_csum_is_sctp(skb)) {
                        type_tucmd = IGC_ADVTXD_TUCMD_L4T_SCTP;
                        break;
                }
index 393d1c2..e08c015 100644 (file)
@@ -2291,7 +2291,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
                               struct ixgbe_ring *rx_ring,
                               const int budget)
 {
-       unsigned int total_rx_bytes = 0, total_rx_packets = 0;
+       unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0;
        struct ixgbe_adapter *adapter = q_vector->adapter;
 #ifdef IXGBE_FCOE
        int ddp_bytes;
@@ -2301,12 +2301,11 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
        unsigned int xdp_xmit = 0;
        struct xdp_buff xdp;
 
-       xdp.rxq = &rx_ring->xdp_rxq;
-
        /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */
 #if (PAGE_SIZE < 8192)
-       xdp.frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0);
+       frame_sz = ixgbe_rx_frame_truesize(rx_ring, 0);
 #endif
+       xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
 
        while (likely(total_rx_packets < budget)) {
                union ixgbe_adv_rx_desc *rx_desc;
@@ -2336,12 +2335,12 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector,
 
                /* retrieve a buffer from the ring */
                if (!skb) {
-                       xdp.data = page_address(rx_buffer->page) +
-                                  rx_buffer->page_offset;
-                       xdp.data_meta = xdp.data;
-                       xdp.data_hard_start = xdp.data -
-                                             ixgbe_rx_offset(rx_ring);
-                       xdp.data_end = xdp.data + size;
+                       unsigned int offset = ixgbe_rx_offset(rx_ring);
+                       unsigned char *hard_start;
+
+                       hard_start = page_address(rx_buffer->page) +
+                                    rx_buffer->page_offset - offset;
+                       xdp_prepare_buff(&xdp, hard_start, offset, size, true);
 #if (PAGE_SIZE > 4096)
                        /* At larger PAGE_SIZE, frame_sz depend on len size */
                        xdp.frame_sz = ixgbe_rx_frame_truesize(rx_ring, size);
@@ -8040,15 +8039,6 @@ static int ixgbe_tso(struct ixgbe_ring *tx_ring,
        return 1;
 }
 
-static inline bool ixgbe_ipv6_csum_is_sctp(struct sk_buff *skb)
-{
-       unsigned int offset = 0;
-
-       ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL);
-
-       return offset == skb_checksum_start_offset(skb);
-}
-
 static void ixgbe_tx_csum(struct ixgbe_ring *tx_ring,
                          struct ixgbe_tx_buffer *first,
                          struct ixgbe_ipsec_tx_data *itd)
@@ -8074,10 +8064,7 @@ csum_failed:
                break;
        case offsetof(struct sctphdr, checksum):
                /* validate that this is actually an SCTP request */
-               if (((first->protocol == htons(ETH_P_IP)) &&
-                    (ip_hdr(skb)->protocol == IPPROTO_SCTP)) ||
-                   ((first->protocol == htons(ETH_P_IPV6)) &&
-                    ixgbe_ipv6_csum_is_sctp(skb))) {
+               if (skb_csum_is_sctp(skb)) {
                        type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_SCTP;
                        break;
                }
@@ -10278,8 +10265,6 @@ static const struct net_device_ops ixgbe_netdev_ops = {
        .ndo_bridge_getlink     = ixgbe_ndo_bridge_getlink,
        .ndo_dfwd_add_station   = ixgbe_fwd_add,
        .ndo_dfwd_del_station   = ixgbe_fwd_del,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = ixgbe_features_check,
        .ndo_bpf                = ixgbe_xdp,
        .ndo_xdp_xmit           = ixgbe_xdp_xmit,
index 4061cd7..a14e55e 100644 (file)
@@ -1121,19 +1121,18 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
                                struct ixgbevf_ring *rx_ring,
                                int budget)
 {
-       unsigned int total_rx_bytes = 0, total_rx_packets = 0;
+       unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0;
        struct ixgbevf_adapter *adapter = q_vector->adapter;
        u16 cleaned_count = ixgbevf_desc_unused(rx_ring);
        struct sk_buff *skb = rx_ring->skb;
        bool xdp_xmit = false;
        struct xdp_buff xdp;
 
-       xdp.rxq = &rx_ring->xdp_rxq;
-
        /* Frame size depend on rx_ring setup when PAGE_SIZE=4K */
 #if (PAGE_SIZE < 8192)
-       xdp.frame_sz = ixgbevf_rx_frame_truesize(rx_ring, 0);
+       frame_sz = ixgbevf_rx_frame_truesize(rx_ring, 0);
 #endif
+       xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
 
        while (likely(total_rx_packets < budget)) {
                struct ixgbevf_rx_buffer *rx_buffer;
@@ -1161,12 +1160,12 @@ static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector,
 
                /* retrieve a buffer from the ring */
                if (!skb) {
-                       xdp.data = page_address(rx_buffer->page) +
-                                  rx_buffer->page_offset;
-                       xdp.data_meta = xdp.data;
-                       xdp.data_hard_start = xdp.data -
-                                             ixgbevf_rx_offset(rx_ring);
-                       xdp.data_end = xdp.data + size;
+                       unsigned int offset = ixgbevf_rx_offset(rx_ring);
+                       unsigned char *hard_start;
+
+                       hard_start = page_address(rx_buffer->page) +
+                                    rx_buffer->page_offset - offset;
+                       xdp_prepare_buff(&xdp, hard_start, offset, size, true);
 #if (PAGE_SIZE > 4096)
                        /* At larger PAGE_SIZE, frame_sz depend on len size */
                        xdp.frame_sz = ixgbevf_rx_frame_truesize(rx_ring, size);
@@ -3844,15 +3843,6 @@ static int ixgbevf_tso(struct ixgbevf_ring *tx_ring,
        return 1;
 }
 
-static inline bool ixgbevf_ipv6_csum_is_sctp(struct sk_buff *skb)
-{
-       unsigned int offset = 0;
-
-       ipv6_find_hdr(skb, &offset, IPPROTO_SCTP, NULL, NULL);
-
-       return offset == skb_checksum_start_offset(skb);
-}
-
 static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
                            struct ixgbevf_tx_buffer *first,
                            struct ixgbevf_ipsec_tx_data *itd)
@@ -3873,10 +3863,7 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring,
                break;
        case offsetof(struct sctphdr, checksum):
                /* validate that this is actually an SCTP request */
-               if (((first->protocol == htons(ETH_P_IP)) &&
-                    (ip_hdr(skb)->protocol == IPPROTO_SCTP)) ||
-                   ((first->protocol == htons(ETH_P_IPV6)) &&
-                    ixgbevf_ipv6_csum_is_sctp(skb))) {
+               if (skb_csum_is_sctp(skb)) {
                        type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_SCTP;
                        break;
                }
index 41815b6..7fe15a3 100644 (file)
@@ -94,7 +94,6 @@ config MVPP2
 
 config MVPP2_PTP
        bool "Marvell Armada 8K Enable PTP support"
-       depends on NETWORK_PHY_TIMESTAMPING
        depends on (PTP_1588_CLOCK = y && MVPP2 = y) || \
                   (PTP_1588_CLOCK && MVPP2 = m)
 
index 563ceac..6290bfb 100644 (file)
@@ -2263,11 +2263,8 @@ mvneta_swbm_rx_frame(struct mvneta_port *pp,
 
        /* Prefetch header */
        prefetch(data);
-
-       xdp->data_hard_start = data;
-       xdp->data = data + pp->rx_offset_correction + MVNETA_MH_SIZE;
-       xdp->data_end = xdp->data + data_len;
-       xdp_set_data_meta_invalid(xdp);
+       xdp_prepare_buff(xdp, data, pp->rx_offset_correction + MVNETA_MH_SIZE,
+                        data_len, false);
 
        sinfo = xdp_get_shared_info_from_buff(xdp);
        sinfo->nr_frags = 0;
@@ -2363,9 +2360,8 @@ static int mvneta_rx_swbm(struct napi_struct *napi,
        u32 desc_status, frame_sz;
        struct xdp_buff xdp_buf;
 
+       xdp_init_buff(&xdp_buf, PAGE_SIZE, &rxq->xdp_rxq);
        xdp_buf.data_hard_start = NULL;
-       xdp_buf.frame_sz = PAGE_SIZE;
-       xdp_buf.rxq = &rxq->xdp_rxq;
 
        sinfo.nr_frags = 0;
 
@@ -4432,7 +4428,7 @@ static int mvneta_xdp_setup(struct net_device *dev, struct bpf_prog *prog,
        struct bpf_prog *old_prog;
 
        if (prog && dev->mtu > MVNETA_MAX_RX_BUF_SIZE) {
-               NL_SET_ERR_MSG_MOD(extack, "Jumbo frames not supported on XDP");
+               NL_SET_ERR_MSG_MOD(extack, "MTU too large for XDP");
                return -EOPNOTSUPP;
        }
 
@@ -5255,7 +5251,7 @@ static int mvneta_probe(struct platform_device *pdev)
        err = mvneta_port_power_up(pp, pp->phy_interface);
        if (err < 0) {
                dev_err(&pdev->dev, "can't power up port\n");
-               return err;
+               goto err_netdev;
        }
 
        /* Armada3700 network controller does not support per-cpu
index 8867f25..663157d 100644 (file)
@@ -143,7 +143,7 @@ struct mvpp2_cls_c2_entry {
 /* Number of per-port dedicated entries in the C2 TCAM */
 #define MVPP22_CLS_C2_PORT_N_FLOWS     MVPP2_N_RFS_ENTRIES_PER_FLOW
 
-/* Each port has oen range per flow type + one entry controling the global RSS
+/* Each port has one range per flow type + one entry controlling the global RSS
  * setting and the default rx queue
  */
 #define MVPP22_CLS_C2_PORT_RANGE       (MVPP22_CLS_C2_PORT_N_FLOWS + 1)
index afdd228..1435229 100644 (file)
@@ -1231,7 +1231,7 @@ static void mvpp22_gop_init_rgmii(struct mvpp2_port *port)
 
        regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val);
        if (port->gop_id == 2)
-               val |= GENCONF_CTRL0_PORT0_RGMII | GENCONF_CTRL0_PORT1_RGMII;
+               val |= GENCONF_CTRL0_PORT0_RGMII;
        else if (port->gop_id == 3)
                val |= GENCONF_CTRL0_PORT1_RGMII_MII;
        regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val);
@@ -2370,17 +2370,18 @@ static void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port,
 static void mvpp2_tx_pkts_coal_set(struct mvpp2_port *port,
                                   struct mvpp2_tx_queue *txq)
 {
-       unsigned int thread = mvpp2_cpu_to_thread(port->priv, get_cpu());
+       unsigned int thread;
        u32 val;
 
        if (txq->done_pkts_coal > MVPP2_TXQ_THRESH_MASK)
                txq->done_pkts_coal = MVPP2_TXQ_THRESH_MASK;
 
        val = (txq->done_pkts_coal << MVPP2_TXQ_THRESH_OFFSET);
-       mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
-       mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_THRESH_REG, val);
-
-       put_cpu();
+       /* PKT-coalescing registers are per-queue + per-thread */
+       for (thread = 0; thread < MVPP2_MAX_THREADS; thread++) {
+               mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_NUM_REG, txq->id);
+               mvpp2_thread_write(port->priv, thread, MVPP2_TXQ_THRESH_REG, val);
+       }
 }
 
 static u32 mvpp2_usec_to_cycles(u32 usec, unsigned long clk_hz)
@@ -3562,17 +3563,17 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi,
                        frag_size = bm_pool->frag_size;
 
                if (xdp_prog) {
-                       xdp.data_hard_start = data;
-                       xdp.data = data + MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM;
-                       xdp.data_end = xdp.data + rx_bytes;
-                       xdp.frame_sz = PAGE_SIZE;
+                       struct xdp_rxq_info *xdp_rxq;
 
                        if (bm_pool->pkt_size == MVPP2_BM_SHORT_PKT_SIZE)
-                               xdp.rxq = &rxq->xdp_rxq_short;
+                               xdp_rxq = &rxq->xdp_rxq_short;
                        else
-                               xdp.rxq = &rxq->xdp_rxq_long;
+                               xdp_rxq = &rxq->xdp_rxq_long;
 
-                       xdp_set_data_meta_invalid(&xdp);
+                       xdp_init_buff(&xdp, PAGE_SIZE, xdp_rxq);
+                       xdp_prepare_buff(&xdp, data,
+                                        MVPP2_MH_SIZE + MVPP2_SKB_HEADROOM,
+                                        rx_bytes, false);
 
                        ret = mvpp2_run_xdp(port, rxq, xdp_prog, &xdp, pp, &ps);
 
@@ -5487,7 +5488,7 @@ static int mvpp2_port_init(struct mvpp2_port *port)
        struct mvpp2 *priv = port->priv;
        struct mvpp2_txq_pcpu *txq_pcpu;
        unsigned int thread;
-       int queue, err;
+       int queue, err, val;
 
        /* Checks for hardware constraints */
        if (port->first_rxq + port->nrxqs >
@@ -5501,6 +5502,18 @@ static int mvpp2_port_init(struct mvpp2_port *port)
        mvpp2_egress_disable(port);
        mvpp2_port_disable(port);
 
+       if (mvpp2_is_xlg(port->phy_interface)) {
+               val = readl(port->base + MVPP22_XLG_CTRL0_REG);
+               val &= ~MVPP22_XLG_CTRL0_FORCE_LINK_PASS;
+               val |= MVPP22_XLG_CTRL0_FORCE_LINK_DOWN;
+               writel(val, port->base + MVPP22_XLG_CTRL0_REG);
+       } else {
+               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+               val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
+               val |= MVPP2_GMAC_FORCE_LINK_DOWN;
+               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       }
+
        port->tx_time_coal = MVPP2_TXDONE_COAL_USEC;
 
        port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs),
@@ -5869,8 +5882,6 @@ static void mvpp2_phylink_validate(struct phylink_config *config,
 
        phylink_set(mask, Autoneg);
        phylink_set_port_modes(mask);
-       phylink_set(mask, Pause);
-       phylink_set(mask, Asym_Pause);
 
        switch (state->interface) {
        case PHY_INTERFACE_MODE_10GBASER:
index 5692c60..6ee53c5 100644 (file)
@@ -405,6 +405,38 @@ static int mvpp2_prs_tcam_first_free(struct mvpp2 *priv, unsigned char start,
        return -EINVAL;
 }
 
+/* Drop flow control pause frames */
+static void mvpp2_prs_drop_fc(struct mvpp2 *priv)
+{
+       unsigned char da[ETH_ALEN] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x01 };
+       struct mvpp2_prs_entry pe;
+       unsigned int len;
+
+       memset(&pe, 0, sizeof(pe));
+
+       /* For all ports - drop flow control frames */
+       pe.index = MVPP2_PE_FC_DROP;
+       mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_MAC);
+
+       /* Set match on DA */
+       len = ETH_ALEN;
+       while (len--)
+               mvpp2_prs_tcam_data_byte_set(&pe, len, da[len], 0xff);
+
+       mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_DROP_MASK,
+                                MVPP2_PRS_RI_DROP_MASK);
+
+       mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1);
+       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS);
+
+       /* Mask all ports */
+       mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
+
+       /* Update shadow table and hw entry */
+       mvpp2_prs_shadow_set(priv, pe.index, MVPP2_PRS_LU_MAC);
+       mvpp2_prs_hw_write(priv, &pe);
+}
+
 /* Enable/disable dropping all mac da's */
 static void mvpp2_prs_mac_drop_all_set(struct mvpp2 *priv, int port, bool add)
 {
@@ -882,15 +914,15 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto,
        mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4);
        pe.index = tid;
 
-       /* Set next lu to IPv4 */
-       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4);
-       mvpp2_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+       /* Finished: go to flowid generation */
+       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS);
+       mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1);
+
        /* Set L4 offset */
        mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4,
                                  sizeof(struct iphdr) - 4,
                                  MVPP2_PRS_SRAM_OP_SEL_UDF_ADD);
-       mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
-                                MVPP2_PRS_IPV4_DIP_AI_BIT);
+       mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT);
        mvpp2_prs_sram_ri_update(&pe, ri, ri_mask | MVPP2_PRS_RI_IP_FRAG_MASK);
 
        mvpp2_prs_tcam_data_byte_set(&pe, 2, 0x00,
@@ -899,7 +931,8 @@ static int mvpp2_prs_ip4_proto(struct mvpp2 *priv, unsigned short proto,
                                     MVPP2_PRS_TCAM_PROTO_MASK);
 
        mvpp2_prs_tcam_data_byte_set(&pe, 5, proto, MVPP2_PRS_TCAM_PROTO_MASK);
-       mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT);
+       mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
+                                MVPP2_PRS_IPV4_DIP_AI_BIT);
        /* Unmask all ports */
        mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
 
@@ -967,12 +1000,17 @@ static int mvpp2_prs_ip4_cast(struct mvpp2 *priv, unsigned short l3_cast)
                return -EINVAL;
        }
 
-       /* Finished: go to flowid generation */
-       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS);
-       mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1);
+       /* Go again to ipv4 */
+       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4);
 
-       mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
+       mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
                                 MVPP2_PRS_IPV4_DIP_AI_BIT);
+
+       /* Shift back to IPv4 proto */
+       mvpp2_prs_sram_shift_set(&pe, -12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
+       mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT);
+
        /* Unmask all ports */
        mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
 
@@ -1162,6 +1200,7 @@ static void mvpp2_prs_mac_init(struct mvpp2 *priv)
        mvpp2_prs_hw_write(priv, &pe);
 
        /* Create dummy entries for drop all and promiscuous modes */
+       mvpp2_prs_drop_fc(priv);
        mvpp2_prs_mac_drop_all_set(priv, 0, false);
        mvpp2_prs_mac_promisc_set(priv, 0, MVPP2_PRS_L2_UNI_CAST, false);
        mvpp2_prs_mac_promisc_set(priv, 0, MVPP2_PRS_L2_MULTI_CAST, false);
@@ -1392,8 +1431,9 @@ static int mvpp2_prs_etype_init(struct mvpp2 *priv)
        mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4);
        mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4,
                                 MVPP2_PRS_RI_L3_PROTO_MASK);
-       /* Skip eth_type + 4 bytes of IP header */
-       mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4,
+       /* goto ipv4 dest-address (skip eth_type + IP-header-size - 4) */
+       mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN +
+                                sizeof(struct iphdr) - 4,
                                 MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
        /* Set L3 offset */
        mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3,
@@ -1597,8 +1637,9 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv)
        mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4);
        mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP4_OPT,
                                 MVPP2_PRS_RI_L3_PROTO_MASK);
-       /* Skip eth_type + 4 bytes of IP header */
-       mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4,
+       /* goto ipv4 dest-address (skip eth_type + IP-header-size - 4) */
+       mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN +
+                                sizeof(struct iphdr) - 4,
                                 MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
        /* Set L3 offset */
        mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3,
@@ -1647,8 +1688,9 @@ static int mvpp2_prs_pppoe_init(struct mvpp2 *priv)
        mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP6);
        mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_IP6,
                                 MVPP2_PRS_RI_L3_PROTO_MASK);
-       /* Skip eth_type + 4 bytes of IPv6 header */
-       mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 4,
+       /* Jump to DIP of IPV6 header */
+       mvpp2_prs_sram_shift_set(&pe, MVPP2_ETH_TYPE_LEN + 8 +
+                                MVPP2_MAX_L3_ADDR_SIZE,
                                 MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
        /* Set L3 offset */
        mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L3,
@@ -1727,19 +1769,20 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv)
        mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4);
        pe.index = MVPP2_PE_IP4_PROTO_UN;
 
-       /* Set next lu to IPv4 */
-       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4);
-       mvpp2_prs_sram_shift_set(&pe, 12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+       /* Finished: go to flowid generation */
+       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS);
+       mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1);
+
        /* Set L4 offset */
        mvpp2_prs_sram_offset_set(&pe, MVPP2_PRS_SRAM_UDF_TYPE_L4,
                                  sizeof(struct iphdr) - 4,
                                  MVPP2_PRS_SRAM_OP_SEL_UDF_ADD);
-       mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
-                                MVPP2_PRS_IPV4_DIP_AI_BIT);
+       mvpp2_prs_sram_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT);
        mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L4_OTHER,
                                 MVPP2_PRS_RI_L4_PROTO_MASK);
 
-       mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT);
+       mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
+                                MVPP2_PRS_IPV4_DIP_AI_BIT);
        /* Unmask all ports */
        mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
 
@@ -1752,14 +1795,19 @@ static int mvpp2_prs_ip4_init(struct mvpp2 *priv)
        mvpp2_prs_tcam_lu_set(&pe, MVPP2_PRS_LU_IP4);
        pe.index = MVPP2_PE_IP4_ADDR_UN;
 
-       /* Finished: go to flowid generation */
-       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_FLOWS);
-       mvpp2_prs_sram_bits_set(&pe, MVPP2_PRS_SRAM_LU_GEN_BIT, 1);
+       /* Go again to ipv4 */
+       mvpp2_prs_sram_next_lu_set(&pe, MVPP2_PRS_LU_IP4);
+
+       mvpp2_prs_sram_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
+                                MVPP2_PRS_IPV4_DIP_AI_BIT);
+
+       /* Shift back to IPv4 proto */
+       mvpp2_prs_sram_shift_set(&pe, -12, MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD);
+
        mvpp2_prs_sram_ri_update(&pe, MVPP2_PRS_RI_L3_UCAST,
                                 MVPP2_PRS_RI_L3_ADDR_MASK);
+       mvpp2_prs_tcam_ai_update(&pe, 0, MVPP2_PRS_IPV4_DIP_AI_BIT);
 
-       mvpp2_prs_tcam_ai_update(&pe, MVPP2_PRS_IPV4_DIP_AI_BIT,
-                                MVPP2_PRS_IPV4_DIP_AI_BIT);
        /* Unmask all ports */
        mvpp2_prs_tcam_port_map_set(&pe, MVPP2_PRS_PORT_MASK);
 
index e22f6c8..4b68dd3 100644 (file)
 #define MVPP2_PE_VID_EDSA_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 7)
 #define MVPP2_PE_VLAN_DBL              (MVPP2_PRS_TCAM_SRAM_SIZE - 6)
 #define MVPP2_PE_VLAN_NONE             (MVPP2_PRS_TCAM_SRAM_SIZE - 5)
-/* reserved */
+#define MVPP2_PE_FC_DROP               (MVPP2_PRS_TCAM_SRAM_SIZE - 4)
 #define MVPP2_PE_MAC_MC_PROMISCUOUS    (MVPP2_PRS_TCAM_SRAM_SIZE - 3)
 #define MVPP2_PE_MAC_UC_PROMISCUOUS    (MVPP2_PRS_TCAM_SRAM_SIZE - 2)
 #define MVPP2_PE_MAC_NON_PROMISCUOUS   (MVPP2_PRS_TCAM_SRAM_SIZE - 1)
index 7d0f962..84a9123 100644 (file)
@@ -867,12 +867,14 @@ static int cgx_lmac_init(struct cgx *cgx)
                cgx->lmac_count = MAX_LMAC_PER_CGX;
 
        for (i = 0; i < cgx->lmac_count; i++) {
-               lmac = kcalloc(1, sizeof(struct lmac), GFP_KERNEL);
+               lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL);
                if (!lmac)
                        return -ENOMEM;
                lmac->name = kcalloc(1, sizeof("cgx_fwi_xxx_yyy"), GFP_KERNEL);
-               if (!lmac->name)
-                       return -ENOMEM;
+               if (!lmac->name) {
+                       err = -ENOMEM;
+                       goto err_lmac_free;
+               }
                sprintf(lmac->name, "cgx_fwi_%d_%d", cgx->cgx_id, i);
                lmac->lmac_id = i;
                lmac->cgx = cgx;
@@ -883,7 +885,7 @@ static int cgx_lmac_init(struct cgx *cgx)
                                                 CGX_LMAC_FWI + i * 9),
                                   cgx_fwi_event_handler, 0, lmac->name, lmac);
                if (err)
-                       return err;
+                       goto err_irq;
 
                /* Enable interrupt */
                cgx_write(cgx, lmac->lmac_id, CGXX_CMRX_INT_ENA_W1S,
@@ -895,6 +897,12 @@ static int cgx_lmac_init(struct cgx *cgx)
        }
 
        return cgx_lmac_verify_fwi_version(cgx);
+
+err_irq:
+       kfree(lmac->name);
+err_lmac_free:
+       kfree(lmac);
+       return err;
 }
 
 static int cgx_lmac_exit(struct cgx *cgx)
index f919283..89e93eb 100644 (file)
@@ -717,6 +717,8 @@ struct nix_rss_flowkey_cfg {
 #define NIX_FLOW_KEY_TYPE_INNR_ETH_DMAC BIT(17)
 #define NIX_FLOW_KEY_TYPE_VLAN         BIT(20)
 #define NIX_FLOW_KEY_TYPE_IPV4_PROTO   BIT(21)
+#define NIX_FLOW_KEY_TYPE_AH           BIT(22)
+#define NIX_FLOW_KEY_TYPE_ESP          BIT(23)
        u32     flowkey_cfg; /* Flowkey types selected */
        u8      group;       /* RSS context or group */
 };
index a1f7944..3c640f6 100644 (file)
@@ -162,6 +162,11 @@ enum key_fields {
        NPC_DIP_IPV4,
        NPC_SIP_IPV6,
        NPC_DIP_IPV6,
+       NPC_IPPROTO_TCP,
+       NPC_IPPROTO_UDP,
+       NPC_IPPROTO_SCTP,
+       NPC_IPPROTO_AH,
+       NPC_IPPROTO_ESP,
        NPC_SPORT_TCP,
        NPC_DPORT_TCP,
        NPC_SPORT_UDP,
index e8fd712..4ef7fc8 100644 (file)
@@ -646,7 +646,7 @@ setup_vfmsix:
        }
 
        /* HW interprets RVU_AF_MSIXTR_BASE address as an IOVA, hence
-        * create a IOMMU mapping for the physcial address configured by
+        * create an IOMMU mapping for the physical address configured by
         * firmware and reconfig RVU_AF_MSIXTR_BASE with IOVA.
         */
        cfg = rvu_read64(rvu, BLKADDR_RVUM, RVU_PRIV_CONST);
@@ -1323,7 +1323,7 @@ static int rvu_get_attach_blkaddr(struct rvu *rvu, int blktype,
                break;
        default:
                return rvu_get_blkaddr(rvu, blktype, 0);
-       };
+       }
 
        if (is_block_implemented(rvu->hw, blkaddr))
                return blkaddr;
index d298b93..6c6b411 100644 (file)
@@ -469,6 +469,9 @@ int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu,
        int pf = rvu_get_pf(req->hdr.pcifunc);
        u8 cgx_id, lmac_id;
 
+       if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc))
+               return -EPERM;
+
        rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
 
        cgx_lmac_addr_set(cgx_id, lmac_id, req->mac_addr);
@@ -485,6 +488,9 @@ int rvu_mbox_handler_cgx_mac_addr_get(struct rvu *rvu,
        int rc = 0, i;
        u64 cfg;
 
+       if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc))
+               return -EPERM;
+
        rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
 
        rsp->hdr.rc = rc;
index d27543c..f604995 100644 (file)
@@ -1757,6 +1757,7 @@ static void rvu_dbg_npc_mcam_show_flows(struct seq_file *s,
                        seq_printf(s, "mask 0x%x\n", ntohs(rule->mask.dport));
                        break;
                default:
+                       seq_puts(s, "\n");
                        break;
                }
        }
@@ -1785,7 +1786,7 @@ static void rvu_dbg_npc_mcam_show_action(struct seq_file *s,
                        break;
                default:
                        break;
-               };
+               }
        } else {
                switch (rule->rx_action.op) {
                case NIX_RX_ACTIONOP_DROP:
@@ -1806,7 +1807,7 @@ static void rvu_dbg_npc_mcam_show_action(struct seq_file *s,
                        break;
                default:
                        break;
-               };
+               }
        }
 }
 
index bc0e411..10a98bc 100644 (file)
@@ -52,6 +52,650 @@ static bool rvu_common_request_irq(struct rvu *rvu, int offset,
        return rvu->irq_allocated[offset];
 }
 
+static void rvu_nix_intr_work(struct work_struct *work)
+{
+       struct rvu_nix_health_reporters *rvu_nix_health_reporter;
+
+       rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, intr_work);
+       devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_intr_reporter,
+                             "NIX_AF_RVU Error",
+                             rvu_nix_health_reporter->nix_event_ctx);
+}
+
+static irqreturn_t rvu_nix_af_rvu_intr_handler(int irq, void *rvu_irq)
+{
+       struct rvu_nix_event_ctx *nix_event_context;
+       struct rvu_devlink *rvu_dl = rvu_irq;
+       struct rvu *rvu;
+       int blkaddr;
+       u64 intr;
+
+       rvu = rvu_dl->rvu;
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return IRQ_NONE;
+
+       nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+       intr = rvu_read64(rvu, blkaddr, NIX_AF_RVU_INT);
+       nix_event_context->nix_af_rvu_int = intr;
+
+       /* Clear interrupts */
+       rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT, intr);
+       rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL);
+       queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->intr_work);
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_nix_gen_work(struct work_struct *work)
+{
+       struct rvu_nix_health_reporters *rvu_nix_health_reporter;
+
+       rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, gen_work);
+       devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_gen_reporter,
+                             "NIX_AF_GEN Error",
+                             rvu_nix_health_reporter->nix_event_ctx);
+}
+
+static irqreturn_t rvu_nix_af_rvu_gen_handler(int irq, void *rvu_irq)
+{
+       struct rvu_nix_event_ctx *nix_event_context;
+       struct rvu_devlink *rvu_dl = rvu_irq;
+       struct rvu *rvu;
+       int blkaddr;
+       u64 intr;
+
+       rvu = rvu_dl->rvu;
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return IRQ_NONE;
+
+       nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+       intr = rvu_read64(rvu, blkaddr, NIX_AF_GEN_INT);
+       nix_event_context->nix_af_rvu_gen = intr;
+
+       /* Clear interrupts */
+       rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT, intr);
+       rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1C, ~0ULL);
+       queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->gen_work);
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_nix_err_work(struct work_struct *work)
+{
+       struct rvu_nix_health_reporters *rvu_nix_health_reporter;
+
+       rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, err_work);
+       devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_err_reporter,
+                             "NIX_AF_ERR Error",
+                             rvu_nix_health_reporter->nix_event_ctx);
+}
+
+static irqreturn_t rvu_nix_af_rvu_err_handler(int irq, void *rvu_irq)
+{
+       struct rvu_nix_event_ctx *nix_event_context;
+       struct rvu_devlink *rvu_dl = rvu_irq;
+       struct rvu *rvu;
+       int blkaddr;
+       u64 intr;
+
+       rvu = rvu_dl->rvu;
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return IRQ_NONE;
+
+       nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+       intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT);
+       nix_event_context->nix_af_rvu_err = intr;
+
+       /* Clear interrupts */
+       rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT, intr);
+       rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL);
+       queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->err_work);
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_nix_ras_work(struct work_struct *work)
+{
+       struct rvu_nix_health_reporters *rvu_nix_health_reporter;
+
+       rvu_nix_health_reporter = container_of(work, struct rvu_nix_health_reporters, ras_work);
+       devlink_health_report(rvu_nix_health_reporter->rvu_hw_nix_ras_reporter,
+                             "NIX_AF_RAS Error",
+                             rvu_nix_health_reporter->nix_event_ctx);
+}
+
+static irqreturn_t rvu_nix_af_rvu_ras_handler(int irq, void *rvu_irq)
+{
+       struct rvu_nix_event_ctx *nix_event_context;
+       struct rvu_devlink *rvu_dl = rvu_irq;
+       struct rvu *rvu;
+       int blkaddr;
+       u64 intr;
+
+       rvu = rvu_dl->rvu;
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return IRQ_NONE;
+
+       nix_event_context = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+       intr = rvu_read64(rvu, blkaddr, NIX_AF_ERR_INT);
+       nix_event_context->nix_af_rvu_ras = intr;
+
+       /* Clear interrupts */
+       rvu_write64(rvu, blkaddr, NIX_AF_RAS, intr);
+       rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL);
+       queue_work(rvu_dl->devlink_wq, &rvu_dl->rvu_nix_health_reporter->ras_work);
+
+       return IRQ_HANDLED;
+}
+
+static void rvu_nix_unregister_interrupts(struct rvu *rvu)
+{
+       struct rvu_devlink *rvu_dl = rvu->rvu_dl;
+       int offs, i, blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return;
+
+       offs = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff;
+       if (!offs)
+               return;
+
+       rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1C, ~0ULL);
+       rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1C, ~0ULL);
+       rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1C, ~0ULL);
+       rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1C, ~0ULL);
+
+       if (rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU]) {
+               free_irq(pci_irq_vector(rvu->pdev, offs + NIX_AF_INT_VEC_RVU),
+                        rvu_dl);
+               rvu->irq_allocated[offs + NIX_AF_INT_VEC_RVU] = false;
+       }
+
+       for (i = NIX_AF_INT_VEC_AF_ERR; i < NIX_AF_INT_VEC_CNT; i++)
+               if (rvu->irq_allocated[offs + i]) {
+                       free_irq(pci_irq_vector(rvu->pdev, offs + i), rvu_dl);
+                       rvu->irq_allocated[offs + i] = false;
+               }
+}
+
+static int rvu_nix_register_interrupts(struct rvu *rvu)
+{
+       int blkaddr, base;
+       bool rc;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return blkaddr;
+
+       /* Get NIX AF MSIX vectors offset. */
+       base = rvu_read64(rvu, blkaddr, NIX_PRIV_AF_INT_CFG) & 0x3ff;
+       if (!base) {
+               dev_warn(rvu->dev,
+                        "Failed to get NIX%d NIX_AF_INT vector offsets\n",
+                        blkaddr - BLKADDR_NIX0);
+               return 0;
+       }
+       /* Register and enable NIX_AF_RVU_INT interrupt */
+       rc = rvu_common_request_irq(rvu, base +  NIX_AF_INT_VEC_RVU,
+                                   "NIX_AF_RVU_INT",
+                                   rvu_nix_af_rvu_intr_handler);
+       if (!rc)
+               goto err;
+       rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL);
+
+       /* Register and enable NIX_AF_GEN_INT interrupt */
+       rc = rvu_common_request_irq(rvu, base +  NIX_AF_INT_VEC_GEN,
+                                   "NIX_AF_GEN_INT",
+                                   rvu_nix_af_rvu_gen_handler);
+       if (!rc)
+               goto err;
+       rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1S, ~0ULL);
+
+       /* Register and enable NIX_AF_ERR_INT interrupt */
+       rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_AF_ERR,
+                                   "NIX_AF_ERR_INT",
+                                   rvu_nix_af_rvu_err_handler);
+       if (!rc)
+               goto err;
+       rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL);
+
+       /* Register and enable NIX_AF_RAS interrupt */
+       rc = rvu_common_request_irq(rvu, base + NIX_AF_INT_VEC_POISON,
+                                   "NIX_AF_RAS",
+                                   rvu_nix_af_rvu_ras_handler);
+       if (!rc)
+               goto err;
+       rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL);
+
+       return 0;
+err:
+       rvu_nix_unregister_interrupts(rvu);
+       return rc;
+}
+
+static int rvu_nix_report_show(struct devlink_fmsg *fmsg, void *ctx,
+                              enum nix_af_rvu_health health_reporter)
+{
+       struct rvu_nix_event_ctx *nix_event_context;
+       u64 intr_val;
+       int err;
+
+       nix_event_context = ctx;
+       switch (health_reporter) {
+       case NIX_AF_RVU_INTR:
+               intr_val = nix_event_context->nix_af_rvu_int;
+               err = rvu_report_pair_start(fmsg, "NIX_AF_RVU");
+               if (err)
+                       return err;
+               err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX RVU Interrupt Reg ",
+                                               nix_event_context->nix_af_rvu_int);
+               if (err)
+                       return err;
+               if (intr_val & BIT_ULL(0)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tUnmap Slot Error");
+                       if (err)
+                               return err;
+               }
+               err = rvu_report_pair_end(fmsg);
+               if (err)
+                       return err;
+               break;
+       case NIX_AF_RVU_GEN:
+               intr_val = nix_event_context->nix_af_rvu_gen;
+               err = rvu_report_pair_start(fmsg, "NIX_AF_GENERAL");
+               if (err)
+                       return err;
+               err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX General Interrupt Reg ",
+                                               nix_event_context->nix_af_rvu_gen);
+               if (err)
+                       return err;
+               if (intr_val & BIT_ULL(0)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tRx multicast pkt drop");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(1)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tRx mirror pkt drop");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(4)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tSMQ flush done");
+                       if (err)
+                               return err;
+               }
+               err = rvu_report_pair_end(fmsg);
+               if (err)
+                       return err;
+               break;
+       case NIX_AF_RVU_ERR:
+               intr_val = nix_event_context->nix_af_rvu_err;
+               err = rvu_report_pair_start(fmsg, "NIX_AF_ERR");
+               if (err)
+                       return err;
+               err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX Error Interrupt Reg ",
+                                               nix_event_context->nix_af_rvu_err);
+               if (err)
+                       return err;
+               if (intr_val & BIT_ULL(14)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_AQ_INST_S read");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(13)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_AQ_RES_S write");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(12)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tAQ Doorbell Error");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(6)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tRx on unmapped PF_FUNC");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(5)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tRx multicast replication error");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(4)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on NIX_RX_MCE_S read");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(3)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on multicast WQE read");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(2)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on mirror WQE read");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(1)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on mirror pkt write");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(0)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tFault on multicast pkt write");
+                       if (err)
+                               return err;
+               }
+               err = rvu_report_pair_end(fmsg);
+               if (err)
+                       return err;
+               break;
+       case NIX_AF_RVU_RAS:
+               intr_val = nix_event_context->nix_af_rvu_err;
+               err = rvu_report_pair_start(fmsg, "NIX_AF_RAS");
+               if (err)
+                       return err;
+               err = devlink_fmsg_u64_pair_put(fmsg, "\tNIX RAS Interrupt Reg ",
+                                               nix_event_context->nix_af_rvu_err);
+               if (err)
+                       return err;
+               err = devlink_fmsg_string_put(fmsg, "\n\tPoison Data on:");
+               if (err)
+                       return err;
+               if (intr_val & BIT_ULL(34)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tNIX_AQ_INST_S");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(33)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tNIX_AQ_RES_S");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(32)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tHW ctx");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(4)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tPacket from mirror buffer");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(3)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tPacket from multicast buffer");
+
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(2)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tWQE read from mirror buffer");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(1)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tWQE read from multicast buffer");
+                       if (err)
+                               return err;
+               }
+               if (intr_val & BIT_ULL(0)) {
+                       err = devlink_fmsg_string_put(fmsg, "\n\tNIX_RX_MCE_S read");
+                       if (err)
+                               return err;
+               }
+               err = rvu_report_pair_end(fmsg);
+               if (err)
+                       return err;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rvu_hw_nix_intr_dump(struct devlink_health_reporter *reporter,
+                               struct devlink_fmsg *fmsg, void *ctx,
+                               struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_devlink *rvu_dl = rvu->rvu_dl;
+       struct rvu_nix_event_ctx *nix_ctx;
+
+       nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+
+       return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_INTR) :
+                    rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_INTR);
+}
+
+static int rvu_hw_nix_intr_recover(struct devlink_health_reporter *reporter,
+                                  void *ctx, struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_nix_event_ctx *nix_event_ctx = ctx;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return blkaddr;
+
+       if (nix_event_ctx->nix_af_rvu_int)
+               rvu_write64(rvu, blkaddr, NIX_AF_RVU_INT_ENA_W1S, ~0ULL);
+
+       return 0;
+}
+
+static int rvu_hw_nix_gen_dump(struct devlink_health_reporter *reporter,
+                              struct devlink_fmsg *fmsg, void *ctx,
+                              struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_devlink *rvu_dl = rvu->rvu_dl;
+       struct rvu_nix_event_ctx *nix_ctx;
+
+       nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+
+       return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_GEN) :
+                    rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_GEN);
+}
+
+static int rvu_hw_nix_gen_recover(struct devlink_health_reporter *reporter,
+                                 void *ctx, struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_nix_event_ctx *nix_event_ctx = ctx;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return blkaddr;
+
+       if (nix_event_ctx->nix_af_rvu_gen)
+               rvu_write64(rvu, blkaddr, NIX_AF_GEN_INT_ENA_W1S, ~0ULL);
+
+       return 0;
+}
+
+static int rvu_hw_nix_err_dump(struct devlink_health_reporter *reporter,
+                              struct devlink_fmsg *fmsg, void *ctx,
+                              struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_devlink *rvu_dl = rvu->rvu_dl;
+       struct rvu_nix_event_ctx *nix_ctx;
+
+       nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+
+       return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_ERR) :
+                    rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_ERR);
+}
+
+static int rvu_hw_nix_err_recover(struct devlink_health_reporter *reporter,
+                                 void *ctx, struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_nix_event_ctx *nix_event_ctx = ctx;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return blkaddr;
+
+       if (nix_event_ctx->nix_af_rvu_err)
+               rvu_write64(rvu, blkaddr, NIX_AF_ERR_INT_ENA_W1S, ~0ULL);
+
+       return 0;
+}
+
+static int rvu_hw_nix_ras_dump(struct devlink_health_reporter *reporter,
+                              struct devlink_fmsg *fmsg, void *ctx,
+                              struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_devlink *rvu_dl = rvu->rvu_dl;
+       struct rvu_nix_event_ctx *nix_ctx;
+
+       nix_ctx = rvu_dl->rvu_nix_health_reporter->nix_event_ctx;
+
+       return ctx ? rvu_nix_report_show(fmsg, ctx, NIX_AF_RVU_RAS) :
+                    rvu_nix_report_show(fmsg, nix_ctx, NIX_AF_RVU_RAS);
+}
+
+static int rvu_hw_nix_ras_recover(struct devlink_health_reporter *reporter,
+                                 void *ctx, struct netlink_ext_ack *netlink_extack)
+{
+       struct rvu *rvu = devlink_health_reporter_priv(reporter);
+       struct rvu_nix_event_ctx *nix_event_ctx = ctx;
+       int blkaddr;
+
+       blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, 0);
+       if (blkaddr < 0)
+               return blkaddr;
+
+       if (nix_event_ctx->nix_af_rvu_int)
+               rvu_write64(rvu, blkaddr, NIX_AF_RAS_ENA_W1S, ~0ULL);
+
+       return 0;
+}
+
+RVU_REPORTERS(hw_nix_intr);
+RVU_REPORTERS(hw_nix_gen);
+RVU_REPORTERS(hw_nix_err);
+RVU_REPORTERS(hw_nix_ras);
+
+static void rvu_nix_health_reporters_destroy(struct rvu_devlink *rvu_dl);
+
+static int rvu_nix_register_reporters(struct rvu_devlink *rvu_dl)
+{
+       struct rvu_nix_health_reporters *rvu_reporters;
+       struct rvu_nix_event_ctx *nix_event_context;
+       struct rvu *rvu = rvu_dl->rvu;
+
+       rvu_reporters = kzalloc(sizeof(*rvu_reporters), GFP_KERNEL);
+       if (!rvu_reporters)
+               return -ENOMEM;
+
+       rvu_dl->rvu_nix_health_reporter = rvu_reporters;
+       nix_event_context = kzalloc(sizeof(*nix_event_context), GFP_KERNEL);
+       if (!nix_event_context)
+               return -ENOMEM;
+
+       rvu_reporters->nix_event_ctx = nix_event_context;
+       rvu_reporters->rvu_hw_nix_intr_reporter =
+               devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_intr_reporter_ops, 0, rvu);
+       if (IS_ERR(rvu_reporters->rvu_hw_nix_intr_reporter)) {
+               dev_warn(rvu->dev, "Failed to create hw_nix_intr reporter, err=%ld\n",
+                        PTR_ERR(rvu_reporters->rvu_hw_nix_intr_reporter));
+               return PTR_ERR(rvu_reporters->rvu_hw_nix_intr_reporter);
+       }
+
+       rvu_reporters->rvu_hw_nix_gen_reporter =
+               devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_gen_reporter_ops, 0, rvu);
+       if (IS_ERR(rvu_reporters->rvu_hw_nix_gen_reporter)) {
+               dev_warn(rvu->dev, "Failed to create hw_nix_gen reporter, err=%ld\n",
+                        PTR_ERR(rvu_reporters->rvu_hw_nix_gen_reporter));
+               return PTR_ERR(rvu_reporters->rvu_hw_nix_gen_reporter);
+       }
+
+       rvu_reporters->rvu_hw_nix_err_reporter =
+               devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_err_reporter_ops, 0, rvu);
+       if (IS_ERR(rvu_reporters->rvu_hw_nix_err_reporter)) {
+               dev_warn(rvu->dev, "Failed to create hw_nix_err reporter, err=%ld\n",
+                        PTR_ERR(rvu_reporters->rvu_hw_nix_err_reporter));
+               return PTR_ERR(rvu_reporters->rvu_hw_nix_err_reporter);
+       }
+
+       rvu_reporters->rvu_hw_nix_ras_reporter =
+               devlink_health_reporter_create(rvu_dl->dl, &rvu_hw_nix_ras_reporter_ops, 0, rvu);
+       if (IS_ERR(rvu_reporters->rvu_hw_nix_ras_reporter)) {
+               dev_warn(rvu->dev, "Failed to create hw_nix_ras reporter, err=%ld\n",
+                        PTR_ERR(rvu_reporters->rvu_hw_nix_ras_reporter));
+               return PTR_ERR(rvu_reporters->rvu_hw_nix_ras_reporter);
+       }
+
+       rvu_dl->devlink_wq = create_workqueue("rvu_devlink_wq");
+       if (!rvu_dl->devlink_wq)
+               goto err;
+
+       INIT_WORK(&rvu_reporters->intr_work, rvu_nix_intr_work);
+       INIT_WORK(&rvu_reporters->gen_work, rvu_nix_gen_work);
+       INIT_WORK(&rvu_reporters->err_work, rvu_nix_err_work);
+       INIT_WORK(&rvu_reporters->ras_work, rvu_nix_ras_work);
+
+       return 0;
+err:
+       rvu_nix_health_reporters_destroy(rvu_dl);
+       return -ENOMEM;
+}
+
+static int rvu_nix_health_reporters_create(struct rvu_devlink *rvu_dl)
+{
+       struct rvu *rvu = rvu_dl->rvu;
+       int err;
+
+       err = rvu_nix_register_reporters(rvu_dl);
+       if (err) {
+               dev_warn(rvu->dev, "Failed to create nix reporter, err =%d\n",
+                        err);
+               return err;
+       }
+       rvu_nix_register_interrupts(rvu);
+
+       return 0;
+}
+
+static void rvu_nix_health_reporters_destroy(struct rvu_devlink *rvu_dl)
+{
+       struct rvu_nix_health_reporters *nix_reporters;
+       struct rvu *rvu = rvu_dl->rvu;
+
+       nix_reporters = rvu_dl->rvu_nix_health_reporter;
+
+       if (!nix_reporters->rvu_hw_nix_ras_reporter)
+               return;
+       if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_intr_reporter))
+               devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_intr_reporter);
+
+       if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_gen_reporter))
+               devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_gen_reporter);
+
+       if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_err_reporter))
+               devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_err_reporter);
+
+       if (!IS_ERR_OR_NULL(nix_reporters->rvu_hw_nix_ras_reporter))
+               devlink_health_reporter_destroy(nix_reporters->rvu_hw_nix_ras_reporter);
+
+       rvu_nix_unregister_interrupts(rvu);
+       kfree(rvu_dl->rvu_nix_health_reporter->nix_event_ctx);
+       kfree(rvu_dl->rvu_nix_health_reporter);
+}
+
 static void rvu_npa_intr_work(struct work_struct *work)
 {
        struct rvu_npa_health_reporters *rvu_npa_health_reporter;
@@ -698,9 +1342,14 @@ static void rvu_npa_health_reporters_destroy(struct rvu_devlink *rvu_dl)
 static int rvu_health_reporters_create(struct rvu *rvu)
 {
        struct rvu_devlink *rvu_dl;
+       int err;
 
        rvu_dl = rvu->rvu_dl;
-       return rvu_npa_health_reporters_create(rvu_dl);
+       err = rvu_npa_health_reporters_create(rvu_dl);
+       if (err)
+               return err;
+
+       return rvu_nix_health_reporters_create(rvu_dl);
 }
 
 static void rvu_health_reporters_destroy(struct rvu *rvu)
@@ -712,6 +1361,7 @@ static void rvu_health_reporters_destroy(struct rvu *rvu)
 
        rvu_dl = rvu->rvu_dl;
        rvu_npa_health_reporters_destroy(rvu_dl);
+       rvu_nix_health_reporters_destroy(rvu_dl);
 }
 
 static int rvu_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
index d7578fa..471e57d 100644 (file)
@@ -41,11 +41,38 @@ struct rvu_npa_health_reporters {
        struct work_struct              ras_work;
 };
 
+enum nix_af_rvu_health {
+       NIX_AF_RVU_INTR,
+       NIX_AF_RVU_GEN,
+       NIX_AF_RVU_ERR,
+       NIX_AF_RVU_RAS,
+};
+
+struct rvu_nix_event_ctx {
+       u64 nix_af_rvu_int;
+       u64 nix_af_rvu_gen;
+       u64 nix_af_rvu_err;
+       u64 nix_af_rvu_ras;
+};
+
+struct rvu_nix_health_reporters {
+       struct rvu_nix_event_ctx *nix_event_ctx;
+       struct devlink_health_reporter *rvu_hw_nix_intr_reporter;
+       struct work_struct              intr_work;
+       struct devlink_health_reporter *rvu_hw_nix_gen_reporter;
+       struct work_struct              gen_work;
+       struct devlink_health_reporter *rvu_hw_nix_err_reporter;
+       struct work_struct              err_work;
+       struct devlink_health_reporter *rvu_hw_nix_ras_reporter;
+       struct work_struct              ras_work;
+};
+
 struct rvu_devlink {
        struct devlink *dl;
        struct rvu *rvu;
        struct workqueue_struct *devlink_wq;
        struct rvu_npa_health_reporters *rvu_npa_health_reporter;
+       struct rvu_nix_health_reporters *rvu_nix_health_reporter;
 };
 
 /* Devlink APIs */
index a8dfbb6..b54753e 100644 (file)
@@ -2580,6 +2580,7 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
        struct nix_rx_flowkey_alg *field;
        struct nix_rx_flowkey_alg tmp;
        u32 key_type, valid_key;
+       int l4_key_offset;
 
        if (!alg)
                return -EINVAL;
@@ -2712,6 +2713,12 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
                                field_marker = false;
                                keyoff_marker = false;
                        }
+
+                       /* TCP/UDP/SCTP and ESP/AH falls at same offset so
+                        * remember the TCP key offset of 40 byte hash key.
+                        */
+                       if (key_type == NIX_FLOW_KEY_TYPE_TCP)
+                               l4_key_offset = key_off;
                        break;
                case NIX_FLOW_KEY_TYPE_NVGRE:
                        field->lid = NPC_LID_LD;
@@ -2783,11 +2790,31 @@ static int set_flowkey_fields(struct nix_rx_flowkey_alg *alg, u32 flow_cfg)
                        field->ltype_mask = 0xF;
                        field->fn_mask = 1; /* Mask out the first nibble */
                        break;
+               case NIX_FLOW_KEY_TYPE_AH:
+               case NIX_FLOW_KEY_TYPE_ESP:
+                       field->hdr_offset = 0;
+                       field->bytesm1 = 7; /* SPI + sequence number */
+                       field->ltype_mask = 0xF;
+                       field->lid = NPC_LID_LE;
+                       field->ltype_match = NPC_LT_LE_ESP;
+                       if (key_type == NIX_FLOW_KEY_TYPE_AH) {
+                               field->lid = NPC_LID_LD;
+                               field->ltype_match = NPC_LT_LD_AH;
+                               field->hdr_offset = 4;
+                               keyoff_marker = false;
+                       }
+                       break;
                }
                field->ena = 1;
 
                /* Found a valid flow key type */
                if (valid_key) {
+                       /* Use the key offset of TCP/UDP/SCTP fields
+                        * for ESP/AH fields.
+                        */
+                       if (key_type == NIX_FLOW_KEY_TYPE_ESP ||
+                           key_type == NIX_FLOW_KEY_TYPE_AH)
+                               key_off = l4_key_offset;
                        field->key_offset = key_off;
                        memcpy(&alg[nr_field], field, sizeof(*field));
                        max_key_off = max(max_key_off, field->bytesm1 + 1);
index 14832b6..4ba9d54 100644 (file)
@@ -26,6 +26,11 @@ static const char * const npc_flow_names[] = {
        [NPC_DIP_IPV4]  = "ipv4 destination ip",
        [NPC_SIP_IPV6]  = "ipv6 source ip",
        [NPC_DIP_IPV6]  = "ipv6 destination ip",
+       [NPC_IPPROTO_TCP] = "ip proto tcp",
+       [NPC_IPPROTO_UDP] = "ip proto udp",
+       [NPC_IPPROTO_SCTP] = "ip proto sctp",
+       [NPC_IPPROTO_AH] = "ip proto AH",
+       [NPC_IPPROTO_ESP] = "ip proto ESP",
        [NPC_SPORT_TCP] = "tcp source port",
        [NPC_DPORT_TCP] = "tcp destination port",
        [NPC_SPORT_UDP] = "udp source port",
@@ -212,13 +217,13 @@ static bool npc_check_overlap(struct rvu *rvu, int blkaddr,
        return false;
 }
 
-static int npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type,
-                          u8 intf)
+static bool npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type,
+                           u8 intf)
 {
        if (!npc_is_field_present(rvu, type, intf) ||
            npc_check_overlap(rvu, blkaddr, type, 0, intf))
-               return -EOPNOTSUPP;
-       return 0;
+               return false;
+       return true;
 }
 
 static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number,
@@ -269,7 +274,7 @@ static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number,
                break;
        default:
                return;
-       };
+       }
        npc_set_kw_masks(mcam, type, nr_bits, kwi, offset, intf);
 }
 
@@ -448,14 +453,13 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf)
        struct npc_mcam *mcam = &rvu->hw->mcam;
        u64 *features = &mcam->rx_features;
        u64 tcp_udp_sctp;
-       int err, hdr;
+       int hdr;
 
        if (is_npc_intf_tx(intf))
                features = &mcam->tx_features;
 
        for (hdr = NPC_DMAC; hdr < NPC_HEADER_FIELDS_MAX; hdr++) {
-               err = npc_check_field(rvu, blkaddr, hdr, intf);
-               if (!err)
+               if (npc_check_field(rvu, blkaddr, hdr, intf))
                        *features |= BIT_ULL(hdr);
        }
 
@@ -464,13 +468,26 @@ static void npc_set_features(struct rvu *rvu, int blkaddr, u8 intf)
                       BIT_ULL(NPC_SPORT_SCTP) | BIT_ULL(NPC_DPORT_SCTP);
 
        /* for tcp/udp/sctp corresponding layer type should be in the key */
-       if (*features & tcp_udp_sctp)
-               if (npc_check_field(rvu, blkaddr, NPC_LD, intf))
+       if (*features & tcp_udp_sctp) {
+               if (!npc_check_field(rvu, blkaddr, NPC_LD, intf))
                        *features &= ~tcp_udp_sctp;
+               else
+                       *features |= BIT_ULL(NPC_IPPROTO_TCP) |
+                                    BIT_ULL(NPC_IPPROTO_UDP) |
+                                    BIT_ULL(NPC_IPPROTO_SCTP);
+       }
+
+       /* for AH, check if corresponding layer type is present in the key */
+       if (npc_check_field(rvu, blkaddr, NPC_LD, intf))
+               *features |= BIT_ULL(NPC_IPPROTO_AH);
+
+       /* for ESP, check if corresponding layer type is present in the key */
+       if (npc_check_field(rvu, blkaddr, NPC_LE, intf))
+               *features |= BIT_ULL(NPC_IPPROTO_ESP);
 
        /* for vlan corresponding layer type should be in the key */
        if (*features & BIT_ULL(NPC_OUTER_VID))
-               if (npc_check_field(rvu, blkaddr, NPC_LB, intf))
+               if (!npc_check_field(rvu, blkaddr, NPC_LB, intf))
                        *features &= ~BIT_ULL(NPC_OUTER_VID);
 }
 
@@ -743,13 +760,13 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry,
                return;
 
        /* For tcp/udp/sctp LTYPE should be present in entry */
-       if (features & (BIT_ULL(NPC_SPORT_TCP) | BIT_ULL(NPC_DPORT_TCP)))
+       if (features & BIT_ULL(NPC_IPPROTO_TCP))
                npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_TCP,
                                 0, ~0ULL, 0, intf);
-       if (features & (BIT_ULL(NPC_SPORT_UDP) | BIT_ULL(NPC_DPORT_UDP)))
+       if (features & BIT_ULL(NPC_IPPROTO_UDP))
                npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_UDP,
                                 0, ~0ULL, 0, intf);
-       if (features & (BIT_ULL(NPC_SPORT_SCTP) | BIT_ULL(NPC_DPORT_SCTP)))
+       if (features & BIT_ULL(NPC_IPPROTO_SCTP))
                npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_SCTP,
                                 0, ~0ULL, 0, intf);
 
@@ -758,6 +775,15 @@ static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry,
                                 NPC_LT_LB_STAG_QINQ | NPC_LT_LB_CTAG, 0,
                                 NPC_LT_LB_STAG_QINQ & NPC_LT_LB_CTAG, 0, intf);
 
+       /* For AH, LTYPE should be present in entry */
+       if (features & BIT_ULL(NPC_IPPROTO_AH))
+               npc_update_entry(rvu, NPC_LD, entry, NPC_LT_LD_AH,
+                                0, ~0ULL, 0, intf);
+       /* For ESP, LTYPE should be present in entry */
+       if (features & BIT_ULL(NPC_IPPROTO_ESP))
+               npc_update_entry(rvu, NPC_LE, entry, NPC_LT_LE_ESP,
+                                0, ~0ULL, 0, intf);
+
 #define NPC_WRITE_FLOW(field, member, val_lo, val_hi, mask_lo, mask_hi)              \
 do {                                                                         \
        if (features & BIT_ULL((field))) {                                    \
index e2153d4..5e15f4f 100644 (file)
@@ -74,6 +74,16 @@ enum npa_af_int_vec_e {
        NPA_AF_INT_VEC_CNT      = 0x5,
 };
 
+/* NIX Admin function Interrupt Vector Enumeration */
+enum nix_af_int_vec_e {
+       NIX_AF_INT_VEC_RVU      = 0x0,
+       NIX_AF_INT_VEC_GEN      = 0x1,
+       NIX_AF_INT_VEC_AQ_DONE  = 0x2,
+       NIX_AF_INT_VEC_AF_ERR   = 0x3,
+       NIX_AF_INT_VEC_POISON   = 0x4,
+       NIX_AF_INT_VEC_CNT      = 0x5,
+};
+
 /**
  * RVU PF Interrupt Vector Enumeration
  */
index 73fb94d..5ddedc3 100644 (file)
@@ -270,14 +270,17 @@ int otx2_set_flowkey_cfg(struct otx2_nic *pfvf)
        return err;
 }
 
-int otx2_set_rss_table(struct otx2_nic *pfvf)
+int otx2_set_rss_table(struct otx2_nic *pfvf, int ctx_id)
 {
        struct otx2_rss_info *rss = &pfvf->hw.rss_info;
+       const int index = rss->rss_size * ctx_id;
        struct mbox *mbox = &pfvf->mbox;
+       struct otx2_rss_ctx *rss_ctx;
        struct nix_aq_enq_req *aq;
        int idx, err;
 
        mutex_lock(&mbox->lock);
+       rss_ctx = rss->rss_ctx[ctx_id];
        /* Get memory to put this msg */
        for (idx = 0; idx < rss->rss_size; idx++) {
                aq = otx2_mbox_alloc_msg_nix_aq_enq(mbox);
@@ -297,10 +300,10 @@ int otx2_set_rss_table(struct otx2_nic *pfvf)
                        }
                }
 
-               aq->rss.rq = rss->ind_tbl[idx];
+               aq->rss.rq = rss_ctx->ind_tbl[idx];
 
                /* Fill AQ info */
-               aq->qidx = idx;
+               aq->qidx = index + idx;
                aq->ctype = NIX_AQ_CTYPE_RSS;
                aq->op = NIX_AQ_INSTOP_INIT;
        }
@@ -335,9 +338,10 @@ void otx2_set_rss_key(struct otx2_nic *pfvf)
 int otx2_rss_init(struct otx2_nic *pfvf)
 {
        struct otx2_rss_info *rss = &pfvf->hw.rss_info;
+       struct otx2_rss_ctx *rss_ctx;
        int idx, ret = 0;
 
-       rss->rss_size = sizeof(rss->ind_tbl);
+       rss->rss_size = sizeof(*rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP]);
 
        /* Init RSS key if it is not setup already */
        if (!rss->enable)
@@ -345,13 +349,19 @@ int otx2_rss_init(struct otx2_nic *pfvf)
        otx2_set_rss_key(pfvf);
 
        if (!netif_is_rxfh_configured(pfvf->netdev)) {
-               /* Default indirection table */
+               /* Set RSS group 0 as default indirection table */
+               rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP] = kzalloc(rss->rss_size,
+                                                                 GFP_KERNEL);
+               if (!rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP])
+                       return -ENOMEM;
+
+               rss_ctx = rss->rss_ctx[DEFAULT_RSS_CONTEXT_GROUP];
                for (idx = 0; idx < rss->rss_size; idx++)
-                       rss->ind_tbl[idx] =
+                       rss_ctx->ind_tbl[idx] =
                                ethtool_rxfh_indir_default(idx,
                                                           pfvf->hw.rx_queues);
        }
-       ret = otx2_set_rss_table(pfvf);
+       ret = otx2_set_rss_table(pfvf, DEFAULT_RSS_CONTEXT_GROUP);
        if (ret)
                return ret;
 
@@ -478,10 +488,11 @@ dma_addr_t __otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool)
        dma_addr_t iova;
        u8 *buf;
 
-       buf = napi_alloc_frag(pool->rbsize);
+       buf = napi_alloc_frag(pool->rbsize + OTX2_ALIGN);
        if (unlikely(!buf))
                return -ENOMEM;
 
+       buf = PTR_ALIGN(buf, OTX2_ALIGN);
        iova = dma_map_single_attrs(pfvf->dev, buf, pool->rbsize,
                                    DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC);
        if (unlikely(dma_mapping_error(pfvf->dev, iova))) {
@@ -986,7 +997,7 @@ int otx2_config_nix(struct otx2_nic *pfvf)
        nixlf->sq_cnt = pfvf->hw.tx_queues;
        nixlf->cq_cnt = pfvf->qset.cq_cnt;
        nixlf->rss_sz = MAX_RSS_INDIR_TBL_SIZE;
-       nixlf->rss_grps = 1; /* Single RSS indir table supported, for now */
+       nixlf->rss_grps = MAX_RSS_GROUPS;
        nixlf->xqe_sz = NIX_XQESZ_W16;
        /* We don't know absolute NPA LF idx attached.
         * AF will replace 'RVU_DEFAULT_PF_FUNC' with
index 1034304..143ae04 100644 (file)
@@ -51,13 +51,17 @@ enum arua_mapped_qtypes {
 #define NIX_LF_POISON_VEC                      0x82
 
 /* RSS configuration */
+struct otx2_rss_ctx {
+       u8  ind_tbl[MAX_RSS_INDIR_TBL_SIZE];
+};
+
 struct otx2_rss_info {
        u8 enable;
        u32 flowkey_cfg;
        u16 rss_size;
-       u8  ind_tbl[MAX_RSS_INDIR_TBL_SIZE];
 #define RSS_HASH_KEY_SIZE      44   /* 352 bit key */
        u8  key[RSS_HASH_KEY_SIZE];
+       struct otx2_rss_ctx     *rss_ctx[MAX_RSS_GROUPS];
 };
 
 /* NIX (or NPC) RX errors */
@@ -643,7 +647,7 @@ void otx2_cleanup_tx_cqes(struct otx2_nic *pfvf, struct otx2_cq_queue *cq);
 int otx2_rss_init(struct otx2_nic *pfvf);
 int otx2_set_flowkey_cfg(struct otx2_nic *pfvf);
 void otx2_set_rss_key(struct otx2_nic *pfvf);
-int otx2_set_rss_table(struct otx2_nic *pfvf);
+int otx2_set_rss_table(struct otx2_nic *pfvf, int ctx_id);
 
 /* Mbox handlers */
 void mbox_handler_msix_offset(struct otx2_nic *pfvf,
@@ -684,10 +688,11 @@ int otx2_get_flow(struct otx2_nic *pfvf,
 int otx2_get_all_flows(struct otx2_nic *pfvf,
                       struct ethtool_rxnfc *nfc, u32 *rule_locs);
 int otx2_add_flow(struct otx2_nic *pfvf,
-                 struct ethtool_rx_flow_spec *fsp);
+                 struct ethtool_rxnfc *nfc);
 int otx2_remove_flow(struct otx2_nic *pfvf, u32 location);
 int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
                              struct npc_install_flow_req *req);
+void otx2_rss_ctx_flow_del(struct otx2_nic *pfvf, int ctx_id);
 int otx2_del_macfilter(struct net_device *netdev, const u8 *mac);
 int otx2_add_macfilter(struct net_device *netdev, const u8 *mac);
 int otx2_enable_rxvlan(struct otx2_nic *pf, bool enable);
index 67171b6..e0199f0 100644 (file)
@@ -448,10 +448,14 @@ static int otx2_get_rss_hash_opts(struct otx2_nic *pfvf,
                        nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
                break;
        case AH_ESP_V4_FLOW:
+       case AH_ESP_V6_FLOW:
+               if (rss->flowkey_cfg & NIX_FLOW_KEY_TYPE_ESP)
+                       nfc->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+               break;
        case AH_V4_FLOW:
        case ESP_V4_FLOW:
        case IPV4_FLOW:
-       case AH_ESP_V6_FLOW:
+               break;
        case AH_V6_FLOW:
        case ESP_V6_FLOW:
        case IPV6_FLOW:
@@ -459,6 +463,7 @@ static int otx2_get_rss_hash_opts(struct otx2_nic *pfvf,
        default:
                return -EINVAL;
        }
+
        return 0;
 }
 
@@ -527,6 +532,36 @@ static int otx2_set_rss_hash_opts(struct otx2_nic *pfvf,
                        return -EINVAL;
                }
                break;
+       case AH_ESP_V4_FLOW:
+       case AH_ESP_V6_FLOW:
+               switch (nfc->data & rxh_l4) {
+               case 0:
+                       rss_cfg &= ~(NIX_FLOW_KEY_TYPE_ESP |
+                                    NIX_FLOW_KEY_TYPE_AH);
+                       rss_cfg |= NIX_FLOW_KEY_TYPE_VLAN |
+                                  NIX_FLOW_KEY_TYPE_IPV4_PROTO;
+                       break;
+               case (RXH_L4_B_0_1 | RXH_L4_B_2_3):
+                       /* If VLAN hashing is also requested for ESP then do not
+                        * allow because of hardware 40 bytes flow key limit.
+                        */
+                       if (rss_cfg & NIX_FLOW_KEY_TYPE_VLAN) {
+                               netdev_err(pfvf->netdev,
+                                          "RSS hash of ESP or AH with VLAN is not supported\n");
+                               return -EOPNOTSUPP;
+                       }
+
+                       rss_cfg |= NIX_FLOW_KEY_TYPE_ESP | NIX_FLOW_KEY_TYPE_AH;
+                       /* Disable IPv4 proto hashing since IPv6 SA+DA(32 bytes)
+                        * and ESP SPI+sequence(8 bytes) uses hardware maximum
+                        * limit of 40 byte flow key.
+                        */
+                       rss_cfg &= ~NIX_FLOW_KEY_TYPE_IPV4_PROTO;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               break;
        case IPV4_FLOW:
        case IPV6_FLOW:
                rss_cfg = NIX_FLOW_KEY_TYPE_IPV4 | NIX_FLOW_KEY_TYPE_IPV6;
@@ -581,7 +616,7 @@ static int otx2_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *nfc)
                break;
        case ETHTOOL_SRXCLSRLINS:
                if (netif_running(dev) && ntuple)
-                       ret = otx2_add_flow(pfvf, &nfc->fs);
+                       ret = otx2_add_flow(pfvf, nfc);
                break;
        case ETHTOOL_SRXCLSRLDEL:
                if (netif_running(dev) && ntuple)
@@ -641,42 +676,50 @@ static u32 otx2_get_rxfh_key_size(struct net_device *netdev)
 
 static u32 otx2_get_rxfh_indir_size(struct net_device *dev)
 {
-       struct otx2_nic *pfvf = netdev_priv(dev);
-
-       return pfvf->hw.rss_info.rss_size;
+       return  MAX_RSS_INDIR_TBL_SIZE;
 }
 
-/* Get RSS configuration */
-static int otx2_get_rxfh(struct net_device *dev, u32 *indir,
-                        u8 *hkey, u8 *hfunc)
+static int otx2_rss_ctx_delete(struct otx2_nic *pfvf, int ctx_id)
 {
-       struct otx2_nic *pfvf = netdev_priv(dev);
-       struct otx2_rss_info *rss;
-       int idx;
+       struct otx2_rss_info *rss = &pfvf->hw.rss_info;
 
-       rss = &pfvf->hw.rss_info;
+       otx2_rss_ctx_flow_del(pfvf, ctx_id);
+       kfree(rss->rss_ctx[ctx_id]);
+       rss->rss_ctx[ctx_id] = NULL;
 
-       if (indir) {
-               for (idx = 0; idx < rss->rss_size; idx++)
-                       indir[idx] = rss->ind_tbl[idx];
-       }
+       return 0;
+}
 
-       if (hkey)
-               memcpy(hkey, rss->key, sizeof(rss->key));
+static int otx2_rss_ctx_create(struct otx2_nic *pfvf,
+                              u32 *rss_context)
+{
+       struct otx2_rss_info *rss = &pfvf->hw.rss_info;
+       u8 ctx;
 
-       if (hfunc)
-               *hfunc = ETH_RSS_HASH_TOP;
+       for (ctx = 0; ctx < MAX_RSS_GROUPS; ctx++) {
+               if (!rss->rss_ctx[ctx])
+                       break;
+       }
+       if (ctx == MAX_RSS_GROUPS)
+               return -EINVAL;
+
+       rss->rss_ctx[ctx] = kzalloc(sizeof(*rss->rss_ctx[ctx]), GFP_KERNEL);
+       if (!rss->rss_ctx[ctx])
+               return -ENOMEM;
+       *rss_context = ctx;
 
        return 0;
 }
 
-/* Configure RSS table and hash key */
-static int otx2_set_rxfh(struct net_device *dev, const u32 *indir,
-                        const u8 *hkey, const u8 hfunc)
+/* RSS context configuration */
+static int otx2_set_rxfh_context(struct net_device *dev, const u32 *indir,
+                                const u8 *hkey, const u8 hfunc,
+                                u32 *rss_context, bool delete)
 {
        struct otx2_nic *pfvf = netdev_priv(dev);
+       struct otx2_rss_ctx *rss_ctx;
        struct otx2_rss_info *rss;
-       int idx;
+       int ret, idx;
 
        if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
                return -EOPNOTSUPP;
@@ -688,20 +731,85 @@ static int otx2_set_rxfh(struct net_device *dev, const u32 *indir,
                return -EIO;
        }
 
+       if (hkey) {
+               memcpy(rss->key, hkey, sizeof(rss->key));
+               otx2_set_rss_key(pfvf);
+       }
+       if (delete)
+               return otx2_rss_ctx_delete(pfvf, *rss_context);
+
+       if (*rss_context == ETH_RXFH_CONTEXT_ALLOC) {
+               ret = otx2_rss_ctx_create(pfvf, rss_context);
+               if (ret)
+                       return ret;
+       }
        if (indir) {
+               rss_ctx = rss->rss_ctx[*rss_context];
                for (idx = 0; idx < rss->rss_size; idx++)
-                       rss->ind_tbl[idx] = indir[idx];
+                       rss_ctx->ind_tbl[idx] = indir[idx];
        }
+       otx2_set_rss_table(pfvf, *rss_context);
 
-       if (hkey) {
-               memcpy(rss->key, hkey, sizeof(rss->key));
-               otx2_set_rss_key(pfvf);
+       return 0;
+}
+
+static int otx2_get_rxfh_context(struct net_device *dev, u32 *indir,
+                                u8 *hkey, u8 *hfunc, u32 rss_context)
+{
+       struct otx2_nic *pfvf = netdev_priv(dev);
+       struct otx2_rss_ctx *rss_ctx;
+       struct otx2_rss_info *rss;
+       int idx, rx_queues;
+
+       rss = &pfvf->hw.rss_info;
+
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+
+       if (!indir)
+               return 0;
+
+       if (!rss->enable && rss_context == DEFAULT_RSS_CONTEXT_GROUP) {
+               rx_queues = pfvf->hw.rx_queues;
+               for (idx = 0; idx < MAX_RSS_INDIR_TBL_SIZE; idx++)
+                       indir[idx] = ethtool_rxfh_indir_default(idx, rx_queues);
+               return 0;
        }
+       if (rss_context >= MAX_RSS_GROUPS)
+               return -ENOENT;
+
+       rss_ctx = rss->rss_ctx[rss_context];
+       if (!rss_ctx)
+               return -ENOENT;
+
+       if (indir) {
+               for (idx = 0; idx < rss->rss_size; idx++)
+                       indir[idx] = rss_ctx->ind_tbl[idx];
+       }
+       if (hkey)
+               memcpy(hkey, rss->key, sizeof(rss->key));
 
-       otx2_set_rss_table(pfvf);
        return 0;
 }
 
+/* Get RSS configuration */
+static int otx2_get_rxfh(struct net_device *dev, u32 *indir,
+                        u8 *hkey, u8 *hfunc)
+{
+       return otx2_get_rxfh_context(dev, indir, hkey, hfunc,
+                                    DEFAULT_RSS_CONTEXT_GROUP);
+}
+
+/* Configure RSS table and hash key */
+static int otx2_set_rxfh(struct net_device *dev, const u32 *indir,
+                        const u8 *hkey, const u8 hfunc)
+{
+
+       u32 rss_context = DEFAULT_RSS_CONTEXT_GROUP;
+
+       return otx2_set_rxfh_context(dev, indir, hkey, hfunc, &rss_context, 0);
+}
+
 static u32 otx2_get_msglevel(struct net_device *netdev)
 {
        struct otx2_nic *pfvf = netdev_priv(netdev);
@@ -771,6 +879,8 @@ static const struct ethtool_ops otx2_ethtool_ops = {
        .get_rxfh_indir_size    = otx2_get_rxfh_indir_size,
        .get_rxfh               = otx2_get_rxfh,
        .set_rxfh               = otx2_set_rxfh,
+       .get_rxfh_context       = otx2_get_rxfh_context,
+       .set_rxfh_context       = otx2_set_rxfh_context,
        .get_msglevel           = otx2_get_msglevel,
        .set_msglevel           = otx2_set_msglevel,
        .get_pauseparam         = otx2_get_pauseparam,
@@ -866,6 +976,8 @@ static const struct ethtool_ops otx2vf_ethtool_ops = {
        .get_rxfh_indir_size    = otx2_get_rxfh_indir_size,
        .get_rxfh               = otx2_get_rxfh,
        .set_rxfh               = otx2_set_rxfh,
+       .get_rxfh_context       = otx2_get_rxfh_context,
+       .set_rxfh_context       = otx2_set_rxfh_context,
        .get_ringparam          = otx2_get_ringparam,
        .set_ringparam          = otx2_set_ringparam,
        .get_coalesce           = otx2_get_coalesce,
index be8ccfc..0dbbf38 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Marvell OcteonTx2 RVU Physcial Function ethernet driver
+/* Marvell OcteonTx2 RVU Physical Function ethernet driver
  *
  * Copyright (C) 2020 Marvell.
  */
@@ -16,6 +16,7 @@ struct otx2_flow {
        u32 location;
        u16 entry;
        bool is_vf;
+       u8 rss_ctx_id;
        int vf;
 };
 
@@ -245,6 +246,7 @@ int otx2_get_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc,
        list_for_each_entry(iter, &pfvf->flow_cfg->flow_list, list) {
                if (iter->location == location) {
                        nfc->fs = iter->flow_spec;
+                       nfc->rss_context = iter->rss_ctx_id;
                        return 0;
                }
        }
@@ -270,14 +272,16 @@ int otx2_get_all_flows(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc,
        return err;
 }
 
-static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
-                                  struct npc_install_flow_req *req,
-                                  u32 flow_type)
+static int otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
+                                 struct npc_install_flow_req *req,
+                                 u32 flow_type)
 {
        struct ethtool_usrip4_spec *ipv4_usr_mask = &fsp->m_u.usr_ip4_spec;
        struct ethtool_usrip4_spec *ipv4_usr_hdr = &fsp->h_u.usr_ip4_spec;
        struct ethtool_tcpip4_spec *ipv4_l4_mask = &fsp->m_u.tcp_ip4_spec;
        struct ethtool_tcpip4_spec *ipv4_l4_hdr = &fsp->h_u.tcp_ip4_spec;
+       struct ethtool_ah_espip4_spec *ah_esp_hdr = &fsp->h_u.ah_ip4_spec;
+       struct ethtool_ah_espip4_spec *ah_esp_mask = &fsp->m_u.ah_ip4_spec;
        struct flow_msg *pmask = &req->mask;
        struct flow_msg *pkt = &req->packet;
 
@@ -297,10 +301,16 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
                               sizeof(pmask->ip4dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV4);
                }
+               pkt->etype = cpu_to_be16(ETH_P_IP);
+               pmask->etype = cpu_to_be16(0xFFFF);
+               req->features |= BIT_ULL(NPC_ETYPE);
                break;
        case TCP_V4_FLOW:
        case UDP_V4_FLOW:
        case SCTP_V4_FLOW:
+               pkt->etype = cpu_to_be16(ETH_P_IP);
+               pmask->etype = cpu_to_be16(0xFFFF);
+               req->features |= BIT_ULL(NPC_ETYPE);
                if (ipv4_l4_mask->ip4src) {
                        memcpy(&pkt->ip4src, &ipv4_l4_hdr->ip4src,
                               sizeof(pkt->ip4src));
@@ -339,20 +349,60 @@ static void otx2_prepare_ipv4_flow(struct ethtool_rx_flow_spec *fsp,
                        else
                                req->features |= BIT_ULL(NPC_DPORT_SCTP);
                }
+               if (flow_type == UDP_V4_FLOW)
+                       req->features |= BIT_ULL(NPC_IPPROTO_UDP);
+               else if (flow_type == TCP_V4_FLOW)
+                       req->features |= BIT_ULL(NPC_IPPROTO_TCP);
+               else
+                       req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
+               break;
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+               pkt->etype = cpu_to_be16(ETH_P_IP);
+               pmask->etype = cpu_to_be16(0xFFFF);
+               req->features |= BIT_ULL(NPC_ETYPE);
+               if (ah_esp_mask->ip4src) {
+                       memcpy(&pkt->ip4src, &ah_esp_hdr->ip4src,
+                              sizeof(pkt->ip4src));
+                       memcpy(&pmask->ip4src, &ah_esp_mask->ip4src,
+                              sizeof(pmask->ip4src));
+                       req->features |= BIT_ULL(NPC_SIP_IPV4);
+               }
+               if (ah_esp_mask->ip4dst) {
+                       memcpy(&pkt->ip4dst, &ah_esp_hdr->ip4dst,
+                              sizeof(pkt->ip4dst));
+                       memcpy(&pmask->ip4dst, &ah_esp_mask->ip4dst,
+                              sizeof(pmask->ip4dst));
+                       req->features |= BIT_ULL(NPC_DIP_IPV4);
+               }
+
+               /* NPC profile doesn't extract AH/ESP header fields */
+               if ((ah_esp_mask->spi & ah_esp_hdr->spi) ||
+                   (ah_esp_mask->tos & ah_esp_mask->tos))
+                       return -EOPNOTSUPP;
+
+               if (flow_type == AH_V4_FLOW)
+                       req->features |= BIT_ULL(NPC_IPPROTO_AH);
+               else
+                       req->features |= BIT_ULL(NPC_IPPROTO_ESP);
                break;
        default:
                break;
        }
+
+       return 0;
 }
 
-static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
-                                  struct npc_install_flow_req *req,
-                                  u32 flow_type)
+static int otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
+                                 struct npc_install_flow_req *req,
+                                 u32 flow_type)
 {
        struct ethtool_usrip6_spec *ipv6_usr_mask = &fsp->m_u.usr_ip6_spec;
        struct ethtool_usrip6_spec *ipv6_usr_hdr = &fsp->h_u.usr_ip6_spec;
        struct ethtool_tcpip6_spec *ipv6_l4_mask = &fsp->m_u.tcp_ip6_spec;
        struct ethtool_tcpip6_spec *ipv6_l4_hdr = &fsp->h_u.tcp_ip6_spec;
+       struct ethtool_ah_espip6_spec *ah_esp_hdr = &fsp->h_u.ah_ip6_spec;
+       struct ethtool_ah_espip6_spec *ah_esp_mask = &fsp->m_u.ah_ip6_spec;
        struct flow_msg *pmask = &req->mask;
        struct flow_msg *pkt = &req->packet;
 
@@ -372,10 +422,16 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
                               sizeof(pmask->ip6dst));
                        req->features |= BIT_ULL(NPC_DIP_IPV6);
                }
+               pkt->etype = cpu_to_be16(ETH_P_IPV6);
+               pmask->etype = cpu_to_be16(0xFFFF);
+               req->features |= BIT_ULL(NPC_ETYPE);
                break;
        case TCP_V6_FLOW:
        case UDP_V6_FLOW:
        case SCTP_V6_FLOW:
+               pkt->etype = cpu_to_be16(ETH_P_IPV6);
+               pmask->etype = cpu_to_be16(0xFFFF);
+               req->features |= BIT_ULL(NPC_ETYPE);
                if (!ipv6_addr_any((struct in6_addr *)ipv6_l4_mask->ip6src)) {
                        memcpy(&pkt->ip6src, &ipv6_l4_hdr->ip6src,
                               sizeof(pkt->ip6src));
@@ -414,10 +470,47 @@ static void otx2_prepare_ipv6_flow(struct ethtool_rx_flow_spec *fsp,
                        else
                                req->features |= BIT_ULL(NPC_DPORT_SCTP);
                }
+               if (flow_type == UDP_V6_FLOW)
+                       req->features |= BIT_ULL(NPC_IPPROTO_UDP);
+               else if (flow_type == TCP_V6_FLOW)
+                       req->features |= BIT_ULL(NPC_IPPROTO_TCP);
+               else
+                       req->features |= BIT_ULL(NPC_IPPROTO_SCTP);
                break;
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+               pkt->etype = cpu_to_be16(ETH_P_IPV6);
+               pmask->etype = cpu_to_be16(0xFFFF);
+               req->features |= BIT_ULL(NPC_ETYPE);
+               if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6src)) {
+                       memcpy(&pkt->ip6src, &ah_esp_hdr->ip6src,
+                              sizeof(pkt->ip6src));
+                       memcpy(&pmask->ip6src, &ah_esp_mask->ip6src,
+                              sizeof(pmask->ip6src));
+                       req->features |= BIT_ULL(NPC_SIP_IPV6);
+               }
+               if (!ipv6_addr_any((struct in6_addr *)ah_esp_hdr->ip6dst)) {
+                       memcpy(&pkt->ip6dst, &ah_esp_hdr->ip6dst,
+                              sizeof(pkt->ip6dst));
+                       memcpy(&pmask->ip6dst, &ah_esp_mask->ip6dst,
+                              sizeof(pmask->ip6dst));
+                       req->features |= BIT_ULL(NPC_DIP_IPV6);
+               }
+
+               /* NPC profile doesn't extract AH/ESP header fields */
+               if ((ah_esp_mask->spi & ah_esp_hdr->spi) ||
+                   (ah_esp_mask->tclass & ah_esp_mask->tclass))
+                       return -EOPNOTSUPP;
+
+               if (flow_type == AH_V6_FLOW)
+                       req->features |= BIT_ULL(NPC_IPPROTO_AH);
+               else
+                       req->features |= BIT_ULL(NPC_IPPROTO_ESP);
        default:
                break;
        }
+
+       return 0;
 }
 
 int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
@@ -428,8 +521,9 @@ int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
        struct flow_msg *pmask = &req->mask;
        struct flow_msg *pkt = &req->packet;
        u32 flow_type;
+       int ret;
 
-       flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT);
+       flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
        switch (flow_type) {
        /* bits not set in mask are don't care */
        case ETHER_FLOW:
@@ -455,13 +549,21 @@ int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp,
        case TCP_V4_FLOW:
        case UDP_V4_FLOW:
        case SCTP_V4_FLOW:
-               otx2_prepare_ipv4_flow(fsp, req, flow_type);
+       case AH_V4_FLOW:
+       case ESP_V4_FLOW:
+               ret = otx2_prepare_ipv4_flow(fsp, req, flow_type);
+               if (ret)
+                       return ret;
                break;
        case IPV6_USER_FLOW:
        case TCP_V6_FLOW:
        case UDP_V6_FLOW:
        case SCTP_V6_FLOW:
-               otx2_prepare_ipv6_flow(fsp, req, flow_type);
+       case AH_V6_FLOW:
+       case ESP_V6_FLOW:
+               ret = otx2_prepare_ipv6_flow(fsp, req, flow_type);
+               if (ret)
+                       return ret;
                break;
        default:
                return -EOPNOTSUPP;
@@ -532,9 +634,13 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow)
                /* change to unicast only if action of default entry is not
                 * requested by user
                 */
-               if (req->op != NIX_RX_ACTION_DEFAULT)
+               if (flow->flow_spec.flow_type & FLOW_RSS) {
+                       req->op = NIX_RX_ACTIONOP_RSS;
+                       req->index = flow->rss_ctx_id;
+               } else {
                        req->op = NIX_RX_ACTIONOP_UCAST;
-               req->index = ethtool_get_flow_spec_ring(ring_cookie);
+                       req->index = ethtool_get_flow_spec_ring(ring_cookie);
+               }
                vf = ethtool_get_flow_spec_ring_vf(ring_cookie);
                if (vf > pci_num_vf(pfvf->pdev)) {
                        mutex_unlock(&pfvf->mbox.lock);
@@ -555,14 +661,16 @@ static int otx2_add_flow_msg(struct otx2_nic *pfvf, struct otx2_flow *flow)
        return err;
 }
 
-int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rx_flow_spec *fsp)
+int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc)
 {
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
-       u32 ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
+       struct ethtool_rx_flow_spec *fsp = &nfc->fs;
        struct otx2_flow *flow;
        bool new = false;
+       u32 ring;
        int err;
 
+       ring = ethtool_get_flow_spec_ring(fsp->ring_cookie);
        if (!(pfvf->flags & OTX2_FLAG_NTUPLE_SUPPORT))
                return -ENOMEM;
 
@@ -585,6 +693,9 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rx_flow_spec *fsp)
        /* struct copy */
        flow->flow_spec = *fsp;
 
+       if (fsp->flow_type & FLOW_RSS)
+               flow->rss_ctx_id = nfc->rss_context;
+
        err = otx2_add_flow_msg(pfvf, flow);
        if (err) {
                if (new)
@@ -647,6 +758,22 @@ int otx2_remove_flow(struct otx2_nic *pfvf, u32 location)
        return 0;
 }
 
+void otx2_rss_ctx_flow_del(struct otx2_nic *pfvf, int ctx_id)
+{
+       struct otx2_flow *flow, *tmp;
+       int err;
+
+       list_for_each_entry_safe(flow, tmp, &pfvf->flow_cfg->flow_list, list) {
+               if (flow->rss_ctx_id != ctx_id)
+                       continue;
+               err = otx2_remove_flow(pfvf, flow->location);
+               if (err)
+                       netdev_warn(pfvf->netdev,
+                                   "Can't delete the rule %d associated with this rss group err:%d",
+                                   flow->location, err);
+       }
+}
+
 int otx2_destroy_ntuple_flows(struct otx2_nic *pfvf)
 {
        struct otx2_flow_config *flow_cfg = pfvf->flow_cfg;
index 634d606..07ec85a 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Marvell OcteonTx2 RVU Physcial Function ethernet driver
+/* Marvell OcteonTx2 RVU Physical Function ethernet driver
  *
  * Copyright (C) 2020 Marvell International Ltd.
  *
index 7d83e1f..8c2b031 100644 (file)
@@ -580,16 +580,12 @@ int prestera_bridge_port_event(struct net_device *dev, unsigned long event,
 }
 
 static int prestera_port_attr_br_flags_set(struct prestera_port *port,
-                                          struct switchdev_trans *trans,
                                           struct net_device *dev,
                                           unsigned long flags)
 {
        struct prestera_bridge_port *br_port;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev);
        if (!br_port)
                return 0;
@@ -608,35 +604,26 @@ static int prestera_port_attr_br_flags_set(struct prestera_port *port,
 }
 
 static int prestera_port_attr_br_ageing_set(struct prestera_port *port,
-                                           struct switchdev_trans *trans,
                                            unsigned long ageing_clock_t)
 {
        unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
        u32 ageing_time_ms = jiffies_to_msecs(ageing_jiffies);
        struct prestera_switch *sw = port->sw;
 
-       if (switchdev_trans_ph_prepare(trans)) {
-               if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS ||
-                   ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS)
-                       return -ERANGE;
-               else
-                       return 0;
-       }
+       if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS ||
+           ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS)
+               return -ERANGE;
 
        return prestera_hw_switch_ageing_set(sw, ageing_time_ms);
 }
 
 static int prestera_port_attr_br_vlan_set(struct prestera_port *port,
-                                         struct switchdev_trans *trans,
                                          struct net_device *dev,
                                          bool vlan_enabled)
 {
        struct prestera_switch *sw = port->sw;
        struct prestera_bridge *bridge;
 
-       if (!switchdev_trans_ph_prepare(trans))
-               return 0;
-
        bridge = prestera_bridge_by_dev(sw->swdev, dev);
        if (WARN_ON(!bridge))
                return -EINVAL;
@@ -665,19 +652,15 @@ static int prestera_port_bridge_vlan_stp_set(struct prestera_port *port,
        return 0;
 }
 
-static int presterar_port_attr_stp_state_set(struct prestera_port *port,
-                                            struct switchdev_trans *trans,
-                                            struct net_device *dev,
-                                            u8 state)
+static int prestera_port_attr_stp_state_set(struct prestera_port *port,
+                                           struct net_device *dev,
+                                           u8 state)
 {
        struct prestera_bridge_port *br_port;
        struct prestera_bridge_vlan *br_vlan;
        int err;
        u16 vid;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev);
        if (!br_port)
                return 0;
@@ -712,17 +695,15 @@ err_port_stp_set:
 }
 
 static int prestera_port_obj_attr_set(struct net_device *dev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans)
+                                     const struct switchdev_attr *attr)
 {
        struct prestera_port *port = netdev_priv(dev);
        int err = 0;
 
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-               err = presterar_port_attr_stp_state_set(port, trans,
-                                                       attr->orig_dev,
-                                                       attr->u.stp_state);
+               err = prestera_port_attr_stp_state_set(port, attr->orig_dev,
+                                                      attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
                if (attr->u.brport_flags &
@@ -730,17 +711,15 @@ static int prestera_port_obj_attr_set(struct net_device *dev,
                        err = -EINVAL;
                break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               err = prestera_port_attr_br_flags_set(port, trans,
-                                                     attr->orig_dev,
+               err = prestera_port_attr_br_flags_set(port, attr->orig_dev,
                                                      attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
-               err = prestera_port_attr_br_ageing_set(port, trans,
+               err = prestera_port_attr_br_ageing_set(port,
                                                       attr->u.ageing_time);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
-               err = prestera_port_attr_br_vlan_set(port, trans,
-                                                    attr->orig_dev,
+               err = prestera_port_attr_br_vlan_set(port, attr->orig_dev,
                                                     attr->u.vlan_filtering);
                break;
        default:
@@ -1020,7 +999,6 @@ prestera_bridge_port_vlan_del(struct prestera_port *port,
 
 static int prestera_port_vlans_add(struct prestera_port *port,
                                   const struct switchdev_obj_port_vlan *vlan,
-                                  struct switchdev_trans *trans,
                                   struct netlink_ext_ack *extack)
 {
        bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
@@ -1029,14 +1007,10 @@ static int prestera_port_vlans_add(struct prestera_port *port,
        struct prestera_bridge_port *br_port;
        struct prestera_switch *sw = port->sw;
        struct prestera_bridge *bridge;
-       u16 vid;
 
        if (netif_is_bridge_master(dev))
                return 0;
 
-       if (switchdev_trans_ph_commit(trans))
-               return 0;
-
        br_port = prestera_bridge_port_by_dev(sw->swdev, dev);
        if (WARN_ON(!br_port))
                return -EINVAL;
@@ -1045,22 +1019,13 @@ static int prestera_port_vlans_add(struct prestera_port *port,
        if (!bridge->vlan_enabled)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               int err;
-
-               err = prestera_bridge_port_vlan_add(port, br_port,
-                                                   vid, flag_untagged,
-                                                   flag_pvid, extack);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return prestera_bridge_port_vlan_add(port, br_port,
+                                            vlan->vid, flag_untagged,
+                                            flag_pvid, extack);
 }
 
 static int prestera_port_obj_add(struct net_device *dev,
                                 const struct switchdev_obj *obj,
-                                struct switchdev_trans *trans,
                                 struct netlink_ext_ack *extack)
 {
        struct prestera_port *port = netdev_priv(dev);
@@ -1069,7 +1034,7 @@ static int prestera_port_obj_add(struct net_device *dev,
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
-               return prestera_port_vlans_add(port, vlan, trans, extack);
+               return prestera_port_vlans_add(port, vlan, extack);
        default:
                return -EOPNOTSUPP;
        }
@@ -1081,7 +1046,6 @@ static int prestera_port_vlans_del(struct prestera_port *port,
        struct net_device *dev = vlan->obj.orig_dev;
        struct prestera_bridge_port *br_port;
        struct prestera_switch *sw = port->sw;
-       u16 vid;
 
        if (netif_is_bridge_master(dev))
                return -EOPNOTSUPP;
@@ -1093,8 +1057,7 @@ static int prestera_port_vlans_del(struct prestera_port *port,
        if (!br_port->bridge->vlan_enabled)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
-               prestera_bridge_port_vlan_del(port, br_port, vid);
+       prestera_bridge_port_vlan_del(port, br_port, vlan->vid);
 
        return 0;
 }
index 6d2d606..01d3ee4 100644 (file)
@@ -353,7 +353,7 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
        /* Setup gmac */
        mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
        mcr_new = mcr_cur;
-       mcr_new |= MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
+       mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
                   MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
 
        /* Only update control register when needed! */
@@ -759,8 +759,8 @@ static void mtk_get_stats64(struct net_device *dev,
 static inline int mtk_max_frag_size(int mtu)
 {
        /* make sure buf_size will be at least MTK_MAX_RX_LENGTH */
-       if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH)
-               mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+       if (mtu + MTK_RX_ETH_HLEN < MTK_MAX_RX_LENGTH_2K)
+               mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
 
        return SKB_DATA_ALIGN(MTK_RX_HLEN + mtu) +
                SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
@@ -771,7 +771,7 @@ static inline int mtk_max_buf_size(int frag_size)
        int buf_size = frag_size - NET_SKB_PAD - NET_IP_ALIGN -
                       SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
 
-       WARN_ON(buf_size < MTK_MAX_RX_LENGTH);
+       WARN_ON(buf_size < MTK_MAX_RX_LENGTH_2K);
 
        return buf_size;
 }
@@ -2499,6 +2499,35 @@ static void mtk_uninit(struct net_device *dev)
        mtk_rx_irq_disable(eth, ~0);
 }
 
+static int mtk_change_mtu(struct net_device *dev, int new_mtu)
+{
+       int length = new_mtu + MTK_RX_ETH_HLEN;
+       struct mtk_mac *mac = netdev_priv(dev);
+       struct mtk_eth *eth = mac->hw;
+       u32 mcr_cur, mcr_new;
+
+       if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) {
+               mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
+               mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK;
+
+               if (length <= 1518)
+                       mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1518);
+               else if (length <= 1536)
+                       mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1536);
+               else if (length <= 1552)
+                       mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_1552);
+               else
+                       mcr_new |= MAC_MCR_MAX_RX(MAC_MCR_MAX_RX_2048);
+
+               if (mcr_new != mcr_cur)
+                       mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
+       }
+
+       dev->mtu = new_mtu;
+
+       return 0;
+}
+
 static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct mtk_mac *mac = netdev_priv(dev);
@@ -2795,6 +2824,7 @@ static const struct net_device_ops mtk_netdev_ops = {
        .ndo_set_mac_address    = mtk_set_mac_address,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_do_ioctl           = mtk_do_ioctl,
+       .ndo_change_mtu         = mtk_change_mtu,
        .ndo_tx_timeout         = mtk_tx_timeout,
        .ndo_get_stats64        = mtk_get_stats64,
        .ndo_fix_features       = mtk_fix_features,
@@ -2896,7 +2926,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
        eth->netdev[id]->irq = eth->irq[0];
        eth->netdev[id]->dev.of_node = np;
 
-       eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+       if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628))
+               eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH - MTK_RX_ETH_HLEN;
+       else
+               eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN;
 
        return 0;
 
index 454cfcd..fd3cec8 100644 (file)
 #include <linux/phylink.h>
 
 #define MTK_QDMA_PAGE_SIZE     2048
-#define        MTK_MAX_RX_LENGTH       1536
+#define MTK_MAX_RX_LENGTH      1536
+#define MTK_MAX_RX_LENGTH_2K   2048
 #define MTK_TX_DMA_BUF_LEN     0x3fff
 #define MTK_DMA_SIZE           256
 #define MTK_NAPI_WEIGHT                64
 #define MTK_MAC_COUNT          2
-#define MTK_RX_ETH_HLEN                (VLAN_ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN)
+#define MTK_RX_ETH_HLEN                (ETH_HLEN + ETH_FCS_LEN)
 #define MTK_RX_HLEN            (NET_SKB_PAD + MTK_RX_ETH_HLEN + NET_IP_ALIGN)
 #define MTK_DMA_DUMMY_DESC     0xffffffff
 #define MTK_DEFAULT_MSG_ENABLE (NETIF_MSG_DRV | \
 
 /* Mac control registers */
 #define MTK_MAC_MCR(x)         (0x10100 + (x * 0x100))
-#define MAC_MCR_MAX_RX_1536    BIT(24)
+#define MAC_MCR_MAX_RX_MASK    GENMASK(25, 24)
+#define MAC_MCR_MAX_RX(_x)     (MAC_MCR_MAX_RX_MASK & ((_x) << 24))
+#define MAC_MCR_MAX_RX_1518    0x0
+#define MAC_MCR_MAX_RX_1536    0x1
+#define MAC_MCR_MAX_RX_1552    0x2
+#define MAC_MCR_MAX_RX_2048    0x3
 #define MAC_MCR_IPG_CFG                (BIT(18) | BIT(16))
 #define MAC_MCR_FORCE_MODE     BIT(15)
 #define MAC_MCR_TX_EN          BIT(14)
index 32aad4d..51b9700 100644 (file)
@@ -2839,8 +2839,6 @@ static const struct net_device_ops mlx4_netdev_ops = {
        .ndo_rx_flow_steer      = mlx4_en_filter_rfs,
 #endif
        .ndo_get_phys_port_id   = mlx4_en_get_phys_port_id,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = mlx4_en_features_check,
        .ndo_set_tx_maxrate     = mlx4_en_set_tx_maxrate,
        .ndo_bpf                = mlx4_xdp,
@@ -2873,8 +2871,6 @@ static const struct net_device_ops mlx4_netdev_ops_master = {
        .ndo_rx_flow_steer      = mlx4_en_filter_rfs,
 #endif
        .ndo_get_phys_port_id   = mlx4_en_get_phys_port_id,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = mlx4_en_features_check,
        .ndo_set_tx_maxrate     = mlx4_en_set_tx_maxrate,
        .ndo_bpf                = mlx4_xdp,
index c1c9118..e35e4d7 100644 (file)
@@ -682,8 +682,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
        /* Protect accesses to: ring->xdp_prog, priv->mac_hash list */
        rcu_read_lock();
        xdp_prog = rcu_dereference(ring->xdp_prog);
-       xdp.rxq = &ring->xdp_rxq;
-       xdp.frame_sz = priv->frag_info[0].frag_stride;
+       xdp_init_buff(&xdp, priv->frag_info[0].frag_stride, &ring->xdp_rxq);
        doorbell_pending = false;
 
        /* We assume a 1:1 mapping between CQEs and Rx descriptors, so Rx
@@ -777,10 +776,8 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud
                                                priv->frag_info[0].frag_size,
                                                DMA_FROM_DEVICE);
 
-                       xdp.data_hard_start = va - frags[0].page_offset;
-                       xdp.data = va;
-                       xdp_set_data_meta_invalid(&xdp);
-                       xdp.data_end = xdp.data + length;
+                       xdp_prepare_buff(&xdp, va - frags[0].page_offset,
+                                        frags[0].page_offset, length, false);
                        orig_data = xdp.data;
 
                        act = bpf_prog_run_xdp(xdp_prog, &xdp);
index 6e4d7bb..ad45d20 100644 (file)
@@ -203,3 +203,22 @@ config MLX5_SW_STEERING
        default y
        help
        Build support for software-managed steering in the NIC.
+
+config MLX5_SF
+       bool "Mellanox Technologies subfunction device support using auxiliary device"
+       depends on MLX5_CORE && MLX5_CORE_EN
+       default n
+       help
+       Build support for subfuction device in the NIC. A Mellanox subfunction
+       device can support RDMA, netdevice and vdpa device.
+       It is similar to a SRIOV VF but it doesn't require SRIOV support.
+
+config MLX5_SF_MANAGER
+       bool
+       depends on MLX5_SF && MLX5_ESWITCH
+       default y
+       help
+       Build support for subfuction port in the NIC. A Mellanox subfunction
+       port is managed through devlink.  A subfunction supports RDMA, netdevice
+       and vdpa device. It is similar to a SRIOV VF but it doesn't require
+       SRIOV support.
index 7796164..f61ee71 100644 (file)
@@ -16,7 +16,8 @@ mlx5_core-y :=        main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \
                transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \
                fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \
                lib/devcom.o lib/pci_vsc.o lib/dm.o diag/fs_tracepoint.o \
-               diag/fw_tracer.o diag/crdump.o devlink.o diag/rsc_dump.o fw_reset.o
+               diag/fw_tracer.o diag/crdump.o devlink.o diag/rsc_dump.o \
+               fw_reset.o qos.o
 
 #
 # Netdev basic
@@ -25,7 +26,8 @@ mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \
                en_tx.o en_rx.o en_dim.o en_txrx.o en/xdp.o en_stats.o \
                en_selftest.o en/port.o en/monitor_stats.o en/health.o \
                en/reporter_tx.o en/reporter_rx.o en/params.o en/xsk/pool.o \
-               en/xsk/setup.o en/xsk/rx.o en/xsk/tx.o en/devlink.o en/ptp.o
+               en/xsk/setup.o en/xsk/rx.o en/xsk/tx.o en/devlink.o en/ptp.o \
+               en/qos.o en/trap.o
 
 #
 # Netdev extra
@@ -83,5 +85,15 @@ mlx5_core-$(CONFIG_MLX5_SW_STEERING) += steering/dr_domain.o steering/dr_table.o
                                        steering/dr_matcher.o steering/dr_rule.o \
                                        steering/dr_icm_pool.o steering/dr_buddy.o \
                                        steering/dr_ste.o steering/dr_send.o \
+                                       steering/dr_ste_v0.o \
                                        steering/dr_cmd.o steering/dr_fw.o \
                                        steering/dr_action.o steering/fs_dr.o
+#
+# SF device
+#
+mlx5_core-$(CONFIG_MLX5_SF) += sf/vhca_event.o sf/dev/dev.o sf/dev/driver.o
+
+#
+# SF manager
+#
+mlx5_core-$(CONFIG_MLX5_SF_MANAGER) += sf/cmd.o sf/hw_table.o sf/devlink.o
index 50c7b9e..e8cecd5 100644 (file)
@@ -333,6 +333,7 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_DEALLOC_MEMIC:
        case MLX5_CMD_OP_PAGE_FAULT_RESUME:
        case MLX5_CMD_OP_QUERY_ESW_FUNCTIONS:
+       case MLX5_CMD_OP_DEALLOC_SF:
                return MLX5_CMD_STAT_OK;
 
        case MLX5_CMD_OP_QUERY_HCA_CAP:
@@ -464,6 +465,9 @@ static int mlx5_internal_err_ret_value(struct mlx5_core_dev *dev, u16 op,
        case MLX5_CMD_OP_ALLOC_MEMIC:
        case MLX5_CMD_OP_MODIFY_XRQ:
        case MLX5_CMD_OP_RELEASE_XRQ_ERROR:
+       case MLX5_CMD_OP_QUERY_VHCA_STATE:
+       case MLX5_CMD_OP_MODIFY_VHCA_STATE:
+       case MLX5_CMD_OP_ALLOC_SF:
                *status = MLX5_DRIVER_STATUS_ABORTED;
                *synd = MLX5_DRIVER_SYND;
                return -EIO;
@@ -657,6 +661,10 @@ const char *mlx5_command_str(int command)
        MLX5_COMMAND_STR_CASE(DESTROY_UMEM);
        MLX5_COMMAND_STR_CASE(RELEASE_XRQ_ERROR);
        MLX5_COMMAND_STR_CASE(MODIFY_XRQ);
+       MLX5_COMMAND_STR_CASE(QUERY_VHCA_STATE);
+       MLX5_COMMAND_STR_CASE(MODIFY_VHCA_STATE);
+       MLX5_COMMAND_STR_CASE(ALLOC_SF);
+       MLX5_COMMAND_STR_CASE(DEALLOC_SF);
        default: return "unknown command opcode";
        }
 }
index 3261d0d..aa76a6e 100644 (file)
@@ -7,6 +7,8 @@
 #include "fw_reset.h"
 #include "fs_core.h"
 #include "eswitch.h"
+#include "sf/dev/dev.h"
+#include "sf/sf.h"
 
 static int mlx5_devlink_flash_update(struct devlink *devlink,
                                     struct devlink_flash_update_params *params,
@@ -127,6 +129,17 @@ static int mlx5_devlink_reload_down(struct devlink *devlink, bool netns_change,
                                    struct netlink_ext_ack *extack)
 {
        struct mlx5_core_dev *dev = devlink_priv(devlink);
+       bool sf_dev_allocated;
+
+       sf_dev_allocated = mlx5_sf_dev_allocated(dev);
+       if (sf_dev_allocated) {
+               /* Reload results in deleting SF device which further results in
+                * unregistering devlink instance while holding devlink_mutext.
+                * Hence, do not support reload.
+                */
+               NL_SET_ERR_MSG_MOD(extack, "reload is unsupported when SFs are allocated\n");
+               return -EOPNOTSUPP;
+       }
 
        switch (action) {
        case DEVLINK_RELOAD_ACTION_DRIVER_REINIT:
@@ -168,6 +181,91 @@ static int mlx5_devlink_reload_up(struct devlink *devlink, enum devlink_reload_a
        return 0;
 }
 
+static struct mlx5_devlink_trap *mlx5_find_trap_by_id(struct mlx5_core_dev *dev, int trap_id)
+{
+       struct mlx5_devlink_trap *dl_trap;
+
+       list_for_each_entry(dl_trap, &dev->priv.traps, list)
+               if (dl_trap->trap.id == trap_id)
+                       return dl_trap;
+
+       return NULL;
+}
+
+static int mlx5_devlink_trap_init(struct devlink *devlink, const struct devlink_trap *trap,
+                                 void *trap_ctx)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_devlink_trap *dl_trap;
+
+       dl_trap = kzalloc(sizeof(*dl_trap), GFP_KERNEL);
+       if (!dl_trap)
+               return -ENOMEM;
+
+       dl_trap->trap.id = trap->id;
+       dl_trap->trap.action = DEVLINK_TRAP_ACTION_DROP;
+       dl_trap->item = trap_ctx;
+
+       if (mlx5_find_trap_by_id(dev, trap->id)) {
+               kfree(dl_trap);
+               mlx5_core_err(dev, "Devlink trap: Trap 0x%x already found", trap->id);
+               return -EEXIST;
+       }
+
+       list_add_tail(&dl_trap->list, &dev->priv.traps);
+       return 0;
+}
+
+static void mlx5_devlink_trap_fini(struct devlink *devlink, const struct devlink_trap *trap,
+                                  void *trap_ctx)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_devlink_trap *dl_trap;
+
+       dl_trap = mlx5_find_trap_by_id(dev, trap->id);
+       if (!dl_trap) {
+               mlx5_core_err(dev, "Devlink trap: Missing trap id 0x%x", trap->id);
+               return;
+       }
+       list_del(&dl_trap->list);
+       kfree(dl_trap);
+}
+
+static int mlx5_devlink_trap_action_set(struct devlink *devlink,
+                                       const struct devlink_trap *trap,
+                                       enum devlink_trap_action action,
+                                       struct netlink_ext_ack *extack)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       enum devlink_trap_action action_orig;
+       struct mlx5_devlink_trap *dl_trap;
+       int err = 0;
+
+       dl_trap = mlx5_find_trap_by_id(dev, trap->id);
+       if (!dl_trap) {
+               mlx5_core_err(dev, "Devlink trap: Set action on invalid trap id 0x%x", trap->id);
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (action != DEVLINK_TRAP_ACTION_DROP && action != DEVLINK_TRAP_ACTION_TRAP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (action == dl_trap->trap.action)
+               goto out;
+
+       action_orig = dl_trap->trap.action;
+       dl_trap->trap.action = action;
+       err = mlx5_blocking_notifier_call_chain(dev, MLX5_DRIVER_EVENT_TYPE_TRAP,
+                                               &dl_trap->trap);
+       if (err)
+               dl_trap->trap.action = action_orig;
+out:
+       return err;
+}
+
 static const struct devlink_ops mlx5_devlink_ops = {
 #ifdef CONFIG_MLX5_ESWITCH
        .eswitch_mode_set = mlx5_devlink_eswitch_mode_set,
@@ -178,6 +276,12 @@ static const struct devlink_ops mlx5_devlink_ops = {
        .eswitch_encap_mode_get = mlx5_devlink_eswitch_encap_mode_get,
        .port_function_hw_addr_get = mlx5_devlink_port_function_hw_addr_get,
        .port_function_hw_addr_set = mlx5_devlink_port_function_hw_addr_set,
+#endif
+#ifdef CONFIG_MLX5_SF_MANAGER
+       .port_new = mlx5_devlink_sf_port_new,
+       .port_del = mlx5_devlink_sf_port_del,
+       .port_fn_state_get = mlx5_devlink_sf_port_fn_state_get,
+       .port_fn_state_set = mlx5_devlink_sf_port_fn_state_set,
 #endif
        .flash_update = mlx5_devlink_flash_update,
        .info_get = mlx5_devlink_info_get,
@@ -186,8 +290,59 @@ static const struct devlink_ops mlx5_devlink_ops = {
        .reload_limits = BIT(DEVLINK_RELOAD_LIMIT_NO_RESET),
        .reload_down = mlx5_devlink_reload_down,
        .reload_up = mlx5_devlink_reload_up,
+       .trap_init = mlx5_devlink_trap_init,
+       .trap_fini = mlx5_devlink_trap_fini,
+       .trap_action_set = mlx5_devlink_trap_action_set,
 };
 
+void mlx5_devlink_trap_report(struct mlx5_core_dev *dev, int trap_id, struct sk_buff *skb,
+                             struct devlink_port *dl_port)
+{
+       struct devlink *devlink = priv_to_devlink(dev);
+       struct mlx5_devlink_trap *dl_trap;
+
+       dl_trap = mlx5_find_trap_by_id(dev, trap_id);
+       if (!dl_trap) {
+               mlx5_core_err(dev, "Devlink trap: Report on invalid trap id 0x%x", trap_id);
+               return;
+       }
+
+       if (dl_trap->trap.action != DEVLINK_TRAP_ACTION_TRAP) {
+               mlx5_core_dbg(dev, "Devlink trap: Trap id %d has action %d", trap_id,
+                             dl_trap->trap.action);
+               return;
+       }
+       devlink_trap_report(devlink, skb, dl_trap->item, dl_port, NULL);
+}
+
+int mlx5_devlink_trap_get_num_active(struct mlx5_core_dev *dev)
+{
+       struct mlx5_devlink_trap *dl_trap;
+       int count = 0;
+
+       list_for_each_entry(dl_trap, &dev->priv.traps, list)
+               if (dl_trap->trap.action == DEVLINK_TRAP_ACTION_TRAP)
+                       count++;
+
+       return count;
+}
+
+int mlx5_devlink_traps_get_action(struct mlx5_core_dev *dev, int trap_id,
+                                 enum devlink_trap_action *action)
+{
+       struct mlx5_devlink_trap *dl_trap;
+
+       dl_trap = mlx5_find_trap_by_id(dev, trap_id);
+       if (!dl_trap) {
+               mlx5_core_err(dev, "Devlink trap: Get action on invalid trap id 0x%x",
+                             trap_id);
+               return -EINVAL;
+       }
+
+       *action = dl_trap->trap.action;
+       return 0;
+}
+
 struct devlink *mlx5_devlink_alloc(void)
 {
        return devlink_alloc(&mlx5_devlink_ops, sizeof(struct mlx5_core_dev));
@@ -358,6 +513,49 @@ static void mlx5_devlink_set_params_init_values(struct devlink *devlink)
 #endif
 }
 
+#define MLX5_TRAP_DROP(_id, _group_id)                                 \
+       DEVLINK_TRAP_GENERIC(DROP, DROP, _id,                           \
+                            DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
+                            DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT)
+
+static const struct devlink_trap mlx5_traps_arr[] = {
+       MLX5_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
+       MLX5_TRAP_DROP(DMAC_FILTER, L2_DROPS),
+};
+
+static const struct devlink_trap_group mlx5_trap_groups_arr[] = {
+       DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
+};
+
+static int mlx5_devlink_traps_register(struct devlink *devlink)
+{
+       struct mlx5_core_dev *core_dev = devlink_priv(devlink);
+       int err;
+
+       err = devlink_trap_groups_register(devlink, mlx5_trap_groups_arr,
+                                          ARRAY_SIZE(mlx5_trap_groups_arr));
+       if (err)
+               return err;
+
+       err = devlink_traps_register(devlink, mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr),
+                                    &core_dev->priv);
+       if (err)
+               goto err_trap_group;
+       return 0;
+
+err_trap_group:
+       devlink_trap_groups_unregister(devlink, mlx5_trap_groups_arr,
+                                      ARRAY_SIZE(mlx5_trap_groups_arr));
+       return err;
+}
+
+static void mlx5_devlink_traps_unregister(struct devlink *devlink)
+{
+       devlink_traps_unregister(devlink, mlx5_traps_arr, ARRAY_SIZE(mlx5_traps_arr));
+       devlink_trap_groups_unregister(devlink, mlx5_trap_groups_arr,
+                                      ARRAY_SIZE(mlx5_trap_groups_arr));
+}
+
 int mlx5_devlink_register(struct devlink *devlink, struct device *dev)
 {
        int err;
@@ -372,8 +570,16 @@ int mlx5_devlink_register(struct devlink *devlink, struct device *dev)
                goto params_reg_err;
        mlx5_devlink_set_params_init_values(devlink);
        devlink_params_publish(devlink);
+
+       err = mlx5_devlink_traps_register(devlink);
+       if (err)
+               goto traps_reg_err;
+
        return 0;
 
+traps_reg_err:
+       devlink_params_unregister(devlink, mlx5_devlink_params,
+                                 ARRAY_SIZE(mlx5_devlink_params));
 params_reg_err:
        devlink_unregister(devlink);
        return err;
@@ -381,6 +587,7 @@ params_reg_err:
 
 void mlx5_devlink_unregister(struct devlink *devlink)
 {
+       mlx5_devlink_traps_unregister(devlink);
        devlink_params_unregister(devlink, mlx5_devlink_params,
                                  ARRAY_SIZE(mlx5_devlink_params));
        devlink_unregister(devlink);
index f0de327..eff107d 100644 (file)
@@ -12,6 +12,24 @@ enum mlx5_devlink_param_id {
        MLX5_DEVLINK_PARAM_ID_ESW_LARGE_GROUP_NUM,
 };
 
+struct mlx5_trap_ctx {
+       int id;
+       int action;
+};
+
+struct mlx5_devlink_trap {
+       struct mlx5_trap_ctx trap;
+       void *item;
+       struct list_head list;
+};
+
+struct mlx5_core_dev;
+void mlx5_devlink_trap_report(struct mlx5_core_dev *dev, int trap_id, struct sk_buff *skb,
+                             struct devlink_port *dl_port);
+int mlx5_devlink_trap_get_num_active(struct mlx5_core_dev *dev);
+int mlx5_devlink_traps_get_action(struct mlx5_core_dev *dev, int trap_id,
+                                 enum devlink_trap_action *action);
+
 struct devlink *mlx5_devlink_alloc(void);
 void mlx5_devlink_free(struct devlink *devlink);
 int mlx5_devlink_register(struct devlink *devlink, struct device *dev);
index 055baf3..39f389c 100644 (file)
@@ -55,6 +55,7 @@
 #include "en_stats.h"
 #include "en/dcbnl.h"
 #include "en/fs.h"
+#include "en/qos.h"
 #include "lib/hv_vhca.h"
 
 extern const struct net_device_ops mlx5e_netdev_ops;
@@ -161,6 +162,9 @@ do {                                                            \
                            ##__VA_ARGS__);                     \
 } while (0)
 
+#define mlx5e_state_dereference(priv, p) \
+       rcu_dereference_protected((p), lockdep_is_held(&(priv)->state_lock))
+
 enum mlx5e_rq_group {
        MLX5E_RQ_GROUP_REGULAR,
        MLX5E_RQ_GROUP_XSK,
@@ -560,6 +564,7 @@ typedef bool (*mlx5e_fp_post_rx_wqes)(struct mlx5e_rq *rq);
 typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16);
 
 int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool xsk);
+void mlx5e_rq_set_trap_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params);
 
 enum mlx5e_rq_flag {
        MLX5E_RQ_FLAG_XDP_XMIT,
@@ -663,11 +668,13 @@ struct mlx5e_channel {
        struct mlx5e_xdpsq         rq_xdpsq;
        struct mlx5e_txqsq         sq[MLX5E_MAX_NUM_TC];
        struct mlx5e_icosq         icosq;   /* internal control operations */
+       struct mlx5e_txqsq __rcu * __rcu *qos_sqs;
        bool                       xdp;
        struct napi_struct         napi;
        struct device             *pdev;
        struct net_device         *netdev;
        __be32                     mkey_be;
+       u16                        qos_sqs_size;
        u8                         num_tc;
        u8                         lag_port;
 
@@ -756,6 +763,8 @@ struct mlx5e_modify_sq_param {
        int next_state;
        int rl_update;
        int rl_index;
+       bool qos_update;
+       u16 qos_queue_group_id;
 };
 
 #if IS_ENABLED(CONFIG_PCI_HYPERV_INTERFACE)
@@ -788,10 +797,22 @@ struct mlx5e_scratchpad {
        cpumask_var_t cpumask;
 };
 
+struct mlx5e_htb {
+       DECLARE_HASHTABLE(qos_tc2node, order_base_2(MLX5E_QOS_MAX_LEAF_NODES));
+       DECLARE_BITMAP(qos_used_qids, MLX5E_QOS_MAX_LEAF_NODES);
+       struct mlx5e_sq_stats **qos_sq_stats;
+       u16 max_qos_sqs;
+       u16 maj_id;
+       u16 defcls;
+};
+
+struct mlx5e_trap;
+
 struct mlx5e_priv {
        /* priv data path fields - start */
        /* +1 for port ptp ts */
-       struct mlx5e_txqsq *txq2sq[(MLX5E_MAX_NUM_CHANNELS + 1) * MLX5E_MAX_NUM_TC];
+       struct mlx5e_txqsq *txq2sq[(MLX5E_MAX_NUM_CHANNELS + 1) * MLX5E_MAX_NUM_TC +
+                                  MLX5E_QOS_MAX_LEAF_NODES];
        int channel_tc2realtxq[MLX5E_MAX_NUM_CHANNELS][MLX5E_MAX_NUM_TC];
        int port_ptp_tc2realtxq[MLX5E_MAX_NUM_TC];
 #ifdef CONFIG_MLX5_CORE_EN_DCB
@@ -826,8 +847,10 @@ struct mlx5e_priv {
 
        struct mlx5_core_dev      *mdev;
        struct net_device         *netdev;
+       struct mlx5e_trap         *en_trap;
        struct mlx5e_stats         stats;
        struct mlx5e_channel_stats channel_stats[MLX5E_MAX_NUM_CHANNELS];
+       struct mlx5e_channel_stats trap_stats;
        struct mlx5e_port_ptp_stats port_ptp_stats;
        u16                        max_nch;
        u8                         max_opened_tc;
@@ -836,6 +859,7 @@ struct mlx5e_priv {
        u16                        q_counter;
        u16                        drop_rq_q_counter;
        struct notifier_block      events_nb;
+       struct notifier_block      blocking_events_nb;
        int                        num_tc_x_num_ch;
 
        struct udp_tunnel_nic_info nic_info;
@@ -859,6 +883,7 @@ struct mlx5e_priv {
        struct mlx5e_hv_vhca_stats_agent stats_agent;
 #endif
        struct mlx5e_scratchpad    scratchpad;
+       struct mlx5e_htb           htb;
 };
 
 struct mlx5e_rx_handlers {
@@ -942,6 +967,8 @@ int mlx5e_open_rq(struct mlx5e_channel *c, struct mlx5e_params *params,
 int mlx5e_wait_for_min_rx_wqes(struct mlx5e_rq *rq, int wait_time);
 void mlx5e_deactivate_rq(struct mlx5e_rq *rq);
 void mlx5e_close_rq(struct mlx5e_rq *rq);
+int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param);
+void mlx5e_destroy_rq(struct mlx5e_rq *rq);
 
 struct mlx5e_sq_param;
 int mlx5e_open_icosq(struct mlx5e_channel *c, struct mlx5e_params *params,
@@ -986,6 +1013,7 @@ int mlx5e_safe_switch_channels(struct mlx5e_priv *priv,
                               struct mlx5e_channels *new_chs,
                               mlx5e_fp_preactivate preactivate,
                               void *context);
+int mlx5e_update_tx_netdev_queues(struct mlx5e_priv *priv);
 int mlx5e_num_channels_changed(struct mlx5e_priv *priv);
 int mlx5e_num_channels_changed_ctx(struct mlx5e_priv *priv, void *context);
 void mlx5e_activate_priv_channels(struct mlx5e_priv *priv);
@@ -1010,6 +1038,9 @@ void mlx5e_deactivate_icosq(struct mlx5e_icosq *icosq);
 
 int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
                    struct mlx5e_modify_sq_param *p);
+int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix,
+                    struct mlx5e_params *params, struct mlx5e_sq_param *param,
+                    struct mlx5e_txqsq *sq, int tc, u16 qos_queue_group_id, u16 qos_qid);
 void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq);
 void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq);
 void mlx5e_free_txqsq(struct mlx5e_txqsq *sq);
@@ -1020,8 +1051,10 @@ struct mlx5e_create_sq_param;
 int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev,
                        struct mlx5e_sq_param *param,
                        struct mlx5e_create_sq_param *csp,
+                       u16 qos_queue_group_id,
                        u32 *sqn);
 void mlx5e_tx_err_cqe_work(struct work_struct *recover_work);
+void mlx5e_close_txqsq(struct mlx5e_txqsq *sq);
 
 static inline bool mlx5_tx_swp_supported(struct mlx5_core_dev *mdev)
 {
@@ -1047,6 +1080,8 @@ void mlx5e_destroy_q_counters(struct mlx5e_priv *priv);
 int mlx5e_open_drop_rq(struct mlx5e_priv *priv,
                       struct mlx5e_rq *drop_rq);
 void mlx5e_close_drop_rq(struct mlx5e_rq *drop_rq);
+int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node);
+void mlx5e_free_di_list(struct mlx5e_rq *rq);
 
 int mlx5e_create_indirect_rqt(struct mlx5e_priv *priv);
 
index 5749557..a16297e 100644 (file)
@@ -44,6 +44,11 @@ struct mlx5e_l2_rule {
 
 #define MLX5E_L2_ADDR_HASH_SIZE BIT(BITS_PER_BYTE)
 
+struct mlx5e_promisc_table {
+       struct mlx5e_flow_table ft;
+       struct mlx5_flow_handle *rule;
+};
+
 struct mlx5e_vlan_table {
        struct mlx5e_flow_table         ft;
        DECLARE_BITMAP(active_cvlans, VLAN_N_VID);
@@ -53,6 +58,7 @@ struct mlx5e_vlan_table {
        struct mlx5_flow_handle *untagged_rule;
        struct mlx5_flow_handle *any_cvlan_rule;
        struct mlx5_flow_handle *any_svlan_rule;
+       struct mlx5_flow_handle *trap_rule;
        bool                    cvlan_filter_disabled;
 };
 
@@ -62,7 +68,7 @@ struct mlx5e_l2_table {
        struct hlist_head          netdev_mc[MLX5E_L2_ADDR_HASH_SIZE];
        struct mlx5e_l2_rule       broadcast;
        struct mlx5e_l2_rule       allmulti;
-       struct mlx5e_l2_rule       promisc;
+       struct mlx5_flow_handle    *trap_rule;
        bool                       broadcast_enabled;
        bool                       allmulti_enabled;
        bool                       promisc_enabled;
@@ -126,7 +132,8 @@ struct mlx5e_ttc_table {
 
 /* NIC prio FTS */
 enum {
-       MLX5E_VLAN_FT_LEVEL = 0,
+       MLX5E_PROMISC_FT_LEVEL,
+       MLX5E_VLAN_FT_LEVEL,
        MLX5E_L2_FT_LEVEL,
        MLX5E_TTC_FT_LEVEL,
        MLX5E_INNER_TTC_FT_LEVEL,
@@ -241,6 +248,7 @@ struct mlx5e_flow_steering {
        struct mlx5e_ethtool_steering   ethtool;
 #endif
        struct mlx5e_tc_table           tc;
+       struct mlx5e_promisc_table      promisc;
        struct mlx5e_vlan_table         vlan;
        struct mlx5e_l2_table           l2;
        struct mlx5e_ttc_table          ttc;
@@ -288,6 +296,10 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv);
 void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv);
 
 u8 mlx5e_get_proto_by_tunnel_type(enum mlx5e_tunnel_types tt);
+int mlx5e_add_vlan_trap(struct mlx5e_priv *priv, int  trap_id, int tir_num);
+void mlx5e_remove_vlan_trap(struct mlx5e_priv *priv);
+int mlx5e_add_mac_trap(struct mlx5e_priv *priv, int  trap_id, int tir_num);
+void mlx5e_remove_mac_trap(struct mlx5e_priv *priv);
 
 #endif /* __MLX5E_FLOW_STEER_H__ */
 
index 718f8c0..84e501e 100644 (file)
@@ -273,7 +273,7 @@ int mlx5e_health_rsc_fmsg_dump(struct mlx5e_priv *priv, struct mlx5_rsc_key *key
 
        err = devlink_fmsg_binary_pair_nest_start(fmsg, "data");
        if (err)
-               return err;
+               goto free_page;
 
        cmd = mlx5_rsc_dump_cmd_create(mdev, key);
        if (IS_ERR(cmd)) {
index 807147d..ea2cfb0 100644 (file)
@@ -118,6 +118,8 @@ void mlx5e_build_rq_param(struct mlx5e_priv *priv,
                          struct mlx5e_rq_param *param);
 void mlx5e_build_sq_param_common(struct mlx5e_priv *priv,
                                 struct mlx5e_sq_param *param);
+void mlx5e_build_sq_param(struct mlx5e_priv *priv, struct mlx5e_params *params,
+                         struct mlx5e_sq_param *param);
 void mlx5e_build_rx_cq_param(struct mlx5e_priv *priv,
                             struct mlx5e_params *params,
                             struct mlx5e_xsk_param *xsk,
index 2a2bac3..eeddd11 100644 (file)
@@ -261,7 +261,7 @@ static int mlx5e_ptp_open_txqsq(struct mlx5e_port_ptp *c, u32 tisn,
        csp.min_inline_mode = txqsq->min_inline_mode;
        csp.ts_cqe_to_dest_cqn = ptpsq->ts_cq.mcq.cqn;
 
-       err = mlx5e_create_sq_rdy(c->mdev, sqp, &csp, &txqsq->sqn);
+       err = mlx5e_create_sq_rdy(c->mdev, sqp, &csp, 0, &txqsq->sqn);
        if (err)
                goto err_free_txqsq;
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.c
new file mode 100644 (file)
index 0000000..12d7ad0
--- /dev/null
@@ -0,0 +1,984 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */
+
+#include "en.h"
+#include "params.h"
+#include "../qos.h"
+
+#define BYTES_IN_MBIT 125000
+
+int mlx5e_qos_max_leaf_nodes(struct mlx5_core_dev *mdev)
+{
+       return min(MLX5E_QOS_MAX_LEAF_NODES, mlx5_qos_max_leaf_nodes(mdev));
+}
+
+int mlx5e_qos_cur_leaf_nodes(struct mlx5e_priv *priv)
+{
+       int last = find_last_bit(priv->htb.qos_used_qids, mlx5e_qos_max_leaf_nodes(priv->mdev));
+
+       return last == mlx5e_qos_max_leaf_nodes(priv->mdev) ? 0 : last + 1;
+}
+
+/* Software representation of the QoS tree (internal to this file) */
+
+static int mlx5e_find_unused_qos_qid(struct mlx5e_priv *priv)
+{
+       int size = mlx5e_qos_max_leaf_nodes(priv->mdev);
+       int res;
+
+       WARN_ONCE(!mutex_is_locked(&priv->state_lock), "%s: state_lock is not held\n", __func__);
+       res = find_first_zero_bit(priv->htb.qos_used_qids, size);
+
+       return res == size ? -ENOSPC : res;
+}
+
+struct mlx5e_qos_node {
+       struct hlist_node hnode;
+       struct rcu_head rcu;
+       struct mlx5e_qos_node *parent;
+       u64 rate;
+       u32 bw_share;
+       u32 max_average_bw;
+       u32 hw_id;
+       u32 classid; /* 16-bit, except root. */
+       u16 qid;
+};
+
+#define MLX5E_QOS_QID_INNER 0xffff
+#define MLX5E_HTB_CLASSID_ROOT 0xffffffff
+
+static struct mlx5e_qos_node *
+mlx5e_sw_node_create_leaf(struct mlx5e_priv *priv, u16 classid, u16 qid,
+                         struct mlx5e_qos_node *parent)
+{
+       struct mlx5e_qos_node *node;
+
+       node = kzalloc(sizeof(*node), GFP_KERNEL);
+       if (!node)
+               return ERR_PTR(-ENOMEM);
+
+       node->parent = parent;
+
+       node->qid = qid;
+       __set_bit(qid, priv->htb.qos_used_qids);
+
+       node->classid = classid;
+       hash_add_rcu(priv->htb.qos_tc2node, &node->hnode, classid);
+
+       mlx5e_update_tx_netdev_queues(priv);
+
+       return node;
+}
+
+static struct mlx5e_qos_node *mlx5e_sw_node_create_root(struct mlx5e_priv *priv)
+{
+       struct mlx5e_qos_node *node;
+
+       node = kzalloc(sizeof(*node), GFP_KERNEL);
+       if (!node)
+               return ERR_PTR(-ENOMEM);
+
+       node->qid = MLX5E_QOS_QID_INNER;
+       node->classid = MLX5E_HTB_CLASSID_ROOT;
+       hash_add_rcu(priv->htb.qos_tc2node, &node->hnode, node->classid);
+
+       return node;
+}
+
+static struct mlx5e_qos_node *mlx5e_sw_node_find(struct mlx5e_priv *priv, u32 classid)
+{
+       struct mlx5e_qos_node *node = NULL;
+
+       hash_for_each_possible(priv->htb.qos_tc2node, node, hnode, classid) {
+               if (node->classid == classid)
+                       break;
+       }
+
+       return node;
+}
+
+static struct mlx5e_qos_node *mlx5e_sw_node_find_rcu(struct mlx5e_priv *priv, u32 classid)
+{
+       struct mlx5e_qos_node *node = NULL;
+
+       hash_for_each_possible_rcu(priv->htb.qos_tc2node, node, hnode, classid) {
+               if (node->classid == classid)
+                       break;
+       }
+
+       return node;
+}
+
+static void mlx5e_sw_node_delete(struct mlx5e_priv *priv, struct mlx5e_qos_node *node)
+{
+       hash_del_rcu(&node->hnode);
+       if (node->qid != MLX5E_QOS_QID_INNER) {
+               __clear_bit(node->qid, priv->htb.qos_used_qids);
+               mlx5e_update_tx_netdev_queues(priv);
+       }
+       kfree_rcu(node, rcu);
+}
+
+/* TX datapath API */
+
+static u16 mlx5e_qid_from_qos(struct mlx5e_channels *chs, u16 qid)
+{
+       /* These channel params are safe to access from the datapath, because:
+        * 1. This function is called only after checking priv->htb.maj_id != 0,
+        *    and the number of queues can't change while HTB offload is active.
+        * 2. When priv->htb.maj_id becomes 0, synchronize_rcu waits for
+        *    mlx5e_select_queue to finish while holding priv->state_lock,
+        *    preventing other code from changing the number of queues.
+        */
+       bool is_ptp = MLX5E_GET_PFLAG(&chs->params, MLX5E_PFLAG_TX_PORT_TS);
+
+       return (chs->params.num_channels + is_ptp) * chs->params.num_tc + qid;
+}
+
+int mlx5e_get_txq_by_classid(struct mlx5e_priv *priv, u16 classid)
+{
+       struct mlx5e_qos_node *node;
+       u16 qid;
+       int res;
+
+       rcu_read_lock();
+
+       node = mlx5e_sw_node_find_rcu(priv, classid);
+       if (!node) {
+               res = -ENOENT;
+               goto out;
+       }
+       qid = READ_ONCE(node->qid);
+       if (qid == MLX5E_QOS_QID_INNER) {
+               res = -EINVAL;
+               goto out;
+       }
+       res = mlx5e_qid_from_qos(&priv->channels, qid);
+
+out:
+       rcu_read_unlock();
+       return res;
+}
+
+static struct mlx5e_txqsq *mlx5e_get_qos_sq(struct mlx5e_priv *priv, int qid)
+{
+       struct mlx5e_params *params = &priv->channels.params;
+       struct mlx5e_txqsq __rcu **qos_sqs;
+       struct mlx5e_channel *c;
+       int ix;
+
+       ix = qid % params->num_channels;
+       qid /= params->num_channels;
+       c = priv->channels.c[ix];
+
+       qos_sqs = mlx5e_state_dereference(priv, c->qos_sqs);
+       return mlx5e_state_dereference(priv, qos_sqs[qid]);
+}
+
+/* SQ lifecycle */
+
+static int mlx5e_open_qos_sq(struct mlx5e_priv *priv, struct mlx5e_channels *chs,
+                            struct mlx5e_qos_node *node)
+{
+       struct mlx5e_create_cq_param ccp = {};
+       struct mlx5e_txqsq __rcu **qos_sqs;
+       struct mlx5e_sq_param param_sq;
+       struct mlx5e_cq_param param_cq;
+       int txq_ix, ix, qid, err = 0;
+       struct mlx5e_params *params;
+       struct mlx5e_channel *c;
+       struct mlx5e_txqsq *sq;
+
+       params = &chs->params;
+
+       txq_ix = mlx5e_qid_from_qos(chs, node->qid);
+
+       WARN_ON(node->qid > priv->htb.max_qos_sqs);
+       if (node->qid == priv->htb.max_qos_sqs) {
+               struct mlx5e_sq_stats *stats, **stats_list = NULL;
+
+               if (priv->htb.max_qos_sqs == 0) {
+                       stats_list = kvcalloc(mlx5e_qos_max_leaf_nodes(priv->mdev),
+                                             sizeof(*stats_list),
+                                             GFP_KERNEL);
+                       if (!stats_list)
+                               return -ENOMEM;
+               }
+               stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+               if (!stats) {
+                       kvfree(stats_list);
+                       return -ENOMEM;
+               }
+               if (stats_list)
+                       WRITE_ONCE(priv->htb.qos_sq_stats, stats_list);
+               WRITE_ONCE(priv->htb.qos_sq_stats[node->qid], stats);
+               /* Order max_qos_sqs increment after writing the array pointer.
+                * Pairs with smp_load_acquire in en_stats.c.
+                */
+               smp_store_release(&priv->htb.max_qos_sqs, priv->htb.max_qos_sqs + 1);
+       }
+
+       ix = node->qid % params->num_channels;
+       qid = node->qid / params->num_channels;
+       c = chs->c[ix];
+
+       qos_sqs = mlx5e_state_dereference(priv, c->qos_sqs);
+       sq = kzalloc(sizeof(*sq), GFP_KERNEL);
+
+       if (!sq)
+               return -ENOMEM;
+
+       mlx5e_build_create_cq_param(&ccp, c);
+
+       memset(&param_sq, 0, sizeof(param_sq));
+       memset(&param_cq, 0, sizeof(param_cq));
+       mlx5e_build_sq_param(priv, params, &param_sq);
+       mlx5e_build_tx_cq_param(priv, params, &param_cq);
+       err = mlx5e_open_cq(priv, params->tx_cq_moderation, &param_cq, &ccp, &sq->cq);
+       if (err)
+               goto err_free_sq;
+       err = mlx5e_open_txqsq(c, priv->tisn[c->lag_port][0], txq_ix, params,
+                              &param_sq, sq, 0, node->hw_id, node->qid);
+       if (err)
+               goto err_close_cq;
+
+       rcu_assign_pointer(qos_sqs[qid], sq);
+
+       return 0;
+
+err_close_cq:
+       mlx5e_close_cq(&sq->cq);
+err_free_sq:
+       kfree(sq);
+       return err;
+}
+
+static void mlx5e_activate_qos_sq(struct mlx5e_priv *priv, struct mlx5e_qos_node *node)
+{
+       struct mlx5e_txqsq *sq;
+
+       sq = mlx5e_get_qos_sq(priv, node->qid);
+
+       WRITE_ONCE(priv->txq2sq[mlx5e_qid_from_qos(&priv->channels, node->qid)], sq);
+
+       /* Make the change to txq2sq visible before the queue is started.
+        * As mlx5e_xmit runs under a spinlock, there is an implicit ACQUIRE,
+        * which pairs with this barrier.
+        */
+       smp_wmb();
+
+       qos_dbg(priv->mdev, "Activate QoS SQ qid %u\n", node->qid);
+       mlx5e_activate_txqsq(sq);
+}
+
+static void mlx5e_deactivate_qos_sq(struct mlx5e_priv *priv, u16 qid)
+{
+       struct mlx5e_txqsq *sq;
+
+       sq = mlx5e_get_qos_sq(priv, qid);
+       if (!sq) /* Handle the case when the SQ failed to open. */
+               return;
+
+       qos_dbg(priv->mdev, "Deactivate QoS SQ qid %u\n", qid);
+       mlx5e_deactivate_txqsq(sq);
+
+       /* The queue is disabled, no synchronization with datapath is needed. */
+       priv->txq2sq[mlx5e_qid_from_qos(&priv->channels, qid)] = NULL;
+}
+
+static void mlx5e_close_qos_sq(struct mlx5e_priv *priv, u16 qid)
+{
+       struct mlx5e_txqsq __rcu **qos_sqs;
+       struct mlx5e_params *params;
+       struct mlx5e_channel *c;
+       struct mlx5e_txqsq *sq;
+       int ix;
+
+       params = &priv->channels.params;
+
+       ix = qid % params->num_channels;
+       qid /= params->num_channels;
+       c = priv->channels.c[ix];
+       qos_sqs = mlx5e_state_dereference(priv, c->qos_sqs);
+       sq = rcu_replace_pointer(qos_sqs[qid], NULL, lockdep_is_held(&priv->state_lock));
+       if (!sq) /* Handle the case when the SQ failed to open. */
+               return;
+
+       synchronize_rcu(); /* Sync with NAPI. */
+
+       mlx5e_close_txqsq(sq);
+       mlx5e_close_cq(&sq->cq);
+       kfree(sq);
+}
+
+void mlx5e_qos_close_queues(struct mlx5e_channel *c)
+{
+       struct mlx5e_txqsq __rcu **qos_sqs;
+       int i;
+
+       qos_sqs = rcu_replace_pointer(c->qos_sqs, NULL, lockdep_is_held(&c->priv->state_lock));
+       if (!qos_sqs)
+               return;
+       synchronize_rcu(); /* Sync with NAPI. */
+
+       for (i = 0; i < c->qos_sqs_size; i++) {
+               struct mlx5e_txqsq *sq;
+
+               sq = mlx5e_state_dereference(c->priv, qos_sqs[i]);
+               if (!sq) /* Handle the case when the SQ failed to open. */
+                       continue;
+
+               mlx5e_close_txqsq(sq);
+               mlx5e_close_cq(&sq->cq);
+               kfree(sq);
+       }
+
+       kvfree(qos_sqs);
+}
+
+static void mlx5e_qos_close_all_queues(struct mlx5e_channels *chs)
+{
+       int i;
+
+       for (i = 0; i < chs->num; i++)
+               mlx5e_qos_close_queues(chs->c[i]);
+}
+
+static int mlx5e_qos_alloc_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs)
+{
+       u16 qos_sqs_size;
+       int i;
+
+       qos_sqs_size = DIV_ROUND_UP(mlx5e_qos_max_leaf_nodes(priv->mdev), chs->num);
+
+       for (i = 0; i < chs->num; i++) {
+               struct mlx5e_txqsq **sqs;
+
+               sqs = kvcalloc(qos_sqs_size, sizeof(struct mlx5e_txqsq *), GFP_KERNEL);
+               if (!sqs)
+                       goto err_free;
+
+               WRITE_ONCE(chs->c[i]->qos_sqs_size, qos_sqs_size);
+               smp_wmb(); /* Pairs with mlx5e_napi_poll. */
+               rcu_assign_pointer(chs->c[i]->qos_sqs, sqs);
+       }
+
+       return 0;
+
+err_free:
+       while (--i >= 0) {
+               struct mlx5e_txqsq **sqs;
+
+               sqs = rcu_replace_pointer(chs->c[i]->qos_sqs, NULL,
+                                         lockdep_is_held(&priv->state_lock));
+
+               synchronize_rcu(); /* Sync with NAPI. */
+               kvfree(sqs);
+       }
+       return -ENOMEM;
+}
+
+int mlx5e_qos_open_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs)
+{
+       struct mlx5e_qos_node *node = NULL;
+       int bkt, err;
+
+       if (!priv->htb.maj_id)
+               return 0;
+
+       err = mlx5e_qos_alloc_queues(priv, chs);
+       if (err)
+               return err;
+
+       hash_for_each(priv->htb.qos_tc2node, bkt, node, hnode) {
+               if (node->qid == MLX5E_QOS_QID_INNER)
+                       continue;
+               err = mlx5e_open_qos_sq(priv, chs, node);
+               if (err) {
+                       mlx5e_qos_close_all_queues(chs);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+void mlx5e_qos_activate_queues(struct mlx5e_priv *priv)
+{
+       struct mlx5e_qos_node *node = NULL;
+       int bkt;
+
+       hash_for_each(priv->htb.qos_tc2node, bkt, node, hnode) {
+               if (node->qid == MLX5E_QOS_QID_INNER)
+                       continue;
+               mlx5e_activate_qos_sq(priv, node);
+       }
+}
+
+void mlx5e_qos_deactivate_queues(struct mlx5e_channel *c)
+{
+       struct mlx5e_params *params = &c->priv->channels.params;
+       struct mlx5e_txqsq __rcu **qos_sqs;
+       int i;
+
+       qos_sqs = mlx5e_state_dereference(c->priv, c->qos_sqs);
+       if (!qos_sqs)
+               return;
+
+       for (i = 0; i < c->qos_sqs_size; i++) {
+               u16 qid = params->num_channels * i + c->ix;
+               struct mlx5e_txqsq *sq;
+
+               sq = mlx5e_state_dereference(c->priv, qos_sqs[i]);
+               if (!sq) /* Handle the case when the SQ failed to open. */
+                       continue;
+
+               qos_dbg(c->mdev, "Deactivate QoS SQ qid %u\n", qid);
+               mlx5e_deactivate_txqsq(sq);
+
+               /* The queue is disabled, no synchronization with datapath is needed. */
+               c->priv->txq2sq[mlx5e_qid_from_qos(&c->priv->channels, qid)] = NULL;
+       }
+}
+
+static void mlx5e_qos_deactivate_all_queues(struct mlx5e_channels *chs)
+{
+       int i;
+
+       for (i = 0; i < chs->num; i++)
+               mlx5e_qos_deactivate_queues(chs->c[i]);
+}
+
+/* HTB API */
+
+int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls,
+                      struct netlink_ext_ack *extack)
+{
+       struct mlx5e_qos_node *root;
+       bool opened;
+       int err;
+
+       qos_dbg(priv->mdev, "TC_HTB_CREATE handle %04x:, default :%04x\n", htb_maj_id, htb_defcls);
+
+       if (!mlx5_qos_is_supported(priv->mdev)) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Missing QoS capabilities. Try disabling SRIOV or use a supported device.");
+               return -EOPNOTSUPP;
+       }
+
+       opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
+       if (opened) {
+               err = mlx5e_qos_alloc_queues(priv, &priv->channels);
+               if (err)
+                       return err;
+       }
+
+       root = mlx5e_sw_node_create_root(priv);
+       if (IS_ERR(root)) {
+               err = PTR_ERR(root);
+               goto err_free_queues;
+       }
+
+       err = mlx5_qos_create_root_node(priv->mdev, &root->hw_id);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Firmware error. Try upgrading firmware.");
+               goto err_sw_node_delete;
+       }
+
+       WRITE_ONCE(priv->htb.defcls, htb_defcls);
+       /* Order maj_id after defcls - pairs with
+        * mlx5e_select_queue/mlx5e_select_htb_queues.
+        */
+       smp_store_release(&priv->htb.maj_id, htb_maj_id);
+
+       return 0;
+
+err_sw_node_delete:
+       mlx5e_sw_node_delete(priv, root);
+
+err_free_queues:
+       if (opened)
+               mlx5e_qos_close_all_queues(&priv->channels);
+       return err;
+}
+
+int mlx5e_htb_root_del(struct mlx5e_priv *priv)
+{
+       struct mlx5e_qos_node *root;
+       int err;
+
+       qos_dbg(priv->mdev, "TC_HTB_DESTROY\n");
+
+       WRITE_ONCE(priv->htb.maj_id, 0);
+       synchronize_rcu(); /* Sync with mlx5e_select_htb_queue and TX data path. */
+
+       root = mlx5e_sw_node_find(priv, MLX5E_HTB_CLASSID_ROOT);
+       if (!root) {
+               qos_err(priv->mdev, "Failed to find the root node in the QoS tree\n");
+               return -ENOENT;
+       }
+       err = mlx5_qos_destroy_node(priv->mdev, root->hw_id);
+       if (err)
+               qos_err(priv->mdev, "Failed to destroy root node %u, err = %d\n",
+                       root->hw_id, err);
+       mlx5e_sw_node_delete(priv, root);
+
+       mlx5e_qos_deactivate_all_queues(&priv->channels);
+       mlx5e_qos_close_all_queues(&priv->channels);
+
+       return err;
+}
+
+static int mlx5e_htb_convert_rate(struct mlx5e_priv *priv, u64 rate,
+                                 struct mlx5e_qos_node *parent, u32 *bw_share)
+{
+       u64 share = 0;
+
+       while (parent->classid != MLX5E_HTB_CLASSID_ROOT && !parent->max_average_bw)
+               parent = parent->parent;
+
+       if (parent->max_average_bw)
+               share = div64_u64(div_u64(rate * 100, BYTES_IN_MBIT),
+                                 parent->max_average_bw);
+       else
+               share = 101;
+
+       *bw_share = share == 0 ? 1 : share > 100 ? 0 : share;
+
+       qos_dbg(priv->mdev, "Convert: rate %llu, parent ceil %llu -> bw_share %u\n",
+               rate, (u64)parent->max_average_bw * BYTES_IN_MBIT, *bw_share);
+
+       return 0;
+}
+
+static void mlx5e_htb_convert_ceil(struct mlx5e_priv *priv, u64 ceil, u32 *max_average_bw)
+{
+       *max_average_bw = div_u64(ceil, BYTES_IN_MBIT);
+
+       qos_dbg(priv->mdev, "Convert: ceil %llu -> max_average_bw %u\n",
+               ceil, *max_average_bw);
+}
+
+int mlx5e_htb_leaf_alloc_queue(struct mlx5e_priv *priv, u16 classid,
+                              u32 parent_classid, u64 rate, u64 ceil,
+                              struct netlink_ext_ack *extack)
+{
+       struct mlx5e_qos_node *node, *parent;
+       int qid;
+       int err;
+
+       qos_dbg(priv->mdev, "TC_HTB_LEAF_ALLOC_QUEUE classid %04x, parent %04x, rate %llu, ceil %llu\n",
+               classid, parent_classid, rate, ceil);
+
+       qid = mlx5e_find_unused_qos_qid(priv);
+       if (qid < 0) {
+               NL_SET_ERR_MSG_MOD(extack, "Maximum amount of leaf classes is reached.");
+               return qid;
+       }
+
+       parent = mlx5e_sw_node_find(priv, parent_classid);
+       if (!parent)
+               return -EINVAL;
+
+       node = mlx5e_sw_node_create_leaf(priv, classid, qid, parent);
+       if (IS_ERR(node))
+               return PTR_ERR(node);
+
+       node->rate = rate;
+       mlx5e_htb_convert_rate(priv, rate, node->parent, &node->bw_share);
+       mlx5e_htb_convert_ceil(priv, ceil, &node->max_average_bw);
+
+       err = mlx5_qos_create_leaf_node(priv->mdev, node->parent->hw_id,
+                                       node->bw_share, node->max_average_bw,
+                                       &node->hw_id);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating a leaf node.");
+               qos_err(priv->mdev, "Failed to create a leaf node (class %04x), err = %d\n",
+                       classid, err);
+               mlx5e_sw_node_delete(priv, node);
+               return err;
+       }
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               err = mlx5e_open_qos_sq(priv, &priv->channels, node);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ.");
+                       qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x), err = %d\n",
+                                classid, err);
+               } else {
+                       mlx5e_activate_qos_sq(priv, node);
+               }
+       }
+
+       return mlx5e_qid_from_qos(&priv->channels, node->qid);
+}
+
+int mlx5e_htb_leaf_to_inner(struct mlx5e_priv *priv, u16 classid, u16 child_classid,
+                           u64 rate, u64 ceil, struct netlink_ext_ack *extack)
+{
+       struct mlx5e_qos_node *node, *child;
+       int err, tmp_err;
+       u32 new_hw_id;
+       u16 qid;
+
+       qos_dbg(priv->mdev, "TC_HTB_LEAF_TO_INNER classid %04x, upcoming child %04x, rate %llu, ceil %llu\n",
+               classid, child_classid, rate, ceil);
+
+       node = mlx5e_sw_node_find(priv, classid);
+       if (!node)
+               return -ENOENT;
+
+       err = mlx5_qos_create_inner_node(priv->mdev, node->parent->hw_id,
+                                        node->bw_share, node->max_average_bw,
+                                        &new_hw_id);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating an inner node.");
+               qos_err(priv->mdev, "Failed to create an inner node (class %04x), err = %d\n",
+                       classid, err);
+               return err;
+       }
+
+       /* Intentionally reuse the qid for the upcoming first child. */
+       child = mlx5e_sw_node_create_leaf(priv, child_classid, node->qid, node);
+       if (IS_ERR(child)) {
+               err = PTR_ERR(child);
+               goto err_destroy_hw_node;
+       }
+
+       child->rate = rate;
+       mlx5e_htb_convert_rate(priv, rate, node, &child->bw_share);
+       mlx5e_htb_convert_ceil(priv, ceil, &child->max_average_bw);
+
+       err = mlx5_qos_create_leaf_node(priv->mdev, new_hw_id, child->bw_share,
+                                       child->max_average_bw, &child->hw_id);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating a leaf node.");
+               qos_err(priv->mdev, "Failed to create a leaf node (class %04x), err = %d\n",
+                       classid, err);
+               goto err_delete_sw_node;
+       }
+
+       /* No fail point. */
+
+       qid = node->qid;
+       /* Pairs with mlx5e_get_txq_by_classid. */
+       WRITE_ONCE(node->qid, MLX5E_QOS_QID_INNER);
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               mlx5e_deactivate_qos_sq(priv, qid);
+               mlx5e_close_qos_sq(priv, qid);
+       }
+
+       err = mlx5_qos_destroy_node(priv->mdev, node->hw_id);
+       if (err) /* Not fatal. */
+               qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n",
+                        node->hw_id, classid, err);
+
+       node->hw_id = new_hw_id;
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               err = mlx5e_open_qos_sq(priv, &priv->channels, child);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ.");
+                       qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x), err = %d\n",
+                                classid, err);
+               } else {
+                       mlx5e_activate_qos_sq(priv, child);
+               }
+       }
+
+       return 0;
+
+err_delete_sw_node:
+       child->qid = MLX5E_QOS_QID_INNER;
+       mlx5e_sw_node_delete(priv, child);
+
+err_destroy_hw_node:
+       tmp_err = mlx5_qos_destroy_node(priv->mdev, new_hw_id);
+       if (tmp_err) /* Not fatal. */
+               qos_warn(priv->mdev, "Failed to roll back creation of an inner node %u (class %04x), err = %d\n",
+                        new_hw_id, classid, tmp_err);
+       return err;
+}
+
+static struct mlx5e_qos_node *mlx5e_sw_node_find_by_qid(struct mlx5e_priv *priv, u16 qid)
+{
+       struct mlx5e_qos_node *node = NULL;
+       int bkt;
+
+       hash_for_each(priv->htb.qos_tc2node, bkt, node, hnode)
+               if (node->qid == qid)
+                       break;
+
+       return node;
+}
+
+static void mlx5e_reactivate_qos_sq(struct mlx5e_priv *priv, u16 qid, struct netdev_queue *txq)
+{
+       qos_dbg(priv->mdev, "Reactivate QoS SQ qid %u\n", qid);
+       netdev_tx_reset_queue(txq);
+       netif_tx_start_queue(txq);
+}
+
+static void mlx5e_reset_qdisc(struct net_device *dev, u16 qid)
+{
+       struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, qid);
+       struct Qdisc *qdisc = dev_queue->qdisc_sleeping;
+
+       if (!qdisc)
+               return;
+
+       spin_lock_bh(qdisc_lock(qdisc));
+       qdisc_reset(qdisc);
+       spin_unlock_bh(qdisc_lock(qdisc));
+}
+
+int mlx5e_htb_leaf_del(struct mlx5e_priv *priv, u16 classid, u16 *old_qid,
+                      u16 *new_qid, struct netlink_ext_ack *extack)
+{
+       struct mlx5e_qos_node *node;
+       struct netdev_queue *txq;
+       u16 qid, moved_qid;
+       bool opened;
+       int err;
+
+       qos_dbg(priv->mdev, "TC_HTB_LEAF_DEL classid %04x\n", classid);
+
+       *old_qid = *new_qid = 0;
+
+       node = mlx5e_sw_node_find(priv, classid);
+       if (!node)
+               return -ENOENT;
+
+       /* Store qid for reuse. */
+       qid = node->qid;
+
+       opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
+       if (opened) {
+               txq = netdev_get_tx_queue(priv->netdev,
+                                         mlx5e_qid_from_qos(&priv->channels, qid));
+               mlx5e_deactivate_qos_sq(priv, qid);
+               mlx5e_close_qos_sq(priv, qid);
+       }
+
+       err = mlx5_qos_destroy_node(priv->mdev, node->hw_id);
+       if (err) /* Not fatal. */
+               qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n",
+                        node->hw_id, classid, err);
+
+       mlx5e_sw_node_delete(priv, node);
+
+       moved_qid = mlx5e_qos_cur_leaf_nodes(priv);
+
+       if (moved_qid == 0) {
+               /* The last QoS SQ was just destroyed. */
+               if (opened)
+                       mlx5e_reactivate_qos_sq(priv, qid, txq);
+               return 0;
+       }
+       moved_qid--;
+
+       if (moved_qid < qid) {
+               /* The highest QoS SQ was just destroyed. */
+               WARN(moved_qid != qid - 1, "Gaps in queue numeration: destroyed queue %u, the highest queue is %u",
+                    qid, moved_qid);
+               if (opened)
+                       mlx5e_reactivate_qos_sq(priv, qid, txq);
+               return 0;
+       }
+
+       WARN(moved_qid == qid, "Can't move node with qid %u to itself", qid);
+       qos_dbg(priv->mdev, "Moving QoS SQ %u to %u\n", moved_qid, qid);
+
+       node = mlx5e_sw_node_find_by_qid(priv, moved_qid);
+       WARN(!node, "Could not find a node with qid %u to move to queue %u",
+            moved_qid, qid);
+
+       /* Stop traffic to the old queue. */
+       WRITE_ONCE(node->qid, MLX5E_QOS_QID_INNER);
+       __clear_bit(moved_qid, priv->htb.qos_used_qids);
+
+       if (opened) {
+               txq = netdev_get_tx_queue(priv->netdev,
+                                         mlx5e_qid_from_qos(&priv->channels, moved_qid));
+               mlx5e_deactivate_qos_sq(priv, moved_qid);
+               mlx5e_close_qos_sq(priv, moved_qid);
+       }
+
+       /* Prevent packets from the old class from getting into the new one. */
+       mlx5e_reset_qdisc(priv->netdev, moved_qid);
+
+       __set_bit(qid, priv->htb.qos_used_qids);
+       WRITE_ONCE(node->qid, qid);
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               err = mlx5e_open_qos_sq(priv, &priv->channels, node);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ.");
+                       qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x) while moving qid %u to %u, err = %d\n",
+                                node->classid, moved_qid, qid, err);
+               } else {
+                       mlx5e_activate_qos_sq(priv, node);
+               }
+       }
+
+       mlx5e_update_tx_netdev_queues(priv);
+       if (opened)
+               mlx5e_reactivate_qos_sq(priv, moved_qid, txq);
+
+       *old_qid = mlx5e_qid_from_qos(&priv->channels, moved_qid);
+       *new_qid = mlx5e_qid_from_qos(&priv->channels, qid);
+       return 0;
+}
+
+int mlx5e_htb_leaf_del_last(struct mlx5e_priv *priv, u16 classid, bool force,
+                           struct netlink_ext_ack *extack)
+{
+       struct mlx5e_qos_node *node, *parent;
+       u32 old_hw_id, new_hw_id;
+       int err, saved_err = 0;
+       u16 qid;
+
+       qos_dbg(priv->mdev, "TC_HTB_LEAF_DEL_LAST%s classid %04x\n",
+               force ? "_FORCE" : "", classid);
+
+       node = mlx5e_sw_node_find(priv, classid);
+       if (!node)
+               return -ENOENT;
+
+       err = mlx5_qos_create_leaf_node(priv->mdev, node->parent->parent->hw_id,
+                                       node->parent->bw_share,
+                                       node->parent->max_average_bw,
+                                       &new_hw_id);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Firmware error when creating a leaf node.");
+               qos_err(priv->mdev, "Failed to create a leaf node (class %04x), err = %d\n",
+                       classid, err);
+               if (!force)
+                       return err;
+               saved_err = err;
+       }
+
+       /* Store qid for reuse and prevent clearing the bit. */
+       qid = node->qid;
+       /* Pairs with mlx5e_get_txq_by_classid. */
+       WRITE_ONCE(node->qid, MLX5E_QOS_QID_INNER);
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               mlx5e_deactivate_qos_sq(priv, qid);
+               mlx5e_close_qos_sq(priv, qid);
+       }
+
+       /* Prevent packets from the old class from getting into the new one. */
+       mlx5e_reset_qdisc(priv->netdev, qid);
+
+       err = mlx5_qos_destroy_node(priv->mdev, node->hw_id);
+       if (err) /* Not fatal. */
+               qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n",
+                        node->hw_id, classid, err);
+
+       parent = node->parent;
+       mlx5e_sw_node_delete(priv, node);
+
+       node = parent;
+       WRITE_ONCE(node->qid, qid);
+
+       /* Early return on error in force mode. Parent will still be an inner
+        * node to be deleted by a following delete operation.
+        */
+       if (saved_err)
+               return saved_err;
+
+       old_hw_id = node->hw_id;
+       node->hw_id = new_hw_id;
+
+       if (test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               err = mlx5e_open_qos_sq(priv, &priv->channels, node);
+               if (err) {
+                       NL_SET_ERR_MSG_MOD(extack, "Error creating an SQ.");
+                       qos_warn(priv->mdev, "Failed to create a QoS SQ (class %04x), err = %d\n",
+                                classid, err);
+               } else {
+                       mlx5e_activate_qos_sq(priv, node);
+               }
+       }
+
+       err = mlx5_qos_destroy_node(priv->mdev, old_hw_id);
+       if (err) /* Not fatal. */
+               qos_warn(priv->mdev, "Failed to destroy leaf node %u (class %04x), err = %d\n",
+                        node->hw_id, classid, err);
+
+       return 0;
+}
+
+static int mlx5e_qos_update_children(struct mlx5e_priv *priv, struct mlx5e_qos_node *node,
+                                    struct netlink_ext_ack *extack)
+{
+       struct mlx5e_qos_node *child;
+       int err = 0;
+       int bkt;
+
+       hash_for_each(priv->htb.qos_tc2node, bkt, child, hnode) {
+               u32 old_bw_share = child->bw_share;
+               int err_one;
+
+               if (child->parent != node)
+                       continue;
+
+               mlx5e_htb_convert_rate(priv, child->rate, node, &child->bw_share);
+               if (child->bw_share == old_bw_share)
+                       continue;
+
+               err_one = mlx5_qos_update_node(priv->mdev, child->hw_id, child->bw_share,
+                                              child->max_average_bw, child->hw_id);
+               if (!err && err_one) {
+                       err = err_one;
+
+                       NL_SET_ERR_MSG_MOD(extack, "Firmware error when modifying a child node.");
+                       qos_err(priv->mdev, "Failed to modify a child node (class %04x), err = %d\n",
+                               node->classid, err);
+               }
+       }
+
+       return err;
+}
+
+int mlx5e_htb_node_modify(struct mlx5e_priv *priv, u16 classid, u64 rate, u64 ceil,
+                         struct netlink_ext_ack *extack)
+{
+       u32 bw_share, max_average_bw;
+       struct mlx5e_qos_node *node;
+       bool ceil_changed = false;
+       int err;
+
+       qos_dbg(priv->mdev, "TC_HTB_LEAF_MODIFY classid %04x, rate %llu, ceil %llu\n",
+               classid, rate, ceil);
+
+       node = mlx5e_sw_node_find(priv, classid);
+       if (!node)
+               return -ENOENT;
+
+       node->rate = rate;
+       mlx5e_htb_convert_rate(priv, rate, node->parent, &bw_share);
+       mlx5e_htb_convert_ceil(priv, ceil, &max_average_bw);
+
+       err = mlx5_qos_update_node(priv->mdev, node->parent->hw_id, bw_share,
+                                  max_average_bw, node->hw_id);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Firmware error when modifying a node.");
+               qos_err(priv->mdev, "Failed to modify a node (class %04x), err = %d\n",
+                       classid, err);
+               return err;
+       }
+
+       if (max_average_bw != node->max_average_bw)
+               ceil_changed = true;
+
+       node->bw_share = bw_share;
+       node->max_average_bw = max_average_bw;
+
+       if (ceil_changed)
+               err = mlx5e_qos_update_children(priv, node, extack);
+
+       return err;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h b/drivers/net/ethernet/mellanox/mlx5/core/en/qos.h
new file mode 100644 (file)
index 0000000..5af7991
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */
+
+#ifndef __MLX5E_EN_QOS_H
+#define __MLX5E_EN_QOS_H
+
+#include <linux/mlx5/driver.h>
+
+#define MLX5E_QOS_MAX_LEAF_NODES 256
+
+struct mlx5e_priv;
+struct mlx5e_channels;
+struct mlx5e_channel;
+
+int mlx5e_qos_max_leaf_nodes(struct mlx5_core_dev *mdev);
+int mlx5e_qos_cur_leaf_nodes(struct mlx5e_priv *priv);
+
+/* TX datapath API */
+int mlx5e_get_txq_by_classid(struct mlx5e_priv *priv, u16 classid);
+struct mlx5e_txqsq *mlx5e_get_sq(struct mlx5e_priv *priv, int qid);
+
+/* SQ lifecycle */
+int mlx5e_qos_open_queues(struct mlx5e_priv *priv, struct mlx5e_channels *chs);
+void mlx5e_qos_activate_queues(struct mlx5e_priv *priv);
+void mlx5e_qos_deactivate_queues(struct mlx5e_channel *c);
+void mlx5e_qos_close_queues(struct mlx5e_channel *c);
+
+/* HTB API */
+int mlx5e_htb_root_add(struct mlx5e_priv *priv, u16 htb_maj_id, u16 htb_defcls,
+                      struct netlink_ext_ack *extack);
+int mlx5e_htb_root_del(struct mlx5e_priv *priv);
+int mlx5e_htb_leaf_alloc_queue(struct mlx5e_priv *priv, u16 classid,
+                              u32 parent_classid, u64 rate, u64 ceil,
+                              struct netlink_ext_ack *extack);
+int mlx5e_htb_leaf_to_inner(struct mlx5e_priv *priv, u16 classid, u16 child_classid,
+                           u64 rate, u64 ceil, struct netlink_ext_ack *extack);
+int mlx5e_htb_leaf_del(struct mlx5e_priv *priv, u16 classid, u16 *old_qid,
+                      u16 *new_qid, struct netlink_ext_ack *extack);
+int mlx5e_htb_leaf_del_last(struct mlx5e_priv *priv, u16 classid, bool force,
+                           struct netlink_ext_ack *extack);
+int mlx5e_htb_node_modify(struct mlx5e_priv *priv, u16 classid, u64 rate, u64 ceil,
+                         struct netlink_ext_ack *extack);
+
+#endif
index d29af7b..76177f7 100644 (file)
@@ -626,6 +626,11 @@ bool mlx5e_rep_tc_update_skb(struct mlx5_cqe64 *cqe,
        if (!reg_c0)
                return true;
 
+       /* If reg_c0 is not equal to the default flow tag then skb->mark
+        * is not supported and must be reset back to 0.
+        */
+       skb->mark = 0;
+
        priv = netdev_priv(skb->dev);
        esw = priv->mdev->priv.eswitch;
 
index e521254..e417904 100644 (file)
@@ -27,6 +27,7 @@
 #define MLX5_CT_STATE_ESTABLISHED_BIT BIT(1)
 #define MLX5_CT_STATE_TRK_BIT BIT(2)
 #define MLX5_CT_STATE_NAT_BIT BIT(3)
+#define MLX5_CT_STATE_REPLY_BIT BIT(4)
 
 #define MLX5_FTE_ID_BITS (mlx5e_tc_attr_to_reg_mappings[FTEID_TO_REG].mlen * 8)
 #define MLX5_FTE_ID_MAX GENMASK(MLX5_FTE_ID_BITS - 1, 0)
@@ -118,16 +119,17 @@ struct mlx5_ct_tuple {
        u16 zone;
 };
 
-struct mlx5_ct_shared_counter {
+struct mlx5_ct_counter {
        struct mlx5_fc *counter;
        refcount_t refcount;
+       bool is_shared;
 };
 
 struct mlx5_ct_entry {
        struct rhash_head node;
        struct rhash_head tuple_node;
        struct rhash_head tuple_nat_node;
-       struct mlx5_ct_shared_counter *shared_counter;
+       struct mlx5_ct_counter *counter;
        unsigned long cookie;
        unsigned long restore_cookie;
        struct mlx5_ct_tuple tuple;
@@ -166,6 +168,12 @@ static const struct rhashtable_params tuples_nat_ht_params = {
        .min_size = 16 * 1024,
 };
 
+static bool
+mlx5_tc_ct_entry_has_nat(struct mlx5_ct_entry *entry)
+{
+       return !!(entry->tuple_nat_node.next);
+}
+
 static int
 mlx5_tc_ct_rule_to_tuple(struct mlx5_ct_tuple *tuple, struct flow_rule *rule)
 {
@@ -394,13 +402,14 @@ mlx5_tc_ct_set_tuple_match(struct mlx5e_priv *priv, struct mlx5_flow_spec *spec,
 }
 
 static void
-mlx5_tc_ct_shared_counter_put(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_entry *entry)
+mlx5_tc_ct_counter_put(struct mlx5_tc_ct_priv *ct_priv, struct mlx5_ct_entry *entry)
 {
-       if (!refcount_dec_and_test(&entry->shared_counter->refcount))
+       if (entry->counter->is_shared &&
+           !refcount_dec_and_test(&entry->counter->refcount))
                return;
 
-       mlx5_fc_destroy(ct_priv->dev, entry->shared_counter->counter);
-       kfree(entry->shared_counter);
+       mlx5_fc_destroy(ct_priv->dev, entry->counter->counter);
+       kfree(entry->counter);
 }
 
 static void
@@ -633,6 +642,7 @@ mlx5_tc_ct_entry_create_mod_hdr(struct mlx5_tc_ct_priv *ct_priv,
        }
 
        ct_state |= MLX5_CT_STATE_ESTABLISHED_BIT | MLX5_CT_STATE_TRK_BIT;
+       ct_state |= meta->ct_metadata.orig_dir ? 0 : MLX5_CT_STATE_REPLY_BIT;
        err = mlx5_tc_ct_entry_set_registers(ct_priv, &mod_acts,
                                             ct_state,
                                             meta->ct_metadata.mark,
@@ -699,13 +709,11 @@ mlx5_tc_ct_entry_add_rule(struct mlx5_tc_ct_priv *ct_priv,
        attr->dest_ft = ct_priv->post_ct;
        attr->ft = nat ? ct_priv->ct_nat : ct_priv->ct;
        attr->outer_match_level = MLX5_MATCH_L4;
-       attr->counter = entry->shared_counter->counter;
+       attr->counter = entry->counter->counter;
        attr->flags |= MLX5_ESW_ATTR_FLAG_NO_IN_PORT;
 
        mlx5_tc_ct_set_tuple_match(netdev_priv(ct_priv->netdev), spec, flow_rule);
-       mlx5e_tc_match_to_reg_match(spec, ZONE_TO_REG,
-                                   entry->tuple.zone & MLX5_CT_ZONE_MASK,
-                                   MLX5_CT_ZONE_MASK);
+       mlx5e_tc_match_to_reg_match(spec, ZONE_TO_REG, entry->tuple.zone, MLX5_CT_ZONE_MASK);
 
        zone_rule->rule = mlx5_tc_rule_insert(priv, spec, attr);
        if (IS_ERR(zone_rule->rule)) {
@@ -732,13 +740,34 @@ err_attr:
        return err;
 }
 
-static struct mlx5_ct_shared_counter *
+static struct mlx5_ct_counter *
+mlx5_tc_ct_counter_create(struct mlx5_tc_ct_priv *ct_priv)
+{
+       struct mlx5_ct_counter *counter;
+       int ret;
+
+       counter = kzalloc(sizeof(*counter), GFP_KERNEL);
+       if (!counter)
+               return ERR_PTR(-ENOMEM);
+
+       counter->is_shared = false;
+       counter->counter = mlx5_fc_create(ct_priv->dev, true);
+       if (IS_ERR(counter->counter)) {
+               ct_dbg("Failed to create counter for ct entry");
+               ret = PTR_ERR(counter->counter);
+               kfree(counter);
+               return ERR_PTR(ret);
+       }
+
+       return counter;
+}
+
+static struct mlx5_ct_counter *
 mlx5_tc_ct_shared_counter_get(struct mlx5_tc_ct_priv *ct_priv,
                              struct mlx5_ct_entry *entry)
 {
        struct mlx5_ct_tuple rev_tuple = entry->tuple;
-       struct mlx5_ct_shared_counter *shared_counter;
-       struct mlx5_core_dev *dev = ct_priv->dev;
+       struct mlx5_ct_counter *shared_counter;
        struct mlx5_ct_entry *rev_entry;
        __be16 tmp_port;
        int ret;
@@ -767,25 +796,20 @@ mlx5_tc_ct_shared_counter_get(struct mlx5_tc_ct_priv *ct_priv,
        rev_entry = rhashtable_lookup_fast(&ct_priv->ct_tuples_ht, &rev_tuple,
                                           tuples_ht_params);
        if (rev_entry) {
-               if (refcount_inc_not_zero(&rev_entry->shared_counter->refcount)) {
+               if (refcount_inc_not_zero(&rev_entry->counter->refcount)) {
                        mutex_unlock(&ct_priv->shared_counter_lock);
-                       return rev_entry->shared_counter;
+                       return rev_entry->counter;
                }
        }
        mutex_unlock(&ct_priv->shared_counter_lock);
 
-       shared_counter = kzalloc(sizeof(*shared_counter), GFP_KERNEL);
-       if (!shared_counter)
-               return ERR_PTR(-ENOMEM);
-
-       shared_counter->counter = mlx5_fc_create(dev, true);
-       if (IS_ERR(shared_counter->counter)) {
-               ct_dbg("Failed to create counter for ct entry");
-               ret = PTR_ERR(shared_counter->counter);
-               kfree(shared_counter);
+       shared_counter = mlx5_tc_ct_counter_create(ct_priv);
+       if (IS_ERR(shared_counter)) {
+               ret = PTR_ERR(shared_counter);
                return ERR_PTR(ret);
        }
 
+       shared_counter->is_shared = true;
        refcount_set(&shared_counter->refcount, 1);
        return shared_counter;
 }
@@ -798,10 +822,13 @@ mlx5_tc_ct_entry_add_rules(struct mlx5_tc_ct_priv *ct_priv,
 {
        int err;
 
-       entry->shared_counter = mlx5_tc_ct_shared_counter_get(ct_priv, entry);
-       if (IS_ERR(entry->shared_counter)) {
-               err = PTR_ERR(entry->shared_counter);
-               ct_dbg("Failed to create counter for ct entry");
+       if (nf_ct_acct_enabled(dev_net(ct_priv->netdev)))
+               entry->counter = mlx5_tc_ct_counter_create(ct_priv);
+       else
+               entry->counter = mlx5_tc_ct_shared_counter_get(ct_priv, entry);
+
+       if (IS_ERR(entry->counter)) {
+               err = PTR_ERR(entry->counter);
                return err;
        }
 
@@ -820,7 +847,7 @@ mlx5_tc_ct_entry_add_rules(struct mlx5_tc_ct_priv *ct_priv,
 err_nat:
        mlx5_tc_ct_entry_del_rule(ct_priv, entry, false);
 err_orig:
-       mlx5_tc_ct_shared_counter_put(ct_priv, entry);
+       mlx5_tc_ct_counter_put(ct_priv, entry);
        return err;
 }
 
@@ -890,13 +917,13 @@ mlx5_tc_ct_block_flow_offload_add(struct mlx5_ct_ft *ft,
 err_insert:
        mlx5_tc_ct_entry_del_rules(ct_priv, entry);
 err_rules:
-       rhashtable_remove_fast(&ct_priv->ct_tuples_nat_ht,
-                              &entry->tuple_nat_node, tuples_nat_ht_params);
+       if (mlx5_tc_ct_entry_has_nat(entry))
+               rhashtable_remove_fast(&ct_priv->ct_tuples_nat_ht,
+                                      &entry->tuple_nat_node, tuples_nat_ht_params);
 err_tuple_nat:
-       if (entry->tuple_node.next)
-               rhashtable_remove_fast(&ct_priv->ct_tuples_ht,
-                                      &entry->tuple_node,
-                                      tuples_ht_params);
+       rhashtable_remove_fast(&ct_priv->ct_tuples_ht,
+                              &entry->tuple_node,
+                              tuples_ht_params);
 err_tuple:
 err_set:
        kfree(entry);
@@ -911,14 +938,14 @@ mlx5_tc_ct_del_ft_entry(struct mlx5_tc_ct_priv *ct_priv,
 {
        mlx5_tc_ct_entry_del_rules(ct_priv, entry);
        mutex_lock(&ct_priv->shared_counter_lock);
-       if (entry->tuple_node.next)
+       if (mlx5_tc_ct_entry_has_nat(entry))
                rhashtable_remove_fast(&ct_priv->ct_tuples_nat_ht,
                                       &entry->tuple_nat_node,
                                       tuples_nat_ht_params);
        rhashtable_remove_fast(&ct_priv->ct_tuples_ht, &entry->tuple_node,
                               tuples_ht_params);
        mutex_unlock(&ct_priv->shared_counter_lock);
-       mlx5_tc_ct_shared_counter_put(ct_priv, entry);
+       mlx5_tc_ct_counter_put(ct_priv, entry);
 
 }
 
@@ -956,7 +983,7 @@ mlx5_tc_ct_block_flow_offload_stats(struct mlx5_ct_ft *ft,
        if (!entry)
                return -ENOENT;
 
-       mlx5_fc_query_cached(entry->shared_counter->counter, &bytes, &packets, &lastuse);
+       mlx5_fc_query_cached(entry->counter->counter, &bytes, &packets, &lastuse);
        flow_stats_update(&f->stats, bytes, packets, 0, lastuse,
                          FLOW_ACTION_HW_STATS_DELAYED);
 
@@ -1061,8 +1088,8 @@ mlx5_tc_ct_match_add(struct mlx5_tc_ct_priv *priv,
                     struct netlink_ext_ack *extack)
 {
        struct flow_rule *rule = flow_cls_offload_flow_rule(f);
+       bool trk, est, untrk, unest, new, rpl, unrpl;
        struct flow_dissector_key_ct *mask, *key;
-       bool trk, est, untrk, unest, new;
        u32 ctstate = 0, ctstate_mask = 0;
        u16 ct_state_on, ct_state_off;
        u16 ct_state, ct_state_mask;
@@ -1088,9 +1115,10 @@ mlx5_tc_ct_match_add(struct mlx5_tc_ct_priv *priv,
 
        if (ct_state_mask & ~(TCA_FLOWER_KEY_CT_FLAGS_TRACKED |
                              TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED |
-                             TCA_FLOWER_KEY_CT_FLAGS_NEW)) {
+                             TCA_FLOWER_KEY_CT_FLAGS_NEW |
+                             TCA_FLOWER_KEY_CT_FLAGS_REPLY)) {
                NL_SET_ERR_MSG_MOD(extack,
-                                  "only ct_state trk, est and new are supported for offload");
+                                  "only ct_state trk, est, new and rpl are supported for offload");
                return -EOPNOTSUPP;
        }
 
@@ -1099,13 +1127,17 @@ mlx5_tc_ct_match_add(struct mlx5_tc_ct_priv *priv,
        trk = ct_state_on & TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
        new = ct_state_on & TCA_FLOWER_KEY_CT_FLAGS_NEW;
        est = ct_state_on & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED;
+       rpl = ct_state_on & TCA_FLOWER_KEY_CT_FLAGS_REPLY;
        untrk = ct_state_off & TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
        unest = ct_state_off & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED;
+       unrpl = ct_state_off & TCA_FLOWER_KEY_CT_FLAGS_REPLY;
 
        ctstate |= trk ? MLX5_CT_STATE_TRK_BIT : 0;
        ctstate |= est ? MLX5_CT_STATE_ESTABLISHED_BIT : 0;
+       ctstate |= rpl ? MLX5_CT_STATE_REPLY_BIT : 0;
        ctstate_mask |= (untrk || trk) ? MLX5_CT_STATE_TRK_BIT : 0;
        ctstate_mask |= (unest || est) ? MLX5_CT_STATE_ESTABLISHED_BIT : 0;
+       ctstate_mask |= (unrpl || rpl) ? MLX5_CT_STATE_REPLY_BIT : 0;
 
        if (new) {
                NL_SET_ERR_MSG_MOD(extack,
@@ -1220,9 +1252,8 @@ static int tc_ct_pre_ct_add_rules(struct mlx5_ct_ft *ct_ft,
        pre_ct->flow_rule = rule;
 
        /* add miss rule */
-       memset(spec, 0, sizeof(*spec));
        dest.ft = nat ? ct_priv->ct_nat : ct_priv->ct;
-       rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1);
+       rule = mlx5_add_flow_rules(ft, NULL, &flow_act, &dest, 1);
        if (IS_ERR(rule)) {
                err = PTR_ERR(rule);
                ct_dbg("Failed to add pre ct miss rule zone %d", zone);
index 1f95262..3479672 100644 (file)
@@ -81,8 +81,8 @@ static int parse_tunnel(struct mlx5e_priv *priv,
        if (!enc_keyid.mask->keyid)
                return 0;
 
-       if (!(MLX5_CAP_GEN(priv->mdev, flex_parser_protocols) &
-             MLX5_FLEX_PROTO_CW_MPLS_UDP))
+       if (!MLX5_CAP_ETH(priv->mdev, tunnel_stateless_mpls_over_udp) &&
+           !(MLX5_CAP_GEN(priv->mdev, flex_parser_protocols) & MLX5_FLEX_PROTO_CW_MPLS_UDP))
                return -EOPNOTSUPP;
 
        flow_rule_match_mpls(rule, &match);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c
new file mode 100644 (file)
index 0000000..37fc1d7
--- /dev/null
@@ -0,0 +1,457 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies */
+
+#include <net/page_pool.h>
+#include "en/txrx.h"
+#include "en/params.h"
+#include "en/trap.h"
+
+static int mlx5e_trap_napi_poll(struct napi_struct *napi, int budget)
+{
+       struct mlx5e_trap *trap_ctx = container_of(napi, struct mlx5e_trap, napi);
+       struct mlx5e_ch_stats *ch_stats = trap_ctx->stats;
+       struct mlx5e_rq *rq = &trap_ctx->rq;
+       bool busy = false;
+       int work_done = 0;
+
+       ch_stats->poll++;
+
+       work_done = mlx5e_poll_rx_cq(&rq->cq, budget);
+       busy |= work_done == budget;
+       busy |= rq->post_wqes(rq);
+
+       if (busy)
+               return budget;
+
+       if (unlikely(!napi_complete_done(napi, work_done)))
+               return work_done;
+
+       mlx5e_cq_arm(&rq->cq);
+       return work_done;
+}
+
+static int mlx5e_alloc_trap_rq(struct mlx5e_priv *priv, struct mlx5e_rq_param *rqp,
+                              struct mlx5e_rq_stats *stats, struct mlx5e_params *params,
+                              struct mlx5e_ch_stats *ch_stats,
+                              struct mlx5e_rq *rq)
+{
+       void *rqc_wq = MLX5_ADDR_OF(rqc, rqp->rqc, wq);
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct page_pool_params pp_params = {};
+       int node = dev_to_node(mdev->device);
+       u32 pool_size;
+       int wq_sz;
+       int err;
+       int i;
+
+       rqp->wq.db_numa_node = node;
+
+       rq->wq_type  = params->rq_wq_type;
+       rq->pdev     = mdev->device;
+       rq->netdev   = priv->netdev;
+       rq->mdev     = mdev;
+       rq->priv     = priv;
+       rq->stats    = stats;
+       rq->clock    = &mdev->clock;
+       rq->tstamp   = &priv->tstamp;
+       rq->hw_mtu   = MLX5E_SW2HW_MTU(params, params->sw_mtu);
+
+       xdp_rxq_info_unused(&rq->xdp_rxq);
+
+       rq->buff.map_dir = DMA_FROM_DEVICE;
+       rq->buff.headroom = mlx5e_get_rq_headroom(mdev, params, NULL);
+       pool_size = 1 << params->log_rq_mtu_frames;
+
+       err = mlx5_wq_cyc_create(mdev, &rqp->wq, rqc_wq, &rq->wqe.wq, &rq->wq_ctrl);
+       if (err)
+               return err;
+
+       rq->wqe.wq.db = &rq->wqe.wq.db[MLX5_RCV_DBR];
+
+       wq_sz = mlx5_wq_cyc_get_size(&rq->wqe.wq);
+
+       rq->wqe.info = rqp->frags_info;
+       rq->buff.frame0_sz = rq->wqe.info.arr[0].frag_stride;
+       rq->wqe.frags = kvzalloc_node(array_size(sizeof(*rq->wqe.frags),
+                                                (wq_sz << rq->wqe.info.log_num_frags)),
+                                     GFP_KERNEL, node);
+       if (!rq->wqe.frags) {
+               err = -ENOMEM;
+               goto err_wq_cyc_destroy;
+       }
+
+       err = mlx5e_init_di_list(rq, wq_sz, node);
+       if (err)
+               goto err_free_frags;
+
+       rq->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.mkey.key);
+
+       mlx5e_rq_set_trap_handlers(rq, params);
+
+       /* Create a page_pool and register it with rxq */
+       pp_params.order     = 0;
+       pp_params.flags     = 0; /* No-internal DMA mapping in page_pool */
+       pp_params.pool_size = pool_size;
+       pp_params.nid       = node;
+       pp_params.dev       = mdev->device;
+       pp_params.dma_dir   = rq->buff.map_dir;
+
+       /* page_pool can be used even when there is no rq->xdp_prog,
+        * given page_pool does not handle DMA mapping there is no
+        * required state to clear. And page_pool gracefully handle
+        * elevated refcnt.
+        */
+       rq->page_pool = page_pool_create(&pp_params);
+       if (IS_ERR(rq->page_pool)) {
+               err = PTR_ERR(rq->page_pool);
+               rq->page_pool = NULL;
+               goto err_free_di_list;
+       }
+       for (i = 0; i < wq_sz; i++) {
+               struct mlx5e_rx_wqe_cyc *wqe =
+                       mlx5_wq_cyc_get_wqe(&rq->wqe.wq, i);
+               int f;
+
+               for (f = 0; f < rq->wqe.info.num_frags; f++) {
+                       u32 frag_size = rq->wqe.info.arr[f].frag_size |
+                               MLX5_HW_START_PADDING;
+
+                       wqe->data[f].byte_count = cpu_to_be32(frag_size);
+                       wqe->data[f].lkey = rq->mkey_be;
+               }
+               /* check if num_frags is not a pow of two */
+               if (rq->wqe.info.num_frags < (1 << rq->wqe.info.log_num_frags)) {
+                       wqe->data[f].byte_count = 0;
+                       wqe->data[f].lkey = cpu_to_be32(MLX5_INVALID_LKEY);
+                       wqe->data[f].addr = 0;
+               }
+       }
+       return 0;
+
+err_free_di_list:
+       mlx5e_free_di_list(rq);
+err_free_frags:
+       kvfree(rq->wqe.frags);
+err_wq_cyc_destroy:
+       mlx5_wq_destroy(&rq->wq_ctrl);
+
+       return err;
+}
+
+static void mlx5e_free_trap_rq(struct mlx5e_rq *rq)
+{
+       page_pool_destroy(rq->page_pool);
+       mlx5e_free_di_list(rq);
+       kvfree(rq->wqe.frags);
+       mlx5_wq_destroy(&rq->wq_ctrl);
+}
+
+static int mlx5e_open_trap_rq(struct mlx5e_priv *priv, struct napi_struct *napi,
+                             struct mlx5e_rq_stats *stats, struct mlx5e_params *params,
+                             struct mlx5e_rq_param *rq_param,
+                             struct mlx5e_ch_stats *ch_stats,
+                             struct mlx5e_rq *rq)
+{
+       struct mlx5_core_dev *mdev = priv->mdev;
+       struct mlx5e_create_cq_param ccp = {};
+       struct dim_cq_moder trap_moder = {};
+       struct mlx5e_cq *cq = &rq->cq;
+       int err;
+
+       ccp.node     = dev_to_node(mdev->device);
+       ccp.ch_stats = ch_stats;
+       ccp.napi     = napi;
+       ccp.ix       = 0;
+       err = mlx5e_open_cq(priv, trap_moder, &rq_param->cqp, &ccp, cq);
+       if (err)
+               return err;
+
+       err = mlx5e_alloc_trap_rq(priv, rq_param, stats, params, ch_stats, rq);
+       if (err)
+               goto err_destroy_cq;
+
+       err = mlx5e_create_rq(rq, rq_param);
+       if (err)
+               goto err_free_rq;
+
+       err = mlx5e_modify_rq_state(rq, MLX5_RQC_STATE_RST, MLX5_RQC_STATE_RDY);
+       if (err)
+               goto err_destroy_rq;
+
+       return 0;
+
+err_destroy_rq:
+       mlx5e_destroy_rq(rq);
+       mlx5e_free_rx_descs(rq);
+err_free_rq:
+       mlx5e_free_trap_rq(rq);
+err_destroy_cq:
+       mlx5e_close_cq(cq);
+
+       return err;
+}
+
+static void mlx5e_close_trap_rq(struct mlx5e_rq *rq)
+{
+       mlx5e_destroy_rq(rq);
+       mlx5e_free_rx_descs(rq);
+       mlx5e_free_trap_rq(rq);
+       mlx5e_close_cq(&rq->cq);
+}
+
+static int mlx5e_create_trap_direct_rq_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir,
+                                          u32 rqn)
+{
+       void *tirc;
+       int inlen;
+       u32 *in;
+       int err;
+
+       inlen = MLX5_ST_SZ_BYTES(create_tir_in);
+       in = kvzalloc(inlen, GFP_KERNEL);
+       if (!in)
+               return -ENOMEM;
+
+       tirc = MLX5_ADDR_OF(create_tir_in, in, ctx);
+       MLX5_SET(tirc, tirc, transport_domain, mdev->mlx5e_res.td.tdn);
+       MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_NONE);
+       MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_DIRECT);
+       MLX5_SET(tirc, tirc, inline_rqn, rqn);
+       err = mlx5e_create_tir(mdev, tir, in);
+       kvfree(in);
+
+       return err;
+}
+
+static void mlx5e_destroy_trap_direct_rq_tir(struct mlx5_core_dev *mdev, struct mlx5e_tir *tir)
+{
+       mlx5e_destroy_tir(mdev, tir);
+}
+
+static void mlx5e_activate_trap_rq(struct mlx5e_rq *rq)
+{
+       set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state);
+}
+
+static void mlx5e_deactivate_trap_rq(struct mlx5e_rq *rq)
+{
+       clear_bit(MLX5E_RQ_STATE_ENABLED, &rq->state);
+}
+
+static void mlx5e_build_trap_params(struct mlx5e_priv *priv, struct mlx5e_trap *t)
+{
+       struct mlx5e_params *params = &t->params;
+
+       params->rq_wq_type = MLX5_WQ_TYPE_CYCLIC;
+       mlx5e_init_rq_type_params(priv->mdev, params);
+       params->sw_mtu = priv->netdev->max_mtu;
+       mlx5e_build_rq_param(priv, params, NULL, &t->rq_param);
+}
+
+static struct mlx5e_trap *mlx5e_open_trap(struct mlx5e_priv *priv)
+{
+       int cpu = cpumask_first(mlx5_comp_irq_get_affinity_mask(priv->mdev, 0));
+       struct net_device *netdev = priv->netdev;
+       struct mlx5e_trap *t;
+       int err;
+
+       t = kvzalloc_node(sizeof(*t), GFP_KERNEL, cpu_to_node(cpu));
+       if (!t)
+               return ERR_PTR(-ENOMEM);
+
+       mlx5e_build_trap_params(priv, t);
+
+       t->priv     = priv;
+       t->mdev     = priv->mdev;
+       t->tstamp   = &priv->tstamp;
+       t->pdev     = mlx5_core_dma_dev(priv->mdev);
+       t->netdev   = priv->netdev;
+       t->mkey_be  = cpu_to_be32(priv->mdev->mlx5e_res.mkey.key);
+       t->stats    = &priv->trap_stats.ch;
+
+       netif_napi_add(netdev, &t->napi, mlx5e_trap_napi_poll, 64);
+
+       err = mlx5e_open_trap_rq(priv, &t->napi,
+                                &priv->trap_stats.rq,
+                                &t->params, &t->rq_param,
+                                &priv->trap_stats.ch,
+                                &t->rq);
+       if (unlikely(err))
+               goto err_napi_del;
+
+       err = mlx5e_create_trap_direct_rq_tir(t->mdev, &t->tir, t->rq.rqn);
+       if (err)
+               goto err_close_trap_rq;
+
+       return t;
+
+err_close_trap_rq:
+       mlx5e_close_trap_rq(&t->rq);
+err_napi_del:
+       netif_napi_del(&t->napi);
+       kvfree(t);
+       return ERR_PTR(err);
+}
+
+void mlx5e_close_trap(struct mlx5e_trap *trap)
+{
+       mlx5e_destroy_trap_direct_rq_tir(trap->mdev, &trap->tir);
+       mlx5e_close_trap_rq(&trap->rq);
+       netif_napi_del(&trap->napi);
+       kvfree(trap);
+}
+
+static void mlx5e_activate_trap(struct mlx5e_trap *trap)
+{
+       napi_enable(&trap->napi);
+       mlx5e_activate_trap_rq(&trap->rq);
+       napi_schedule(&trap->napi);
+}
+
+void mlx5e_deactivate_trap(struct mlx5e_priv *priv)
+{
+       struct mlx5e_trap *trap = priv->en_trap;
+
+       mlx5e_deactivate_trap_rq(&trap->rq);
+       napi_disable(&trap->napi);
+}
+
+static struct mlx5e_trap *mlx5e_add_trap_queue(struct mlx5e_priv *priv)
+{
+       struct mlx5e_trap *trap;
+
+       trap = mlx5e_open_trap(priv);
+       if (IS_ERR(trap))
+               goto out;
+
+       mlx5e_activate_trap(trap);
+out:
+       return trap;
+}
+
+static void mlx5e_del_trap_queue(struct mlx5e_priv *priv)
+{
+       mlx5e_deactivate_trap(priv);
+       mlx5e_close_trap(priv->en_trap);
+       priv->en_trap = NULL;
+}
+
+static int mlx5e_trap_get_tirn(struct mlx5e_trap *en_trap)
+{
+       return en_trap->tir.tirn;
+}
+
+static int mlx5e_handle_action_trap(struct mlx5e_priv *priv, int trap_id)
+{
+       bool open_queue = !priv->en_trap;
+       struct mlx5e_trap *trap;
+       int err;
+
+       if (open_queue) {
+               trap = mlx5e_add_trap_queue(priv);
+               if (IS_ERR(trap))
+                       return PTR_ERR(trap);
+               priv->en_trap = trap;
+       }
+
+       switch (trap_id) {
+       case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER:
+               err = mlx5e_add_vlan_trap(priv, trap_id, mlx5e_trap_get_tirn(priv->en_trap));
+               if (err)
+                       goto err_out;
+               break;
+       case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER:
+               err = mlx5e_add_mac_trap(priv, trap_id, mlx5e_trap_get_tirn(priv->en_trap));
+               if (err)
+                       goto err_out;
+               break;
+       default:
+               netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id);
+               err = -EINVAL;
+               goto err_out;
+       }
+       return 0;
+
+err_out:
+       if (open_queue)
+               mlx5e_del_trap_queue(priv);
+       return err;
+}
+
+static int mlx5e_handle_action_drop(struct mlx5e_priv *priv, int trap_id)
+{
+       switch (trap_id) {
+       case DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER:
+               mlx5e_remove_vlan_trap(priv);
+               break;
+       case DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER:
+               mlx5e_remove_mac_trap(priv);
+               break;
+       default:
+               netdev_warn(priv->netdev, "%s: Unknown trap id %d\n", __func__, trap_id);
+               return -EINVAL;
+       }
+       if (priv->en_trap && !mlx5_devlink_trap_get_num_active(priv->mdev))
+               mlx5e_del_trap_queue(priv);
+
+       return 0;
+}
+
+int mlx5e_handle_trap_event(struct mlx5e_priv *priv, struct mlx5_trap_ctx *trap_ctx)
+{
+       int err = 0;
+
+       /* Traps are unarmed when interface is down, no need to update
+        * them. The configuration is saved in the core driver,
+        * queried and applied upon interface up operation in
+        * mlx5e_open_locked().
+        */
+       if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
+               return 0;
+
+       switch (trap_ctx->action) {
+       case DEVLINK_TRAP_ACTION_TRAP:
+               err = mlx5e_handle_action_trap(priv, trap_ctx->id);
+               break;
+       case DEVLINK_TRAP_ACTION_DROP:
+               err = mlx5e_handle_action_drop(priv, trap_ctx->id);
+               break;
+       default:
+               netdev_warn(priv->netdev, "%s: Unsupported action %d\n", __func__,
+                           trap_ctx->action);
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static int mlx5e_apply_trap(struct mlx5e_priv *priv, int trap_id, bool enable)
+{
+       enum devlink_trap_action action;
+       int err;
+
+       err = mlx5_devlink_traps_get_action(priv->mdev, trap_id, &action);
+       if (err)
+               return err;
+       if (action == DEVLINK_TRAP_ACTION_TRAP)
+               err = enable ? mlx5e_handle_action_trap(priv, trap_id) :
+                              mlx5e_handle_action_drop(priv, trap_id);
+       return err;
+}
+
+static const int mlx5e_traps_arr[] = {
+       DEVLINK_TRAP_GENERIC_ID_INGRESS_VLAN_FILTER,
+       DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER,
+};
+
+int mlx5e_apply_traps(struct mlx5e_priv *priv, bool enable)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mlx5e_traps_arr); i++) {
+               err = mlx5e_apply_trap(priv, mlx5e_traps_arr[i], enable);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h
new file mode 100644 (file)
index 0000000..aa3f176
--- /dev/null
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020, Mellanox Technologies */
+
+#ifndef __MLX5E_TRAP_H__
+#define __MLX5E_TRAP_H__
+
+#include "../en.h"
+#include "../devlink.h"
+
+struct mlx5e_trap {
+       /* data path */
+       struct mlx5e_rq            rq;
+       struct mlx5e_tir           tir;
+       struct napi_struct         napi;
+       struct device             *pdev;
+       struct net_device         *netdev;
+       __be32                     mkey_be;
+
+       /* data path - accessed per napi poll */
+       struct mlx5e_ch_stats     *stats;
+
+       /* control */
+       struct mlx5e_priv         *priv;
+       struct mlx5_core_dev      *mdev;
+       struct hwtstamp_config    *tstamp;
+       DECLARE_BITMAP(state, MLX5E_CHANNEL_NUM_STATES);
+
+       struct mlx5e_params        params;
+       struct mlx5e_rq_param      rq_param;
+};
+
+void mlx5e_close_trap(struct mlx5e_trap *trap);
+void mlx5e_deactivate_trap(struct mlx5e_priv *priv);
+int mlx5e_handle_trap_event(struct mlx5e_priv *priv, struct mlx5_trap_ctx *trap_ctx);
+int mlx5e_apply_traps(struct mlx5e_priv *priv, bool enable);
+
+#endif
index 7943eb3..4880f21 100644 (file)
@@ -371,6 +371,15 @@ struct mlx5e_swp_spec {
        u8 tun_l4_proto;
 };
 
+static inline void mlx5e_eseg_swp_offsets_add_vlan(struct mlx5_wqe_eth_seg *eseg)
+{
+       /* SWP offsets are in 2-bytes words */
+       eseg->swp_outer_l3_offset += VLAN_HLEN / 2;
+       eseg->swp_outer_l4_offset += VLAN_HLEN / 2;
+       eseg->swp_inner_l3_offset += VLAN_HLEN / 2;
+       eseg->swp_inner_l4_offset += VLAN_HLEN / 2;
+}
+
 static inline void
 mlx5e_set_eseg_swp(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg,
                   struct mlx5e_swp_spec *swp_spec)
index 899b98a..6488098 100644 (file)
@@ -51,7 +51,7 @@ static inline bool mlx5_geneve_tx_allowed(struct mlx5_core_dev *mdev)
 }
 
 static inline void
-mlx5e_tx_tunnel_accel(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg)
+mlx5e_tx_tunnel_accel(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg, u16 ihs)
 {
        struct mlx5e_swp_spec swp_spec = {};
        unsigned int offset = 0;
@@ -85,6 +85,8 @@ mlx5e_tx_tunnel_accel(struct sk_buff *skb, struct mlx5_wqe_eth_seg *eseg)
        }
 
        mlx5e_set_eseg_swp(skb, eseg, &swp_spec);
+       if (skb_vlan_tag_present(skb) &&  ihs)
+               mlx5e_eseg_swp_offsets_add_vlan(eseg);
 }
 
 #else
@@ -142,9 +144,9 @@ static inline bool mlx5e_accel_tx_is_ipsec_flow(struct mlx5e_accel_tx_state *sta
 {
 #ifdef CONFIG_MLX5_EN_IPSEC
        return mlx5e_ipsec_is_tx_flow(&state->ipsec);
-#endif
-
+#else
        return false;
+#endif
 }
 
 static inline unsigned int mlx5e_accel_tx_ids_len(struct mlx5e_txqsq *sq,
@@ -163,7 +165,7 @@ static inline unsigned int mlx5e_accel_tx_ids_len(struct mlx5e_txqsq *sq,
 
 static inline bool mlx5e_accel_tx_eseg(struct mlx5e_priv *priv,
                                       struct sk_buff *skb,
-                                      struct mlx5_wqe_eth_seg *eseg)
+                                      struct mlx5_wqe_eth_seg *eseg, u16 ihs)
 {
 #ifdef CONFIG_MLX5_EN_IPSEC
        if (xfrm_offload(skb))
@@ -172,7 +174,7 @@ static inline bool mlx5e_accel_tx_eseg(struct mlx5e_priv *priv,
 
 #if IS_ENABLED(CONFIG_GENEVE)
        if (skb->encapsulation)
-               mlx5e_tx_tunnel_accel(skb, eseg);
+               mlx5e_tx_tunnel_accel(skb, eseg, ihs);
 #endif
 
        return true;
index a9b4560..a97e8d2 100644 (file)
@@ -497,20 +497,6 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev,
        }
 }
 
-bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
-                              netdev_features_t features)
-{
-       struct sec_path *sp = skb_sec_path(skb);
-       struct xfrm_state *x;
-
-       if (sp && sp->len) {
-               x = sp->xvec[0];
-               if (x && x->xso.offload_handle)
-                       return true;
-       }
-       return false;
-}
-
 void mlx5e_ipsec_build_inverse_table(void)
 {
        u16 mss_inv;
index 9df9b9a..3e80742 100644 (file)
@@ -57,8 +57,6 @@ struct sk_buff *mlx5e_ipsec_handle_rx_skb(struct net_device *netdev,
                                          struct sk_buff *skb, u32 *cqe_bcnt);
 
 void mlx5e_ipsec_inverse_table_init(void);
-bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
-                              netdev_features_t features);
 void mlx5e_ipsec_set_iv_esn(struct sk_buff *skb, struct xfrm_state *x,
                            struct xfrm_offload *xo);
 void mlx5e_ipsec_set_iv(struct sk_buff *skb, struct xfrm_state *x,
@@ -87,8 +85,28 @@ static inline bool mlx5e_ipsec_is_tx_flow(struct mlx5e_accel_tx_ipsec_state *ips
        return ipsec_st->x;
 }
 
+static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg)
+{
+       return eseg->flow_table_metadata & cpu_to_be32(MLX5_ETH_WQE_FT_META_IPSEC);
+}
+
 void mlx5e_ipsec_tx_build_eseg(struct mlx5e_priv *priv, struct sk_buff *skb,
                               struct mlx5_wqe_eth_seg *eseg);
+
+static inline bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
+                                            netdev_features_t features)
+{
+       struct sec_path *sp = skb_sec_path(skb);
+
+       if (sp && sp->len) {
+               struct xfrm_state *x = sp->xvec[0];
+
+               if (x && x->xso.offload_handle)
+                       return true;
+       }
+       return false;
+}
+
 #else
 static inline
 void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev,
@@ -96,7 +114,14 @@ void mlx5e_ipsec_offload_handle_rx_skb(struct net_device *netdev,
                                       struct mlx5_cqe64 *cqe)
 {}
 
+static inline bool mlx5e_ipsec_eseg_meta(struct mlx5_wqe_eth_seg *eseg)
+{
+       return false;
+}
+
 static inline bool mlx5_ipsec_is_rx_flow(struct mlx5_cqe64 *cqe) { return false; }
+static inline bool mlx5e_ipsec_feature_check(struct sk_buff *skb, struct net_device *netdev,
+                                            netdev_features_t features) { return false; }
 #endif /* CONFIG_MLX5_EN_IPSEC */
 
 #endif /* __MLX5E_IPSEC_RXTX_H__ */
index 6c5c54b..5cb9365 100644 (file)
@@ -76,7 +76,7 @@ static const struct counter_desc mlx5e_ipsec_sw_stats_desc[] = {
 
 static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec_sw)
 {
-       return NUM_IPSEC_SW_COUNTERS;
+       return priv->ipsec ? NUM_IPSEC_SW_COUNTERS : 0;
 }
 
 static inline MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ipsec_sw) {}
@@ -105,7 +105,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(ipsec_sw)
 
 static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ipsec_hw)
 {
-       return (mlx5_fpga_ipsec_device_caps(priv->mdev)) ? NUM_IPSEC_HW_COUNTERS : 0;
+       return (priv->ipsec && mlx5_fpga_ipsec_device_caps(priv->mdev)) ? NUM_IPSEC_HW_COUNTERS : 0;
 }
 
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(ipsec_hw)
index d20243d..f23c675 100644 (file)
@@ -1151,6 +1151,7 @@ static int mlx5e_set_trust_state(struct mlx5e_priv *priv, u8 trust_state)
 {
        struct mlx5e_channels new_channels = {};
        bool reset_channels = true;
+       bool opened;
        int err = 0;
 
        mutex_lock(&priv->state_lock);
@@ -1159,22 +1160,24 @@ static int mlx5e_set_trust_state(struct mlx5e_priv *priv, u8 trust_state)
        mlx5e_params_calc_trust_tx_min_inline_mode(priv->mdev, &new_channels.params,
                                                   trust_state);
 
-       if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
-               priv->channels.params = new_channels.params;
+       opened = test_bit(MLX5E_STATE_OPENED, &priv->state);
+       if (!opened)
                reset_channels = false;
-       }
 
        /* Skip if tx_min_inline is the same */
        if (new_channels.params.tx_min_inline_mode ==
            priv->channels.params.tx_min_inline_mode)
                reset_channels = false;
 
-       if (reset_channels)
+       if (reset_channels) {
                err = mlx5e_safe_switch_channels(priv, &new_channels,
                                                 mlx5e_update_trust_state_hw,
                                                 &trust_state);
-       else
+       } else {
                err = mlx5e_update_trust_state_hw(priv, &trust_state);
+               if (!err && !opened)
+                       priv->channels.params = new_channels.params;
+       }
 
        mutex_unlock(&priv->state_lock);
 
index d9076d5..5e9474d 100644 (file)
@@ -447,12 +447,29 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv,
                goto out;
        }
 
-       new_channels.params = priv->channels.params;
+       /* Don't allow changing the number of channels if HTB offload is active,
+        * because the numeration of the QoS SQs will change, while per-queue
+        * qdiscs are attached.
+        */
+       if (priv->htb.maj_id) {
+               err = -EINVAL;
+               netdev_err(priv->netdev, "%s: HTB offload is active, cannot change the number of channels\n",
+                          __func__);
+               goto out;
+       }
+
+       new_channels.params = *cur_params;
        new_channels.params.num_channels = count;
 
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               struct mlx5e_params old_params;
+
+               old_params = *cur_params;
                *cur_params = new_channels.params;
                err = mlx5e_num_channels_changed(priv);
+               if (err)
+                       *cur_params = old_params;
+
                goto out;
        }
 
@@ -1010,6 +1027,22 @@ static int mlx5e_get_link_ksettings(struct net_device *netdev,
        return mlx5e_ethtool_get_link_ksettings(priv, link_ksettings);
 }
 
+static int mlx5e_speed_validate(struct net_device *netdev, bool ext,
+                               const unsigned long link_modes, u8 autoneg)
+{
+       /* Extended link-mode has no speed limitations. */
+       if (ext)
+               return 0;
+
+       if ((link_modes & MLX5E_PROT_MASK(MLX5E_56GBASE_R4)) &&
+           autoneg != AUTONEG_ENABLE) {
+               netdev_err(netdev, "%s: 56G link speed requires autoneg enabled\n",
+                          __func__);
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static u32 mlx5e_ethtool2ptys_adver_link(const unsigned long *link_modes)
 {
        u32 i, ptys_modes = 0;
@@ -1103,13 +1136,9 @@ int mlx5e_ethtool_set_link_ksettings(struct mlx5e_priv *priv,
        link_modes = autoneg == AUTONEG_ENABLE ? ethtool2ptys_adver_func(adver) :
                mlx5e_port_speed2linkmodes(mdev, speed, !ext);
 
-       if ((link_modes & MLX5E_PROT_MASK(MLX5E_56GBASE_R4)) &&
-           autoneg != AUTONEG_ENABLE) {
-               netdev_err(priv->netdev, "%s: 56G link speed requires autoneg enabled\n",
-                          __func__);
-               err = -EINVAL;
+       err = mlx5e_speed_validate(priv->netdev, ext, link_modes, autoneg);
+       if (err)
                goto out;
-       }
 
        link_modes = link_modes & eproto.cap;
        if (!link_modes) {
@@ -1954,6 +1983,16 @@ static int set_pflag_tx_port_ts(struct net_device *netdev, bool enable)
        if (!MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn))
                return -EOPNOTSUPP;
 
+       /* Don't allow changing the PTP state if HTB offload is active, because
+        * the numeration of the QoS SQs will change, while per-queue qdiscs are
+        * attached.
+        */
+       if (priv->htb.maj_id) {
+               netdev_err(priv->netdev, "%s: HTB offload is active, cannot change the PTP state\n",
+                          __func__);
+               return -EINVAL;
+       }
+
        new_channels.params = priv->channels.params;
        MLX5E_SET_PFLAG(&new_channels.params, MLX5E_PFLAG_TX_PORT_TS, enable);
        /* No need to verify SQ stop room as
index fa8149f..16ce775 100644 (file)
@@ -46,7 +46,6 @@ static void mlx5e_del_l2_flow_rule(struct mlx5e_priv *priv,
 enum {
        MLX5E_FULLMATCH = 0,
        MLX5E_ALLMULTI  = 1,
-       MLX5E_PROMISC   = 2,
 };
 
 enum {
@@ -306,6 +305,79 @@ static int mlx5e_add_any_vid_rules(struct mlx5e_priv *priv)
        return mlx5e_add_vlan_rule(priv, MLX5E_VLAN_RULE_TYPE_ANY_STAG_VID, 0);
 }
 
+static struct mlx5_flow_handle *
+mlx5e_add_trap_rule(struct mlx5_flow_table *ft, int trap_id, int tir_num)
+{
+       struct mlx5_flow_destination dest = {};
+       MLX5_DECLARE_FLOW_ACT(flow_act);
+       struct mlx5_flow_handle *rule;
+       struct mlx5_flow_spec *spec;
+
+       spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return ERR_PTR(-ENOMEM);
+       spec->flow_context.flags |= FLOW_CONTEXT_HAS_TAG;
+       spec->flow_context.flow_tag = trap_id;
+       dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
+       dest.tir_num = tir_num;
+
+       rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1);
+       kvfree(spec);
+       return rule;
+}
+
+int mlx5e_add_vlan_trap(struct mlx5e_priv *priv, int trap_id, int tir_num)
+{
+       struct mlx5_flow_table *ft = priv->fs.vlan.ft.t;
+       struct mlx5_flow_handle *rule;
+       int err;
+
+       rule = mlx5e_add_trap_rule(ft, trap_id, tir_num);
+       if (IS_ERR(rule)) {
+               err = PTR_ERR(rule);
+               priv->fs.vlan.trap_rule = NULL;
+               netdev_err(priv->netdev, "%s: add VLAN trap rule failed, err %d\n",
+                          __func__, err);
+               return err;
+       }
+       priv->fs.vlan.trap_rule = rule;
+       return 0;
+}
+
+void mlx5e_remove_vlan_trap(struct mlx5e_priv *priv)
+{
+       if (priv->fs.vlan.trap_rule) {
+               mlx5_del_flow_rules(priv->fs.vlan.trap_rule);
+               priv->fs.vlan.trap_rule = NULL;
+       }
+}
+
+int mlx5e_add_mac_trap(struct mlx5e_priv *priv, int trap_id, int tir_num)
+{
+       struct mlx5_flow_table *ft = priv->fs.l2.ft.t;
+       struct mlx5_flow_handle *rule;
+       int err;
+
+       rule = mlx5e_add_trap_rule(ft, trap_id, tir_num);
+       if (IS_ERR(rule)) {
+               err = PTR_ERR(rule);
+               priv->fs.l2.trap_rule = NULL;
+               netdev_err(priv->netdev, "%s: add MAC trap rule failed, err %d\n",
+                          __func__, err);
+               return err;
+       }
+       priv->fs.l2.trap_rule = rule;
+       return 0;
+}
+
+void mlx5e_remove_mac_trap(struct mlx5e_priv *priv)
+{
+       if (priv->fs.l2.trap_rule) {
+               mlx5_del_flow_rules(priv->fs.l2.trap_rule);
+               priv->fs.l2.trap_rule = NULL;
+       }
+}
+
 void mlx5e_enable_cvlan_filter(struct mlx5e_priv *priv)
 {
        if (!priv->fs.vlan.cvlan_filter_disabled)
@@ -419,6 +491,8 @@ static void mlx5e_del_vlan_rules(struct mlx5e_priv *priv)
 
        WARN_ON_ONCE(!(test_bit(MLX5E_STATE_DESTROYING, &priv->state)));
 
+       mlx5e_remove_vlan_trap(priv);
+
        /* must be called after DESTROY bit is set and
         * set_rx_mode is called and flushed
         */
@@ -596,6 +670,83 @@ static void mlx5e_handle_netdev_addr(struct mlx5e_priv *priv)
        mlx5e_apply_netdev_addr(priv);
 }
 
+#define MLX5E_PROMISC_GROUP0_SIZE BIT(0)
+#define MLX5E_PROMISC_TABLE_SIZE MLX5E_PROMISC_GROUP0_SIZE
+
+static int mlx5e_add_promisc_rule(struct mlx5e_priv *priv)
+{
+       struct mlx5_flow_table *ft = priv->fs.promisc.ft.t;
+       struct mlx5_flow_destination dest = {};
+       struct mlx5_flow_handle **rule_p;
+       MLX5_DECLARE_FLOW_ACT(flow_act);
+       struct mlx5_flow_spec *spec;
+       int err = 0;
+
+       spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
+       dest.ft = priv->fs.ttc.ft.t;
+
+       rule_p = &priv->fs.promisc.rule;
+       *rule_p = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1);
+       if (IS_ERR(*rule_p)) {
+               err = PTR_ERR(*rule_p);
+               *rule_p = NULL;
+               netdev_err(priv->netdev, "%s: add promiscuous rule failed\n", __func__);
+       }
+       kvfree(spec);
+       return err;
+}
+
+static int mlx5e_create_promisc_table(struct mlx5e_priv *priv)
+{
+       struct mlx5e_flow_table *ft = &priv->fs.promisc.ft;
+       struct mlx5_flow_table_attr ft_attr = {};
+       int err;
+
+       ft_attr.max_fte = MLX5E_PROMISC_TABLE_SIZE;
+       ft_attr.autogroup.max_num_groups = 1;
+       ft_attr.level = MLX5E_PROMISC_FT_LEVEL;
+       ft_attr.prio = MLX5E_NIC_PRIO;
+
+       ft->t = mlx5_create_auto_grouped_flow_table(priv->fs.ns, &ft_attr);
+       if (IS_ERR(ft->t)) {
+               err = PTR_ERR(ft->t);
+               netdev_err(priv->netdev, "fail to create promisc table err=%d\n", err);
+               return err;
+       }
+
+       err = mlx5e_add_promisc_rule(priv);
+       if (err)
+               goto err_destroy_promisc_table;
+
+       return 0;
+
+err_destroy_promisc_table:
+       mlx5_destroy_flow_table(ft->t);
+       ft->t = NULL;
+
+       return err;
+}
+
+static void mlx5e_del_promisc_rule(struct mlx5e_priv *priv)
+{
+       if (WARN(!priv->fs.promisc.rule, "Trying to remove non-existing promiscuous rule"))
+               return;
+       mlx5_del_flow_rules(priv->fs.promisc.rule);
+       priv->fs.promisc.rule = NULL;
+}
+
+static void mlx5e_destroy_promisc_table(struct mlx5e_priv *priv)
+{
+       if (WARN(!priv->fs.promisc.ft.t, "Trying to remove non-existing promiscuous table"))
+               return;
+       mlx5e_del_promisc_rule(priv);
+       mlx5_destroy_flow_table(priv->fs.promisc.ft.t);
+       priv->fs.promisc.ft.t = NULL;
+}
+
 void mlx5e_set_rx_mode_work(struct work_struct *work)
 {
        struct mlx5e_priv *priv = container_of(work, struct mlx5e_priv,
@@ -615,14 +766,15 @@ void mlx5e_set_rx_mode_work(struct work_struct *work)
        bool disable_allmulti  =  ea->allmulti_enabled  && !allmulti_enabled;
        bool enable_broadcast  = !ea->broadcast_enabled &&  broadcast_enabled;
        bool disable_broadcast =  ea->broadcast_enabled && !broadcast_enabled;
+       int err;
 
        if (enable_promisc) {
-               if (!priv->channels.params.vlan_strip_disable)
+               err = mlx5e_create_promisc_table(priv);
+               if (err)
+                       enable_promisc = false;
+               if (!priv->channels.params.vlan_strip_disable && !err)
                        netdev_warn_once(ndev,
                                         "S-tagged traffic will be dropped while C-tag vlan stripping is enabled\n");
-               mlx5e_add_l2_flow_rule(priv, &ea->promisc, MLX5E_PROMISC);
-               if (!priv->fs.vlan.cvlan_filter_disabled)
-                       mlx5e_add_any_vid_rules(priv);
        }
        if (enable_allmulti)
                mlx5e_add_l2_flow_rule(priv, &ea->allmulti, MLX5E_ALLMULTI);
@@ -635,11 +787,8 @@ void mlx5e_set_rx_mode_work(struct work_struct *work)
                mlx5e_del_l2_flow_rule(priv, &ea->broadcast);
        if (disable_allmulti)
                mlx5e_del_l2_flow_rule(priv, &ea->allmulti);
-       if (disable_promisc) {
-               if (!priv->fs.vlan.cvlan_filter_disabled)
-                       mlx5e_del_any_vid_rules(priv);
-               mlx5e_del_l2_flow_rule(priv, &ea->promisc);
-       }
+       if (disable_promisc)
+               mlx5e_destroy_promisc_table(priv);
 
        ea->promisc_enabled   = promisc_enabled;
        ea->allmulti_enabled  = allmulti_enabled;
@@ -942,6 +1091,7 @@ static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc,
        in = kvzalloc(inlen, GFP_KERNEL);
        if (!in) {
                kfree(ft->g);
+               ft->g = NULL;
                return -ENOMEM;
        }
 
@@ -1087,6 +1237,7 @@ static int mlx5e_create_inner_ttc_table_groups(struct mlx5e_ttc_table *ttc)
        in = kvzalloc(inlen, GFP_KERNEL);
        if (!in) {
                kfree(ft->g);
+               ft->g = NULL;
                return -ENOMEM;
        }
 
@@ -1304,9 +1455,6 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv,
                mc_dmac[0] = 0x01;
                mv_dmac[0] = 0x01;
                break;
-
-       case MLX5E_PROMISC:
-               break;
        }
 
        ai->rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1);
@@ -1323,12 +1471,12 @@ static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv,
 }
 
 #define MLX5E_NUM_L2_GROUPS       3
-#define MLX5E_L2_GROUP1_SIZE      BIT(0)
-#define MLX5E_L2_GROUP2_SIZE      BIT(15)
-#define MLX5E_L2_GROUP3_SIZE      BIT(0)
+#define MLX5E_L2_GROUP1_SIZE      BIT(15)
+#define MLX5E_L2_GROUP2_SIZE      BIT(0)
+#define MLX5E_L2_GROUP_TRAP_SIZE   BIT(0) /* must be last */
 #define MLX5E_L2_TABLE_SIZE       (MLX5E_L2_GROUP1_SIZE +\
                                    MLX5E_L2_GROUP2_SIZE +\
-                                   MLX5E_L2_GROUP3_SIZE)
+                                   MLX5E_L2_GROUP_TRAP_SIZE)
 static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table)
 {
        int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
@@ -1351,7 +1499,9 @@ static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table)
        mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
        mc_dmac = MLX5_ADDR_OF(fte_match_param, mc,
                               outer_headers.dmac_47_16);
-       /* Flow Group for promiscuous */
+       /* Flow Group for full match */
+       eth_broadcast_addr(mc_dmac);
+       MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
        MLX5_SET_CFG(in, start_flow_index, ix);
        ix += MLX5E_L2_GROUP1_SIZE;
        MLX5_SET_CFG(in, end_flow_index, ix - 1);
@@ -1360,9 +1510,9 @@ static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table)
                goto err_destroy_groups;
        ft->num_groups++;
 
-       /* Flow Group for full match */
-       eth_broadcast_addr(mc_dmac);
-       MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+       /* Flow Group for allmulti */
+       eth_zero_addr(mc_dmac);
+       mc_dmac[0] = 0x01;
        MLX5_SET_CFG(in, start_flow_index, ix);
        ix += MLX5E_L2_GROUP2_SIZE;
        MLX5_SET_CFG(in, end_flow_index, ix - 1);
@@ -1371,11 +1521,10 @@ static int mlx5e_create_l2_table_groups(struct mlx5e_l2_table *l2_table)
                goto err_destroy_groups;
        ft->num_groups++;
 
-       /* Flow Group for allmulti */
-       eth_zero_addr(mc_dmac);
-       mc_dmac[0] = 0x01;
+       /* Flow Group for l2 traps */
+       memset(in, 0, inlen);
        MLX5_SET_CFG(in, start_flow_index, ix);
-       ix += MLX5E_L2_GROUP3_SIZE;
+       ix += MLX5E_L2_GROUP_TRAP_SIZE;
        MLX5_SET_CFG(in, end_flow_index, ix - 1);
        ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
        if (IS_ERR(ft->g[ft->num_groups]))
@@ -1390,6 +1539,7 @@ err_destroy_groups:
        ft->g[ft->num_groups] = NULL;
        mlx5e_destroy_groups(ft);
        kvfree(in);
+       kfree(ft->g);
 
        return err;
 }
@@ -1432,15 +1582,17 @@ err_destroy_flow_table:
        return err;
 }
 
-#define MLX5E_NUM_VLAN_GROUPS  4
+#define MLX5E_NUM_VLAN_GROUPS  5
 #define MLX5E_VLAN_GROUP0_SIZE BIT(12)
 #define MLX5E_VLAN_GROUP1_SIZE BIT(12)
 #define MLX5E_VLAN_GROUP2_SIZE BIT(1)
 #define MLX5E_VLAN_GROUP3_SIZE BIT(0)
+#define MLX5E_VLAN_GROUP_TRAP_SIZE BIT(0) /* must be last */
 #define MLX5E_VLAN_TABLE_SIZE  (MLX5E_VLAN_GROUP0_SIZE +\
                                 MLX5E_VLAN_GROUP1_SIZE +\
                                 MLX5E_VLAN_GROUP2_SIZE +\
-                                MLX5E_VLAN_GROUP3_SIZE)
+                                MLX5E_VLAN_GROUP3_SIZE +\
+                                MLX5E_VLAN_GROUP_TRAP_SIZE)
 
 static int __mlx5e_create_vlan_table_groups(struct mlx5e_flow_table *ft, u32 *in,
                                            int inlen)
@@ -1495,6 +1647,15 @@ static int __mlx5e_create_vlan_table_groups(struct mlx5e_flow_table *ft, u32 *in
                goto err_destroy_groups;
        ft->num_groups++;
 
+       memset(in, 0, inlen);
+       MLX5_SET_CFG(in, start_flow_index, ix);
+       ix += MLX5E_VLAN_GROUP_TRAP_SIZE;
+       MLX5_SET_CFG(in, end_flow_index, ix - 1);
+       ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in);
+       if (IS_ERR(ft->g[ft->num_groups]))
+               goto err_destroy_groups;
+       ft->num_groups++;
+
        return 0;
 
 err_destroy_groups:
index 7a79d33..aad3887 100644 (file)
@@ -65,6 +65,8 @@
 #include "en/devlink.h"
 #include "lib/mlx5.h"
 #include "en/ptp.h"
+#include "qos.h"
+#include "en/trap.h"
 
 bool mlx5e_check_fragmented_striding_rq_cap(struct mlx5_core_dev *mdev)
 {
@@ -211,6 +213,33 @@ static void mlx5e_disable_async_events(struct mlx5e_priv *priv)
        mlx5_notifier_unregister(priv->mdev, &priv->events_nb);
 }
 
+static int blocking_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+       struct mlx5e_priv *priv = container_of(nb, struct mlx5e_priv, blocking_events_nb);
+       int err;
+
+       switch (event) {
+       case MLX5_DRIVER_EVENT_TYPE_TRAP:
+               err = mlx5e_handle_trap_event(priv, data);
+               break;
+       default:
+               netdev_warn(priv->netdev, "Sync event: Unknouwn event %ld\n", event);
+               err = -EINVAL;
+       }
+       return err;
+}
+
+static void mlx5e_enable_blocking_events(struct mlx5e_priv *priv)
+{
+       priv->blocking_events_nb.notifier_call = blocking_event;
+       mlx5_blocking_notifier_register(priv->mdev, &priv->blocking_events_nb);
+}
+
+static void mlx5e_disable_blocking_events(struct mlx5e_priv *priv)
+{
+       mlx5_blocking_notifier_unregister(priv->mdev, &priv->blocking_events_nb);
+}
+
 static inline void mlx5e_build_umr_wqe(struct mlx5e_rq *rq,
                                       struct mlx5e_icosq *sq,
                                       struct mlx5e_umr_wqe *wqe)
@@ -342,13 +371,11 @@ static void mlx5e_init_frags_partition(struct mlx5e_rq *rq)
                prev->last_in_page = true;
 }
 
-static int mlx5e_init_di_list(struct mlx5e_rq *rq,
-                             int wq_sz, int cpu)
+int mlx5e_init_di_list(struct mlx5e_rq *rq, int wq_sz, int node)
 {
        int len = wq_sz << rq->wqe.info.log_num_frags;
 
-       rq->wqe.di = kvzalloc_node(array_size(len, sizeof(*rq->wqe.di)),
-                                  GFP_KERNEL, cpu_to_node(cpu));
+       rq->wqe.di = kvzalloc_node(array_size(len, sizeof(*rq->wqe.di)), GFP_KERNEL, node);
        if (!rq->wqe.di)
                return -ENOMEM;
 
@@ -357,7 +384,7 @@ static int mlx5e_init_di_list(struct mlx5e_rq *rq,
        return 0;
 }
 
-static void mlx5e_free_di_list(struct mlx5e_rq *rq)
+void mlx5e_free_di_list(struct mlx5e_rq *rq)
 {
        kvfree(rq->wqe.di);
 }
@@ -499,7 +526,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c,
                        goto err_rq_wq_destroy;
                }
 
-               err = mlx5e_init_di_list(rq, wq_sz, c->cpu);
+               err = mlx5e_init_di_list(rq, wq_sz, cpu_to_node(c->cpu));
                if (err)
                        goto err_rq_frags;
 
@@ -650,8 +677,7 @@ static void mlx5e_free_rq(struct mlx5e_rq *rq)
        mlx5_wq_destroy(&rq->wq_ctrl);
 }
 
-static int mlx5e_create_rq(struct mlx5e_rq *rq,
-                          struct mlx5e_rq_param *param)
+int mlx5e_create_rq(struct mlx5e_rq *rq, struct mlx5e_rq_param *param)
 {
        struct mlx5_core_dev *mdev = rq->mdev;
 
@@ -774,7 +800,7 @@ static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd)
        return err;
 }
 
-static void mlx5e_destroy_rq(struct mlx5e_rq *rq)
+void mlx5e_destroy_rq(struct mlx5e_rq *rq)
 {
        mlx5_core_destroy_rq(rq->mdev, rq->rqn);
 }
@@ -1143,7 +1169,6 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c,
        sq->uar_map   = mdev->mlx5e_res.bfreg.map;
        sq->min_inline_mode = params->tx_min_inline_mode;
        sq->hw_mtu    = MLX5E_SW2HW_MTU(params, params->sw_mtu);
-       sq->stats     = &c->priv->channel_stats[c->ix].sq[tc];
        INIT_WORK(&sq->recover_work, mlx5e_tx_err_cqe_work);
        if (!MLX5_CAP_ETH(mdev, wqe_vlan_insert))
                set_bit(MLX5E_SQ_STATE_VLAN_NEED_L2_INLINE, &sq->state);
@@ -1233,6 +1258,7 @@ static int mlx5e_create_sq(struct mlx5_core_dev *mdev,
 int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
                    struct mlx5e_modify_sq_param *p)
 {
+       u64 bitmask = 0;
        void *in;
        void *sqc;
        int inlen;
@@ -1248,9 +1274,14 @@ int mlx5e_modify_sq(struct mlx5_core_dev *mdev, u32 sqn,
        MLX5_SET(modify_sq_in, in, sq_state, p->curr_state);
        MLX5_SET(sqc, sqc, state, p->next_state);
        if (p->rl_update && p->next_state == MLX5_SQC_STATE_RDY) {
-               MLX5_SET64(modify_sq_in, in, modify_bitmask, 1);
-               MLX5_SET(sqc,  sqc, packet_pacing_rate_limit_index, p->rl_index);
+               bitmask |= 1;
+               MLX5_SET(sqc, sqc, packet_pacing_rate_limit_index, p->rl_index);
+       }
+       if (p->qos_update && p->next_state == MLX5_SQC_STATE_RDY) {
+               bitmask |= 1 << 2;
+               MLX5_SET(sqc, sqc, qos_queue_group_id, p->qos_queue_group_id);
        }
+       MLX5_SET64(modify_sq_in, in, modify_bitmask, bitmask);
 
        err = mlx5_core_modify_sq(mdev, sqn, in);
 
@@ -1267,6 +1298,7 @@ static void mlx5e_destroy_sq(struct mlx5_core_dev *mdev, u32 sqn)
 int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev,
                        struct mlx5e_sq_param *param,
                        struct mlx5e_create_sq_param *csp,
+                       u16 qos_queue_group_id,
                        u32 *sqn)
 {
        struct mlx5e_modify_sq_param msp = {0};
@@ -1278,6 +1310,10 @@ int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev,
 
        msp.curr_state = MLX5_SQC_STATE_RST;
        msp.next_state = MLX5_SQC_STATE_RDY;
+       if (qos_queue_group_id) {
+               msp.qos_update = true;
+               msp.qos_queue_group_id = qos_queue_group_id;
+       }
        err = mlx5e_modify_sq(mdev, *sqn, &msp);
        if (err)
                mlx5e_destroy_sq(mdev, *sqn);
@@ -1288,13 +1324,9 @@ int mlx5e_create_sq_rdy(struct mlx5_core_dev *mdev,
 static int mlx5e_set_sq_maxrate(struct net_device *dev,
                                struct mlx5e_txqsq *sq, u32 rate);
 
-static int mlx5e_open_txqsq(struct mlx5e_channel *c,
-                           u32 tisn,
-                           int txq_ix,
-                           struct mlx5e_params *params,
-                           struct mlx5e_sq_param *param,
-                           struct mlx5e_txqsq *sq,
-                           int tc)
+int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix,
+                    struct mlx5e_params *params, struct mlx5e_sq_param *param,
+                    struct mlx5e_txqsq *sq, int tc, u16 qos_queue_group_id, u16 qos_qid)
 {
        struct mlx5e_create_sq_param csp = {};
        u32 tx_rate;
@@ -1304,12 +1336,17 @@ static int mlx5e_open_txqsq(struct mlx5e_channel *c,
        if (err)
                return err;
 
+       if (qos_queue_group_id)
+               sq->stats = c->priv->htb.qos_sq_stats[qos_qid];
+       else
+               sq->stats = &c->priv->channel_stats[c->ix].sq[tc];
+
        csp.tisn            = tisn;
        csp.tis_lst_sz      = 1;
        csp.cqn             = sq->cq.mcq.cqn;
        csp.wq_ctrl         = &sq->wq_ctrl;
        csp.min_inline_mode = sq->min_inline_mode;
-       err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn);
+       err = mlx5e_create_sq_rdy(c->mdev, param, &csp, qos_queue_group_id, &sq->sqn);
        if (err)
                goto err_free_txqsq;
 
@@ -1366,7 +1403,7 @@ void mlx5e_deactivate_txqsq(struct mlx5e_txqsq *sq)
        }
 }
 
-static void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
+void mlx5e_close_txqsq(struct mlx5e_txqsq *sq)
 {
        struct mlx5_core_dev *mdev = sq->mdev;
        struct mlx5_rate_limit rl = {0};
@@ -1403,7 +1440,7 @@ int mlx5e_open_icosq(struct mlx5e_channel *c, struct mlx5e_params *params,
        csp.cqn             = sq->cq.mcq.cqn;
        csp.wq_ctrl         = &sq->wq_ctrl;
        csp.min_inline_mode = params->tx_min_inline_mode;
-       err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn);
+       err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn);
        if (err)
                goto err_free_icosq;
 
@@ -1452,7 +1489,7 @@ int mlx5e_open_xdpsq(struct mlx5e_channel *c, struct mlx5e_params *params,
        csp.wq_ctrl         = &sq->wq_ctrl;
        csp.min_inline_mode = sq->min_inline_mode;
        set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
-       err = mlx5e_create_sq_rdy(c->mdev, param, &csp, &sq->sqn);
+       err = mlx5e_create_sq_rdy(c->mdev, param, &csp, 0, &sq->sqn);
        if (err)
                goto err_free_xdpsq;
 
@@ -1703,7 +1740,7 @@ static int mlx5e_open_sqs(struct mlx5e_channel *c,
                int txq_ix = c->ix + tc * params->num_channels;
 
                err = mlx5e_open_txqsq(c, c->priv->tisn[c->lag_port][tc], txq_ix,
-                                      params, &cparam->txq_sq, &c->sq[tc], tc);
+                                      params, &cparam->txq_sq, &c->sq[tc], tc, 0, 0);
                if (err)
                        goto err_close_sqs;
        }
@@ -2044,6 +2081,8 @@ static void mlx5e_deactivate_channel(struct mlx5e_channel *c)
        mlx5e_deactivate_icosq(&c->icosq);
        for (tc = 0; tc < c->num_tc; tc++)
                mlx5e_deactivate_txqsq(&c->sq[tc]);
+
+       mlx5e_qos_deactivate_queues(c);
 }
 
 static void mlx5e_close_channel(struct mlx5e_channel *c)
@@ -2051,6 +2090,7 @@ static void mlx5e_close_channel(struct mlx5e_channel *c)
        if (test_bit(MLX5E_CHANNEL_STATE_XSK, c->state))
                mlx5e_close_xsk(c);
        mlx5e_close_queues(c);
+       mlx5e_qos_close_queues(c);
        netif_napi_del(&c->napi);
 
        kvfree(c);
@@ -2068,10 +2108,8 @@ static void mlx5e_build_rq_frags_info(struct mlx5_core_dev *mdev,
        u32 buf_size = 0;
        int i;
 
-#ifdef CONFIG_MLX5_EN_IPSEC
        if (MLX5_IPSEC_DEV(mdev))
                byte_count += MLX5E_METADATA_ETHER_LEN;
-#endif
 
        if (mlx5e_rx_is_linear_skb(params, xsk)) {
                int frag_stride;
@@ -2200,9 +2238,8 @@ void mlx5e_build_sq_param_common(struct mlx5e_priv *priv,
        param->wq.buf_numa_node = dev_to_node(mlx5_core_dma_dev(priv->mdev));
 }
 
-static void mlx5e_build_sq_param(struct mlx5e_priv *priv,
-                                struct mlx5e_params *params,
-                                struct mlx5e_sq_param *param)
+void mlx5e_build_sq_param(struct mlx5e_priv *priv, struct mlx5e_params *params,
+                         struct mlx5e_sq_param *param)
 {
        void *sqc = param->sqc;
        void *wq = MLX5_ADDR_OF(sqc, sqc, wq);
@@ -2381,10 +2418,18 @@ int mlx5e_open_channels(struct mlx5e_priv *priv,
                        goto err_close_channels;
        }
 
+       err = mlx5e_qos_open_queues(priv, chs);
+       if (err)
+               goto err_close_ptp;
+
        mlx5e_health_channels_update(priv);
        kvfree(cparam);
        return 0;
 
+err_close_ptp:
+       if (chs->port_ptp)
+               mlx5e_port_ptp_close(chs->port_ptp);
+
 err_close_channels:
        for (i--; i >= 0; i--)
                mlx5e_close_channel(chs->c[i]);
@@ -2917,11 +2962,31 @@ static void mlx5e_netdev_set_tcs(struct net_device *netdev, u16 nch, u8 ntc)
                netdev_set_tc_queue(netdev, tc, nch, 0);
 }
 
+int mlx5e_update_tx_netdev_queues(struct mlx5e_priv *priv)
+{
+       int qos_queues, nch, ntc, num_txqs, err;
+
+       qos_queues = mlx5e_qos_cur_leaf_nodes(priv);
+
+       nch = priv->channels.params.num_channels;
+       ntc = priv->channels.params.num_tc;
+       num_txqs = nch * ntc + qos_queues;
+       if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_TX_PORT_TS))
+               num_txqs += ntc;
+
+       mlx5e_dbg(DRV, priv, "Setting num_txqs %d\n", num_txqs);
+       err = netif_set_real_num_tx_queues(priv->netdev, num_txqs);
+       if (err)
+               netdev_warn(priv->netdev, "netif_set_real_num_tx_queues failed, %d\n", err);
+
+       return err;
+}
+
 static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv)
 {
        struct net_device *netdev = priv->netdev;
-       int num_txqs, num_rxqs, nch, ntc;
        int old_num_txqs, old_ntc;
+       int num_rxqs, nch, ntc;
        int err;
 
        old_num_txqs = netdev->real_num_tx_queues;
@@ -2929,18 +2994,13 @@ static int mlx5e_update_netdev_queues(struct mlx5e_priv *priv)
 
        nch = priv->channels.params.num_channels;
        ntc = priv->channels.params.num_tc;
-       num_txqs = nch * ntc;
-       if (MLX5E_GET_PFLAG(&priv->channels.params, MLX5E_PFLAG_TX_PORT_TS))
-               num_txqs += ntc;
        num_rxqs = nch * priv->profile->rq_groups;
 
        mlx5e_netdev_set_tcs(netdev, nch, ntc);
 
-       err = netif_set_real_num_tx_queues(netdev, num_txqs);
-       if (err) {
-               netdev_warn(netdev, "netif_set_real_num_tx_queues failed, %d\n", err);
+       err = mlx5e_update_tx_netdev_queues(priv);
+       if (err)
                goto err_tcs;
-       }
        err = netif_set_real_num_rx_queues(netdev, num_rxqs);
        if (err) {
                netdev_warn(netdev, "netif_set_real_num_rx_queues failed, %d\n", err);
@@ -3044,6 +3104,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv)
        mlx5e_update_num_tc_x_num_ch(priv);
        mlx5e_build_txq_maps(priv);
        mlx5e_activate_channels(&priv->channels);
+       mlx5e_qos_activate_queues(priv);
        mlx5e_xdp_tx_enable(priv);
        netif_tx_start_all_queues(priv->netdev);
 
@@ -3161,7 +3222,8 @@ static void mlx5e_modify_admin_state(struct mlx5_core_dev *mdev,
 
        mlx5_set_port_admin_status(mdev, state);
 
-       if (mlx5_eswitch_mode(mdev) != MLX5_ESWITCH_LEGACY)
+       if (mlx5_eswitch_mode(mdev) == MLX5_ESWITCH_OFFLOADS ||
+           !MLX5_CAP_GEN(mdev, uplink_follow))
                return;
 
        if (state == MLX5_PORT_UP)
@@ -3185,6 +3247,7 @@ int mlx5e_open_locked(struct net_device *netdev)
 
        priv->profile->update_rx(priv);
        mlx5e_activate_priv_channels(priv);
+       mlx5e_apply_traps(priv, true);
        if (priv->profile->update_carrier)
                priv->profile->update_carrier(priv);
 
@@ -3220,6 +3283,7 @@ int mlx5e_close_locked(struct net_device *netdev)
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
                return 0;
 
+       mlx5e_apply_traps(priv, false);
        clear_bit(MLX5E_STATE_OPENED, &priv->state);
 
        netif_carrier_off(priv->netdev);
@@ -3609,11 +3673,26 @@ static int mlx5e_setup_tc_mqprio(struct mlx5e_priv *priv,
 
        mutex_lock(&priv->state_lock);
 
+       /* MQPRIO is another toplevel qdisc that can't be attached
+        * simultaneously with the offloaded HTB.
+        */
+       if (WARN_ON(priv->htb.maj_id)) {
+               err = -EINVAL;
+               goto out;
+       }
+
        new_channels.params = priv->channels.params;
        new_channels.params.num_tc = tc ? tc : 1;
 
        if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) {
+               struct mlx5e_params old_params;
+
+               old_params = priv->channels.params;
                priv->channels.params = new_channels.params;
+               err = mlx5e_num_channels_changed(priv);
+               if (err)
+                       priv->channels.params = old_params;
+
                goto out;
        }
 
@@ -3629,12 +3708,55 @@ out:
        return err;
 }
 
+static int mlx5e_setup_tc_htb(struct mlx5e_priv *priv, struct tc_htb_qopt_offload *htb)
+{
+       int res;
+
+       switch (htb->command) {
+       case TC_HTB_CREATE:
+               return mlx5e_htb_root_add(priv, htb->parent_classid, htb->classid,
+                                         htb->extack);
+       case TC_HTB_DESTROY:
+               return mlx5e_htb_root_del(priv);
+       case TC_HTB_LEAF_ALLOC_QUEUE:
+               res = mlx5e_htb_leaf_alloc_queue(priv, htb->classid, htb->parent_classid,
+                                                htb->rate, htb->ceil, htb->extack);
+               if (res < 0)
+                       return res;
+               htb->qid = res;
+               return 0;
+       case TC_HTB_LEAF_TO_INNER:
+               return mlx5e_htb_leaf_to_inner(priv, htb->parent_classid, htb->classid,
+                                              htb->rate, htb->ceil, htb->extack);
+       case TC_HTB_LEAF_DEL:
+               return mlx5e_htb_leaf_del(priv, htb->classid, &htb->moved_qid, &htb->qid,
+                                         htb->extack);
+       case TC_HTB_LEAF_DEL_LAST:
+       case TC_HTB_LEAF_DEL_LAST_FORCE:
+               return mlx5e_htb_leaf_del_last(priv, htb->classid,
+                                              htb->command == TC_HTB_LEAF_DEL_LAST_FORCE,
+                                              htb->extack);
+       case TC_HTB_NODE_MODIFY:
+               return mlx5e_htb_node_modify(priv, htb->classid, htb->rate, htb->ceil,
+                                            htb->extack);
+       case TC_HTB_LEAF_QUERY_QUEUE:
+               res = mlx5e_get_txq_by_classid(priv, htb->classid);
+               if (res < 0)
+                       return res;
+               htb->qid = res;
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
 static LIST_HEAD(mlx5e_block_cb_list);
 
 static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
                          void *type_data)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
+       int err;
 
        switch (type) {
        case TC_SETUP_BLOCK: {
@@ -3648,6 +3770,11 @@ static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type,
        }
        case TC_SETUP_QDISC_MQPRIO:
                return mlx5e_setup_tc_mqprio(priv, type_data);
+       case TC_SETUP_QDISC_HTB:
+               mutex_lock(&priv->state_lock);
+               err = mlx5e_setup_tc_htb(priv, type_data);
+               mutex_unlock(&priv->state_lock);
+               return err;
        default:
                return -EOPNOTSUPP;
        }
@@ -3756,7 +3883,7 @@ static int set_feature_lro(struct net_device *netdev, bool enable)
        struct mlx5e_priv *priv = netdev_priv(netdev);
        struct mlx5_core_dev *mdev = priv->mdev;
        struct mlx5e_channels new_channels = {};
-       struct mlx5e_params *old_params;
+       struct mlx5e_params *cur_params;
        int err = 0;
        bool reset;
 
@@ -3769,8 +3896,8 @@ static int set_feature_lro(struct net_device *netdev, bool enable)
                goto out;
        }
 
-       old_params = &priv->channels.params;
-       if (enable && !MLX5E_GET_PFLAG(old_params, MLX5E_PFLAG_RX_STRIDING_RQ)) {
+       cur_params = &priv->channels.params;
+       if (enable && !MLX5E_GET_PFLAG(cur_params, MLX5E_PFLAG_RX_STRIDING_RQ)) {
                netdev_warn(netdev, "can't set LRO with legacy RQ\n");
                err = -EINVAL;
                goto out;
@@ -3778,18 +3905,23 @@ static int set_feature_lro(struct net_device *netdev, bool enable)
 
        reset = test_bit(MLX5E_STATE_OPENED, &priv->state);
 
-       new_channels.params = *old_params;
+       new_channels.params = *cur_params;
        new_channels.params.lro_en = enable;
 
-       if (old_params->rq_wq_type != MLX5_WQ_TYPE_CYCLIC) {
-               if (mlx5e_rx_mpwqe_is_linear_skb(mdev, old_params, NULL) ==
+       if (cur_params->rq_wq_type != MLX5_WQ_TYPE_CYCLIC) {
+               if (mlx5e_rx_mpwqe_is_linear_skb(mdev, cur_params, NULL) ==
                    mlx5e_rx_mpwqe_is_linear_skb(mdev, &new_channels.params, NULL))
                        reset = false;
        }
 
        if (!reset) {
-               *old_params = new_channels.params;
+               struct mlx5e_params old_params;
+
+               old_params = *cur_params;
+               *cur_params = new_channels.params;
                err = mlx5e_modify_tirs_lro(priv);
+               if (err)
+                       *cur_params = old_params;
                goto out;
        }
 
@@ -3812,20 +3944,25 @@ static int set_feature_cvlan_filter(struct net_device *netdev, bool enable)
        return 0;
 }
 
-#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
-static int set_feature_tc_num_filters(struct net_device *netdev, bool enable)
+static int set_feature_hw_tc(struct net_device *netdev, bool enable)
 {
        struct mlx5e_priv *priv = netdev_priv(netdev);
 
+#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
        if (!enable && mlx5e_tc_num_filters(priv, MLX5_TC_FLAG(NIC_OFFLOAD))) {
                netdev_err(netdev,
                           "Active offloaded tc filters, can't turn hw_tc_offload off\n");
                return -EINVAL;
        }
+#endif
+
+       if (!enable && priv->htb.maj_id) {
+               netdev_err(netdev, "Active HTB offload, can't turn hw_tc_offload off\n");
+               return -EINVAL;
+       }
 
        return 0;
 }
-#endif
 
 static int set_feature_rx_all(struct net_device *netdev, bool enable)
 {
@@ -3923,9 +4060,7 @@ int mlx5e_set_features(struct net_device *netdev, netdev_features_t features)
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_FILTER,
                                    set_feature_cvlan_filter);
-#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
-       err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_tc_num_filters);
-#endif
+       err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_hw_tc);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXALL, set_feature_rx_all);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs);
        err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan);
@@ -4066,9 +4201,16 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu,
        }
 
        if (!reset) {
+               unsigned int old_mtu = params->sw_mtu;
+
                params->sw_mtu = new_mtu;
-               if (preactivate)
-                       preactivate(priv, NULL);
+               if (preactivate) {
+                       err = preactivate(priv, NULL);
+                       if (err) {
+                               params->sw_mtu = old_mtu;
+                               goto out;
+                       }
+               }
                netdev->mtu = params->sw_mtu;
                goto out;
        }
@@ -4375,10 +4517,8 @@ netdev_features_t mlx5e_features_check(struct sk_buff *skb,
        features = vlan_features_check(skb, features);
        features = vxlan_features_check(skb, features);
 
-#ifdef CONFIG_MLX5_EN_IPSEC
        if (mlx5e_ipsec_feature_check(skb, netdev, features))
                return features;
-#endif
 
        /* Validate if the tunneled packet is being offloaded by HW */
        if (skb->encapsulation &&
@@ -4621,8 +4761,6 @@ const struct net_device_ops mlx5e_netdev_ops = {
        .ndo_change_mtu          = mlx5e_change_nic_mtu,
        .ndo_do_ioctl            = mlx5e_ioctl,
        .ndo_set_tx_maxrate      = mlx5e_set_tx_maxrate,
-       .ndo_udp_tunnel_add      = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del      = udp_tunnel_nic_del_port,
        .ndo_features_check      = mlx5e_features_check,
        .ndo_tx_timeout          = mlx5e_tx_timeout,
        .ndo_bpf                 = mlx5e_xdp,
@@ -5026,13 +5164,15 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
            FT_CAP(modify_root) &&
            FT_CAP(identified_miss_table_mode) &&
            FT_CAP(flow_table_modify)) {
-#ifdef CONFIG_MLX5_ESWITCH
+#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
                netdev->hw_features      |= NETIF_F_HW_TC;
 #endif
 #ifdef CONFIG_MLX5_EN_ARFS
                netdev->hw_features      |= NETIF_F_NTUPLE;
 #endif
        }
+       if (mlx5_qos_is_supported(mdev))
+               netdev->features |= NETIF_F_HW_TC;
 
        netdev->features         |= NETIF_F_HIGHDMA;
        netdev->features         |= NETIF_F_HW_VLAN_STAG_FILTER;
@@ -5250,6 +5390,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv)
        mlx5_lag_add(mdev, netdev);
 
        mlx5e_enable_async_events(priv);
+       mlx5e_enable_blocking_events(priv);
        if (mlx5e_monitor_counter_supported(priv))
                mlx5e_monitor_counter_init(priv);
 
@@ -5287,6 +5428,12 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
        if (mlx5e_monitor_counter_supported(priv))
                mlx5e_monitor_counter_cleanup(priv);
 
+       mlx5e_disable_blocking_events(priv);
+       if (priv->en_trap) {
+               mlx5e_deactivate_trap(priv);
+               mlx5e_close_trap(priv->en_trap);
+               priv->en_trap = NULL;
+       }
        mlx5e_disable_async_events(priv);
        mlx5_lag_remove(mdev);
        mlx5_vxlan_reset_to_default(mdev->vxlan);
@@ -5338,6 +5485,7 @@ int mlx5e_netdev_init(struct net_device *netdev,
                return -ENOMEM;
 
        mutex_init(&priv->state_lock);
+       hash_init(priv->htb.qos_tc2node);
        INIT_WORK(&priv->update_carrier_work, mlx5e_update_carrier_work);
        INIT_WORK(&priv->set_rx_mode_work, mlx5e_set_rx_mode_work);
        INIT_WORK(&priv->tx_timeout_work, mlx5e_tx_timeout_work);
@@ -5360,8 +5508,14 @@ err_free_cpumask:
 
 void mlx5e_netdev_cleanup(struct net_device *netdev, struct mlx5e_priv *priv)
 {
+       int i;
+
        destroy_workqueue(priv->wq);
        free_cpumask_var(priv->scratchpad.cpumask);
+
+       for (i = 0; i < priv->htb.max_qos_sqs; i++)
+               kfree(priv->htb.qos_sq_stats[i]);
+       kvfree(priv->htb.qos_sq_stats);
 }
 
 struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev,
@@ -5371,13 +5525,17 @@ struct net_device *mlx5e_create_netdev(struct mlx5_core_dev *mdev,
 {
        struct net_device *netdev;
        unsigned int ptp_txqs = 0;
+       int qos_sqs = 0;
        int err;
 
        if (MLX5_CAP_GEN(mdev, ts_cqe_to_dest_cqn))
                ptp_txqs = profile->max_tc;
 
+       if (mlx5_qos_is_supported(mdev))
+               qos_sqs = mlx5e_qos_max_leaf_nodes(mdev);
+
        netdev = alloc_etherdev_mqs(sizeof(struct mlx5e_priv),
-                                   nch * profile->max_tc + ptp_txqs,
+                                   nch * profile->max_tc + ptp_txqs + qos_sqs,
                                    nch * profile->rq_groups);
        if (!netdev) {
                mlx5_core_err(mdev, "alloc_etherdev_mqs() failed\n");
index 989c70c..629ae6c 100644 (file)
@@ -653,8 +653,6 @@ static const struct net_device_ops mlx5e_netdev_ops_uplink_rep = {
        .ndo_has_offload_stats   = mlx5e_rep_has_offload_stats,
        .ndo_get_offload_stats   = mlx5e_rep_get_offload_stats,
        .ndo_change_mtu          = mlx5e_uplink_rep_change_mtu,
-       .ndo_udp_tunnel_add      = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del      = udp_tunnel_nic_del_port,
        .ndo_features_check      = mlx5e_features_check,
        .ndo_set_vf_mac          = mlx5e_set_vf_mac,
        .ndo_set_vf_rate         = mlx5e_set_vf_rate,
@@ -737,7 +735,9 @@ static void mlx5e_build_rep_netdev(struct net_device *netdev)
 
        netdev->features       |= NETIF_F_NETNS_LOCAL;
 
+#if IS_ENABLED(CONFIG_MLX5_CLS_ACT)
        netdev->hw_features    |= NETIF_F_HW_TC;
+#endif
        netdev->hw_features    |= NETIF_F_SG;
        netdev->hw_features    |= NETIF_F_IP_CSUM;
        netdev->hw_features    |= NETIF_F_IPV6_CSUM;
index 7f5851c..98b56f4 100644 (file)
@@ -52,6 +52,7 @@
 #include "en/xsk/rx.h"
 #include "en/health.h"
 #include "en/params.h"
+#include "devlink.h"
 
 static struct sk_buff *
 mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
@@ -1126,12 +1127,8 @@ struct sk_buff *mlx5e_build_linear_skb(struct mlx5e_rq *rq, void *va,
 static void mlx5e_fill_xdp_buff(struct mlx5e_rq *rq, void *va, u16 headroom,
                                u32 len, struct xdp_buff *xdp)
 {
-       xdp->data_hard_start = va;
-       xdp->data = va + headroom;
-       xdp_set_data_meta_invalid(xdp);
-       xdp->data_end = xdp->data + len;
-       xdp->rxq = &rq->xdp_rxq;
-       xdp->frame_sz = rq->buff.frame0_sz;
+       xdp_init_buff(xdp, rq->buff.frame0_sz, &rq->xdp_rxq);
+       xdp_prepare_buff(xdp, va, headroom, len, false);
 }
 
 static struct sk_buff *
@@ -1786,12 +1783,10 @@ int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool
                rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe;
 
                rq->handle_rx_cqe = priv->profile->rx_handlers->handle_rx_cqe_mpwqe;
-#ifdef CONFIG_MLX5_EN_IPSEC
                if (MLX5_IPSEC_DEV(mdev)) {
                        netdev_err(netdev, "MPWQE RQ with IPSec offload not supported\n");
                        return -EINVAL;
                }
-#endif
                if (!rq->handle_rx_cqe) {
                        netdev_err(netdev, "RX handler of MPWQE RQ is not set\n");
                        return -EINVAL;
@@ -1821,3 +1816,48 @@ int mlx5e_rq_set_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params, bool
 
        return 0;
 }
+
+static void mlx5e_trap_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
+{
+       struct mlx5e_priv *priv = netdev_priv(rq->netdev);
+       struct mlx5_wq_cyc *wq = &rq->wqe.wq;
+       struct mlx5e_wqe_frag_info *wi;
+       struct sk_buff *skb;
+       u32 cqe_bcnt;
+       u16 trap_id;
+       u16 ci;
+
+       trap_id  = get_cqe_flow_tag(cqe);
+       ci       = mlx5_wq_cyc_ctr2ix(wq, be16_to_cpu(cqe->wqe_counter));
+       wi       = get_frag(rq, ci);
+       cqe_bcnt = be32_to_cpu(cqe->byte_cnt);
+
+       if (unlikely(MLX5E_RX_ERR_CQE(cqe))) {
+               rq->stats->wqe_err++;
+               goto free_wqe;
+       }
+
+       skb = mlx5e_skb_from_cqe_nonlinear(rq, cqe, wi, cqe_bcnt);
+       if (!skb)
+               goto free_wqe;
+
+       mlx5e_complete_rx_cqe(rq, cqe, cqe_bcnt, skb);
+       skb_push(skb, ETH_HLEN);
+
+       mlx5_devlink_trap_report(rq->mdev, trap_id, skb, &priv->dl_port);
+       dev_kfree_skb_any(skb);
+
+free_wqe:
+       mlx5e_free_rx_wqe(rq, wi, false);
+       mlx5_wq_cyc_pop(wq);
+}
+
+void mlx5e_rq_set_trap_handlers(struct mlx5e_rq *rq, struct mlx5e_params *params)
+{
+       rq->wqe.skb_from_cqe = mlx5e_rx_is_linear_skb(params, NULL) ?
+                              mlx5e_skb_from_cqe_linear :
+                              mlx5e_skb_from_cqe_nonlinear;
+       rq->post_wqes = mlx5e_post_rx_wqes;
+       rq->dealloc_wqe = mlx5e_dealloc_rx_wqe;
+       rq->handle_rx_cqe = mlx5e_trap_handle_rx_cqe;
+}
index 2cf2042..92c5b81 100644 (file)
@@ -420,6 +420,25 @@ static void mlx5e_stats_grp_sw_update_stats_ptp(struct mlx5e_priv *priv,
        }
 }
 
+static void mlx5e_stats_grp_sw_update_stats_qos(struct mlx5e_priv *priv,
+                                               struct mlx5e_sw_stats *s)
+{
+       struct mlx5e_sq_stats **stats;
+       u16 max_qos_sqs;
+       int i;
+
+       /* Pairs with smp_store_release in mlx5e_open_qos_sq. */
+       max_qos_sqs = smp_load_acquire(&priv->htb.max_qos_sqs);
+       stats = READ_ONCE(priv->htb.qos_sq_stats);
+
+       for (i = 0; i < max_qos_sqs; i++) {
+               mlx5e_stats_grp_sw_update_stats_sq(s, READ_ONCE(stats[i]));
+
+               /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92657 */
+               barrier();
+       }
+}
+
 static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
 {
        struct mlx5e_sw_stats *s = &priv->stats.sw;
@@ -449,6 +468,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(sw)
                }
        }
        mlx5e_stats_grp_sw_update_stats_ptp(priv, s);
+       mlx5e_stats_grp_sw_update_stats_qos(priv, s);
 }
 
 static const struct counter_desc q_stats_desc[] = {
@@ -1740,6 +1760,41 @@ static const struct counter_desc ptp_cq_stats_desc[] = {
        { MLX5E_DECLARE_PTP_CQ_STAT(struct mlx5e_ptp_cq_stats, abort_abs_diff_ns) },
 };
 
+static const struct counter_desc qos_sq_stats_desc[] = {
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, packets) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, bytes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_packets) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_bytes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_inner_packets) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tso_inner_bytes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, csum_partial) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, csum_partial_inner) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, added_vlan_packets) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, nop) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, mpwqe_blks) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, mpwqe_pkts) },
+#ifdef CONFIG_MLX5_EN_TLS
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_encrypted_packets) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_encrypted_bytes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_ctx) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_ooo) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_dump_packets) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_dump_bytes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_resync_bytes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_skip_no_sync_data) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_drop_no_sync_data) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, tls_drop_bypass_req) },
+#endif
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, csum_none) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, stopped) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, dropped) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, xmit_more) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, recover) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, cqes) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, wake) },
+       { MLX5E_DECLARE_QOS_TX_STAT(struct mlx5e_sq_stats, cqe_err) },
+};
+
 #define NUM_RQ_STATS                   ARRAY_SIZE(rq_stats_desc)
 #define NUM_SQ_STATS                   ARRAY_SIZE(sq_stats_desc)
 #define NUM_XDPSQ_STATS                        ARRAY_SIZE(xdpsq_stats_desc)
@@ -1750,6 +1805,49 @@ static const struct counter_desc ptp_cq_stats_desc[] = {
 #define NUM_PTP_SQ_STATS               ARRAY_SIZE(ptp_sq_stats_desc)
 #define NUM_PTP_CH_STATS               ARRAY_SIZE(ptp_ch_stats_desc)
 #define NUM_PTP_CQ_STATS               ARRAY_SIZE(ptp_cq_stats_desc)
+#define NUM_QOS_SQ_STATS               ARRAY_SIZE(qos_sq_stats_desc)
+
+static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(qos)
+{
+       /* Pairs with smp_store_release in mlx5e_open_qos_sq. */
+       return NUM_QOS_SQ_STATS * smp_load_acquire(&priv->htb.max_qos_sqs);
+}
+
+static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(qos)
+{
+       /* Pairs with smp_store_release in mlx5e_open_qos_sq. */
+       u16 max_qos_sqs = smp_load_acquire(&priv->htb.max_qos_sqs);
+       int i, qid;
+
+       for (qid = 0; qid < max_qos_sqs; qid++)
+               for (i = 0; i < NUM_QOS_SQ_STATS; i++)
+                       sprintf(data + (idx++) * ETH_GSTRING_LEN,
+                               qos_sq_stats_desc[i].format, qid);
+
+       return idx;
+}
+
+static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(qos)
+{
+       struct mlx5e_sq_stats **stats;
+       u16 max_qos_sqs;
+       int i, qid;
+
+       /* Pairs with smp_store_release in mlx5e_open_qos_sq. */
+       max_qos_sqs = smp_load_acquire(&priv->htb.max_qos_sqs);
+       stats = READ_ONCE(priv->htb.qos_sq_stats);
+
+       for (qid = 0; qid < max_qos_sqs; qid++) {
+               struct mlx5e_sq_stats *s = READ_ONCE(stats[qid]);
+
+               for (i = 0; i < NUM_QOS_SQ_STATS; i++)
+                       data[idx++] = MLX5E_READ_CTR64_CPU(s, qos_sq_stats_desc, i);
+       }
+
+       return idx;
+}
+
+static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(qos) { return; }
 
 static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(ptp)
 {
@@ -1932,6 +2030,7 @@ MLX5E_DEFINE_STATS_GRP(per_port_buff_congest, 0);
 MLX5E_DEFINE_STATS_GRP(eth_ext, 0);
 static MLX5E_DEFINE_STATS_GRP(tls, 0);
 static MLX5E_DEFINE_STATS_GRP(ptp, 0);
+static MLX5E_DEFINE_STATS_GRP(qos, 0);
 
 /* The stats groups order is opposite to the update_stats() order calls */
 mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = {
@@ -1955,6 +2054,7 @@ mlx5e_stats_grp_t mlx5e_nic_stats_grps[] = {
        &MLX5E_STATS_GRP(channels),
        &MLX5E_STATS_GRP(per_port_buff_congest),
        &MLX5E_STATS_GRP(ptp),
+       &MLX5E_STATS_GRP(qos),
 };
 
 unsigned int mlx5e_nic_stats_grps_num(struct mlx5e_priv *priv)
index e41fc11..93c4131 100644 (file)
@@ -55,6 +55,8 @@
 #define MLX5E_DECLARE_PTP_CH_STAT(type, fld) "ptp_ch_"#fld, offsetof(type, fld)
 #define MLX5E_DECLARE_PTP_CQ_STAT(type, fld) "ptp_cq%d_"#fld, offsetof(type, fld)
 
+#define MLX5E_DECLARE_QOS_TX_STAT(type, fld) "qos_tx%d_"#fld, offsetof(type, fld)
+
 struct counter_desc {
        char            format[ETH_GSTRING_LEN];
        size_t          offset; /* Byte offset */
index 4cdf834..8fd38ad 100644 (file)
@@ -67,6 +67,7 @@
 #include "lib/geneve.h"
 #include "lib/fs_chains.h"
 #include "diag/en_tc_tracepoint.h"
+#include <asm/div64.h>
 
 #define nic_chains(priv) ((priv)->fs.tc.chains)
 #define MLX5_MH_ACT_SZ MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)
@@ -1162,6 +1163,9 @@ mlx5e_tc_offload_fdb_rules(struct mlx5_eswitch *esw,
        struct mlx5e_tc_mod_hdr_acts *mod_hdr_acts;
        struct mlx5_flow_handle *rule;
 
+       if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH)
+               return mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
+
        if (flow_flag_test(flow, CT)) {
                mod_hdr_acts = &attr->parse_attr->mod_hdr_acts;
 
@@ -1192,6 +1196,9 @@ mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw,
 {
        flow_flag_clear(flow, OFFLOADED);
 
+       if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH)
+               goto offload_rule_0;
+
        if (flow_flag_test(flow, CT)) {
                mlx5_tc_ct_delete_flow(get_ct_priv(flow->priv), flow, attr);
                return;
@@ -1200,6 +1207,7 @@ mlx5e_tc_unoffload_fdb_rules(struct mlx5_eswitch *esw,
        if (attr->esw_attr->split_count)
                mlx5_eswitch_del_fwd_rule(esw, flow->rule[1], attr);
 
+offload_rule_0:
        mlx5_eswitch_del_offloaded_rule(esw, flow->rule[0], attr);
 }
 
@@ -1317,12 +1325,6 @@ mlx5e_tc_add_fdb_flow(struct mlx5e_priv *priv,
        int err = 0;
        int out_index;
 
-       if (!mlx5_chains_prios_supported(esw_chains(esw)) && attr->prio != 1) {
-               NL_SET_ERR_MSG_MOD(extack,
-                                  "E-switch priorities unsupported, upgrade FW");
-               return -EOPNOTSUPP;
-       }
-
        /* We check chain range only for tc flows.
         * For ft flows, we checked attr->chain was originally 0 and set it to
         * FDB_FT_CHAIN which is outside tc range.
@@ -2269,8 +2271,8 @@ static int __parse_cls_flower(struct mlx5e_priv *priv,
              BIT(FLOW_DISSECTOR_KEY_ENC_OPTS) |
              BIT(FLOW_DISSECTOR_KEY_MPLS))) {
                NL_SET_ERR_MSG_MOD(extack, "Unsupported key");
-               netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
-                           dissector->used_keys);
+               netdev_dbg(priv->netdev, "Unsupported key used: 0x%x\n",
+                          dissector->used_keys);
                return -EOPNOTSUPP;
        }
 
@@ -5007,13 +5009,13 @@ errout:
        return err;
 }
 
-static int apply_police_params(struct mlx5e_priv *priv, u32 rate,
+static int apply_police_params(struct mlx5e_priv *priv, u64 rate,
                               struct netlink_ext_ack *extack)
 {
        struct mlx5e_rep_priv *rpriv = priv->ppriv;
        struct mlx5_eswitch *esw;
+       u32 rate_mbps = 0;
        u16 vport_num;
-       u32 rate_mbps;
        int err;
 
        vport_num = rpriv->rep->vport;
@@ -5030,7 +5032,11 @@ static int apply_police_params(struct mlx5e_priv *priv, u32 rate,
         * Moreover, if rate is non zero we choose to configure to a minimum of
         * 1 mbit/sec.
         */
-       rate_mbps = rate ? max_t(u32, (rate * 8 + 500000) / 1000000, 1) : 0;
+       if (rate) {
+               rate = (rate * BITS_PER_BYTE) + 500000;
+               rate_mbps = max_t(u32, do_div(rate, 1000000), 1);
+       }
+
        err = mlx5_esw_modify_vport_rate(esw, vport_num, rate_mbps);
        if (err)
                NL_SET_ERR_MSG_MOD(extack, "failed applying action to hardware");
index e47e2a0..da6a358 100644 (file)
@@ -106,28 +106,53 @@ return_txq:
        return priv->port_ptp_tc2realtxq[up];
 }
 
+static int mlx5e_select_htb_queue(struct mlx5e_priv *priv, struct sk_buff *skb,
+                                 u16 htb_maj_id)
+{
+       u16 classid;
+
+       if ((TC_H_MAJ(skb->priority) >> 16) == htb_maj_id)
+               classid = TC_H_MIN(skb->priority);
+       else
+               classid = READ_ONCE(priv->htb.defcls);
+
+       if (!classid)
+               return 0;
+
+       return mlx5e_get_txq_by_classid(priv, classid);
+}
+
 u16 mlx5e_select_queue(struct net_device *dev, struct sk_buff *skb,
                       struct net_device *sb_dev)
 {
        struct mlx5e_priv *priv = netdev_priv(dev);
+       int num_tc_x_num_ch;
        int txq_ix;
        int up = 0;
        int ch_ix;
 
-       if (unlikely(priv->channels.port_ptp)) {
-               int num_tc_x_num_ch;
+       /* Sync with mlx5e_update_num_tc_x_num_ch - avoid refetching. */
+       num_tc_x_num_ch = READ_ONCE(priv->num_tc_x_num_ch);
+       if (unlikely(dev->real_num_tx_queues > num_tc_x_num_ch)) {
+               /* Order maj_id before defcls - pairs with mlx5e_htb_root_add. */
+               u16 htb_maj_id = smp_load_acquire(&priv->htb.maj_id);
 
-               if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
-                   mlx5e_use_ptpsq(skb))
-                       return mlx5e_select_ptpsq(dev, skb);
+               if (unlikely(htb_maj_id)) {
+                       txq_ix = mlx5e_select_htb_queue(priv, skb, htb_maj_id);
+                       if (txq_ix > 0)
+                               return txq_ix;
+               }
 
-               /* Sync with mlx5e_update_num_tc_x_num_ch - avoid refetching. */
-               num_tc_x_num_ch = READ_ONCE(priv->num_tc_x_num_ch);
+               if (unlikely(priv->channels.port_ptp))
+                       if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+                           mlx5e_use_ptpsq(skb))
+                               return mlx5e_select_ptpsq(dev, skb);
 
                txq_ix = netdev_pick_tx(dev, skb, NULL);
-               /* Fix netdev_pick_tx() not to choose ptp_channel txqs.
+               /* Fix netdev_pick_tx() not to choose ptp_channel and HTB txqs.
                 * If they are selected, switch to regular queues.
-                * Driver to select these queues only at mlx5e_select_ptpsq().
+                * Driver to select these queues only at mlx5e_select_ptpsq()
+                * and mlx5e_select_htb_queue().
                 */
                if (unlikely(txq_ix >= num_tc_x_num_ch))
                        txq_ix %= num_tc_x_num_ch;
@@ -241,9 +266,8 @@ mlx5e_txwqe_build_eseg_csum(struct mlx5e_txqsq *sq, struct sk_buff *skb,
                eseg->cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM;
                sq->stats->csum_partial++;
 #endif
-       } else if (unlikely(eseg->flow_table_metadata & cpu_to_be32(MLX5_ETH_WQE_FT_META_IPSEC))) {
+       } else if (unlikely(mlx5e_ipsec_eseg_meta(eseg))) {
                ipsec_txwqe_build_eseg_csum(sq, skb, eseg);
-
        } else
                sq->stats->csum_none++;
 }
@@ -682,9 +706,9 @@ void mlx5e_tx_mpwqe_ensure_complete(struct mlx5e_txqsq *sq)
 
 static bool mlx5e_txwqe_build_eseg(struct mlx5e_priv *priv, struct mlx5e_txqsq *sq,
                                   struct sk_buff *skb, struct mlx5e_accel_tx_state *accel,
-                                  struct mlx5_wqe_eth_seg *eseg)
+                                  struct mlx5_wqe_eth_seg *eseg, u16 ihs)
 {
-       if (unlikely(!mlx5e_accel_tx_eseg(priv, skb, eseg)))
+       if (unlikely(!mlx5e_accel_tx_eseg(priv, skb, eseg, ihs)))
                return false;
 
        mlx5e_txwqe_build_eseg_csum(sq, skb, accel, eseg);
@@ -703,6 +727,10 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
        u16 pi;
 
        sq = priv->txq2sq[skb_get_queue_mapping(skb)];
+       if (unlikely(!sq)) {
+               dev_kfree_skb_any(skb);
+               return NETDEV_TX_OK;
+       }
 
        /* May send SKBs and WQEs. */
        if (unlikely(!mlx5e_accel_tx_begin(dev, sq, skb, &accel)))
@@ -714,7 +742,8 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
                if (mlx5e_tx_skb_supports_mpwqe(skb, &attr)) {
                        struct mlx5_wqe_eth_seg eseg = {};
 
-                       if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &accel, &eseg)))
+                       if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &accel, &eseg,
+                                                            attr.ihs)))
                                return NETDEV_TX_OK;
 
                        mlx5e_sq_xmit_mpwqe(sq, skb, &eseg, netdev_xmit_more());
@@ -731,7 +760,7 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
        /* May update the WQE, but may not post other WQEs. */
        mlx5e_accel_tx_finish(sq, wqe, &accel,
                              (struct mlx5_wqe_inline_seg *)(wqe->data + wqe_attr.ds_cnt_inl));
-       if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &accel, &wqe->eth)))
+       if (unlikely(!mlx5e_txwqe_build_eseg(priv, sq, skb, &accel, &wqe->eth, attr.ihs)))
                return NETDEV_TX_OK;
 
        mlx5e_sq_xmit_wqe(sq, skb, &attr, &wqe_attr, wqe, pi, netdev_xmit_more());
index a3cfe06..d54da37 100644 (file)
@@ -115,17 +115,21 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
                                               napi);
        struct mlx5e_ch_stats *ch_stats = c->stats;
        struct mlx5e_xdpsq *xsksq = &c->xsksq;
+       struct mlx5e_txqsq __rcu **qos_sqs;
        struct mlx5e_rq *xskrq = &c->xskrq;
        struct mlx5e_rq *rq = &c->rq;
        bool aff_change = false;
        bool busy_xsk = false;
        bool busy = false;
        int work_done = 0;
+       u16 qos_sqs_size;
        bool xsk_open;
        int i;
 
        rcu_read_lock();
 
+       qos_sqs = rcu_dereference(c->qos_sqs);
+
        xsk_open = test_bit(MLX5E_CHANNEL_STATE_XSK, c->state);
 
        ch_stats->poll++;
@@ -133,6 +137,18 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
        for (i = 0; i < c->num_tc; i++)
                busy |= mlx5e_poll_tx_cq(&c->sq[i].cq, budget);
 
+       if (unlikely(qos_sqs)) {
+               smp_rmb(); /* Pairs with mlx5e_qos_alloc_queues. */
+               qos_sqs_size = READ_ONCE(c->qos_sqs_size);
+
+               for (i = 0; i < qos_sqs_size; i++) {
+                       struct mlx5e_txqsq *sq = rcu_dereference(qos_sqs[i]);
+
+                       if (sq)
+                               busy |= mlx5e_poll_tx_cq(&sq->cq, budget);
+               }
+       }
+
        busy |= mlx5e_poll_xdpsq_cq(&c->xdpsq.cq);
 
        if (c->xdp)
@@ -186,6 +202,16 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget)
                mlx5e_handle_tx_dim(&c->sq[i]);
                mlx5e_cq_arm(&c->sq[i].cq);
        }
+       if (unlikely(qos_sqs)) {
+               for (i = 0; i < qos_sqs_size; i++) {
+                       struct mlx5e_txqsq *sq = rcu_dereference(qos_sqs[i]);
+
+                       if (sq) {
+                               mlx5e_handle_tx_dim(sq);
+                               mlx5e_cq_arm(&sq->cq);
+                       }
+               }
+       }
 
        mlx5e_handle_rx_dim(rq);
 
index fc0afa0..174dfbc 100644 (file)
@@ -467,7 +467,7 @@ int mlx5_eq_table_init(struct mlx5_core_dev *dev)
        for (i = 0; i < MLX5_EVENT_TYPE_MAX; i++)
                ATOMIC_INIT_NOTIFIER_HEAD(&eq_table->nh[i]);
 
-       eq_table->irq_table = dev->priv.irq_table;
+       eq_table->irq_table = mlx5_irq_table_get(dev);
        return 0;
 }
 
@@ -595,6 +595,9 @@ static void gather_async_events_mask(struct mlx5_core_dev *dev, u64 mask[4])
                async_event_mask |=
                        (1ull << MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED);
 
+       if (MLX5_CAP_GEN_MAX(dev, vhca_state))
+               async_event_mask |= (1ull << MLX5_EVENT_TYPE_VHCA_STATE_CHANGE);
+
        mask[0] = async_event_mask;
 
        if (MLX5_CAP_GEN(dev, event_cap))
index 2b85d47..3e19b17 100644 (file)
@@ -95,22 +95,21 @@ int esw_acl_egress_lgcy_setup(struct mlx5_eswitch *esw,
                return 0;
        }
 
-       if (!IS_ERR_OR_NULL(vport->egress.acl))
-               return 0;
-
-       vport->egress.acl = esw_acl_table_create(esw, vport->vport,
-                                                MLX5_FLOW_NAMESPACE_ESW_EGRESS,
-                                                table_size);
-       if (IS_ERR(vport->egress.acl)) {
-               err = PTR_ERR(vport->egress.acl);
-               vport->egress.acl = NULL;
-               goto out;
+       if (!vport->egress.acl) {
+               vport->egress.acl = esw_acl_table_create(esw, vport->vport,
+                                                        MLX5_FLOW_NAMESPACE_ESW_EGRESS,
+                                                        table_size);
+               if (IS_ERR(vport->egress.acl)) {
+                       err = PTR_ERR(vport->egress.acl);
+                       vport->egress.acl = NULL;
+                       goto out;
+               }
+
+               err = esw_acl_egress_lgcy_groups_create(esw, vport);
+               if (err)
+                       goto out;
        }
 
-       err = esw_acl_egress_lgcy_groups_create(esw, vport);
-       if (err)
-               goto out;
-
        esw_debug(esw->dev,
                  "vport[%d] configure egress rules, vlan(%d) qos(%d)\n",
                  vport->vport, vport->info.vlan, vport->info.qos);
index 4c74e26..26b37a0 100644 (file)
@@ -150,7 +150,7 @@ static void esw_acl_egress_ofld_groups_destroy(struct mlx5_vport *vport)
 
 static bool esw_acl_egress_needed(const struct mlx5_eswitch *esw, u16 vport_num)
 {
-       return mlx5_eswitch_is_vf_vport(esw, vport_num);
+       return mlx5_eswitch_is_vf_vport(esw, vport_num) || mlx5_esw_is_sf_vport(esw, vport_num);
 }
 
 int esw_acl_egress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
index ffff11b..cb1e181 100644 (file)
@@ -122,3 +122,44 @@ struct devlink_port *mlx5_esw_offloads_devlink_port(struct mlx5_eswitch *esw, u1
        vport = mlx5_eswitch_get_vport(esw, vport_num);
        return vport->dl_port;
 }
+
+int mlx5_esw_devlink_sf_port_register(struct mlx5_eswitch *esw, struct devlink_port *dl_port,
+                                     u16 vport_num, u32 sfnum)
+{
+       struct mlx5_core_dev *dev = esw->dev;
+       struct netdev_phys_item_id ppid = {};
+       unsigned int dl_port_index;
+       struct mlx5_vport *vport;
+       struct devlink *devlink;
+       u16 pfnum;
+       int err;
+
+       vport = mlx5_eswitch_get_vport(esw, vport_num);
+       if (IS_ERR(vport))
+               return PTR_ERR(vport);
+
+       pfnum = PCI_FUNC(dev->pdev->devfn);
+       mlx5_esw_get_port_parent_id(dev, &ppid);
+       memcpy(dl_port->attrs.switch_id.id, &ppid.id[0], ppid.id_len);
+       dl_port->attrs.switch_id.id_len = ppid.id_len;
+       devlink_port_attrs_pci_sf_set(dl_port, 0, pfnum, sfnum);
+       devlink = priv_to_devlink(dev);
+       dl_port_index = mlx5_esw_vport_to_devlink_port_index(dev, vport_num);
+       err = devlink_port_register(devlink, dl_port, dl_port_index);
+       if (err)
+               return err;
+
+       vport->dl_port = dl_port;
+       return 0;
+}
+
+void mlx5_esw_devlink_sf_port_unregister(struct mlx5_eswitch *esw, u16 vport_num)
+{
+       struct mlx5_vport *vport;
+
+       vport = mlx5_eswitch_get_vport(esw, vport_num);
+       if (IS_ERR(vport))
+               return;
+       devlink_port_unregister(vport->dl_port);
+       vport->dl_port = NULL;
+}
index da901e3..820305b 100644 (file)
@@ -1042,8 +1042,7 @@ static int esw_vport_enable_qos(struct mlx5_eswitch *esw,
        void *vport_elem;
        int err = 0;
 
-       if (!esw->qos.enabled || !MLX5_CAP_GEN(dev, qos) ||
-           !MLX5_CAP_QOS(dev, esw_scheduling))
+       if (!esw->qos.enabled)
                return 0;
 
        if (vport->qos.enabled)
@@ -1273,8 +1272,8 @@ static void esw_vport_cleanup(struct mlx5_eswitch *esw, struct mlx5_vport *vport
        esw_vport_cleanup_acl(esw, vport);
 }
 
-static int esw_enable_vport(struct mlx5_eswitch *esw, u16 vport_num,
-                           enum mlx5_eswitch_vport_event enabled_events)
+int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, u16 vport_num,
+                         enum mlx5_eswitch_vport_event enabled_events)
 {
        struct mlx5_vport *vport;
        int ret;
@@ -1310,7 +1309,7 @@ done:
        return ret;
 }
 
-static void esw_disable_vport(struct mlx5_eswitch *esw, u16 vport_num)
+void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, u16 vport_num)
 {
        struct mlx5_vport *vport;
 
@@ -1366,9 +1365,15 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev)
 {
        int outlen = MLX5_ST_SZ_BYTES(query_esw_functions_out);
        u32 in[MLX5_ST_SZ_DW(query_esw_functions_in)] = {};
+       u16 max_sf_vports;
        u32 *out;
        int err;
 
+       max_sf_vports = mlx5_sf_max_functions(dev);
+       /* Device interface is array of 64-bits */
+       if (max_sf_vports)
+               outlen += DIV_ROUND_UP(max_sf_vports, BITS_PER_TYPE(__be64)) * sizeof(__be64);
+
        out = kvzalloc(outlen, GFP_KERNEL);
        if (!out)
                return ERR_PTR(-ENOMEM);
@@ -1376,7 +1381,7 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev)
        MLX5_SET(query_esw_functions_in, in, opcode,
                 MLX5_CMD_OP_QUERY_ESW_FUNCTIONS);
 
-       err = mlx5_cmd_exec_inout(dev, query_esw_functions, in, out);
+       err = mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
        if (!err)
                return out;
 
@@ -1426,7 +1431,7 @@ int mlx5_eswitch_load_vport(struct mlx5_eswitch *esw, u16 vport_num,
 {
        int err;
 
-       err = esw_enable_vport(esw, vport_num, enabled_events);
+       err = mlx5_esw_vport_enable(esw, vport_num, enabled_events);
        if (err)
                return err;
 
@@ -1437,14 +1442,14 @@ int mlx5_eswitch_load_vport(struct mlx5_eswitch *esw, u16 vport_num,
        return err;
 
 err_rep:
-       esw_disable_vport(esw, vport_num);
+       mlx5_esw_vport_disable(esw, vport_num);
        return err;
 }
 
 void mlx5_eswitch_unload_vport(struct mlx5_eswitch *esw, u16 vport_num)
 {
        esw_offloads_unload_rep(esw, vport_num);
-       esw_disable_vport(esw, vport_num);
+       mlx5_esw_vport_disable(esw, vport_num);
 }
 
 void mlx5_eswitch_unload_vf_vports(struct mlx5_eswitch *esw, u16 num_vfs)
@@ -1594,6 +1599,15 @@ mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, int num_vfs)
        kvfree(out);
 }
 
+static void mlx5_esw_mode_change_notify(struct mlx5_eswitch *esw, u16 mode)
+{
+       struct mlx5_esw_event_info info = {};
+
+       info.new_mode = mode;
+
+       blocking_notifier_call_chain(&esw->n_head, 0, &info);
+}
+
 /**
  * mlx5_eswitch_enable_locked - Enable eswitch
  * @esw:       Pointer to eswitch
@@ -1654,6 +1668,8 @@ int mlx5_eswitch_enable_locked(struct mlx5_eswitch *esw, int mode, int num_vfs)
                 mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS",
                 esw->esw_funcs.num_vfs, esw->enabled_vports);
 
+       mlx5_esw_mode_change_notify(esw, mode);
+
        return 0;
 
 abort:
@@ -1710,6 +1726,11 @@ void mlx5_eswitch_disable_locked(struct mlx5_eswitch *esw, bool clear_vf)
                 esw->mode == MLX5_ESWITCH_LEGACY ? "LEGACY" : "OFFLOADS",
                 esw->esw_funcs.num_vfs, esw->enabled_vports);
 
+       /* Notify eswitch users that it is exiting from current mode.
+        * So that it can do necessary cleanup before the eswitch is disabled.
+        */
+       mlx5_esw_mode_change_notify(esw, MLX5_ESWITCH_NONE);
+
        mlx5_eswitch_event_handlers_unregister(esw);
 
        if (esw->mode == MLX5_ESWITCH_LEGACY)
@@ -1810,6 +1831,7 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
        esw->offloads.inline_mode = MLX5_INLINE_MODE_NONE;
 
        dev->priv.eswitch = esw;
+       BLOCKING_INIT_NOTIFIER_HEAD(&esw->n_head);
        return 0;
 abort:
        if (esw->work_queue)
@@ -1899,7 +1921,8 @@ static bool
 is_port_function_supported(const struct mlx5_eswitch *esw, u16 vport_num)
 {
        return vport_num == MLX5_VPORT_PF ||
-              mlx5_eswitch_is_vf_vport(esw, vport_num);
+              mlx5_eswitch_is_vf_vport(esw, vport_num) ||
+              mlx5_esw_is_sf_vport(esw, vport_num);
 }
 
 int mlx5_devlink_port_function_hw_addr_get(struct devlink *devlink,
@@ -2500,4 +2523,12 @@ bool mlx5_esw_multipath_prereq(struct mlx5_core_dev *dev0,
                dev1->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS);
 }
 
+int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&esw->n_head, nb);
+}
 
+void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *nb)
+{
+       blocking_notifier_chain_unregister(&esw->n_head, nb);
+}
index cf87de9..479d2ac 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/mlx5/fs.h>
 #include "lib/mpfs.h"
 #include "lib/fs_chains.h"
+#include "sf/sf.h"
 #include "en/tc_ct.h"
 
 #ifdef CONFIG_MLX5_ESWITCH
@@ -277,6 +278,7 @@ struct mlx5_eswitch {
        struct {
                u32             large_group_num;
        }  params;
+       struct blocking_notifier_head n_head;
 };
 
 void esw_offloads_disable(struct mlx5_eswitch *esw);
@@ -499,6 +501,40 @@ static inline u16 mlx5_eswitch_first_host_vport_num(struct mlx5_core_dev *dev)
                MLX5_VPORT_PF : MLX5_VPORT_FIRST_VF;
 }
 
+static inline int mlx5_esw_sf_start_idx(const struct mlx5_eswitch *esw)
+{
+       /* PF and VF vports indices start from 0 to max_vfs */
+       return MLX5_VPORT_PF_PLACEHOLDER + mlx5_core_max_vfs(esw->dev);
+}
+
+static inline int mlx5_esw_sf_end_idx(const struct mlx5_eswitch *esw)
+{
+       return mlx5_esw_sf_start_idx(esw) + mlx5_sf_max_functions(esw->dev);
+}
+
+static inline int
+mlx5_esw_sf_vport_num_to_index(const struct mlx5_eswitch *esw, u16 vport_num)
+{
+       return vport_num - mlx5_sf_start_function_id(esw->dev) +
+              MLX5_VPORT_PF_PLACEHOLDER + mlx5_core_max_vfs(esw->dev);
+}
+
+static inline u16
+mlx5_esw_sf_vport_index_to_num(const struct mlx5_eswitch *esw, int idx)
+{
+       return mlx5_sf_start_function_id(esw->dev) + idx -
+              (MLX5_VPORT_PF_PLACEHOLDER + mlx5_core_max_vfs(esw->dev));
+}
+
+static inline bool
+mlx5_esw_is_sf_vport(const struct mlx5_eswitch *esw, u16 vport_num)
+{
+       return mlx5_sf_supported(esw->dev) &&
+              vport_num >= mlx5_sf_start_function_id(esw->dev) &&
+              (vport_num < (mlx5_sf_start_function_id(esw->dev) +
+                            mlx5_sf_max_functions(esw->dev)));
+}
+
 static inline bool mlx5_eswitch_is_funcs_handler(const struct mlx5_core_dev *dev)
 {
        return mlx5_core_is_ecpf_esw_manager(dev);
@@ -527,6 +563,10 @@ static inline int mlx5_eswitch_vport_num_to_index(struct mlx5_eswitch *esw,
        if (vport_num == MLX5_VPORT_UPLINK)
                return mlx5_eswitch_uplink_idx(esw);
 
+       if (mlx5_esw_is_sf_vport(esw, vport_num))
+               return mlx5_esw_sf_vport_num_to_index(esw, vport_num);
+
+       /* PF and VF vports start from 0 to max_vfs */
        return vport_num;
 }
 
@@ -540,6 +580,12 @@ static inline u16 mlx5_eswitch_index_to_vport_num(struct mlx5_eswitch *esw,
        if (index == mlx5_eswitch_uplink_idx(esw))
                return MLX5_VPORT_UPLINK;
 
+       /* SF vports indices are after VFs and before ECPF */
+       if (mlx5_sf_supported(esw->dev) &&
+           index > mlx5_core_max_vfs(esw->dev))
+               return mlx5_esw_sf_vport_index_to_num(esw, index);
+
+       /* PF and VF vports start from 0 to max_vfs */
        return index;
 }
 
@@ -625,6 +671,11 @@ void mlx5e_tc_clean_fdb_peer_flows(struct mlx5_eswitch *esw);
        for ((vport) = (nvfs);                                          \
             (vport) >= (esw)->first_host_vport; (vport)--)
 
+#define mlx5_esw_for_each_sf_rep(esw, i, rep)          \
+       for ((i) = mlx5_esw_sf_start_idx(esw);          \
+            (rep) = &(esw)->offloads.vport_reps[(i)],  \
+            (i) < mlx5_esw_sf_end_idx(esw); (i++))
+
 struct mlx5_eswitch *mlx5_devlink_eswitch_get(struct devlink *devlink);
 struct mlx5_vport *__must_check
 mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num);
@@ -638,6 +689,10 @@ mlx5_eswitch_enable_pf_vf_vports(struct mlx5_eswitch *esw,
                                 enum mlx5_eswitch_vport_event enabled_events);
 void mlx5_eswitch_disable_pf_vf_vports(struct mlx5_eswitch *esw);
 
+int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, u16 vport_num,
+                         enum mlx5_eswitch_vport_event enabled_events);
+void mlx5_esw_vport_disable(struct mlx5_eswitch *esw, u16 vport_num);
+
 int
 esw_vport_create_offloads_acl_tables(struct mlx5_eswitch *esw,
                                     struct mlx5_vport *vport);
@@ -656,6 +711,9 @@ esw_get_max_restore_tag(struct mlx5_eswitch *esw);
 int esw_offloads_load_rep(struct mlx5_eswitch *esw, u16 vport_num);
 void esw_offloads_unload_rep(struct mlx5_eswitch *esw, u16 vport_num);
 
+int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num);
+void mlx5_esw_offloads_rep_unload(struct mlx5_eswitch *esw, u16 vport_num);
+
 int mlx5_eswitch_load_vport(struct mlx5_eswitch *esw, u16 vport_num,
                            enum mlx5_eswitch_vport_event enabled_events);
 void mlx5_eswitch_unload_vport(struct mlx5_eswitch *esw, u16 vport_num);
@@ -667,6 +725,26 @@ void mlx5_eswitch_unload_vf_vports(struct mlx5_eswitch *esw, u16 num_vfs);
 int mlx5_esw_offloads_devlink_port_register(struct mlx5_eswitch *esw, u16 vport_num);
 void mlx5_esw_offloads_devlink_port_unregister(struct mlx5_eswitch *esw, u16 vport_num);
 struct devlink_port *mlx5_esw_offloads_devlink_port(struct mlx5_eswitch *esw, u16 vport_num);
+
+int mlx5_esw_devlink_sf_port_register(struct mlx5_eswitch *esw, struct devlink_port *dl_port,
+                                     u16 vport_num, u32 sfnum);
+void mlx5_esw_devlink_sf_port_unregister(struct mlx5_eswitch *esw, u16 vport_num);
+
+int mlx5_esw_offloads_sf_vport_enable(struct mlx5_eswitch *esw, struct devlink_port *dl_port,
+                                     u16 vport_num, u32 sfnum);
+void mlx5_esw_offloads_sf_vport_disable(struct mlx5_eswitch *esw, u16 vport_num);
+
+/**
+ * mlx5_esw_event_info - Indicates eswitch mode changed/changing.
+ *
+ * @new_mode: New mode of eswitch.
+ */
+struct mlx5_esw_event_info {
+       u16 new_mode;
+};
+
+int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *n);
+void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *n);
 #else  /* CONFIG_MLX5_ESWITCH */
 /* eswitch API stubs */
 static inline int  mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; }
index 2f6a0ae..7f09f2b 100644 (file)
@@ -1800,11 +1800,22 @@ static void __esw_offloads_unload_rep(struct mlx5_eswitch *esw,
                esw->offloads.rep_ops[rep_type]->unload(rep);
 }
 
+static void __unload_reps_sf_vport(struct mlx5_eswitch *esw, u8 rep_type)
+{
+       struct mlx5_eswitch_rep *rep;
+       int i;
+
+       mlx5_esw_for_each_sf_rep(esw, i, rep)
+               __esw_offloads_unload_rep(esw, rep, rep_type);
+}
+
 static void __unload_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type)
 {
        struct mlx5_eswitch_rep *rep;
        int i;
 
+       __unload_reps_sf_vport(esw, rep_type);
+
        mlx5_esw_for_each_vf_rep_reverse(esw, i, rep, esw->esw_funcs.num_vfs)
                __esw_offloads_unload_rep(esw, rep, rep_type);
 
@@ -1822,7 +1833,7 @@ static void __unload_reps_all_vport(struct mlx5_eswitch *esw, u8 rep_type)
        __esw_offloads_unload_rep(esw, rep, rep_type);
 }
 
-static int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num)
+int mlx5_esw_offloads_rep_load(struct mlx5_eswitch *esw, u16 vport_num)
 {
        struct mlx5_eswitch_rep *rep;
        int rep_type;
@@ -1846,7 +1857,7 @@ err_reps:
        return err;
 }
 
-static void mlx5_esw_offloads_rep_unload(struct mlx5_eswitch *esw, u16 vport_num)
+void mlx5_esw_offloads_rep_unload(struct mlx5_eswitch *esw, u16 vport_num)
 {
        struct mlx5_eswitch_rep *rep;
        int rep_type;
@@ -2824,3 +2835,35 @@ u32 mlx5_eswitch_get_vport_metadata_for_match(struct mlx5_eswitch *esw,
        return vport->metadata << (32 - ESW_SOURCE_PORT_METADATA_BITS);
 }
 EXPORT_SYMBOL(mlx5_eswitch_get_vport_metadata_for_match);
+
+int mlx5_esw_offloads_sf_vport_enable(struct mlx5_eswitch *esw, struct devlink_port *dl_port,
+                                     u16 vport_num, u32 sfnum)
+{
+       int err;
+
+       err = mlx5_esw_vport_enable(esw, vport_num, MLX5_VPORT_UC_ADDR_CHANGE);
+       if (err)
+               return err;
+
+       err = mlx5_esw_devlink_sf_port_register(esw, dl_port, vport_num, sfnum);
+       if (err)
+               goto devlink_err;
+
+       err = mlx5_esw_offloads_rep_load(esw, vport_num);
+       if (err)
+               goto rep_err;
+       return 0;
+
+rep_err:
+       mlx5_esw_devlink_sf_port_unregister(esw, vport_num);
+devlink_err:
+       mlx5_esw_vport_disable(esw, vport_num);
+       return err;
+}
+
+void mlx5_esw_offloads_sf_vport_disable(struct mlx5_eswitch *esw, u16 vport_num)
+{
+       mlx5_esw_offloads_rep_unload(esw, vport_num);
+       mlx5_esw_devlink_sf_port_unregister(esw, vport_num);
+       mlx5_esw_vport_disable(esw, vport_num);
+}
index 3ce17c3..d713ae2 100644 (file)
@@ -23,7 +23,7 @@ static int temp_warn(struct notifier_block *, unsigned long, void *);
 static int port_module(struct notifier_block *, unsigned long, void *);
 static int pcie_core(struct notifier_block *, unsigned long, void *);
 
-/* handler which forwards the event to events->nh, driver notifiers */
+/* handler which forwards the event to events->fw_nh, driver notifiers */
 static int forward_event(struct notifier_block *, unsigned long, void *);
 
 static struct mlx5_nb events_nbs_ref[] = {
@@ -55,12 +55,14 @@ struct mlx5_events {
        struct mlx5_core_dev *dev;
        struct workqueue_struct *wq;
        struct mlx5_event_nb  notifiers[ARRAY_SIZE(events_nbs_ref)];
-       /* driver notifier chain */
-       struct atomic_notifier_head nh;
+       /* driver notifier chain for fw events */
+       struct atomic_notifier_head fw_nh;
        /* port module events stats */
        struct mlx5_pme_stats pme_stats;
        /*pcie_core*/
        struct work_struct pcie_core_work;
+       /* driver notifier chain for sw events */
+       struct blocking_notifier_head sw_nh;
 };
 
 static const char *eqe_type_str(u8 type)
@@ -110,6 +112,8 @@ static const char *eqe_type_str(u8 type)
                return "MLX5_EVENT_TYPE_CMD";
        case MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED:
                return "MLX5_EVENT_TYPE_ESW_FUNCTIONS_CHANGED";
+       case MLX5_EVENT_TYPE_VHCA_STATE_CHANGE:
+               return "MLX5_EVENT_TYPE_VHCA_STATE_CHANGE";
        case MLX5_EVENT_TYPE_PAGE_REQUEST:
                return "MLX5_EVENT_TYPE_PAGE_REQUEST";
        case MLX5_EVENT_TYPE_PAGE_FAULT:
@@ -331,7 +335,7 @@ static int forward_event(struct notifier_block *nb, unsigned long event, void *d
 
        mlx5_core_dbg(events->dev, "Async eqe type %s, subtype (%d) forward to interfaces\n",
                      eqe_type_str(eqe->type), eqe->sub_type);
-       atomic_notifier_call_chain(&events->nh, event, data);
+       atomic_notifier_call_chain(&events->fw_nh, event, data);
        return NOTIFY_OK;
 }
 
@@ -342,7 +346,7 @@ int mlx5_events_init(struct mlx5_core_dev *dev)
        if (!events)
                return -ENOMEM;
 
-       ATOMIC_INIT_NOTIFIER_HEAD(&events->nh);
+       ATOMIC_INIT_NOTIFIER_HEAD(&events->fw_nh);
        events->dev = dev;
        dev->priv.events = events;
        events->wq = create_singlethread_workqueue("mlx5_events");
@@ -351,6 +355,7 @@ int mlx5_events_init(struct mlx5_core_dev *dev)
                return -ENOMEM;
        }
        INIT_WORK(&events->pcie_core_work, mlx5_pcie_event);
+       BLOCKING_INIT_NOTIFIER_HEAD(&events->sw_nh);
 
        return 0;
 }
@@ -383,11 +388,14 @@ void mlx5_events_stop(struct mlx5_core_dev *dev)
        flush_workqueue(events->wq);
 }
 
+/* This API is used only for processing and forwarding firmware
+ * events to mlx5 consumer.
+ */
 int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
 {
        struct mlx5_events *events = dev->priv.events;
 
-       return atomic_notifier_chain_register(&events->nh, nb);
+       return atomic_notifier_chain_register(&events->fw_nh, nb);
 }
 EXPORT_SYMBOL(mlx5_notifier_register);
 
@@ -395,11 +403,41 @@ int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *n
 {
        struct mlx5_events *events = dev->priv.events;
 
-       return atomic_notifier_chain_unregister(&events->nh, nb);
+       return atomic_notifier_chain_unregister(&events->fw_nh, nb);
 }
 EXPORT_SYMBOL(mlx5_notifier_unregister);
 
 int mlx5_notifier_call_chain(struct mlx5_events *events, unsigned int event, void *data)
 {
-       return atomic_notifier_call_chain(&events->nh, event, data);
+       return atomic_notifier_call_chain(&events->fw_nh, event, data);
+}
+
+/* This API is used only for processing and forwarding driver-specific
+ * events to mlx5 consumers.
+ */
+int mlx5_blocking_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
+{
+       struct mlx5_events *events = dev->priv.events;
+
+       return blocking_notifier_chain_register(&events->sw_nh, nb);
+}
+
+int mlx5_blocking_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
+{
+       struct mlx5_events *events = dev->priv.events;
+
+       return blocking_notifier_chain_unregister(&events->sw_nh, nb);
+}
+
+int mlx5_blocking_notifier_call_chain(struct mlx5_core_dev *dev, unsigned int event,
+                                     void *data)
+{
+       struct mlx5_events *events = dev->priv.events;
+
+       return blocking_notifier_call_chain(&events->sw_nh, event, data);
+}
+
+void mlx5_events_work_enqueue(struct mlx5_core_dev *dev, struct work_struct *work)
+{
+       queue_work(dev->priv.events->wq, work);
 }
index b899539..07b4dbd 100644 (file)
 #define ETHTOOL_PRIO_NUM_LEVELS 1
 #define ETHTOOL_NUM_PRIOS 11
 #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS)
-/* Vlan, mac, ttc, inner ttc, {aRFS/accel and esp/esp_err} */
-#define KERNEL_NIC_PRIO_NUM_LEVELS 6
+/* Promiscuous, Vlan, mac, ttc, inner ttc, {aRFS/accel and esp/esp_err} */
+#define KERNEL_NIC_PRIO_NUM_LEVELS 7
 #define KERNEL_NIC_NUM_PRIOS 1
 /* One more level for tc */
 #define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1)
@@ -1141,6 +1141,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
 destroy_ft:
        root->cmds->destroy_flow_table(root, ft);
 free_ft:
+       rhltable_destroy(&ft->fgs_hash);
        kfree(ft);
 unlock_root:
        mutex_unlock(&root->chain_lock);
index f3d45ef..83a0537 100644 (file)
@@ -564,7 +564,9 @@ void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev)
        struct mlx5_core_dev *tmp_dev;
        int i, err;
 
-       if (!MLX5_CAP_GEN(dev, vport_group_manager))
+       if (!MLX5_CAP_GEN(dev, vport_group_manager) ||
+           !MLX5_CAP_GEN(dev, lag_master) ||
+           MLX5_CAP_GEN(dev, num_lag_ports) != MLX5_MAX_PORTS)
                return;
 
        tmp_dev = mlx5_get_next_phys_dev(dev);
@@ -582,12 +584,9 @@ void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev)
        if (mlx5_lag_dev_add_pf(ldev, dev, netdev) < 0)
                return;
 
-       for (i = 0; i < MLX5_MAX_PORTS; i++) {
-               tmp_dev = ldev->pf[i].dev;
-               if (!tmp_dev || !MLX5_CAP_GEN(tmp_dev, lag_master) ||
-                   MLX5_CAP_GEN(tmp_dev, num_lag_ports) != MLX5_MAX_PORTS)
+       for (i = 0; i < MLX5_MAX_PORTS; i++)
+               if (!ldev->pf[i].dev)
                        break;
-       }
 
        if (i >= MLX5_MAX_PORTS)
                ldev->flags |= MLX5_LAG_FLAG_READY;
index 947f346..381325b 100644 (file)
@@ -141,9 +141,6 @@ u32 mlx5_chains_get_nf_ft_chain(struct mlx5_fs_chains *chains)
 
 u32 mlx5_chains_get_prio_range(struct mlx5_fs_chains *chains)
 {
-       if (!mlx5_chains_prios_supported(chains))
-               return 1;
-
        if (mlx5_chains_ignore_flow_level_supported(chains))
                return UINT_MAX;
 
@@ -541,13 +538,13 @@ mlx5_chains_create_prio(struct mlx5_fs_chains *chains,
                        u32 chain, u32 prio, u32 level)
 {
        int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
-       struct mlx5_flow_handle *miss_rule = NULL;
+       struct mlx5_flow_handle *miss_rule;
        struct mlx5_flow_group *miss_group;
        struct mlx5_flow_table *next_ft;
        struct mlx5_flow_table *ft;
-       struct prio *prio_s = NULL;
        struct fs_chain *chain_s;
        struct list_head *pos;
+       struct prio *prio_s;
        u32 *flow_group_in;
        int err;
 
index 3a9fa62..d046db7 100644 (file)
@@ -90,4 +90,9 @@ int mlx5_create_encryption_key(struct mlx5_core_dev *mdev,
                               u32 key_type, u32 *p_key_id);
 void mlx5_destroy_encryption_key(struct mlx5_core_dev *mdev, u32 key_id);
 
+static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev)
+{
+       return devlink_net(priv_to_devlink(dev));
+}
+
 #endif
index c08315b..e4c9627 100644 (file)
@@ -73,6 +73,9 @@
 #include "ecpf.h"
 #include "lib/hv_vhca.h"
 #include "diag/rsc_dump.h"
+#include "sf/vhca_event.h"
+#include "sf/dev/dev.h"
+#include "sf/sf.h"
 
 MODULE_AUTHOR("Eli Cohen <eli@mellanox.com>");
 MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver");
@@ -82,7 +85,6 @@ unsigned int mlx5_core_debug_mask;
 module_param_named(debug_mask, mlx5_core_debug_mask, uint, 0644);
 MODULE_PARM_DESC(debug_mask, "debug mask: 1 = dump cmd data, 2 = dump cmd exec time, 3 = both. Default=0");
 
-#define MLX5_DEFAULT_PROF      2
 static unsigned int prof_sel = MLX5_DEFAULT_PROF;
 module_param_named(prof_sel, prof_sel, uint, 0444);
 MODULE_PARM_DESC(prof_sel, "profile selector. Valid range 0 - 2");
@@ -567,6 +569,8 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
        if (MLX5_CAP_GEN_MAX(dev, mkey_by_name))
                MLX5_SET(cmd_hca_cap, set_hca_cap, mkey_by_name, 1);
 
+       mlx5_vhca_state_cap_handle(dev, set_hca_cap);
+
        return set_caps(dev, set_ctx, MLX5_SET_HCA_CAP_OP_MOD_GENERAL_DEVICE);
 }
 
@@ -884,6 +888,24 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
                goto err_eswitch_cleanup;
        }
 
+       err = mlx5_vhca_event_init(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to init vhca event notifier %d\n", err);
+               goto err_fpga_cleanup;
+       }
+
+       err = mlx5_sf_hw_table_init(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to init SF HW table %d\n", err);
+               goto err_sf_hw_table_cleanup;
+       }
+
+       err = mlx5_sf_table_init(dev);
+       if (err) {
+               mlx5_core_err(dev, "Failed to init SF table %d\n", err);
+               goto err_sf_table_cleanup;
+       }
+
        dev->dm = mlx5_dm_create(dev);
        if (IS_ERR(dev->dm))
                mlx5_core_warn(dev, "Failed to init device memory%d\n", err);
@@ -894,6 +916,12 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
 
        return 0;
 
+err_sf_table_cleanup:
+       mlx5_sf_hw_table_cleanup(dev);
+err_sf_hw_table_cleanup:
+       mlx5_vhca_event_cleanup(dev);
+err_fpga_cleanup:
+       mlx5_fpga_cleanup(dev);
 err_eswitch_cleanup:
        mlx5_eswitch_cleanup(dev->priv.eswitch);
 err_sriov_cleanup:
@@ -925,6 +953,9 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
        mlx5_hv_vhca_destroy(dev->hv_vhca);
        mlx5_fw_tracer_destroy(dev->tracer);
        mlx5_dm_cleanup(dev);
+       mlx5_sf_table_cleanup(dev);
+       mlx5_sf_hw_table_cleanup(dev);
+       mlx5_vhca_event_cleanup(dev);
        mlx5_fpga_cleanup(dev);
        mlx5_eswitch_cleanup(dev->priv.eswitch);
        mlx5_sriov_cleanup(dev);
@@ -1129,6 +1160,14 @@ static int mlx5_load(struct mlx5_core_dev *dev)
                goto err_sriov;
        }
 
+       mlx5_vhca_event_start(dev);
+
+       err = mlx5_sf_hw_table_create(dev);
+       if (err) {
+               mlx5_core_err(dev, "sf table create failed %d\n", err);
+               goto err_vhca;
+       }
+
        err = mlx5_ec_init(dev);
        if (err) {
                mlx5_core_err(dev, "Failed to init embedded CPU\n");
@@ -1141,11 +1180,16 @@ static int mlx5_load(struct mlx5_core_dev *dev)
                goto err_sriov;
        }
 
+       mlx5_sf_dev_table_create(dev);
+
        return 0;
 
 err_sriov:
        mlx5_ec_cleanup(dev);
 err_ec:
+       mlx5_sf_hw_table_destroy(dev);
+err_vhca:
+       mlx5_vhca_event_stop(dev);
        mlx5_cleanup_fs(dev);
 err_fs:
        mlx5_accel_tls_cleanup(dev);
@@ -1171,8 +1215,11 @@ err_irq_table:
 
 static void mlx5_unload(struct mlx5_core_dev *dev)
 {
+       mlx5_sf_dev_table_destroy(dev);
        mlx5_sriov_detach(dev);
        mlx5_ec_cleanup(dev);
+       mlx5_sf_hw_table_destroy(dev);
+       mlx5_vhca_event_stop(dev);
        mlx5_cleanup_fs(dev);
        mlx5_accel_ipsec_cleanup(dev);
        mlx5_accel_tls_cleanup(dev);
@@ -1283,7 +1330,7 @@ out:
        mutex_unlock(&dev->intf_state_mutex);
 }
 
-static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
+int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
 {
        struct mlx5_priv *priv = &dev->priv;
        int err;
@@ -1305,6 +1352,8 @@ static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
 
        priv->dbg_root = debugfs_create_dir(dev_name(dev->device),
                                            mlx5_debugfs_root);
+       INIT_LIST_HEAD(&priv->traps);
+
        err = mlx5_health_init(dev);
        if (err)
                goto err_health_init;
@@ -1333,7 +1382,7 @@ err_health_init:
        return err;
 }
 
-static void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
+void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
 {
        struct mlx5_priv *priv = &dev->priv;
 
@@ -1368,8 +1417,10 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *id)
                         MLX5_COREDEV_VF : MLX5_COREDEV_PF;
 
        dev->priv.adev_idx = mlx5_adev_idx_alloc();
-       if (dev->priv.adev_idx < 0)
-               return dev->priv.adev_idx;
+       if (dev->priv.adev_idx < 0) {
+               err = dev->priv.adev_idx;
+               goto adev_init_err;
+       }
 
        err = mlx5_mdev_init(dev, prof_sel);
        if (err)
@@ -1403,6 +1454,7 @@ pci_init_err:
        mlx5_mdev_uninit(dev);
 mdev_init_err:
        mlx5_adev_idx_free(dev->priv.adev_idx);
+adev_init_err:
        mlx5_devlink_free(devlink);
 
        return err;
@@ -1673,6 +1725,10 @@ static int __init init(void)
        if (err)
                goto err_debug;
 
+       err = mlx5_sf_driver_register();
+       if (err)
+               goto err_sf;
+
 #ifdef CONFIG_MLX5_CORE_EN
        err = mlx5e_init();
        if (err) {
@@ -1683,6 +1739,8 @@ static int __init init(void)
 
        return 0;
 
+err_sf:
+       pci_unregister_driver(&mlx5_core_driver);
 err_debug:
        mlx5_unregister_debugfs();
        return err;
@@ -1693,6 +1751,7 @@ static void __exit cleanup(void)
 #ifdef CONFIG_MLX5_CORE_EN
        mlx5e_cleanup();
 #endif
+       mlx5_sf_driver_unregister();
        pci_unregister_driver(&mlx5_core_driver);
        mlx5_unregister_debugfs();
 }
index 0a0302c..3754ef9 100644 (file)
@@ -117,6 +117,8 @@ enum mlx5_semaphore_space_address {
        MLX5_SEMAPHORE_SW_RESET         = 0x20,
 };
 
+#define MLX5_DEFAULT_PROF       2
+
 int mlx5_query_hca_caps(struct mlx5_core_dev *dev);
 int mlx5_query_board_id(struct mlx5_core_dev *dev);
 int mlx5_cmd_init(struct mlx5_core_dev *dev);
@@ -176,6 +178,7 @@ struct cpumask *
 mlx5_irq_get_affinity_mask(struct mlx5_irq_table *irq_table, int vecidx);
 struct cpu_rmap *mlx5_irq_get_rmap(struct mlx5_irq_table *table);
 int mlx5_irq_get_num_comp(struct mlx5_irq_table *table);
+struct mlx5_irq_table *mlx5_irq_table_get(struct mlx5_core_dev *dev);
 
 int mlx5_events_init(struct mlx5_core_dev *dev);
 void mlx5_events_cleanup(struct mlx5_core_dev *dev);
@@ -257,6 +260,15 @@ enum {
 u8 mlx5_get_nic_state(struct mlx5_core_dev *dev);
 void mlx5_set_nic_state(struct mlx5_core_dev *dev, u8 state);
 
+static inline bool mlx5_core_is_sf(const struct mlx5_core_dev *dev)
+{
+       return dev->coredev_type == MLX5_COREDEV_SF;
+}
+
+int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx);
+void mlx5_mdev_uninit(struct mlx5_core_dev *dev);
 void mlx5_unload_one(struct mlx5_core_dev *dev, bool cleanup);
 int mlx5_load_one(struct mlx5_core_dev *dev, bool boot);
+
+void mlx5_events_work_enqueue(struct mlx5_core_dev *dev, struct work_struct *work);
 #endif /* __MLX5_CORE_H__ */
index eb956ce..eaa8958 100644 (file)
@@ -58,7 +58,7 @@ struct fw_page {
        struct rb_node          rb_node;
        u64                     addr;
        struct page            *page;
-       u16                     func_id;
+       u32                     function;
        unsigned long           bitmask;
        struct list_head        list;
        unsigned                free_count;
@@ -74,12 +74,17 @@ enum {
        MLX5_NUM_4K_IN_PAGE             = PAGE_SIZE / MLX5_ADAPTER_PAGE_SIZE,
 };
 
-static struct rb_root *page_root_per_func_id(struct mlx5_core_dev *dev, u16 func_id)
+static u32 get_function(u16 func_id, bool ec_function)
+{
+       return func_id & (ec_function << 16);
+}
+
+static struct rb_root *page_root_per_function(struct mlx5_core_dev *dev, u32 function)
 {
        struct rb_root *root;
        int err;
 
-       root = xa_load(&dev->priv.page_root_xa, func_id);
+       root = xa_load(&dev->priv.page_root_xa, function);
        if (root)
                return root;
 
@@ -87,7 +92,7 @@ static struct rb_root *page_root_per_func_id(struct mlx5_core_dev *dev, u16 func
        if (!root)
                return ERR_PTR(-ENOMEM);
 
-       err = xa_insert(&dev->priv.page_root_xa, func_id, root, GFP_KERNEL);
+       err = xa_insert(&dev->priv.page_root_xa, function, root, GFP_KERNEL);
        if (err) {
                kfree(root);
                return ERR_PTR(err);
@@ -98,7 +103,7 @@ static struct rb_root *page_root_per_func_id(struct mlx5_core_dev *dev, u16 func
        return root;
 }
 
-static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u16 func_id)
+static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u32 function)
 {
        struct rb_node *parent = NULL;
        struct rb_root *root;
@@ -107,7 +112,7 @@ static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u
        struct fw_page *tfp;
        int i;
 
-       root = page_root_per_func_id(dev, func_id);
+       root = page_root_per_function(dev, function);
        if (IS_ERR(root))
                return PTR_ERR(root);
 
@@ -130,7 +135,7 @@ static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u
 
        nfp->addr = addr;
        nfp->page = page;
-       nfp->func_id = func_id;
+       nfp->function = function;
        nfp->free_count = MLX5_NUM_4K_IN_PAGE;
        for (i = 0; i < MLX5_NUM_4K_IN_PAGE; i++)
                set_bit(i, &nfp->bitmask);
@@ -143,14 +148,14 @@ static int insert_page(struct mlx5_core_dev *dev, u64 addr, struct page *page, u
 }
 
 static struct fw_page *find_fw_page(struct mlx5_core_dev *dev, u64 addr,
-                                   u32 func_id)
+                                   u32 function)
 {
        struct fw_page *result = NULL;
        struct rb_root *root;
        struct rb_node *tmp;
        struct fw_page *tfp;
 
-       root = xa_load(&dev->priv.page_root_xa, func_id);
+       root = xa_load(&dev->priv.page_root_xa, function);
        if (WARN_ON_ONCE(!root))
                return NULL;
 
@@ -194,14 +199,14 @@ static int mlx5_cmd_query_pages(struct mlx5_core_dev *dev, u16 *func_id,
        return err;
 }
 
-static int alloc_4k(struct mlx5_core_dev *dev, u64 *addr, u16 func_id)
+static int alloc_4k(struct mlx5_core_dev *dev, u64 *addr, u32 function)
 {
        struct fw_page *fp = NULL;
        struct fw_page *iter;
        unsigned n;
 
        list_for_each_entry(iter, &dev->priv.free_list, list) {
-               if (iter->func_id != func_id)
+               if (iter->function != function)
                        continue;
                fp = iter;
        }
@@ -231,7 +236,7 @@ static void free_fwp(struct mlx5_core_dev *dev, struct fw_page *fwp,
 {
        struct rb_root *root;
 
-       root = xa_load(&dev->priv.page_root_xa, fwp->func_id);
+       root = xa_load(&dev->priv.page_root_xa, fwp->function);
        if (WARN_ON_ONCE(!root))
                return;
 
@@ -244,12 +249,12 @@ static void free_fwp(struct mlx5_core_dev *dev, struct fw_page *fwp,
        kfree(fwp);
 }
 
-static void free_4k(struct mlx5_core_dev *dev, u64 addr, u32 func_id)
+static void free_4k(struct mlx5_core_dev *dev, u64 addr, u32 function)
 {
        struct fw_page *fwp;
        int n;
 
-       fwp = find_fw_page(dev, addr & MLX5_U64_4K_PAGE_MASK, func_id);
+       fwp = find_fw_page(dev, addr & MLX5_U64_4K_PAGE_MASK, function);
        if (!fwp) {
                mlx5_core_warn_rl(dev, "page not found\n");
                return;
@@ -263,7 +268,7 @@ static void free_4k(struct mlx5_core_dev *dev, u64 addr, u32 func_id)
                list_add(&fwp->list, &dev->priv.free_list);
 }
 
-static int alloc_system_page(struct mlx5_core_dev *dev, u16 func_id)
+static int alloc_system_page(struct mlx5_core_dev *dev, u32 function)
 {
        struct device *device = mlx5_core_dma_dev(dev);
        int nid = dev_to_node(device);
@@ -291,7 +296,7 @@ map:
                goto map;
        }
 
-       err = insert_page(dev, addr, page, func_id);
+       err = insert_page(dev, addr, page, function);
        if (err) {
                mlx5_core_err(dev, "failed to track allocated page\n");
                dma_unmap_page(device, addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
@@ -328,6 +333,7 @@ static void page_notify_fail(struct mlx5_core_dev *dev, u16 func_id,
 static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
                      int notify_fail, bool ec_function)
 {
+       u32 function = get_function(func_id, ec_function);
        u32 out[MLX5_ST_SZ_DW(manage_pages_out)] = {0};
        int inlen = MLX5_ST_SZ_BYTES(manage_pages_in);
        u64 addr;
@@ -345,10 +351,10 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
 
        for (i = 0; i < npages; i++) {
 retry:
-               err = alloc_4k(dev, &addr, func_id);
+               err = alloc_4k(dev, &addr, function);
                if (err) {
                        if (err == -ENOMEM)
-                               err = alloc_system_page(dev, func_id);
+                               err = alloc_system_page(dev, function);
                        if (err)
                                goto out_4k;
 
@@ -384,7 +390,7 @@ retry:
 
 out_4k:
        for (i--; i >= 0; i--)
-               free_4k(dev, MLX5_GET64(manage_pages_in, in, pas[i]), func_id);
+               free_4k(dev, MLX5_GET64(manage_pages_in, in, pas[i]), function);
 out_free:
        kvfree(in);
        if (notify_fail)
@@ -392,14 +398,15 @@ out_free:
        return err;
 }
 
-static void release_all_pages(struct mlx5_core_dev *dev, u32 func_id,
+static void release_all_pages(struct mlx5_core_dev *dev, u16 func_id,
                              bool ec_function)
 {
+       u32 function = get_function(func_id, ec_function);
        struct rb_root *root;
        struct rb_node *p;
        int npages = 0;
 
-       root = xa_load(&dev->priv.page_root_xa, func_id);
+       root = xa_load(&dev->priv.page_root_xa, function);
        if (WARN_ON_ONCE(!root))
                return;
 
@@ -446,6 +453,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
        struct rb_root *root;
        struct fw_page *fwp;
        struct rb_node *p;
+       bool ec_function;
        u32 func_id;
        u32 npages;
        u32 i = 0;
@@ -456,8 +464,9 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
        /* No hard feelings, we want our pages back! */
        npages = MLX5_GET(manage_pages_in, in, input_num_entries);
        func_id = MLX5_GET(manage_pages_in, in, function_id);
+       ec_function = MLX5_GET(manage_pages_in, in, embedded_cpu_function);
 
-       root = xa_load(&dev->priv.page_root_xa, func_id);
+       root = xa_load(&dev->priv.page_root_xa, get_function(func_id, ec_function));
        if (WARN_ON_ONCE(!root))
                return -EEXIST;
 
@@ -473,9 +482,10 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
        return 0;
 }
 
-static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
+static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
                         int *nclaimed, bool ec_function)
 {
+       u32 function = get_function(func_id, ec_function);
        int outlen = MLX5_ST_SZ_BYTES(manage_pages_out);
        u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {};
        int num_claimed;
@@ -514,7 +524,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u32 func_id, int npages,
        }
 
        for (i = 0; i < num_claimed; i++)
-               free_4k(dev, MLX5_GET64(manage_pages_out, out, pas[i]), func_id);
+               free_4k(dev, MLX5_GET64(manage_pages_out, out, pas[i]), function);
 
        if (nclaimed)
                *nclaimed = num_claimed;
index 6fd9749..a61e09a 100644 (file)
@@ -30,6 +30,9 @@ int mlx5_irq_table_init(struct mlx5_core_dev *dev)
 {
        struct mlx5_irq_table *irq_table;
 
+       if (mlx5_core_is_sf(dev))
+               return 0;
+
        irq_table = kvzalloc(sizeof(*irq_table), GFP_KERNEL);
        if (!irq_table)
                return -ENOMEM;
@@ -40,6 +43,9 @@ int mlx5_irq_table_init(struct mlx5_core_dev *dev)
 
 void mlx5_irq_table_cleanup(struct mlx5_core_dev *dev)
 {
+       if (mlx5_core_is_sf(dev))
+               return;
+
        kvfree(dev->priv.irq_table);
 }
 
@@ -268,6 +274,9 @@ int mlx5_irq_table_create(struct mlx5_core_dev *dev)
        int nvec;
        int err;
 
+       if (mlx5_core_is_sf(dev))
+               return 0;
+
        nvec = MLX5_CAP_GEN(dev, num_ports) * num_online_cpus() +
               MLX5_IRQ_VEC_COMP_BASE;
        nvec = min_t(int, nvec, num_eqs);
@@ -319,6 +328,9 @@ void mlx5_irq_table_destroy(struct mlx5_core_dev *dev)
        struct mlx5_irq_table *table = dev->priv.irq_table;
        int i;
 
+       if (mlx5_core_is_sf(dev))
+               return;
+
        /* free_irq requires that affinity and rmap will be cleared
         * before calling it. This is why there is asymmetry with set_rmap
         * which should be called after alloc_irq but before request_irq.
@@ -332,3 +344,11 @@ void mlx5_irq_table_destroy(struct mlx5_core_dev *dev)
        kfree(table->irq);
 }
 
+struct mlx5_irq_table *mlx5_irq_table_get(struct mlx5_core_dev *dev)
+{
+#ifdef CONFIG_MLX5_SF
+       if (mlx5_core_is_sf(dev))
+               return dev->priv.parent_mdev->priv.irq_table;
+#endif
+       return dev->priv.irq_table;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/qos.c
new file mode 100644 (file)
index 0000000..0777be2
--- /dev/null
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */
+
+#include "qos.h"
+
+#define MLX5_QOS_DEFAULT_DWRR_UID 0
+
+bool mlx5_qos_is_supported(struct mlx5_core_dev *mdev)
+{
+       if (!MLX5_CAP_GEN(mdev, qos))
+               return false;
+       if (!MLX5_CAP_QOS(mdev, nic_sq_scheduling))
+               return false;
+       if (!MLX5_CAP_QOS(mdev, nic_bw_share))
+               return false;
+       if (!MLX5_CAP_QOS(mdev, nic_rate_limit))
+               return false;
+       return true;
+}
+
+int mlx5_qos_max_leaf_nodes(struct mlx5_core_dev *mdev)
+{
+       return 1 << MLX5_CAP_QOS(mdev, log_max_qos_nic_queue_group);
+}
+
+int mlx5_qos_create_leaf_node(struct mlx5_core_dev *mdev, u32 parent_id,
+                             u32 bw_share, u32 max_avg_bw, u32 *id)
+{
+       u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0};
+
+       MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id);
+       MLX5_SET(scheduling_context, sched_ctx, element_type,
+                SCHEDULING_CONTEXT_ELEMENT_TYPE_QUEUE_GROUP);
+       MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share);
+       MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw);
+
+       return mlx5_create_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC,
+                                                 sched_ctx, id);
+}
+
+int mlx5_qos_create_inner_node(struct mlx5_core_dev *mdev, u32 parent_id,
+                              u32 bw_share, u32 max_avg_bw, u32 *id)
+{
+       u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0};
+       void *attr;
+
+       MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id);
+       MLX5_SET(scheduling_context, sched_ctx, element_type,
+                SCHEDULING_CONTEXT_ELEMENT_TYPE_TSAR);
+       MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share);
+       MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw);
+
+       attr = MLX5_ADDR_OF(scheduling_context, sched_ctx, element_attributes);
+       MLX5_SET(tsar_element, attr, tsar_type, TSAR_ELEMENT_TSAR_TYPE_DWRR);
+
+       return mlx5_create_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC,
+                                                 sched_ctx, id);
+}
+
+int mlx5_qos_create_root_node(struct mlx5_core_dev *mdev, u32 *id)
+{
+       return mlx5_qos_create_inner_node(mdev, MLX5_QOS_DEFAULT_DWRR_UID, 0, 0, id);
+}
+
+int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 parent_id,
+                        u32 bw_share, u32 max_avg_bw, u32 id)
+{
+       u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0};
+       u32 bitmask = 0;
+
+       MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id);
+       MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share);
+       MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw);
+
+       bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_BW_SHARE;
+       bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW;
+
+       return mlx5_modify_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC,
+                                                 sched_ctx, id, bitmask);
+}
+
+int mlx5_qos_destroy_node(struct mlx5_core_dev *mdev, u32 id)
+{
+       return mlx5_destroy_scheduling_element_cmd(mdev, SCHEDULING_HIERARCHY_NIC, id);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qos.h b/drivers/net/ethernet/mellanox/mlx5/core/qos.h
new file mode 100644 (file)
index 0000000..125e4e4
--- /dev/null
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. */
+
+#ifndef __MLX5_QOS_H
+#define __MLX5_QOS_H
+
+#include "mlx5_core.h"
+
+#define MLX5_DEBUG_QOS_MASK BIT(4)
+
+#define qos_err(mdev, fmt, ...) \
+       mlx5_core_err(mdev, "QoS: " fmt, ##__VA_ARGS__)
+#define qos_warn(mdev, fmt, ...) \
+       mlx5_core_warn(mdev, "QoS: " fmt, ##__VA_ARGS__)
+#define qos_dbg(mdev, fmt, ...) \
+       mlx5_core_dbg_mask(mdev, MLX5_DEBUG_QOS_MASK, "QoS: " fmt, ##__VA_ARGS__)
+
+bool mlx5_qos_is_supported(struct mlx5_core_dev *mdev);
+int mlx5_qos_max_leaf_nodes(struct mlx5_core_dev *mdev);
+
+int mlx5_qos_create_leaf_node(struct mlx5_core_dev *mdev, u32 parent_id,
+                             u32 bw_share, u32 max_avg_bw, u32 *id);
+int mlx5_qos_create_inner_node(struct mlx5_core_dev *mdev, u32 parent_id,
+                              u32 bw_share, u32 max_avg_bw, u32 *id);
+int mlx5_qos_create_root_node(struct mlx5_core_dev *mdev, u32 *id);
+int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 parent_id, u32 bw_share,
+                        u32 max_avg_bw, u32 id);
+int mlx5_qos_destroy_node(struct mlx5_core_dev *mdev, u32 id);
+
+#endif
index 0fc7de4..8e0dddc 100644 (file)
@@ -116,7 +116,7 @@ free:
 static void mlx5_rdma_del_roce_addr(struct mlx5_core_dev *dev)
 {
        mlx5_core_roce_gid_set(dev, 0, 0, 0,
-                              NULL, NULL, false, 0, 0);
+                              NULL, NULL, false, 0, 1);
 }
 
 static void mlx5_rdma_make_default_gid(struct mlx5_core_dev *dev, union ib_gid *gid)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/cmd.c
new file mode 100644 (file)
index 0000000..a8d75c2
--- /dev/null
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#include <linux/mlx5/driver.h>
+#include "priv.h"
+
+int mlx5_cmd_alloc_sf(struct mlx5_core_dev *dev, u16 function_id)
+{
+       u32 out[MLX5_ST_SZ_DW(alloc_sf_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(alloc_sf_in)] = {};
+
+       MLX5_SET(alloc_sf_in, in, opcode, MLX5_CMD_OP_ALLOC_SF);
+       MLX5_SET(alloc_sf_in, in, function_id, function_id);
+
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
+
+int mlx5_cmd_dealloc_sf(struct mlx5_core_dev *dev, u16 function_id)
+{
+       u32 out[MLX5_ST_SZ_DW(dealloc_sf_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(dealloc_sf_in)] = {};
+
+       MLX5_SET(dealloc_sf_in, in, opcode, MLX5_CMD_OP_DEALLOC_SF);
+       MLX5_SET(dealloc_sf_in, in, function_id, function_id);
+
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
+
+int mlx5_cmd_sf_enable_hca(struct mlx5_core_dev *dev, u16 func_id)
+{
+       u32 out[MLX5_ST_SZ_DW(enable_hca_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(enable_hca_in)] = {};
+
+       MLX5_SET(enable_hca_in, in, opcode, MLX5_CMD_OP_ENABLE_HCA);
+       MLX5_SET(enable_hca_in, in, function_id, func_id);
+       MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0);
+       return mlx5_cmd_exec(dev, &in, sizeof(in), &out, sizeof(out));
+}
+
+int mlx5_cmd_sf_disable_hca(struct mlx5_core_dev *dev, u16 func_id)
+{
+       u32 out[MLX5_ST_SZ_DW(disable_hca_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(disable_hca_in)] = {};
+
+       MLX5_SET(disable_hca_in, in, opcode, MLX5_CMD_OP_DISABLE_HCA);
+       MLX5_SET(disable_hca_in, in, function_id, func_id);
+       MLX5_SET(enable_hca_in, in, embedded_cpu_function, 0);
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c
new file mode 100644 (file)
index 0000000..b265f27
--- /dev/null
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/device.h>
+#include "mlx5_core.h"
+#include "dev.h"
+#include "sf/vhca_event.h"
+#include "sf/sf.h"
+#include "sf/mlx5_ifc_vhca_event.h"
+#include "ecpf.h"
+
+struct mlx5_sf_dev_table {
+       struct xarray devices;
+       unsigned int max_sfs;
+       phys_addr_t base_address;
+       u64 sf_bar_length;
+       struct notifier_block nb;
+       struct mlx5_core_dev *dev;
+};
+
+static bool mlx5_sf_dev_supported(const struct mlx5_core_dev *dev)
+{
+       return MLX5_CAP_GEN(dev, sf) && mlx5_vhca_event_supported(dev);
+}
+
+bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
+
+       if (!mlx5_sf_dev_supported(dev))
+               return false;
+
+       return !xa_empty(&table->devices);
+}
+
+static ssize_t sfnum_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct auxiliary_device *adev = container_of(dev, struct auxiliary_device, dev);
+       struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
+
+       return scnprintf(buf, PAGE_SIZE, "%u\n", sf_dev->sfnum);
+}
+static DEVICE_ATTR_RO(sfnum);
+
+static struct attribute *sf_device_attrs[] = {
+       &dev_attr_sfnum.attr,
+       NULL,
+};
+
+static const struct attribute_group sf_attr_group = {
+       .attrs = sf_device_attrs,
+};
+
+static const struct attribute_group *sf_attr_groups[2] = {
+       &sf_attr_group,
+       NULL
+};
+
+static void mlx5_sf_dev_release(struct device *device)
+{
+       struct auxiliary_device *adev = container_of(device, struct auxiliary_device, dev);
+       struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
+
+       mlx5_adev_idx_free(adev->id);
+       kfree(sf_dev);
+}
+
+static void mlx5_sf_dev_remove(struct mlx5_sf_dev *sf_dev)
+{
+       auxiliary_device_delete(&sf_dev->adev);
+       auxiliary_device_uninit(&sf_dev->adev);
+}
+
+static void mlx5_sf_dev_add(struct mlx5_core_dev *dev, u16 sf_index, u32 sfnum)
+{
+       struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
+       struct mlx5_sf_dev *sf_dev;
+       struct pci_dev *pdev;
+       int err;
+       int id;
+
+       id = mlx5_adev_idx_alloc();
+       if (id < 0) {
+               err = id;
+               goto add_err;
+       }
+
+       sf_dev = kzalloc(sizeof(*sf_dev), GFP_KERNEL);
+       if (!sf_dev) {
+               mlx5_adev_idx_free(id);
+               err = -ENOMEM;
+               goto add_err;
+       }
+       pdev = dev->pdev;
+       sf_dev->adev.id = id;
+       sf_dev->adev.name = MLX5_SF_DEV_ID_NAME;
+       sf_dev->adev.dev.release = mlx5_sf_dev_release;
+       sf_dev->adev.dev.parent = &pdev->dev;
+       sf_dev->adev.dev.groups = sf_attr_groups;
+       sf_dev->sfnum = sfnum;
+       sf_dev->parent_mdev = dev;
+
+       if (!table->max_sfs) {
+               mlx5_adev_idx_free(id);
+               kfree(sf_dev);
+               err = -EOPNOTSUPP;
+               goto add_err;
+       }
+       sf_dev->bar_base_addr = table->base_address + (sf_index * table->sf_bar_length);
+
+       err = auxiliary_device_init(&sf_dev->adev);
+       if (err) {
+               mlx5_adev_idx_free(id);
+               kfree(sf_dev);
+               goto add_err;
+       }
+
+       err = auxiliary_device_add(&sf_dev->adev);
+       if (err) {
+               put_device(&sf_dev->adev.dev);
+               goto add_err;
+       }
+
+       err = xa_insert(&table->devices, sf_index, sf_dev, GFP_KERNEL);
+       if (err)
+               goto xa_err;
+       return;
+
+xa_err:
+       mlx5_sf_dev_remove(sf_dev);
+add_err:
+       mlx5_core_err(dev, "SF DEV: fail device add for index=%d sfnum=%d err=%d\n",
+                     sf_index, sfnum, err);
+}
+
+static void mlx5_sf_dev_del(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_dev, u16 sf_index)
+{
+       struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
+
+       xa_erase(&table->devices, sf_index);
+       mlx5_sf_dev_remove(sf_dev);
+}
+
+static int
+mlx5_sf_dev_state_change_handler(struct notifier_block *nb, unsigned long event_code, void *data)
+{
+       struct mlx5_sf_dev_table *table = container_of(nb, struct mlx5_sf_dev_table, nb);
+       const struct mlx5_vhca_state_event *event = data;
+       struct mlx5_sf_dev *sf_dev;
+       u16 sf_index;
+
+       sf_index = event->function_id - MLX5_CAP_GEN(table->dev, sf_base_id);
+       sf_dev = xa_load(&table->devices, sf_index);
+       switch (event->new_vhca_state) {
+       case MLX5_VHCA_STATE_ALLOCATED:
+               if (sf_dev)
+                       mlx5_sf_dev_del(table->dev, sf_dev, sf_index);
+               break;
+       case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
+               if (sf_dev)
+                       mlx5_sf_dev_del(table->dev, sf_dev, sf_index);
+               else
+                       mlx5_core_err(table->dev,
+                                     "SF DEV: teardown state for invalid dev index=%d fn_id=0x%x\n",
+                                     sf_index, event->sw_function_id);
+               break;
+       case MLX5_VHCA_STATE_ACTIVE:
+               if (!sf_dev)
+                       mlx5_sf_dev_add(table->dev, sf_index, event->sw_function_id);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int mlx5_sf_dev_vhca_arm_all(struct mlx5_sf_dev_table *table)
+{
+       struct mlx5_core_dev *dev = table->dev;
+       u16 max_functions;
+       u16 function_id;
+       int err = 0;
+       bool ecpu;
+       int i;
+
+       max_functions = mlx5_sf_max_functions(dev);
+       function_id = MLX5_CAP_GEN(dev, sf_base_id);
+       ecpu = mlx5_read_embedded_cpu(dev);
+       /* Arm the vhca context as the vhca event notifier */
+       for (i = 0; i < max_functions; i++) {
+               err = mlx5_vhca_event_arm(dev, function_id, ecpu);
+               if (err)
+                       return err;
+
+               function_id++;
+       }
+       return 0;
+}
+
+void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_dev_table *table;
+       unsigned int max_sfs;
+       int err;
+
+       if (!mlx5_sf_dev_supported(dev) || !mlx5_vhca_event_supported(dev))
+               return;
+
+       table = kzalloc(sizeof(*table), GFP_KERNEL);
+       if (!table) {
+               err = -ENOMEM;
+               goto table_err;
+       }
+
+       table->nb.notifier_call = mlx5_sf_dev_state_change_handler;
+       table->dev = dev;
+       if (MLX5_CAP_GEN(dev, max_num_sf))
+               max_sfs = MLX5_CAP_GEN(dev, max_num_sf);
+       else
+               max_sfs = 1 << MLX5_CAP_GEN(dev, log_max_sf);
+       table->sf_bar_length = 1 << (MLX5_CAP_GEN(dev, log_min_sf_size) + 12);
+       table->base_address = pci_resource_start(dev->pdev, 2);
+       table->max_sfs = max_sfs;
+       xa_init(&table->devices);
+       dev->priv.sf_dev_table = table;
+
+       err = mlx5_vhca_event_notifier_register(dev, &table->nb);
+       if (err)
+               goto vhca_err;
+       err = mlx5_sf_dev_vhca_arm_all(table);
+       if (err)
+               goto arm_err;
+       mlx5_core_dbg(dev, "SF DEV: max sf devices=%d\n", max_sfs);
+       return;
+
+arm_err:
+       mlx5_vhca_event_notifier_unregister(dev, &table->nb);
+vhca_err:
+       table->max_sfs = 0;
+       kfree(table);
+       dev->priv.sf_dev_table = NULL;
+table_err:
+       mlx5_core_err(dev, "SF DEV table create err = %d\n", err);
+}
+
+static void mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table *table)
+{
+       struct mlx5_sf_dev *sf_dev;
+       unsigned long index;
+
+       xa_for_each(&table->devices, index, sf_dev) {
+               xa_erase(&table->devices, index);
+               mlx5_sf_dev_remove(sf_dev);
+       }
+}
+
+void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
+
+       if (!table)
+               return;
+
+       mlx5_vhca_event_notifier_unregister(dev, &table->nb);
+
+       /* Now that event handler is not running, it is safe to destroy
+        * the sf device without race.
+        */
+       mlx5_sf_dev_destroy_all(table);
+
+       WARN_ON(!xa_empty(&table->devices));
+       kfree(table);
+       dev->priv.sf_dev_table = NULL;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
new file mode 100644 (file)
index 0000000..4de0290
--- /dev/null
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#ifndef __MLX5_SF_DEV_H__
+#define __MLX5_SF_DEV_H__
+
+#ifdef CONFIG_MLX5_SF
+
+#include <linux/auxiliary_bus.h>
+
+#define MLX5_SF_DEV_ID_NAME "sf"
+
+struct mlx5_sf_dev {
+       struct auxiliary_device adev;
+       struct mlx5_core_dev *parent_mdev;
+       struct mlx5_core_dev *mdev;
+       phys_addr_t bar_base_addr;
+       u32 sfnum;
+};
+
+void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev);
+void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev);
+
+int mlx5_sf_driver_register(void);
+void mlx5_sf_driver_unregister(void);
+
+bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev);
+
+#else
+
+static inline void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
+{
+}
+
+static inline void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
+{
+}
+
+static inline int mlx5_sf_driver_register(void)
+{
+       return 0;
+}
+
+static inline void mlx5_sf_driver_unregister(void)
+{
+}
+
+static inline bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev)
+{
+       return 0;
+}
+
+#endif
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/driver.c
new file mode 100644 (file)
index 0000000..daf63a8
--- /dev/null
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#include <linux/mlx5/driver.h>
+#include <linux/mlx5/device.h>
+#include "mlx5_core.h"
+#include "dev.h"
+#include "devlink.h"
+
+static int mlx5_sf_dev_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
+{
+       struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
+       struct mlx5_core_dev *mdev;
+       struct devlink *devlink;
+       int err;
+
+       devlink = mlx5_devlink_alloc();
+       if (!devlink)
+               return -ENOMEM;
+
+       mdev = devlink_priv(devlink);
+       mdev->device = &adev->dev;
+       mdev->pdev = sf_dev->parent_mdev->pdev;
+       mdev->bar_addr = sf_dev->bar_base_addr;
+       mdev->iseg_base = sf_dev->bar_base_addr;
+       mdev->coredev_type = MLX5_COREDEV_SF;
+       mdev->priv.parent_mdev = sf_dev->parent_mdev;
+       mdev->priv.adev_idx = adev->id;
+       sf_dev->mdev = mdev;
+
+       err = mlx5_mdev_init(mdev, MLX5_DEFAULT_PROF);
+       if (err) {
+               mlx5_core_warn(mdev, "mlx5_mdev_init on err=%d\n", err);
+               goto mdev_err;
+       }
+
+       mdev->iseg = ioremap(mdev->iseg_base, sizeof(*mdev->iseg));
+       if (!mdev->iseg) {
+               mlx5_core_warn(mdev, "remap error\n");
+               goto remap_err;
+       }
+
+       err = mlx5_load_one(mdev, true);
+       if (err) {
+               mlx5_core_warn(mdev, "mlx5_load_one err=%d\n", err);
+               goto load_one_err;
+       }
+       return 0;
+
+load_one_err:
+       iounmap(mdev->iseg);
+remap_err:
+       mlx5_mdev_uninit(mdev);
+mdev_err:
+       mlx5_devlink_free(devlink);
+       return err;
+}
+
+static void mlx5_sf_dev_remove(struct auxiliary_device *adev)
+{
+       struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
+       struct devlink *devlink;
+
+       devlink = priv_to_devlink(sf_dev->mdev);
+       mlx5_unload_one(sf_dev->mdev, true);
+       iounmap(sf_dev->mdev->iseg);
+       mlx5_mdev_uninit(sf_dev->mdev);
+       mlx5_devlink_free(devlink);
+}
+
+static void mlx5_sf_dev_shutdown(struct auxiliary_device *adev)
+{
+       struct mlx5_sf_dev *sf_dev = container_of(adev, struct mlx5_sf_dev, adev);
+
+       mlx5_unload_one(sf_dev->mdev, false);
+}
+
+static const struct auxiliary_device_id mlx5_sf_dev_id_table[] = {
+       { .name = MLX5_ADEV_NAME "." MLX5_SF_DEV_ID_NAME, },
+       { },
+};
+
+MODULE_DEVICE_TABLE(auxiliary, mlx5_sf_dev_id_table);
+
+static struct auxiliary_driver mlx5_sf_driver = {
+       .name = MLX5_SF_DEV_ID_NAME,
+       .probe = mlx5_sf_dev_probe,
+       .remove = mlx5_sf_dev_remove,
+       .shutdown = mlx5_sf_dev_shutdown,
+       .id_table = mlx5_sf_dev_id_table,
+};
+
+int mlx5_sf_driver_register(void)
+{
+       return auxiliary_driver_register(&mlx5_sf_driver);
+}
+
+void mlx5_sf_driver_unregister(void)
+{
+       auxiliary_driver_unregister(&mlx5_sf_driver);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
new file mode 100644 (file)
index 0000000..c2ba41b
--- /dev/null
@@ -0,0 +1,556 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#include <linux/mlx5/driver.h>
+#include "eswitch.h"
+#include "priv.h"
+#include "sf/dev/dev.h"
+#include "mlx5_ifc_vhca_event.h"
+#include "vhca_event.h"
+#include "ecpf.h"
+
+struct mlx5_sf {
+       struct devlink_port dl_port;
+       unsigned int port_index;
+       u16 id;
+       u16 hw_fn_id;
+       u16 hw_state;
+};
+
+struct mlx5_sf_table {
+       struct mlx5_core_dev *dev; /* To refer from notifier context. */
+       struct xarray port_indices; /* port index based lookup. */
+       refcount_t refcount;
+       struct completion disable_complete;
+       struct mutex sf_state_lock; /* Serializes sf state among user cmds & vhca event handler. */
+       struct notifier_block esw_nb;
+       struct notifier_block vhca_nb;
+       u8 ecpu: 1;
+};
+
+static struct mlx5_sf *
+mlx5_sf_lookup_by_index(struct mlx5_sf_table *table, unsigned int port_index)
+{
+       return xa_load(&table->port_indices, port_index);
+}
+
+static struct mlx5_sf *
+mlx5_sf_lookup_by_function_id(struct mlx5_sf_table *table, unsigned int fn_id)
+{
+       unsigned long index;
+       struct mlx5_sf *sf;
+
+       xa_for_each(&table->port_indices, index, sf) {
+               if (sf->hw_fn_id == fn_id)
+                       return sf;
+       }
+       return NULL;
+}
+
+static int mlx5_sf_id_insert(struct mlx5_sf_table *table, struct mlx5_sf *sf)
+{
+       return xa_insert(&table->port_indices, sf->port_index, sf, GFP_KERNEL);
+}
+
+static void mlx5_sf_id_erase(struct mlx5_sf_table *table, struct mlx5_sf *sf)
+{
+       xa_erase(&table->port_indices, sf->port_index);
+}
+
+static struct mlx5_sf *
+mlx5_sf_alloc(struct mlx5_sf_table *table, u32 sfnum, struct netlink_ext_ack *extack)
+{
+       unsigned int dl_port_index;
+       struct mlx5_sf *sf;
+       u16 hw_fn_id;
+       int id_err;
+       int err;
+
+       id_err = mlx5_sf_hw_table_sf_alloc(table->dev, sfnum);
+       if (id_err < 0) {
+               err = id_err;
+               goto id_err;
+       }
+
+       sf = kzalloc(sizeof(*sf), GFP_KERNEL);
+       if (!sf) {
+               err = -ENOMEM;
+               goto alloc_err;
+       }
+       sf->id = id_err;
+       hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, sf->id);
+       dl_port_index = mlx5_esw_vport_to_devlink_port_index(table->dev, hw_fn_id);
+       sf->port_index = dl_port_index;
+       sf->hw_fn_id = hw_fn_id;
+       sf->hw_state = MLX5_VHCA_STATE_ALLOCATED;
+
+       err = mlx5_sf_id_insert(table, sf);
+       if (err)
+               goto insert_err;
+
+       return sf;
+
+insert_err:
+       kfree(sf);
+alloc_err:
+       mlx5_sf_hw_table_sf_free(table->dev, id_err);
+id_err:
+       if (err == -EEXIST)
+               NL_SET_ERR_MSG_MOD(extack, "SF already exist. Choose different sfnum");
+       return ERR_PTR(err);
+}
+
+static void mlx5_sf_free(struct mlx5_sf_table *table, struct mlx5_sf *sf)
+{
+       mlx5_sf_id_erase(table, sf);
+       mlx5_sf_hw_table_sf_free(table->dev, sf->id);
+       kfree(sf);
+}
+
+static struct mlx5_sf_table *mlx5_sf_table_try_get(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_table *table = dev->priv.sf_table;
+
+       if (!table)
+               return NULL;
+
+       return refcount_inc_not_zero(&table->refcount) ? table : NULL;
+}
+
+static void mlx5_sf_table_put(struct mlx5_sf_table *table)
+{
+       if (refcount_dec_and_test(&table->refcount))
+               complete(&table->disable_complete);
+}
+
+static enum devlink_port_fn_state mlx5_sf_to_devlink_state(u8 hw_state)
+{
+       switch (hw_state) {
+       case MLX5_VHCA_STATE_ACTIVE:
+       case MLX5_VHCA_STATE_IN_USE:
+       case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
+               return DEVLINK_PORT_FN_STATE_ACTIVE;
+       case MLX5_VHCA_STATE_INVALID:
+       case MLX5_VHCA_STATE_ALLOCATED:
+       default:
+               return DEVLINK_PORT_FN_STATE_INACTIVE;
+       }
+}
+
+static enum devlink_port_fn_opstate mlx5_sf_to_devlink_opstate(u8 hw_state)
+{
+       switch (hw_state) {
+       case MLX5_VHCA_STATE_IN_USE:
+       case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
+               return DEVLINK_PORT_FN_OPSTATE_ATTACHED;
+       case MLX5_VHCA_STATE_INVALID:
+       case MLX5_VHCA_STATE_ALLOCATED:
+       case MLX5_VHCA_STATE_ACTIVE:
+       default:
+               return DEVLINK_PORT_FN_OPSTATE_DETACHED;
+       }
+}
+
+static bool mlx5_sf_is_active(const struct mlx5_sf *sf)
+{
+       return sf->hw_state == MLX5_VHCA_STATE_ACTIVE || sf->hw_state == MLX5_VHCA_STATE_IN_USE;
+}
+
+int mlx5_devlink_sf_port_fn_state_get(struct devlink *devlink, struct devlink_port *dl_port,
+                                     enum devlink_port_fn_state *state,
+                                     enum devlink_port_fn_opstate *opstate,
+                                     struct netlink_ext_ack *extack)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_sf_table *table;
+       struct mlx5_sf *sf;
+       int err = 0;
+
+       table = mlx5_sf_table_try_get(dev);
+       if (!table)
+               return -EOPNOTSUPP;
+
+       sf = mlx5_sf_lookup_by_index(table, dl_port->index);
+       if (!sf) {
+               err = -EOPNOTSUPP;
+               goto sf_err;
+       }
+       mutex_lock(&table->sf_state_lock);
+       *state = mlx5_sf_to_devlink_state(sf->hw_state);
+       *opstate = mlx5_sf_to_devlink_opstate(sf->hw_state);
+       mutex_unlock(&table->sf_state_lock);
+sf_err:
+       mlx5_sf_table_put(table);
+       return err;
+}
+
+static int mlx5_sf_activate(struct mlx5_core_dev *dev, struct mlx5_sf *sf)
+{
+       int err;
+
+       if (mlx5_sf_is_active(sf))
+               return 0;
+       if (sf->hw_state != MLX5_VHCA_STATE_ALLOCATED)
+               return -EINVAL;
+
+       err = mlx5_cmd_sf_enable_hca(dev, sf->hw_fn_id);
+       if (err)
+               return err;
+
+       sf->hw_state = MLX5_VHCA_STATE_ACTIVE;
+       return 0;
+}
+
+static int mlx5_sf_deactivate(struct mlx5_core_dev *dev, struct mlx5_sf *sf)
+{
+       int err;
+
+       if (!mlx5_sf_is_active(sf))
+               return 0;
+
+       err = mlx5_cmd_sf_disable_hca(dev, sf->hw_fn_id);
+       if (err)
+               return err;
+
+       sf->hw_state = MLX5_VHCA_STATE_TEARDOWN_REQUEST;
+       return 0;
+}
+
+static int mlx5_sf_state_set(struct mlx5_core_dev *dev, struct mlx5_sf_table *table,
+                            struct mlx5_sf *sf,
+                            enum devlink_port_fn_state state)
+{
+       int err = 0;
+
+       mutex_lock(&table->sf_state_lock);
+       if (state == mlx5_sf_to_devlink_state(sf->hw_state))
+               goto out;
+       if (state == DEVLINK_PORT_FN_STATE_ACTIVE)
+               err = mlx5_sf_activate(dev, sf);
+       else if (state == DEVLINK_PORT_FN_STATE_INACTIVE)
+               err = mlx5_sf_deactivate(dev, sf);
+       else
+               err = -EINVAL;
+out:
+       mutex_unlock(&table->sf_state_lock);
+       return err;
+}
+
+int mlx5_devlink_sf_port_fn_state_set(struct devlink *devlink, struct devlink_port *dl_port,
+                                     enum devlink_port_fn_state state,
+                                     struct netlink_ext_ack *extack)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_sf_table *table;
+       struct mlx5_sf *sf;
+       int err;
+
+       table = mlx5_sf_table_try_get(dev);
+       if (!table) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Port state set is only supported in eswitch switchdev mode or SF ports are disabled.");
+               return -EOPNOTSUPP;
+       }
+       sf = mlx5_sf_lookup_by_index(table, dl_port->index);
+       if (!sf) {
+               err = -ENODEV;
+               goto out;
+       }
+
+       err = mlx5_sf_state_set(dev, table, sf, state);
+out:
+       mlx5_sf_table_put(table);
+       return err;
+}
+
+static int mlx5_sf_add(struct mlx5_core_dev *dev, struct mlx5_sf_table *table,
+                      const struct devlink_port_new_attrs *new_attr,
+                      struct netlink_ext_ack *extack,
+                      unsigned int *new_port_index)
+{
+       struct mlx5_eswitch *esw = dev->priv.eswitch;
+       struct mlx5_sf *sf;
+       u16 hw_fn_id;
+       int err;
+
+       sf = mlx5_sf_alloc(table, new_attr->sfnum, extack);
+       if (IS_ERR(sf))
+               return PTR_ERR(sf);
+
+       hw_fn_id = mlx5_sf_sw_to_hw_id(dev, sf->id);
+       err = mlx5_esw_offloads_sf_vport_enable(esw, &sf->dl_port, hw_fn_id, new_attr->sfnum);
+       if (err)
+               goto esw_err;
+       *new_port_index = sf->port_index;
+       return 0;
+
+esw_err:
+       mlx5_sf_free(table, sf);
+       return err;
+}
+
+static int
+mlx5_sf_new_check_attr(struct mlx5_core_dev *dev, const struct devlink_port_new_attrs *new_attr,
+                      struct netlink_ext_ack *extack)
+{
+       if (new_attr->flavour != DEVLINK_PORT_FLAVOUR_PCI_SF) {
+               NL_SET_ERR_MSG_MOD(extack, "Driver supports only SF port addition");
+               return -EOPNOTSUPP;
+       }
+       if (new_attr->port_index_valid) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Driver does not support user defined port index assignment");
+               return -EOPNOTSUPP;
+       }
+       if (!new_attr->sfnum_valid) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "User must provide unique sfnum. Driver does not support auto assignment");
+               return -EOPNOTSUPP;
+       }
+       if (new_attr->controller_valid && new_attr->controller) {
+               NL_SET_ERR_MSG_MOD(extack, "External controller is unsupported");
+               return -EOPNOTSUPP;
+       }
+       if (new_attr->pfnum != PCI_FUNC(dev->pdev->devfn)) {
+               NL_SET_ERR_MSG_MOD(extack, "Invalid pfnum supplied");
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+int mlx5_devlink_sf_port_new(struct devlink *devlink,
+                            const struct devlink_port_new_attrs *new_attr,
+                            struct netlink_ext_ack *extack,
+                            unsigned int *new_port_index)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_sf_table *table;
+       int err;
+
+       err = mlx5_sf_new_check_attr(dev, new_attr, extack);
+       if (err)
+               return err;
+
+       table = mlx5_sf_table_try_get(dev);
+       if (!table) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Port add is only supported in eswitch switchdev mode or SF ports are disabled.");
+               return -EOPNOTSUPP;
+       }
+       err = mlx5_sf_add(dev, table, new_attr, extack, new_port_index);
+       mlx5_sf_table_put(table);
+       return err;
+}
+
+static void mlx5_sf_dealloc(struct mlx5_sf_table *table, struct mlx5_sf *sf)
+{
+       if (sf->hw_state == MLX5_VHCA_STATE_ALLOCATED) {
+               mlx5_sf_free(table, sf);
+       } else if (mlx5_sf_is_active(sf)) {
+               /* Even if its active, it is treated as in_use because by the time,
+                * it is disabled here, it may getting used. So it is safe to
+                * always look for the event to ensure that it is recycled only after
+                * firmware gives confirmation that it is detached by the driver.
+                */
+               mlx5_cmd_sf_disable_hca(table->dev, sf->hw_fn_id);
+               mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->id);
+               kfree(sf);
+       } else {
+               mlx5_sf_hw_table_sf_deferred_free(table->dev, sf->id);
+               kfree(sf);
+       }
+}
+
+int mlx5_devlink_sf_port_del(struct devlink *devlink, unsigned int port_index,
+                            struct netlink_ext_ack *extack)
+{
+       struct mlx5_core_dev *dev = devlink_priv(devlink);
+       struct mlx5_eswitch *esw = dev->priv.eswitch;
+       struct mlx5_sf_table *table;
+       struct mlx5_sf *sf;
+       int err = 0;
+
+       table = mlx5_sf_table_try_get(dev);
+       if (!table) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Port del is only supported in eswitch switchdev mode or SF ports are disabled.");
+               return -EOPNOTSUPP;
+       }
+       sf = mlx5_sf_lookup_by_index(table, port_index);
+       if (!sf) {
+               err = -ENODEV;
+               goto sf_err;
+       }
+
+       mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id);
+       mlx5_sf_id_erase(table, sf);
+
+       mutex_lock(&table->sf_state_lock);
+       mlx5_sf_dealloc(table, sf);
+       mutex_unlock(&table->sf_state_lock);
+sf_err:
+       mlx5_sf_table_put(table);
+       return err;
+}
+
+static bool mlx5_sf_state_update_check(const struct mlx5_sf *sf, u8 new_state)
+{
+       if (sf->hw_state == MLX5_VHCA_STATE_ACTIVE && new_state == MLX5_VHCA_STATE_IN_USE)
+               return true;
+
+       if (sf->hw_state == MLX5_VHCA_STATE_IN_USE && new_state == MLX5_VHCA_STATE_ACTIVE)
+               return true;
+
+       if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST &&
+           new_state == MLX5_VHCA_STATE_ALLOCATED)
+               return true;
+
+       return false;
+}
+
+static int mlx5_sf_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
+{
+       struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, vhca_nb);
+       const struct mlx5_vhca_state_event *event = data;
+       bool update = false;
+       struct mlx5_sf *sf;
+
+       table = mlx5_sf_table_try_get(table->dev);
+       if (!table)
+               return 0;
+
+       mutex_lock(&table->sf_state_lock);
+       sf = mlx5_sf_lookup_by_function_id(table, event->function_id);
+       if (!sf)
+               goto sf_err;
+
+       /* When driver is attached or detached to a function, an event
+        * notifies such state change.
+        */
+       update = mlx5_sf_state_update_check(sf, event->new_vhca_state);
+       if (update)
+               sf->hw_state = event->new_vhca_state;
+sf_err:
+       mutex_unlock(&table->sf_state_lock);
+       mlx5_sf_table_put(table);
+       return 0;
+}
+
+static void mlx5_sf_table_enable(struct mlx5_sf_table *table)
+{
+       if (!mlx5_sf_max_functions(table->dev))
+               return;
+
+       init_completion(&table->disable_complete);
+       refcount_set(&table->refcount, 1);
+}
+
+static void mlx5_sf_deactivate_all(struct mlx5_sf_table *table)
+{
+       struct mlx5_eswitch *esw = table->dev->priv.eswitch;
+       unsigned long index;
+       struct mlx5_sf *sf;
+
+       /* At this point, no new user commands can start and no vhca event can
+        * arrive. It is safe to destroy all user created SFs.
+        */
+       xa_for_each(&table->port_indices, index, sf) {
+               mlx5_esw_offloads_sf_vport_disable(esw, sf->hw_fn_id);
+               mlx5_sf_id_erase(table, sf);
+               mlx5_sf_dealloc(table, sf);
+       }
+}
+
+static void mlx5_sf_table_disable(struct mlx5_sf_table *table)
+{
+       if (!mlx5_sf_max_functions(table->dev))
+               return;
+
+       if (!refcount_read(&table->refcount))
+               return;
+
+       /* Balances with refcount_set; drop the reference so that new user cmd cannot start
+        * and new vhca event handler cannnot run.
+        */
+       mlx5_sf_table_put(table);
+       wait_for_completion(&table->disable_complete);
+
+       mlx5_sf_deactivate_all(table);
+}
+
+static int mlx5_sf_esw_event(struct notifier_block *nb, unsigned long event, void *data)
+{
+       struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, esw_nb);
+       const struct mlx5_esw_event_info *mode = data;
+
+       switch (mode->new_mode) {
+       case MLX5_ESWITCH_OFFLOADS:
+               mlx5_sf_table_enable(table);
+               break;
+       case MLX5_ESWITCH_NONE:
+               mlx5_sf_table_disable(table);
+               break;
+       default:
+               break;
+       };
+
+       return 0;
+}
+
+static bool mlx5_sf_table_supported(const struct mlx5_core_dev *dev)
+{
+       return dev->priv.eswitch && MLX5_ESWITCH_MANAGER(dev) && mlx5_sf_supported(dev);
+}
+
+int mlx5_sf_table_init(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_table *table;
+       int err;
+
+       if (!mlx5_sf_table_supported(dev) || !mlx5_vhca_event_supported(dev))
+               return 0;
+
+       table = kzalloc(sizeof(*table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       mutex_init(&table->sf_state_lock);
+       table->dev = dev;
+       xa_init(&table->port_indices);
+       dev->priv.sf_table = table;
+       refcount_set(&table->refcount, 0);
+       table->esw_nb.notifier_call = mlx5_sf_esw_event;
+       err = mlx5_esw_event_notifier_register(dev->priv.eswitch, &table->esw_nb);
+       if (err)
+               goto reg_err;
+
+       table->vhca_nb.notifier_call = mlx5_sf_vhca_event;
+       err = mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb);
+       if (err)
+               goto vhca_err;
+
+       return 0;
+
+vhca_err:
+       mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
+reg_err:
+       mutex_destroy(&table->sf_state_lock);
+       kfree(table);
+       dev->priv.sf_table = NULL;
+       return err;
+}
+
+void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_table *table = dev->priv.sf_table;
+
+       if (!table)
+               return;
+
+       mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb);
+       mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
+       WARN_ON(refcount_read(&table->refcount));
+       mutex_destroy(&table->sf_state_lock);
+       WARN_ON(!xa_empty(&table->port_indices));
+       kfree(table);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
new file mode 100644 (file)
index 0000000..58b6be0
--- /dev/null
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+#include <linux/mlx5/driver.h>
+#include "vhca_event.h"
+#include "priv.h"
+#include "sf.h"
+#include "mlx5_ifc_vhca_event.h"
+#include "vhca_event.h"
+#include "ecpf.h"
+
+struct mlx5_sf_hw {
+       u32 usr_sfnum;
+       u8 allocated: 1;
+       u8 pending_delete: 1;
+};
+
+struct mlx5_sf_hw_table {
+       struct mlx5_core_dev *dev;
+       struct mlx5_sf_hw *sfs;
+       int max_local_functions;
+       u8 ecpu: 1;
+       struct mutex table_lock; /* Serializes sf deletion and vhca state change handler. */
+       struct notifier_block vhca_nb;
+};
+
+u16 mlx5_sf_sw_to_hw_id(const struct mlx5_core_dev *dev, u16 sw_id)
+{
+       return sw_id + mlx5_sf_start_function_id(dev);
+}
+
+static u16 mlx5_sf_hw_to_sw_id(const struct mlx5_core_dev *dev, u16 hw_id)
+{
+       return hw_id - mlx5_sf_start_function_id(dev);
+}
+
+int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 usr_sfnum)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+       int sw_id = -ENOSPC;
+       u16 hw_fn_id;
+       int err;
+       int i;
+
+       if (!table->max_local_functions)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&table->table_lock);
+       /* Check if sf with same sfnum already exists or not. */
+       for (i = 0; i < table->max_local_functions; i++) {
+               if (table->sfs[i].allocated && table->sfs[i].usr_sfnum == usr_sfnum) {
+                       err = -EEXIST;
+                       goto exist_err;
+               }
+       }
+
+       /* Find the free entry and allocate the entry from the array */
+       for (i = 0; i < table->max_local_functions; i++) {
+               if (!table->sfs[i].allocated) {
+                       table->sfs[i].usr_sfnum = usr_sfnum;
+                       table->sfs[i].allocated = true;
+                       sw_id = i;
+                       break;
+               }
+       }
+       if (sw_id == -ENOSPC) {
+               err = -ENOSPC;
+               goto err;
+       }
+
+       hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, sw_id);
+       err = mlx5_cmd_alloc_sf(table->dev, hw_fn_id);
+       if (err)
+               goto err;
+
+       err = mlx5_modify_vhca_sw_id(dev, hw_fn_id, table->ecpu, usr_sfnum);
+       if (err)
+               goto vhca_err;
+
+       mutex_unlock(&table->table_lock);
+       return sw_id;
+
+vhca_err:
+       mlx5_cmd_dealloc_sf(table->dev, hw_fn_id);
+err:
+       table->sfs[i].allocated = false;
+exist_err:
+       mutex_unlock(&table->table_lock);
+       return err;
+}
+
+static void _mlx5_sf_hw_id_free(struct mlx5_core_dev *dev, u16 id)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+       u16 hw_fn_id;
+
+       hw_fn_id = mlx5_sf_sw_to_hw_id(table->dev, id);
+       mlx5_cmd_dealloc_sf(table->dev, hw_fn_id);
+       table->sfs[id].allocated = false;
+       table->sfs[id].pending_delete = false;
+}
+
+void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u16 id)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+
+       mutex_lock(&table->table_lock);
+       _mlx5_sf_hw_id_free(dev, id);
+       mutex_unlock(&table->table_lock);
+}
+
+void mlx5_sf_hw_table_sf_deferred_free(struct mlx5_core_dev *dev, u16 id)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+       u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
+       u16 hw_fn_id;
+       u8 state;
+       int err;
+
+       hw_fn_id = mlx5_sf_sw_to_hw_id(dev, id);
+       mutex_lock(&table->table_lock);
+       err = mlx5_cmd_query_vhca_state(dev, hw_fn_id, table->ecpu, out, sizeof(out));
+       if (err)
+               goto err;
+       state = MLX5_GET(query_vhca_state_out, out, vhca_state_context.vhca_state);
+       if (state == MLX5_VHCA_STATE_ALLOCATED) {
+               mlx5_cmd_dealloc_sf(table->dev, hw_fn_id);
+               table->sfs[id].allocated = false;
+       } else {
+               table->sfs[id].pending_delete = true;
+       }
+err:
+       mutex_unlock(&table->table_lock);
+}
+
+static void mlx5_sf_hw_dealloc_all(struct mlx5_sf_hw_table *table)
+{
+       int i;
+
+       for (i = 0; i < table->max_local_functions; i++) {
+               if (table->sfs[i].allocated)
+                       _mlx5_sf_hw_id_free(table->dev, i);
+       }
+}
+
+int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_hw_table *table;
+       struct mlx5_sf_hw *sfs;
+       int max_functions;
+
+       if (!mlx5_sf_supported(dev) || !mlx5_vhca_event_supported(dev))
+               return 0;
+
+       max_functions = mlx5_sf_max_functions(dev);
+       table = kzalloc(sizeof(*table), GFP_KERNEL);
+       if (!table)
+               return -ENOMEM;
+
+       sfs = kcalloc(max_functions, sizeof(*sfs), GFP_KERNEL);
+       if (!sfs)
+               goto table_err;
+
+       mutex_init(&table->table_lock);
+       table->dev = dev;
+       table->sfs = sfs;
+       table->max_local_functions = max_functions;
+       table->ecpu = mlx5_read_embedded_cpu(dev);
+       dev->priv.sf_hw_table = table;
+       mlx5_core_dbg(dev, "SF HW table: max sfs = %d\n", max_functions);
+       return 0;
+
+table_err:
+       kfree(table);
+       return -ENOMEM;
+}
+
+void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+
+       if (!table)
+               return;
+
+       mutex_destroy(&table->table_lock);
+       kfree(table->sfs);
+       kfree(table);
+}
+
+static int mlx5_sf_hw_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
+{
+       struct mlx5_sf_hw_table *table = container_of(nb, struct mlx5_sf_hw_table, vhca_nb);
+       const struct mlx5_vhca_state_event *event = data;
+       struct mlx5_sf_hw *sf_hw;
+       u16 sw_id;
+
+       if (event->new_vhca_state != MLX5_VHCA_STATE_ALLOCATED)
+               return 0;
+
+       sw_id = mlx5_sf_hw_to_sw_id(table->dev, event->function_id);
+       sf_hw = &table->sfs[sw_id];
+
+       mutex_lock(&table->table_lock);
+       /* SF driver notified through firmware that SF is finally detached.
+        * Hence recycle the sf hardware id for reuse.
+        */
+       if (sf_hw->allocated && sf_hw->pending_delete)
+               _mlx5_sf_hw_id_free(table->dev, sw_id);
+       mutex_unlock(&table->table_lock);
+       return 0;
+}
+
+int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+
+       if (!table)
+               return 0;
+
+       table->vhca_nb.notifier_call = mlx5_sf_hw_vhca_event;
+       return mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb);
+}
+
+void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
+{
+       struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
+
+       if (!table)
+               return;
+
+       mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb);
+       /* Dealloc SFs whose firmware event has been missed. */
+       mlx5_sf_hw_dealloc_all(table);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/mlx5_ifc_vhca_event.h
new file mode 100644 (file)
index 0000000..1daf5a1
--- /dev/null
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#ifndef __MLX5_IFC_VHCA_EVENT_H__
+#define __MLX5_IFC_VHCA_EVENT_H__
+
+enum mlx5_ifc_vhca_state {
+       MLX5_VHCA_STATE_INVALID = 0x0,
+       MLX5_VHCA_STATE_ALLOCATED = 0x1,
+       MLX5_VHCA_STATE_ACTIVE = 0x2,
+       MLX5_VHCA_STATE_IN_USE = 0x3,
+       MLX5_VHCA_STATE_TEARDOWN_REQUEST = 0x4,
+};
+
+struct mlx5_ifc_vhca_state_context_bits {
+       u8         arm_change_event[0x1];
+       u8         reserved_at_1[0xb];
+       u8         vhca_state[0x4];
+       u8         reserved_at_10[0x10];
+
+       u8         sw_function_id[0x20];
+
+       u8         reserved_at_40[0x80];
+};
+
+struct mlx5_ifc_query_vhca_state_out_bits {
+       u8         status[0x8];
+       u8         reserved_at_8[0x18];
+
+       u8         syndrome[0x20];
+
+       u8         reserved_at_40[0x40];
+
+       struct mlx5_ifc_vhca_state_context_bits vhca_state_context;
+};
+
+struct mlx5_ifc_query_vhca_state_in_bits {
+       u8         opcode[0x10];
+       u8         uid[0x10];
+
+       u8         reserved_at_20[0x10];
+       u8         op_mod[0x10];
+
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
+       u8         function_id[0x10];
+
+       u8         reserved_at_60[0x20];
+};
+
+struct mlx5_ifc_vhca_state_field_select_bits {
+       u8         reserved_at_0[0x1e];
+       u8         sw_function_id[0x1];
+       u8         arm_change_event[0x1];
+};
+
+struct mlx5_ifc_modify_vhca_state_out_bits {
+       u8         status[0x8];
+       u8         reserved_at_8[0x18];
+
+       u8         syndrome[0x20];
+
+       u8         reserved_at_40[0x40];
+};
+
+struct mlx5_ifc_modify_vhca_state_in_bits {
+       u8         opcode[0x10];
+       u8         uid[0x10];
+
+       u8         reserved_at_20[0x10];
+       u8         op_mod[0x10];
+
+       u8         embedded_cpu_function[0x1];
+       u8         reserved_at_41[0xf];
+       u8         function_id[0x10];
+
+       struct mlx5_ifc_vhca_state_field_select_bits vhca_state_field_select;
+
+       struct mlx5_ifc_vhca_state_context_bits vhca_state_context;
+};
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/priv.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/priv.h
new file mode 100644 (file)
index 0000000..cb02a51
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#ifndef __MLX5_SF_PRIV_H__
+#define __MLX5_SF_PRIV_H__
+
+#include <linux/mlx5/driver.h>
+
+int mlx5_cmd_alloc_sf(struct mlx5_core_dev *dev, u16 function_id);
+int mlx5_cmd_dealloc_sf(struct mlx5_core_dev *dev, u16 function_id);
+
+int mlx5_cmd_sf_enable_hca(struct mlx5_core_dev *dev, u16 func_id);
+int mlx5_cmd_sf_disable_hca(struct mlx5_core_dev *dev, u16 func_id);
+
+u16 mlx5_sf_sw_to_hw_id(const struct mlx5_core_dev *dev, u16 sw_id);
+
+int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 usr_sfnum);
+void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u16 id);
+void mlx5_sf_hw_table_sf_deferred_free(struct mlx5_core_dev *dev, u16 id);
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
new file mode 100644 (file)
index 0000000..0b6aea1
--- /dev/null
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#ifndef __MLX5_SF_H__
+#define __MLX5_SF_H__
+
+#include <linux/mlx5/driver.h>
+
+static inline u16 mlx5_sf_start_function_id(const struct mlx5_core_dev *dev)
+{
+       return MLX5_CAP_GEN(dev, sf_base_id);
+}
+
+#ifdef CONFIG_MLX5_SF
+
+static inline bool mlx5_sf_supported(const struct mlx5_core_dev *dev)
+{
+       return MLX5_CAP_GEN(dev, sf);
+}
+
+static inline u16 mlx5_sf_max_functions(const struct mlx5_core_dev *dev)
+{
+       if (!mlx5_sf_supported(dev))
+               return 0;
+       if (MLX5_CAP_GEN(dev, max_num_sf))
+               return MLX5_CAP_GEN(dev, max_num_sf);
+       else
+               return 1 << MLX5_CAP_GEN(dev, log_max_sf);
+}
+
+#else
+
+static inline bool mlx5_sf_supported(const struct mlx5_core_dev *dev)
+{
+       return false;
+}
+
+static inline u16 mlx5_sf_max_functions(const struct mlx5_core_dev *dev)
+{
+       return 0;
+}
+
+#endif
+
+#ifdef CONFIG_MLX5_SF_MANAGER
+
+int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev);
+void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev);
+
+int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev);
+void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev);
+
+int mlx5_sf_table_init(struct mlx5_core_dev *dev);
+void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev);
+
+int mlx5_devlink_sf_port_new(struct devlink *devlink,
+                            const struct devlink_port_new_attrs *add_attr,
+                            struct netlink_ext_ack *extack,
+                            unsigned int *new_port_index);
+int mlx5_devlink_sf_port_del(struct devlink *devlink, unsigned int port_index,
+                            struct netlink_ext_ack *extack);
+int mlx5_devlink_sf_port_fn_state_get(struct devlink *devlink, struct devlink_port *dl_port,
+                                     enum devlink_port_fn_state *state,
+                                     enum devlink_port_fn_opstate *opstate,
+                                     struct netlink_ext_ack *extack);
+int mlx5_devlink_sf_port_fn_state_set(struct devlink *devlink, struct devlink_port *dl_port,
+                                     enum devlink_port_fn_state state,
+                                     struct netlink_ext_ack *extack);
+#else
+
+static inline int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev)
+{
+       return 0;
+}
+
+static inline void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev)
+{
+}
+
+static inline int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev)
+{
+       return 0;
+}
+
+static inline void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
+{
+}
+
+static inline int mlx5_sf_table_init(struct mlx5_core_dev *dev)
+{
+       return 0;
+}
+
+static inline void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
+{
+}
+
+#endif
+
+#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c
new file mode 100644 (file)
index 0000000..af2f2dd
--- /dev/null
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#include <linux/mlx5/driver.h>
+#include "mlx5_ifc_vhca_event.h"
+#include "mlx5_core.h"
+#include "vhca_event.h"
+#include "ecpf.h"
+
+struct mlx5_vhca_state_notifier {
+       struct mlx5_core_dev *dev;
+       struct mlx5_nb nb;
+       struct blocking_notifier_head n_head;
+};
+
+struct mlx5_vhca_event_work {
+       struct work_struct work;
+       struct mlx5_vhca_state_notifier *notifier;
+       struct mlx5_vhca_state_event event;
+};
+
+int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id,
+                             bool ecpu, u32 *out, u32 outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(query_vhca_state_in)] = {};
+
+       MLX5_SET(query_vhca_state_in, in, opcode, MLX5_CMD_OP_QUERY_VHCA_STATE);
+       MLX5_SET(query_vhca_state_in, in, function_id, function_id);
+       MLX5_SET(query_vhca_state_in, in, embedded_cpu_function, ecpu);
+
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
+}
+
+static int mlx5_cmd_modify_vhca_state(struct mlx5_core_dev *dev, u16 function_id,
+                                     bool ecpu, u32 *in, u32 inlen)
+{
+       u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
+
+       MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
+       MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
+       MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, ecpu);
+
+       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+}
+
+int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, bool ecpu, u32 sw_fn_id)
+{
+       u32 out[MLX5_ST_SZ_DW(modify_vhca_state_out)] = {};
+       u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};
+
+       MLX5_SET(modify_vhca_state_in, in, opcode, MLX5_CMD_OP_MODIFY_VHCA_STATE);
+       MLX5_SET(modify_vhca_state_in, in, function_id, function_id);
+       MLX5_SET(modify_vhca_state_in, in, embedded_cpu_function, ecpu);
+       MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.sw_function_id, 1);
+       MLX5_SET(modify_vhca_state_in, in, vhca_state_context.sw_function_id, sw_fn_id);
+
+       return mlx5_cmd_exec_inout(dev, modify_vhca_state, in, out);
+}
+
+int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id, bool ecpu)
+{
+       u32 in[MLX5_ST_SZ_DW(modify_vhca_state_in)] = {};
+
+       MLX5_SET(modify_vhca_state_in, in, vhca_state_context.arm_change_event, 1);
+       MLX5_SET(modify_vhca_state_in, in, vhca_state_field_select.arm_change_event, 1);
+
+       return mlx5_cmd_modify_vhca_state(dev, function_id, ecpu, in, sizeof(in));
+}
+
+static void
+mlx5_vhca_event_notify(struct mlx5_core_dev *dev, struct mlx5_vhca_state_event *event)
+{
+       u32 out[MLX5_ST_SZ_DW(query_vhca_state_out)] = {};
+       int err;
+
+       err = mlx5_cmd_query_vhca_state(dev, event->function_id, event->ecpu, out, sizeof(out));
+       if (err)
+               return;
+
+       event->sw_function_id = MLX5_GET(query_vhca_state_out, out,
+                                        vhca_state_context.sw_function_id);
+       event->new_vhca_state = MLX5_GET(query_vhca_state_out, out,
+                                        vhca_state_context.vhca_state);
+
+       mlx5_vhca_event_arm(dev, event->function_id, event->ecpu);
+
+       blocking_notifier_call_chain(&dev->priv.vhca_state_notifier->n_head, 0, event);
+}
+
+static void mlx5_vhca_state_work_handler(struct work_struct *_work)
+{
+       struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work);
+       struct mlx5_vhca_state_notifier *notifier = work->notifier;
+       struct mlx5_core_dev *dev = notifier->dev;
+
+       mlx5_vhca_event_notify(dev, &work->event);
+}
+
+static int
+mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data)
+{
+       struct mlx5_vhca_state_notifier *notifier =
+                               mlx5_nb_cof(nb, struct mlx5_vhca_state_notifier, nb);
+       struct mlx5_vhca_event_work *work;
+       struct mlx5_eqe *eqe = data;
+
+       work = kzalloc(sizeof(*work), GFP_ATOMIC);
+       if (!work)
+               return NOTIFY_DONE;
+       INIT_WORK(&work->work, &mlx5_vhca_state_work_handler);
+       work->notifier = notifier;
+       work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id);
+       work->event.ecpu = be16_to_cpu(eqe->data.vhca_state.ec_function);
+       mlx5_events_work_enqueue(notifier->dev, &work->work);
+       return NOTIFY_OK;
+}
+
+void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap)
+{
+       if (!mlx5_vhca_event_supported(dev))
+               return;
+
+       MLX5_SET(cmd_hca_cap, set_hca_cap, vhca_state, 1);
+       MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_allocated, 1);
+       MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_active, 1);
+       MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_in_use, 1);
+       MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1);
+}
+
+int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
+{
+       struct mlx5_vhca_state_notifier *notifier;
+
+       if (!mlx5_vhca_event_supported(dev))
+               return 0;
+
+       notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
+       if (!notifier)
+               return -ENOMEM;
+
+       dev->priv.vhca_state_notifier = notifier;
+       notifier->dev = dev;
+       BLOCKING_INIT_NOTIFIER_HEAD(&notifier->n_head);
+       MLX5_NB_INIT(&notifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE);
+       return 0;
+}
+
+void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
+{
+       if (!mlx5_vhca_event_supported(dev))
+               return;
+
+       kfree(dev->priv.vhca_state_notifier);
+       dev->priv.vhca_state_notifier = NULL;
+}
+
+void mlx5_vhca_event_start(struct mlx5_core_dev *dev)
+{
+       struct mlx5_vhca_state_notifier *notifier;
+
+       if (!dev->priv.vhca_state_notifier)
+               return;
+
+       notifier = dev->priv.vhca_state_notifier;
+       mlx5_eq_notifier_register(dev, &notifier->nb);
+}
+
+void mlx5_vhca_event_stop(struct mlx5_core_dev *dev)
+{
+       struct mlx5_vhca_state_notifier *notifier;
+
+       if (!dev->priv.vhca_state_notifier)
+               return;
+
+       notifier = dev->priv.vhca_state_notifier;
+       mlx5_eq_notifier_unregister(dev, &notifier->nb);
+}
+
+int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
+{
+       if (!dev->priv.vhca_state_notifier)
+               return -EOPNOTSUPP;
+       return blocking_notifier_chain_register(&dev->priv.vhca_state_notifier->n_head, nb);
+}
+
+void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
+{
+       blocking_notifier_chain_unregister(&dev->priv.vhca_state_notifier->n_head, nb);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h
new file mode 100644 (file)
index 0000000..1fe1ec6
--- /dev/null
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020 Mellanox Technologies Ltd */
+
+#ifndef __MLX5_VHCA_EVENT_H__
+#define __MLX5_VHCA_EVENT_H__
+
+#ifdef CONFIG_MLX5_SF
+
+struct mlx5_vhca_state_event {
+       u16 function_id;
+       u16 sw_function_id;
+       u8 new_vhca_state;
+       bool ecpu;
+};
+
+static inline bool mlx5_vhca_event_supported(const struct mlx5_core_dev *dev)
+{
+       return MLX5_CAP_GEN_MAX(dev, vhca_state);
+}
+
+void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap);
+int mlx5_vhca_event_init(struct mlx5_core_dev *dev);
+void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev);
+void mlx5_vhca_event_start(struct mlx5_core_dev *dev);
+void mlx5_vhca_event_stop(struct mlx5_core_dev *dev);
+int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb);
+void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb);
+int mlx5_modify_vhca_sw_id(struct mlx5_core_dev *dev, u16 function_id, bool ecpu, u32 sw_fn_id);
+int mlx5_vhca_event_arm(struct mlx5_core_dev *dev, u16 function_id, bool ecpu);
+int mlx5_cmd_query_vhca_state(struct mlx5_core_dev *dev, u16 function_id,
+                             bool ecpu, u32 *out, u32 outlen);
+#else
+
+static inline void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap)
+{
+}
+
+static inline int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
+{
+       return 0;
+}
+
+static inline void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
+{
+}
+
+static inline void mlx5_vhca_event_start(struct mlx5_core_dev *dev)
+{
+}
+
+static inline void mlx5_vhca_event_stop(struct mlx5_core_dev *dev)
+{
+}
+
+#endif
+
+#endif
index df1363a..27c2b84 100644 (file)
@@ -218,158 +218,6 @@ next_action_state[DR_ACTION_DOMAIN_MAX][DR_ACTION_STATE_MAX][DR_ACTION_TYP_MAX]
        },
 };
 
-struct dr_action_modify_field_conv {
-       u16 hw_field;
-       u8 start;
-       u8 end;
-       u8 l3_type;
-       u8 l4_type;
-};
-
-static const struct dr_action_modify_field_conv dr_action_conv_arr[] = {
-       [MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_1, .start = 16, .end = 47,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_1, .start = 0, .end = 15,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_2, .start = 32, .end = 47,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_0, .start = 16, .end = 47,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_0, .start = 0, .end = 15,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_IP_DSCP] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_1, .start = 0, .end = 5,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 48, .end = 56,
-               .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 0, .end = 15,
-               .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 16, .end = 31,
-               .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_IP_TTL] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_1, .start = 8, .end = 15,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_1, .start = 8, .end = 15,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 0, .end = 15,
-               .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_UDP,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_0, .start = 16, .end = 31,
-               .l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_UDP,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_3, .start = 32, .end = 63,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_3, .start = 0, .end = 31,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_4, .start = 32, .end = 63,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_4, .start = 0, .end = 31,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 32, .end = 63,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 0, .end = 31,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_2, .start = 32, .end = 63,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_2, .start = 0, .end = 31,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_SIPV4] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 0, .end = 31,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_DIPV4] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L3_0, .start = 32, .end = 63,
-               .l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_A] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_METADATA, .start = 0, .end = 31,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_B] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_METADATA, .start = 32, .end = 63,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_0] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_0, .start = 32, .end = 63,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_1] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_0, .start = 0, .end = 31,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_2] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_1, .start = 32, .end = 63,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_3] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_1, .start = 0, .end = 31,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_4] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_2, .start = 32, .end = 63,
-       },
-       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_5] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_REG_2, .start = 0, .end = 31,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_1, .start = 32, .end = 63,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L4_1, .start = 0, .end = 31,
-       },
-       [MLX5_ACTION_IN_FIELD_OUT_FIRST_VID] = {
-               .hw_field = MLX5DR_ACTION_MDFY_HW_FLD_L2_2, .start = 0, .end = 15,
-       },
-};
-
-#define MAX_VLANS 2
-struct dr_action_vlan_info {
-       int     count;
-       u32     headers[MAX_VLANS];
-};
-
-struct dr_action_apply_attr {
-       u32     modify_index;
-       u16     modify_actions;
-       u32     decap_index;
-       u16     decap_actions;
-       u8      decap_with_vlan:1;
-       u64     final_icm_addr;
-       u32     flow_tag;
-       u32     ctr_id;
-       u16     gvmi;
-       u16     hit_gvmi;
-       u32     reformat_id;
-       u32     reformat_size;
-       struct  dr_action_vlan_info vlans;
-};
-
 static int
 dr_action_reformat_to_action_type(enum mlx5dr_action_reformat_type reformat_type,
                                  enum mlx5dr_action_type *action_type)
@@ -394,141 +242,6 @@ dr_action_reformat_to_action_type(enum mlx5dr_action_reformat_type reformat_type
        return 0;
 }
 
-static void dr_actions_init_next_ste(u8 **last_ste,
-                                    u32 *added_stes,
-                                    enum mlx5dr_ste_entry_type entry_type,
-                                    u16 gvmi)
-{
-       (*added_stes)++;
-       *last_ste += DR_STE_SIZE;
-       mlx5dr_ste_init(*last_ste, MLX5DR_STE_LU_TYPE_DONT_CARE, entry_type, gvmi);
-}
-
-static void dr_actions_apply_tx(struct mlx5dr_domain *dmn,
-                               u8 *action_type_set,
-                               u8 *last_ste,
-                               struct dr_action_apply_attr *attr,
-                               u32 *added_stes)
-{
-       bool encap = action_type_set[DR_ACTION_TYP_L2_TO_TNL_L2] ||
-               action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3];
-
-       /* We want to make sure the modify header comes before L2
-        * encapsulation. The reason for that is that we support
-        * modify headers for outer headers only
-        */
-       if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) {
-               mlx5dr_ste_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT);
-               mlx5dr_ste_set_rewrite_actions(last_ste,
-                                              attr->modify_actions,
-                                              attr->modify_index);
-       }
-
-       if (action_type_set[DR_ACTION_TYP_PUSH_VLAN]) {
-               int i;
-
-               for (i = 0; i < attr->vlans.count; i++) {
-                       if (i || action_type_set[DR_ACTION_TYP_MODIFY_HDR])
-                               dr_actions_init_next_ste(&last_ste,
-                                                        added_stes,
-                                                        MLX5DR_STE_TYPE_TX,
-                                                        attr->gvmi);
-
-                       mlx5dr_ste_set_tx_push_vlan(last_ste,
-                                                   attr->vlans.headers[i],
-                                                   encap);
-               }
-       }
-
-       if (encap) {
-               /* Modify header and encapsulation require a different STEs.
-                * Since modify header STE format doesn't support encapsulation
-                * tunneling_action.
-                */
-               if (action_type_set[DR_ACTION_TYP_MODIFY_HDR] ||
-                   action_type_set[DR_ACTION_TYP_PUSH_VLAN])
-                       dr_actions_init_next_ste(&last_ste,
-                                                added_stes,
-                                                MLX5DR_STE_TYPE_TX,
-                                                attr->gvmi);
-
-               mlx5dr_ste_set_tx_encap(last_ste,
-                                       attr->reformat_id,
-                                       attr->reformat_size,
-                                       action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]);
-               /* Whenever prio_tag_required enabled, we can be sure that the
-                * previous table (ACL) already push vlan to our packet,
-                * And due to HW limitation we need to set this bit, otherwise
-                * push vlan + reformat will not work.
-                */
-               if (MLX5_CAP_GEN(dmn->mdev, prio_tag_required))
-                       mlx5dr_ste_set_go_back_bit(last_ste);
-       }
-
-       if (action_type_set[DR_ACTION_TYP_CTR])
-               mlx5dr_ste_set_counter_id(last_ste, attr->ctr_id);
-}
-
-static void dr_actions_apply_rx(u8 *action_type_set,
-                               u8 *last_ste,
-                               struct dr_action_apply_attr *attr,
-                               u32 *added_stes)
-{
-       if (action_type_set[DR_ACTION_TYP_CTR])
-               mlx5dr_ste_set_counter_id(last_ste, attr->ctr_id);
-
-       if (action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2]) {
-               mlx5dr_ste_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT);
-               mlx5dr_ste_set_rx_decap_l3(last_ste, attr->decap_with_vlan);
-               mlx5dr_ste_set_rewrite_actions(last_ste,
-                                              attr->decap_actions,
-                                              attr->decap_index);
-       }
-
-       if (action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2])
-               mlx5dr_ste_set_rx_decap(last_ste);
-
-       if (action_type_set[DR_ACTION_TYP_POP_VLAN]) {
-               int i;
-
-               for (i = 0; i < attr->vlans.count; i++) {
-                       if (i ||
-                           action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2] ||
-                           action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2])
-                               dr_actions_init_next_ste(&last_ste,
-                                                        added_stes,
-                                                        MLX5DR_STE_TYPE_RX,
-                                                        attr->gvmi);
-
-                       mlx5dr_ste_set_rx_pop_vlan(last_ste);
-               }
-       }
-
-       if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) {
-               if (mlx5dr_ste_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT)
-                       dr_actions_init_next_ste(&last_ste,
-                                                added_stes,
-                                                MLX5DR_STE_TYPE_MODIFY_PKT,
-                                                attr->gvmi);
-               else
-                       mlx5dr_ste_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT);
-
-               mlx5dr_ste_set_rewrite_actions(last_ste,
-                                              attr->modify_actions,
-                                              attr->modify_index);
-       }
-
-       if (action_type_set[DR_ACTION_TYP_TAG]) {
-               if (mlx5dr_ste_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT)
-                       dr_actions_init_next_ste(&last_ste,
-                                                added_stes,
-                                                MLX5DR_STE_TYPE_RX,
-                                                attr->gvmi);
-
-               mlx5dr_ste_rx_set_flow_tag(last_ste, attr->flow_tag);
-       }
-}
-
 /* Apply the actions on the rule STE array starting from the last_ste.
  * Actions might require more than one STE, new_num_stes will return
  * the new size of the STEs array, rule with actions.
@@ -537,21 +250,20 @@ static void dr_actions_apply(struct mlx5dr_domain *dmn,
                             enum mlx5dr_ste_entry_type ste_type,
                             u8 *action_type_set,
                             u8 *last_ste,
-                            struct dr_action_apply_attr *attr,
+                            struct mlx5dr_ste_actions_attr *attr,
                             u32 *new_num_stes)
 {
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        u32 added_stes = 0;
 
        if (ste_type == MLX5DR_STE_TYPE_RX)
-               dr_actions_apply_rx(action_type_set, last_ste, attr, &added_stes);
+               mlx5dr_ste_set_actions_rx(ste_ctx, dmn, action_type_set,
+                                         last_ste, attr, &added_stes);
        else
-               dr_actions_apply_tx(dmn, action_type_set, last_ste, attr, &added_stes);
+               mlx5dr_ste_set_actions_tx(ste_ctx, dmn, action_type_set,
+                                         last_ste, attr, &added_stes);
 
-       last_ste += added_stes * DR_STE_SIZE;
        *new_num_stes += added_stes;
-
-       mlx5dr_ste_set_hit_gvmi(last_ste, attr->hit_gvmi);
-       mlx5dr_ste_set_hit_addr(last_ste, attr->final_icm_addr, 1);
 }
 
 static enum dr_action_domain
@@ -643,9 +355,9 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher,
        bool rx_rule = nic_dmn->ste_type == MLX5DR_STE_TYPE_RX;
        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
        u8 action_type_set[DR_ACTION_TYP_MAX] = {};
+       struct mlx5dr_ste_actions_attr attr = {};
        struct mlx5dr_action *dest_action = NULL;
        u32 state = DR_ACTION_STATE_NO_ACTION;
-       struct dr_action_apply_attr attr = {};
        enum dr_action_domain action_domain;
        bool recalc_cs_required = false;
        u8 *last_ste;
@@ -756,12 +468,12 @@ int mlx5dr_actions_build_ste_arr(struct mlx5dr_matcher *matcher,
                        }
                        break;
                case DR_ACTION_TYP_POP_VLAN:
-                       max_actions_type = MAX_VLANS;
+                       max_actions_type = MLX5DR_MAX_VLANS;
                        attr.vlans.count++;
                        break;
                case DR_ACTION_TYP_PUSH_VLAN:
-                       max_actions_type = MAX_VLANS;
-                       if (attr.vlans.count == MAX_VLANS)
+                       max_actions_type = MLX5DR_MAX_VLANS;
+                       if (attr.vlans.count == MLX5DR_MAX_VLANS)
                                return -EINVAL;
 
                        attr.vlans.headers[attr.vlans.count++] = action->push_vlan.vlan_hdr;
@@ -817,132 +529,6 @@ out_invalid_arg:
        return -EINVAL;
 }
 
-#define CVLAN_ETHERTYPE 0x8100
-#define SVLAN_ETHERTYPE 0x88a8
-#define HDR_LEN_L2_ONLY 14
-#define HDR_LEN_L2_VLAN 18
-#define REWRITE_HW_ACTION_NUM 6
-
-static int dr_actions_l2_rewrite(struct mlx5dr_domain *dmn,
-                                struct mlx5dr_action *action,
-                                void *data, size_t data_sz)
-{
-       struct mlx5_ifc_l2_hdr_bits *l2_hdr = data;
-       u64 ops[REWRITE_HW_ACTION_NUM] = {};
-       u32 hdr_fld_4b;
-       u16 hdr_fld_2b;
-       u16 vlan_type;
-       bool vlan;
-       int i = 0;
-       int ret;
-
-       vlan = (data_sz != HDR_LEN_L2_ONLY);
-
-       /* dmac_47_16 */
-       MLX5_SET(dr_action_hw_set, ops + i,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_length, 0);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_0);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_left_shifter, 16);
-       hdr_fld_4b = MLX5_GET(l2_hdr, l2_hdr, dmac_47_16);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                inline_data, hdr_fld_4b);
-       i++;
-
-       /* smac_47_16 */
-       MLX5_SET(dr_action_hw_set, ops + i,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_length, 0);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_1);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_left_shifter, 16);
-       hdr_fld_4b = (MLX5_GET(l2_hdr, l2_hdr, smac_31_0) >> 16 |
-                     MLX5_GET(l2_hdr, l2_hdr, smac_47_32) << 16);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                inline_data, hdr_fld_4b);
-       i++;
-
-       /* dmac_15_0 */
-       MLX5_SET(dr_action_hw_set, ops + i,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_length, 16);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_0);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_left_shifter, 0);
-       hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, dmac_15_0);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                inline_data, hdr_fld_2b);
-       i++;
-
-       /* ethertype + (optional) vlan */
-       MLX5_SET(dr_action_hw_set, ops + i,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_2);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_left_shifter, 32);
-       if (!vlan) {
-               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype);
-               MLX5_SET(dr_action_hw_set, ops + i, inline_data, hdr_fld_2b);
-               MLX5_SET(dr_action_hw_set, ops + i, destination_length, 16);
-       } else {
-               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype);
-               vlan_type = hdr_fld_2b == SVLAN_ETHERTYPE ? DR_STE_SVLAN : DR_STE_CVLAN;
-               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan);
-               hdr_fld_4b = (vlan_type << 16) | hdr_fld_2b;
-               MLX5_SET(dr_action_hw_set, ops + i, inline_data, hdr_fld_4b);
-               MLX5_SET(dr_action_hw_set, ops + i, destination_length, 18);
-       }
-       i++;
-
-       /* smac_15_0 */
-       MLX5_SET(dr_action_hw_set, ops + i,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_length, 16);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_1);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                destination_left_shifter, 0);
-       hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, smac_31_0);
-       MLX5_SET(dr_action_hw_set, ops + i,
-                inline_data, hdr_fld_2b);
-       i++;
-
-       if (vlan) {
-               MLX5_SET(dr_action_hw_set, ops + i,
-                        opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan_type);
-               MLX5_SET(dr_action_hw_set, ops + i,
-                        inline_data, hdr_fld_2b);
-               MLX5_SET(dr_action_hw_set, ops + i,
-                        destination_length, 16);
-               MLX5_SET(dr_action_hw_set, ops + i,
-                        destination_field_code, MLX5DR_ACTION_MDFY_HW_FLD_L2_2);
-               MLX5_SET(dr_action_hw_set, ops + i,
-                        destination_left_shifter, 0);
-               i++;
-       }
-
-       action->rewrite.data = (void *)ops;
-       action->rewrite.num_of_actions = i;
-
-       ret = mlx5dr_send_postsend_action(dmn, action);
-       if (ret) {
-               mlx5dr_dbg(dmn, "Writing encapsulation action to ICM failed\n");
-               return ret;
-       }
-
-       return 0;
-}
-
 static struct mlx5dr_action *
 dr_action_create_generic(enum mlx5dr_action_type action_type)
 {
@@ -1217,21 +803,34 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn,
        }
        case DR_ACTION_TYP_TNL_L3_TO_L2:
        {
-               /* Only Ethernet frame is supported, with VLAN (18) or without (14) */
-               if (data_sz != HDR_LEN_L2_ONLY && data_sz != HDR_LEN_L2_VLAN)
-                       return -EINVAL;
+               u8 hw_actions[ACTION_CACHE_LINE_SIZE] = {};
+               int ret;
+
+               ret = mlx5dr_ste_set_action_decap_l3_list(dmn->ste_ctx,
+                                                         data, data_sz,
+                                                         hw_actions,
+                                                         ACTION_CACHE_LINE_SIZE,
+                                                         &action->rewrite.num_of_actions);
+               if (ret) {
+                       mlx5dr_dbg(dmn, "Failed creating decap l3 action list\n");
+                       return ret;
+               }
 
                action->rewrite.chunk = mlx5dr_icm_alloc_chunk(dmn->action_icm_pool,
                                                               DR_CHUNK_SIZE_8);
-               if (!action->rewrite.chunk)
+               if (!action->rewrite.chunk) {
+                       mlx5dr_dbg(dmn, "Failed allocating modify header chunk\n");
                        return -ENOMEM;
+               }
 
+               action->rewrite.data = (void *)hw_actions;
                action->rewrite.index = (action->rewrite.chunk->icm_addr -
                                         dmn->info.caps.hdr_modify_icm_addr) /
                                         ACTION_CACHE_LINE_SIZE;
 
-               ret = dr_actions_l2_rewrite(dmn, action, data, data_sz);
+               ret = mlx5dr_send_postsend_action(dmn, action);
                if (ret) {
+                       mlx5dr_dbg(dmn, "Writing decap l3 actions to ICM failed\n");
                        mlx5dr_icm_free_chunk(action->rewrite.chunk);
                        return ret;
                }
@@ -1243,6 +842,9 @@ dr_action_create_reformat_action(struct mlx5dr_domain *dmn,
        }
 }
 
+#define CVLAN_ETHERTYPE 0x8100
+#define SVLAN_ETHERTYPE 0x88a8
+
 struct mlx5dr_action *mlx5dr_action_create_pop_vlan(void)
 {
        return dr_action_create_generic(DR_ACTION_TYP_POP_VLAN);
@@ -1315,31 +917,13 @@ dec_ref:
        return NULL;
 }
 
-static const struct dr_action_modify_field_conv *
-dr_action_modify_get_hw_info(u16 sw_field)
-{
-       const struct dr_action_modify_field_conv *hw_action_info;
-
-       if (sw_field >= ARRAY_SIZE(dr_action_conv_arr))
-               goto not_found;
-
-       hw_action_info = &dr_action_conv_arr[sw_field];
-       if (!hw_action_info->end && !hw_action_info->start)
-               goto not_found;
-
-       return hw_action_info;
-
-not_found:
-       return NULL;
-}
-
 static int
 dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn,
                              __be64 *sw_action,
                              __be64 *hw_action,
-                             const struct dr_action_modify_field_conv **ret_hw_info)
+                             const struct mlx5dr_ste_action_modify_field **ret_hw_info)
 {
-       const struct dr_action_modify_field_conv *hw_action_info;
+       const struct mlx5dr_ste_action_modify_field *hw_action_info;
        u8 max_length;
        u16 sw_field;
        u32 data;
@@ -1349,7 +933,7 @@ dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn,
        data = MLX5_GET(set_action_in, sw_action, data);
 
        /* Convert SW data to HW modify action format */
-       hw_action_info = dr_action_modify_get_hw_info(sw_field);
+       hw_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, sw_field);
        if (!hw_action_info) {
                mlx5dr_dbg(dmn, "Modify add action invalid field given\n");
                return -EINVAL;
@@ -1357,20 +941,12 @@ dr_action_modify_sw_to_hw_add(struct mlx5dr_domain *dmn,
 
        max_length = hw_action_info->end - hw_action_info->start + 1;
 
-       MLX5_SET(dr_action_hw_set, hw_action,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_ADD);
-
-       MLX5_SET(dr_action_hw_set, hw_action, destination_field_code,
-                hw_action_info->hw_field);
-
-       MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter,
-                hw_action_info->start);
-
-       /* PRM defines that length zero specific length of 32bits */
-       MLX5_SET(dr_action_hw_set, hw_action, destination_length,
-                max_length == 32 ? 0 : max_length);
-
-       MLX5_SET(dr_action_hw_set, hw_action, inline_data, data);
+       mlx5dr_ste_set_action_add(dmn->ste_ctx,
+                                 hw_action,
+                                 hw_action_info->hw_field,
+                                 hw_action_info->start,
+                                 max_length,
+                                 data);
 
        *ret_hw_info = hw_action_info;
 
@@ -1381,9 +957,9 @@ static int
 dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn,
                              __be64 *sw_action,
                              __be64 *hw_action,
-                             const struct dr_action_modify_field_conv **ret_hw_info)
+                             const struct mlx5dr_ste_action_modify_field **ret_hw_info)
 {
-       const struct dr_action_modify_field_conv *hw_action_info;
+       const struct mlx5dr_ste_action_modify_field *hw_action_info;
        u8 offset, length, max_length;
        u16 sw_field;
        u32 data;
@@ -1395,7 +971,7 @@ dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn,
        data = MLX5_GET(set_action_in, sw_action, data);
 
        /* Convert SW data to HW modify action format */
-       hw_action_info = dr_action_modify_get_hw_info(sw_field);
+       hw_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, sw_field);
        if (!hw_action_info) {
                mlx5dr_dbg(dmn, "Modify set action invalid field given\n");
                return -EINVAL;
@@ -1411,19 +987,12 @@ dr_action_modify_sw_to_hw_set(struct mlx5dr_domain *dmn,
                return -EINVAL;
        }
 
-       MLX5_SET(dr_action_hw_set, hw_action,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_SET);
-
-       MLX5_SET(dr_action_hw_set, hw_action, destination_field_code,
-                hw_action_info->hw_field);
-
-       MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter,
-                hw_action_info->start + offset);
-
-       MLX5_SET(dr_action_hw_set, hw_action, destination_length,
-                length == 32 ? 0 : length);
-
-       MLX5_SET(dr_action_hw_set, hw_action, inline_data, data);
+       mlx5dr_ste_set_action_set(dmn->ste_ctx,
+                                 hw_action,
+                                 hw_action_info->hw_field,
+                                 hw_action_info->start + offset,
+                                 length,
+                                 data);
 
        *ret_hw_info = hw_action_info;
 
@@ -1434,12 +1003,12 @@ static int
 dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn,
                               __be64 *sw_action,
                               __be64 *hw_action,
-                              const struct dr_action_modify_field_conv **ret_dst_hw_info,
-                              const struct dr_action_modify_field_conv **ret_src_hw_info)
+                              const struct mlx5dr_ste_action_modify_field **ret_dst_hw_info,
+                              const struct mlx5dr_ste_action_modify_field **ret_src_hw_info)
 {
        u8 src_offset, dst_offset, src_max_length, dst_max_length, length;
-       const struct dr_action_modify_field_conv *hw_dst_action_info;
-       const struct dr_action_modify_field_conv *hw_src_action_info;
+       const struct mlx5dr_ste_action_modify_field *hw_dst_action_info;
+       const struct mlx5dr_ste_action_modify_field *hw_src_action_info;
        u16 src_field, dst_field;
 
        /* Get SW modify action data */
@@ -1450,8 +1019,8 @@ dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn,
        length = MLX5_GET(copy_action_in, sw_action, length);
 
        /* Convert SW data to HW modify action format */
-       hw_src_action_info = dr_action_modify_get_hw_info(src_field);
-       hw_dst_action_info = dr_action_modify_get_hw_info(dst_field);
+       hw_src_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, src_field);
+       hw_dst_action_info = mlx5dr_ste_conv_modify_hdr_sw_field(dmn->ste_ctx, dst_field);
        if (!hw_src_action_info || !hw_dst_action_info) {
                mlx5dr_dbg(dmn, "Modify copy action invalid field given\n");
                return -EINVAL;
@@ -1471,23 +1040,13 @@ dr_action_modify_sw_to_hw_copy(struct mlx5dr_domain *dmn,
                return -EINVAL;
        }
 
-       MLX5_SET(dr_action_hw_copy, hw_action,
-                opcode, MLX5DR_ACTION_MDFY_HW_OP_COPY);
-
-       MLX5_SET(dr_action_hw_copy, hw_action, destination_field_code,
-                hw_dst_action_info->hw_field);
-
-       MLX5_SET(dr_action_hw_copy, hw_action, destination_left_shifter,
-                hw_dst_action_info->start + dst_offset);
-
-       MLX5_SET(dr_action_hw_copy, hw_action, destination_length,
-                length == 32 ? 0 : length);
-
-       MLX5_SET(dr_action_hw_copy, hw_action, source_field_code,
-                hw_src_action_info->hw_field);
-
-       MLX5_SET(dr_action_hw_copy, hw_action, source_left_shifter,
-                hw_src_action_info->start + dst_offset);
+       mlx5dr_ste_set_action_copy(dmn->ste_ctx,
+                                  hw_action,
+                                  hw_dst_action_info->hw_field,
+                                  hw_dst_action_info->start + dst_offset,
+                                  length,
+                                  hw_src_action_info->hw_field,
+                                  hw_src_action_info->start + src_offset);
 
        *ret_dst_hw_info = hw_dst_action_info;
        *ret_src_hw_info = hw_src_action_info;
@@ -1499,8 +1058,8 @@ static int
 dr_action_modify_sw_to_hw(struct mlx5dr_domain *dmn,
                          __be64 *sw_action,
                          __be64 *hw_action,
-                         const struct dr_action_modify_field_conv **ret_dst_hw_info,
-                         const struct dr_action_modify_field_conv **ret_src_hw_info)
+                         const struct mlx5dr_ste_action_modify_field **ret_dst_hw_info,
+                         const struct mlx5dr_ste_action_modify_field **ret_src_hw_info)
 {
        u8 action;
        int ret;
@@ -1677,15 +1236,15 @@ static int dr_actions_convert_modify_header(struct mlx5dr_action *action,
                                            u32 *num_hw_actions,
                                            bool *modify_ttl)
 {
-       const struct dr_action_modify_field_conv *hw_dst_action_info;
-       const struct dr_action_modify_field_conv *hw_src_action_info;
-       u16 hw_field = MLX5DR_ACTION_MDFY_HW_FLD_RESERVED;
-       u32 l3_type = MLX5DR_ACTION_MDFY_HW_HDR_L3_NONE;
-       u32 l4_type = MLX5DR_ACTION_MDFY_HW_HDR_L4_NONE;
+       const struct mlx5dr_ste_action_modify_field *hw_dst_action_info;
+       const struct mlx5dr_ste_action_modify_field *hw_src_action_info;
        struct mlx5dr_domain *dmn = action->rewrite.dmn;
        int ret, i, hw_idx = 0;
        __be64 *sw_action;
        __be64 hw_action;
+       u16 hw_field = 0;
+       u32 l3_type = 0;
+       u32 l4_type = 0;
 
        *modify_ttl = false;
 
index aa2c2d6..47ec889 100644 (file)
@@ -57,6 +57,12 @@ static int dr_domain_init_resources(struct mlx5dr_domain *dmn)
 {
        int ret;
 
+       dmn->ste_ctx = mlx5dr_ste_get_ctx(dmn->info.caps.sw_format_ver);
+       if (!dmn->ste_ctx) {
+               mlx5dr_err(dmn, "SW Steering on this device is unsupported\n");
+               return -EOPNOTSUPP;
+       }
+
        ret = mlx5_core_alloc_pd(dmn->mdev, &dmn->pdn);
        if (ret) {
                mlx5dr_err(dmn, "Couldn't allocate PD, ret: %d", ret);
index 6527eb4..e3a0029 100644 (file)
@@ -221,6 +221,7 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher,
 {
        struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn;
        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        struct mlx5dr_match_param mask = {};
        struct mlx5dr_ste_build *sb;
        bool inner, rx;
@@ -259,80 +260,89 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher,
                inner = false;
 
                if (dr_mask_is_wqe_metadata_set(&mask.misc2))
-                       mlx5dr_ste_build_general_purpose(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_general_purpose(ste_ctx, &sb[idx++],
+                                                        &mask, inner, rx);
 
                if (dr_mask_is_reg_c_0_3_set(&mask.misc2))
-                       mlx5dr_ste_build_register_0(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_register_0(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (dr_mask_is_reg_c_4_7_set(&mask.misc2))
-                       mlx5dr_ste_build_register_1(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_register_1(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (dr_mask_is_gvmi_or_qpn_set(&mask.misc) &&
                    (dmn->type == MLX5DR_DOMAIN_TYPE_FDB ||
                     dmn->type == MLX5DR_DOMAIN_TYPE_NIC_RX)) {
-                       mlx5dr_ste_build_src_gvmi_qpn(&sb[idx++], &mask,
-                                                     dmn, inner, rx);
+                       mlx5dr_ste_build_src_gvmi_qpn(ste_ctx, &sb[idx++],
+                                                     &mask, dmn, inner, rx);
                }
 
                if (dr_mask_is_smac_set(&mask.outer) &&
                    dr_mask_is_dmac_set(&mask.outer)) {
-                       mlx5dr_ste_build_eth_l2_src_dst(&sb[idx++], &mask,
-                                                       inner, rx);
+                       mlx5dr_ste_build_eth_l2_src_dst(ste_ctx, &sb[idx++],
+                                                       &mask, inner, rx);
                }
 
                if (dr_mask_is_smac_set(&mask.outer))
-                       mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l2_src(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (DR_MASK_IS_L2_DST(mask.outer, mask.misc, outer))
-                       mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l2_dst(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (outer_ipv == DR_RULE_IPV6) {
                        if (dr_mask_is_dst_addr_set(&mask.outer))
-                               mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask,
-                                                                inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv6_dst(ste_ctx, &sb[idx++],
+                                                                &mask, inner, rx);
 
                        if (dr_mask_is_src_addr_set(&mask.outer))
-                               mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask,
-                                                                inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv6_src(ste_ctx, &sb[idx++],
+                                                                &mask, inner, rx);
 
                        if (DR_MASK_IS_ETH_L4_SET(mask.outer, mask.misc, outer))
-                               mlx5dr_ste_build_eth_ipv6_l3_l4(&sb[idx++], &mask,
-                                                               inner, rx);
+                               mlx5dr_ste_build_eth_ipv6_l3_l4(ste_ctx, &sb[idx++],
+                                                               &mask, inner, rx);
                } else {
                        if (dr_mask_is_ipv4_5_tuple_set(&mask.outer))
-                               mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask,
-                                                                    inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv4_5_tuple(ste_ctx, &sb[idx++],
+                                                                    &mask, inner, rx);
 
                        if (dr_mask_is_ttl_set(&mask.outer))
-                               mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask,
-                                                                 inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv4_misc(ste_ctx, &sb[idx++],
+                                                                 &mask, inner, rx);
                }
 
                if (dr_mask_is_tnl_vxlan_gpe(&mask, dmn))
-                       mlx5dr_ste_build_tnl_vxlan_gpe(&sb[idx++], &mask,
-                                                      inner, rx);
+                       mlx5dr_ste_build_tnl_vxlan_gpe(ste_ctx, &sb[idx++],
+                                                      &mask, inner, rx);
                else if (dr_mask_is_tnl_geneve(&mask, dmn))
-                       mlx5dr_ste_build_tnl_geneve(&sb[idx++], &mask,
-                                                   inner, rx);
+                       mlx5dr_ste_build_tnl_geneve(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, outer))
-                       mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l4_misc(ste_ctx, &sb[idx++],
+                                                    &mask, inner, rx);
 
                if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, outer))
-                       mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_mpls(ste_ctx, &sb[idx++],
+                                             &mask, inner, rx);
 
                if (DR_MASK_IS_TNL_MPLS_SET(mask.misc2))
-                       mlx5dr_ste_build_tnl_mpls(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_tnl_mpls(ste_ctx, &sb[idx++],
+                                                 &mask, inner, rx);
 
                if (dr_mask_is_icmp(&mask, dmn)) {
-                       ret = mlx5dr_ste_build_icmp(&sb[idx++],
+                       ret = mlx5dr_ste_build_icmp(ste_ctx, &sb[idx++],
                                                    &mask, &dmn->info.caps,
                                                    inner, rx);
                        if (ret)
                                return ret;
                }
                if (dr_mask_is_tnl_gre_set(&mask.misc))
-                       mlx5dr_ste_build_tnl_gre(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_tnl_gre(ste_ctx, &sb[idx++],
+                                                &mask, inner, rx);
        }
 
        /* Inner */
@@ -343,50 +353,56 @@ static int dr_matcher_set_ste_builders(struct mlx5dr_matcher *matcher,
                inner = true;
 
                if (dr_mask_is_eth_l2_tnl_set(&mask.misc))
-                       mlx5dr_ste_build_eth_l2_tnl(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l2_tnl(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (dr_mask_is_smac_set(&mask.inner) &&
                    dr_mask_is_dmac_set(&mask.inner)) {
-                       mlx5dr_ste_build_eth_l2_src_dst(&sb[idx++],
+                       mlx5dr_ste_build_eth_l2_src_dst(ste_ctx, &sb[idx++],
                                                        &mask, inner, rx);
                }
 
                if (dr_mask_is_smac_set(&mask.inner))
-                       mlx5dr_ste_build_eth_l2_src(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l2_src(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (DR_MASK_IS_L2_DST(mask.inner, mask.misc, inner))
-                       mlx5dr_ste_build_eth_l2_dst(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l2_dst(ste_ctx, &sb[idx++],
+                                                   &mask, inner, rx);
 
                if (inner_ipv == DR_RULE_IPV6) {
                        if (dr_mask_is_dst_addr_set(&mask.inner))
-                               mlx5dr_ste_build_eth_l3_ipv6_dst(&sb[idx++], &mask,
-                                                                inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv6_dst(ste_ctx, &sb[idx++],
+                                                                &mask, inner, rx);
 
                        if (dr_mask_is_src_addr_set(&mask.inner))
-                               mlx5dr_ste_build_eth_l3_ipv6_src(&sb[idx++], &mask,
-                                                                inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv6_src(ste_ctx, &sb[idx++],
+                                                                &mask, inner, rx);
 
                        if (DR_MASK_IS_ETH_L4_SET(mask.inner, mask.misc, inner))
-                               mlx5dr_ste_build_eth_ipv6_l3_l4(&sb[idx++], &mask,
-                                                               inner, rx);
+                               mlx5dr_ste_build_eth_ipv6_l3_l4(ste_ctx, &sb[idx++],
+                                                               &mask, inner, rx);
                } else {
                        if (dr_mask_is_ipv4_5_tuple_set(&mask.inner))
-                               mlx5dr_ste_build_eth_l3_ipv4_5_tuple(&sb[idx++], &mask,
-                                                                    inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv4_5_tuple(ste_ctx, &sb[idx++],
+                                                                    &mask, inner, rx);
 
                        if (dr_mask_is_ttl_set(&mask.inner))
-                               mlx5dr_ste_build_eth_l3_ipv4_misc(&sb[idx++], &mask,
-                                                                 inner, rx);
+                               mlx5dr_ste_build_eth_l3_ipv4_misc(ste_ctx, &sb[idx++],
+                                                                 &mask, inner, rx);
                }
 
                if (DR_MASK_IS_ETH_L4_MISC_SET(mask.misc3, inner))
-                       mlx5dr_ste_build_eth_l4_misc(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_eth_l4_misc(ste_ctx, &sb[idx++],
+                                                    &mask, inner, rx);
 
                if (DR_MASK_IS_FIRST_MPLS_SET(mask.misc2, inner))
-                       mlx5dr_ste_build_mpls(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_mpls(ste_ctx, &sb[idx++],
+                                             &mask, inner, rx);
 
                if (DR_MASK_IS_TNL_MPLS_SET(mask.misc2))
-                       mlx5dr_ste_build_tnl_mpls(&sb[idx++], &mask, inner, rx);
+                       mlx5dr_ste_build_tnl_mpls(ste_ctx, &sb[idx++],
+                                                 &mask, inner, rx);
        }
        /* Empty matcher, takes all */
        if (matcher->match_criteria == DR_MATCHER_CRITERIA_EMPTY)
index 6d73719..ddcb701 100644 (file)
@@ -10,7 +10,8 @@ struct mlx5dr_rule_action_member {
        struct list_head list;
 };
 
-static int dr_rule_append_to_miss_list(struct mlx5dr_ste *new_last_ste,
+static int dr_rule_append_to_miss_list(struct mlx5dr_ste_ctx *ste_ctx,
+                                      struct mlx5dr_ste *new_last_ste,
                                       struct list_head *miss_list,
                                       struct list_head *send_list)
 {
@@ -25,7 +26,7 @@ static int dr_rule_append_to_miss_list(struct mlx5dr_ste *new_last_ste,
        if (!ste_info_last)
                return -ENOMEM;
 
-       mlx5dr_ste_set_miss_addr(last_ste->hw_ste,
+       mlx5dr_ste_set_miss_addr(ste_ctx, last_ste->hw_ste,
                                 mlx5dr_ste_get_icm_addr(new_last_ste));
        list_add_tail(&new_last_ste->miss_list_node, miss_list);
 
@@ -42,6 +43,7 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher,
                              u8 *hw_ste)
 {
        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        struct mlx5dr_ste_htbl *new_htbl;
        struct mlx5dr_ste *ste;
 
@@ -57,7 +59,8 @@ dr_rule_create_collision_htbl(struct mlx5dr_matcher *matcher,
 
        /* One and only entry, never grows */
        ste = new_htbl->ste_arr;
-       mlx5dr_ste_set_miss_addr(hw_ste, nic_matcher->e_anchor->chunk->icm_addr);
+       mlx5dr_ste_set_miss_addr(ste_ctx, hw_ste,
+                                nic_matcher->e_anchor->chunk->icm_addr);
        mlx5dr_htbl_get(new_htbl);
 
        return ste;
@@ -169,6 +172,7 @@ dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher,
                                struct mlx5dr_ste *col_ste,
                                u8 *hw_ste)
 {
+       struct mlx5dr_domain *dmn = matcher->tbl->dmn;
        struct mlx5dr_ste *new_ste;
        int ret;
 
@@ -180,11 +184,11 @@ dr_rule_rehash_handle_collision(struct mlx5dr_matcher *matcher,
        new_ste->htbl->miss_list = mlx5dr_ste_get_miss_list(col_ste);
 
        /* Update the previous from the list */
-       ret = dr_rule_append_to_miss_list(new_ste,
+       ret = dr_rule_append_to_miss_list(dmn->ste_ctx, new_ste,
                                          mlx5dr_ste_get_miss_list(col_ste),
                                          update_list);
        if (ret) {
-               mlx5dr_dbg(matcher->tbl->dmn, "Failed update dup entry\n");
+               mlx5dr_dbg(dmn, "Failed update dup entry\n");
                goto err_exit;
        }
 
@@ -224,6 +228,7 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher,
                        struct mlx5dr_ste_htbl *new_htbl,
                        struct list_head *update_list)
 {
+       struct mlx5dr_domain *dmn = matcher->tbl->dmn;
        struct mlx5dr_ste_send_info *ste_info;
        bool use_update_list = false;
        u8 hw_ste[DR_STE_SIZE] = {};
@@ -237,7 +242,8 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher,
 
        /* Copy STE control and tag */
        memcpy(hw_ste, cur_ste->hw_ste, DR_STE_SIZE_REDUCED);
-       mlx5dr_ste_set_miss_addr(hw_ste, nic_matcher->e_anchor->chunk->icm_addr);
+       mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste,
+                                nic_matcher->e_anchor->chunk->icm_addr);
 
        new_idx = mlx5dr_ste_calc_hash_index(hw_ste, new_htbl);
        new_ste = &new_htbl->ste_arr[new_idx];
@@ -253,7 +259,7 @@ dr_rule_rehash_copy_ste(struct mlx5dr_matcher *matcher,
                                                          new_ste,
                                                          hw_ste);
                if (!new_ste) {
-                       mlx5dr_dbg(matcher->tbl->dmn, "Failed adding collision entry, index: %d\n",
+                       mlx5dr_dbg(dmn, "Failed adding collision entry, index: %d\n",
                                   new_idx);
                        return NULL;
                }
@@ -391,7 +397,8 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule,
        /* Write new table to HW */
        info.type = CONNECT_MISS;
        info.miss_icm_addr = nic_matcher->e_anchor->chunk->icm_addr;
-       mlx5dr_ste_set_formatted_ste(dmn->info.caps.gvmi,
+       mlx5dr_ste_set_formatted_ste(dmn->ste_ctx,
+                                    dmn->info.caps.gvmi,
                                     nic_dmn,
                                     new_htbl,
                                     formatted_ste,
@@ -436,13 +443,15 @@ dr_rule_rehash_htbl(struct mlx5dr_rule *rule,
                /* It is safe to operate dr_ste_set_hit_addr on the hw_ste here
                 * (48B len) which works only on first 32B
                 */
-               mlx5dr_ste_set_hit_addr(prev_htbl->ste_arr[0].hw_ste,
+               mlx5dr_ste_set_hit_addr(dmn->ste_ctx,
+                                       prev_htbl->ste_arr[0].hw_ste,
                                        new_htbl->chunk->icm_addr,
                                        new_htbl->chunk->num_of_entries);
 
                ste_to_update = &prev_htbl->ste_arr[0];
        } else {
-               mlx5dr_ste_set_hit_addr_by_next_htbl(cur_htbl->pointing_ste->hw_ste,
+               mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx,
+                                                    cur_htbl->pointing_ste->hw_ste,
                                                     new_htbl);
                ste_to_update = cur_htbl->pointing_ste;
        }
@@ -496,6 +505,8 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher,
                         struct list_head *miss_list,
                         struct list_head *send_list)
 {
+       struct mlx5dr_domain *dmn = matcher->tbl->dmn;
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        struct mlx5dr_ste_send_info *ste_info;
        struct mlx5dr_ste *new_ste;
 
@@ -507,8 +518,9 @@ dr_rule_handle_collision(struct mlx5dr_matcher *matcher,
        if (!new_ste)
                goto free_send_info;
 
-       if (dr_rule_append_to_miss_list(new_ste, miss_list, send_list)) {
-               mlx5dr_dbg(matcher->tbl->dmn, "Failed to update prev miss_list\n");
+       if (dr_rule_append_to_miss_list(ste_ctx, new_ste,
+                                       miss_list, send_list)) {
+               mlx5dr_dbg(dmn, "Failed to update prev miss_list\n");
                goto err_exit;
        }
 
@@ -659,6 +671,7 @@ static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule,
        struct mlx5dr_ste_send_info *ste_info_arr[DR_ACTION_MAX_STES];
        u8 num_of_builders = nic_matcher->num_of_builders;
        struct mlx5dr_matcher *matcher = rule->matcher;
+       struct mlx5dr_domain *dmn = matcher->tbl->dmn;
        u8 *curr_hw_ste, *prev_hw_ste;
        struct mlx5dr_ste *action_ste;
        int i, k, ret;
@@ -692,10 +705,12 @@ static int dr_rule_handle_action_stes(struct mlx5dr_rule *rule,
                        goto err_exit;
 
                /* Point current ste to the new action */
-               mlx5dr_ste_set_hit_addr_by_next_htbl(prev_hw_ste, action_ste->htbl);
+               mlx5dr_ste_set_hit_addr_by_next_htbl(dmn->ste_ctx,
+                                                    prev_hw_ste,
+                                                    action_ste->htbl);
                ret = dr_rule_add_member(nic_rule, action_ste);
                if (ret) {
-                       mlx5dr_dbg(matcher->tbl->dmn, "Failed adding rule member\n");
+                       mlx5dr_dbg(dmn, "Failed adding rule member\n");
                        goto free_ste_info;
                }
                mlx5dr_send_fill_and_append_ste_send_info(action_ste, DR_STE_SIZE, 0,
@@ -722,6 +737,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher,
                                      struct list_head *miss_list,
                                      struct list_head *send_list)
 {
+       struct mlx5dr_domain *dmn = matcher->tbl->dmn;
        struct mlx5dr_ste_send_info *ste_info;
 
        /* Take ref on table, only on first time this ste is used */
@@ -730,7 +746,8 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher,
        /* new entry -> new branch */
        list_add_tail(&ste->miss_list_node, miss_list);
 
-       mlx5dr_ste_set_miss_addr(hw_ste, nic_matcher->e_anchor->chunk->icm_addr);
+       mlx5dr_ste_set_miss_addr(dmn->ste_ctx, hw_ste,
+                                nic_matcher->e_anchor->chunk->icm_addr);
 
        ste->ste_chain_location = ste_location;
 
@@ -743,7 +760,7 @@ static int dr_rule_handle_empty_entry(struct mlx5dr_matcher *matcher,
                                        ste,
                                        hw_ste,
                                        DR_CHUNK_SIZE_1)) {
-               mlx5dr_dbg(matcher->tbl->dmn, "Failed allocating table\n");
+               mlx5dr_dbg(dmn, "Failed allocating table\n");
                goto clean_ste_info;
        }
 
index d275823..1614481 100644 (file)
@@ -3,104 +3,7 @@
 
 #include <linux/types.h>
 #include <linux/crc32.h>
-#include "dr_types.h"
-
-#define DR_STE_CRC_POLY 0xEDB88320L
-#define STE_IPV4 0x1
-#define STE_IPV6 0x2
-#define STE_TCP 0x1
-#define STE_UDP 0x2
-#define STE_SPI 0x3
-#define IP_VERSION_IPV4 0x4
-#define IP_VERSION_IPV6 0x6
-#define STE_SVLAN 0x1
-#define STE_CVLAN 0x2
-
-#define DR_STE_ENABLE_FLOW_TAG BIT(31)
-
-/* Set to STE a specific value using DR_STE_SET */
-#define DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, value) do { \
-       if ((spec)->s_fname) { \
-               MLX5_SET(ste_##lookup_type, tag, t_fname, value); \
-               (spec)->s_fname = 0; \
-       } \
-} while (0)
-
-/* Set to STE spec->s_fname to tag->t_fname */
-#define DR_STE_SET_TAG(lookup_type, tag, t_fname, spec, s_fname) \
-       DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, spec->s_fname)
-
-/* Set to STE -1 to bit_mask->bm_fname and set spec->s_fname as used */
-#define DR_STE_SET_MASK(lookup_type, bit_mask, bm_fname, spec, s_fname) \
-       DR_STE_SET_VAL(lookup_type, bit_mask, bm_fname, spec, s_fname, -1)
-
-/* Set to STE spec->s_fname to bit_mask->bm_fname and set spec->s_fname as used */
-#define DR_STE_SET_MASK_V(lookup_type, bit_mask, bm_fname, spec, s_fname) \
-       DR_STE_SET_VAL(lookup_type, bit_mask, bm_fname, spec, s_fname, (spec)->s_fname)
-
-#define DR_STE_SET_TCP_FLAGS(lookup_type, tag, spec) do { \
-       MLX5_SET(ste_##lookup_type, tag, tcp_ns, !!((spec)->tcp_flags & (1 << 8))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_cwr, !!((spec)->tcp_flags & (1 << 7))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_ece, !!((spec)->tcp_flags & (1 << 6))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_urg, !!((spec)->tcp_flags & (1 << 5))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_ack, !!((spec)->tcp_flags & (1 << 4))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_psh, !!((spec)->tcp_flags & (1 << 3))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_rst, !!((spec)->tcp_flags & (1 << 2))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_syn, !!((spec)->tcp_flags & (1 << 1))); \
-       MLX5_SET(ste_##lookup_type, tag, tcp_fin, !!((spec)->tcp_flags & (1 << 0))); \
-} while (0)
-
-#define DR_STE_SET_MPLS_MASK(lookup_type, mask, in_out, bit_mask) do { \
-       DR_STE_SET_MASK_V(lookup_type, mask, mpls0_label, mask, \
-                         in_out##_first_mpls_label);\
-       DR_STE_SET_MASK_V(lookup_type, mask, mpls0_s_bos, mask, \
-                         in_out##_first_mpls_s_bos); \
-       DR_STE_SET_MASK_V(lookup_type, mask, mpls0_exp, mask, \
-                         in_out##_first_mpls_exp); \
-       DR_STE_SET_MASK_V(lookup_type, mask, mpls0_ttl, mask, \
-                         in_out##_first_mpls_ttl); \
-} while (0)
-
-#define DR_STE_SET_MPLS_TAG(lookup_type, mask, in_out, tag) do { \
-       DR_STE_SET_TAG(lookup_type, tag, mpls0_label, mask, \
-                      in_out##_first_mpls_label);\
-       DR_STE_SET_TAG(lookup_type, tag, mpls0_s_bos, mask, \
-                      in_out##_first_mpls_s_bos); \
-       DR_STE_SET_TAG(lookup_type, tag, mpls0_exp, mask, \
-                      in_out##_first_mpls_exp); \
-       DR_STE_SET_TAG(lookup_type, tag, mpls0_ttl, mask, \
-                      in_out##_first_mpls_ttl); \
-} while (0)
-
-#define DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(_misc) (\
-       (_misc)->outer_first_mpls_over_gre_label || \
-       (_misc)->outer_first_mpls_over_gre_exp || \
-       (_misc)->outer_first_mpls_over_gre_s_bos || \
-       (_misc)->outer_first_mpls_over_gre_ttl)
-#define DR_STE_IS_OUTER_MPLS_OVER_UDP_SET(_misc) (\
-       (_misc)->outer_first_mpls_over_udp_label || \
-       (_misc)->outer_first_mpls_over_udp_exp || \
-       (_misc)->outer_first_mpls_over_udp_s_bos || \
-       (_misc)->outer_first_mpls_over_udp_ttl)
-
-#define DR_STE_CALC_LU_TYPE(lookup_type, rx, inner) \
-       ((inner) ? MLX5DR_STE_LU_TYPE_##lookup_type##_I : \
-                  (rx) ? MLX5DR_STE_LU_TYPE_##lookup_type##_D : \
-                         MLX5DR_STE_LU_TYPE_##lookup_type##_O)
-
-enum dr_ste_tunl_action {
-       DR_STE_TUNL_ACTION_NONE         = 0,
-       DR_STE_TUNL_ACTION_ENABLE       = 1,
-       DR_STE_TUNL_ACTION_DECAP        = 2,
-       DR_STE_TUNL_ACTION_L3_DECAP     = 3,
-       DR_STE_TUNL_ACTION_POP_VLAN     = 4,
-};
-
-enum dr_ste_action_type {
-       DR_STE_ACTION_TYPE_PUSH_VLAN    = 1,
-       DR_STE_ACTION_TYPE_ENCAP_L3     = 3,
-       DR_STE_ACTION_TYPE_ENCAP        = 4,
-};
+#include "dr_ste.h"
 
 struct dr_hw_ste_format {
        u8 ctrl[DR_STE_SIZE_CTRL];
@@ -142,7 +45,7 @@ u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl)
        return index;
 }
 
-static u16 dr_ste_conv_bit_to_byte_mask(u8 *bit_mask)
+u16 mlx5dr_ste_conv_bit_to_byte_mask(u8 *bit_mask)
 {
        u16 byte_mask = 0;
        int i;
@@ -155,7 +58,7 @@ static u16 dr_ste_conv_bit_to_byte_mask(u8 *bit_mask)
        return byte_mask;
 }
 
-static u8 *mlx5dr_ste_get_tag(u8 *hw_ste_p)
+static u8 *dr_ste_get_tag(u8 *hw_ste_p)
 {
        struct dr_hw_ste_format *hw_ste = (struct dr_hw_ste_format *)hw_ste_p;
 
@@ -169,104 +72,6 @@ void mlx5dr_ste_set_bit_mask(u8 *hw_ste_p, u8 *bit_mask)
        memcpy(hw_ste->mask, bit_mask, DR_STE_SIZE_MASK);
 }
 
-void mlx5dr_ste_rx_set_flow_tag(u8 *hw_ste_p, u32 flow_tag)
-{
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, qp_list_pointer,
-                DR_STE_ENABLE_FLOW_TAG | flow_tag);
-}
-
-void mlx5dr_ste_set_counter_id(u8 *hw_ste_p, u32 ctr_id)
-{
-       /* This can be used for both rx_steering_mult and for sx_transmit */
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_15_0, ctr_id);
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_23_16, ctr_id >> 16);
-}
-
-void mlx5dr_ste_set_go_back_bit(u8 *hw_ste_p)
-{
-       MLX5_SET(ste_sx_transmit, hw_ste_p, go_back, 1);
-}
-
-void mlx5dr_ste_set_tx_push_vlan(u8 *hw_ste_p, u32 vlan_hdr,
-                                bool go_back)
-{
-       MLX5_SET(ste_sx_transmit, hw_ste_p, action_type,
-                DR_STE_ACTION_TYPE_PUSH_VLAN);
-       MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, vlan_hdr);
-       /* Due to HW limitation we need to set this bit, otherwise reforamt +
-        * push vlan will not work.
-        */
-       if (go_back)
-               mlx5dr_ste_set_go_back_bit(hw_ste_p);
-}
-
-void mlx5dr_ste_set_tx_encap(void *hw_ste_p, u32 reformat_id, int size, bool encap_l3)
-{
-       MLX5_SET(ste_sx_transmit, hw_ste_p, action_type,
-                encap_l3 ? DR_STE_ACTION_TYPE_ENCAP_L3 : DR_STE_ACTION_TYPE_ENCAP);
-       /* The hardware expects here size in words (2 byte) */
-       MLX5_SET(ste_sx_transmit, hw_ste_p, action_description, size / 2);
-       MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, reformat_id);
-}
-
-void mlx5dr_ste_set_rx_decap(u8 *hw_ste_p)
-{
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action,
-                DR_STE_TUNL_ACTION_DECAP);
-}
-
-void mlx5dr_ste_set_rx_pop_vlan(u8 *hw_ste_p)
-{
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action,
-                DR_STE_TUNL_ACTION_POP_VLAN);
-}
-
-void mlx5dr_ste_set_rx_decap_l3(u8 *hw_ste_p, bool vlan)
-{
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action,
-                DR_STE_TUNL_ACTION_L3_DECAP);
-       MLX5_SET(ste_modify_packet, hw_ste_p, action_description, vlan ? 1 : 0);
-}
-
-void mlx5dr_ste_set_entry_type(u8 *hw_ste_p, u8 entry_type)
-{
-       MLX5_SET(ste_general, hw_ste_p, entry_type, entry_type);
-}
-
-u8 mlx5dr_ste_get_entry_type(u8 *hw_ste_p)
-{
-       return MLX5_GET(ste_general, hw_ste_p, entry_type);
-}
-
-void mlx5dr_ste_set_rewrite_actions(u8 *hw_ste_p, u16 num_of_actions,
-                                   u32 re_write_index)
-{
-       MLX5_SET(ste_modify_packet, hw_ste_p, number_of_re_write_actions,
-                num_of_actions);
-       MLX5_SET(ste_modify_packet, hw_ste_p, header_re_write_actions_pointer,
-                re_write_index);
-}
-
-void mlx5dr_ste_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi)
-{
-       MLX5_SET(ste_general, hw_ste_p, next_table_base_63_48, gvmi);
-}
-
-void mlx5dr_ste_init(u8 *hw_ste_p, u8 lu_type, u8 entry_type,
-                    u16 gvmi)
-{
-       MLX5_SET(ste_general, hw_ste_p, entry_type, entry_type);
-       MLX5_SET(ste_general, hw_ste_p, entry_sub_type, lu_type);
-       MLX5_SET(ste_general, hw_ste_p, next_lu_type, MLX5DR_STE_LU_TYPE_DONT_CARE);
-
-       /* Set GVMI once, this is the same for RX/TX
-        * bits 63_48 of next table base / miss address encode the next GVMI
-        */
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, gvmi, gvmi);
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, next_table_base_63_48, gvmi);
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_63_48, gvmi);
-}
-
 static void dr_ste_set_always_hit(struct dr_hw_ste_format *hw_ste)
 {
        memset(&hw_ste->tag, 0, sizeof(hw_ste->tag));
@@ -279,21 +84,26 @@ static void dr_ste_set_always_miss(struct dr_hw_ste_format *hw_ste)
        hw_ste->mask[0] = 0;
 }
 
-u64 mlx5dr_ste_get_miss_addr(u8 *hw_ste)
+void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx,
+                             u8 *hw_ste_p, u64 miss_addr)
 {
-       u64 index =
-               (MLX5_GET(ste_rx_steering_mult, hw_ste, miss_address_31_6) |
-                MLX5_GET(ste_rx_steering_mult, hw_ste, miss_address_39_32) << 26);
-
-       return index << 6;
+       ste_ctx->set_miss_addr(hw_ste_p, miss_addr);
 }
 
-void mlx5dr_ste_set_hit_addr(u8 *hw_ste, u64 icm_addr, u32 ht_size)
+static void dr_ste_always_miss_addr(struct mlx5dr_ste_ctx *ste_ctx,
+                                   struct mlx5dr_ste *ste, u64 miss_addr)
 {
-       u64 index = (icm_addr >> 5) | ht_size;
+       u8 *hw_ste_p = ste->hw_ste;
 
-       MLX5_SET(ste_general, hw_ste, next_table_base_39_32_size, index >> 27);
-       MLX5_SET(ste_general, hw_ste, next_table_base_31_5_size, index);
+       ste_ctx->set_next_lu_type(hw_ste_p, MLX5DR_STE_LU_TYPE_DONT_CARE);
+       ste_ctx->set_miss_addr(hw_ste_p, miss_addr);
+       dr_ste_set_always_miss((struct dr_hw_ste_format *)ste->hw_ste);
+}
+
+void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx,
+                            u8 *hw_ste, u64 icm_addr, u32 ht_size)
+{
+       ste_ctx->set_hit_addr(hw_ste, icm_addr, ht_size);
 }
 
 u64 mlx5dr_ste_get_icm_addr(struct mlx5dr_ste *ste)
@@ -317,15 +127,16 @@ struct list_head *mlx5dr_ste_get_miss_list(struct mlx5dr_ste *ste)
        return &ste->htbl->miss_list[index];
 }
 
-static void dr_ste_always_hit_htbl(struct mlx5dr_ste *ste,
+static void dr_ste_always_hit_htbl(struct mlx5dr_ste_ctx *ste_ctx,
+                                  struct mlx5dr_ste *ste,
                                   struct mlx5dr_ste_htbl *next_htbl)
 {
        struct mlx5dr_icm_chunk *chunk = next_htbl->chunk;
        u8 *hw_ste = ste->hw_ste;
 
-       MLX5_SET(ste_general, hw_ste, byte_mask, next_htbl->byte_mask);
-       MLX5_SET(ste_general, hw_ste, next_lu_type, next_htbl->lu_type);
-       mlx5dr_ste_set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries);
+       ste_ctx->set_byte_mask(hw_ste, next_htbl->byte_mask);
+       ste_ctx->set_next_lu_type(hw_ste, next_htbl->lu_type);
+       ste_ctx->set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries);
 
        dr_ste_set_always_hit((struct dr_hw_ste_format *)ste->hw_ste);
 }
@@ -363,7 +174,8 @@ static void dr_ste_replace(struct mlx5dr_ste *dst, struct mlx5dr_ste *src)
 
 /* Free ste which is the head and the only one in miss_list */
 static void
-dr_ste_remove_head_ste(struct mlx5dr_ste *ste,
+dr_ste_remove_head_ste(struct mlx5dr_ste_ctx *ste_ctx,
+                      struct mlx5dr_ste *ste,
                       struct mlx5dr_matcher_rx_tx *nic_matcher,
                       struct mlx5dr_ste_send_info *ste_info_head,
                       struct list_head *send_ste_list,
@@ -380,7 +192,7 @@ dr_ste_remove_head_ste(struct mlx5dr_ste *ste,
         */
        memcpy(tmp_ste.hw_ste, ste->hw_ste, DR_STE_SIZE_REDUCED);
        miss_addr = nic_matcher->e_anchor->chunk->icm_addr;
-       mlx5dr_ste_always_miss_addr(&tmp_ste, miss_addr);
+       dr_ste_always_miss_addr(ste_ctx, &tmp_ste, miss_addr);
        memcpy(ste->hw_ste, tmp_ste.hw_ste, DR_STE_SIZE_REDUCED);
 
        list_del_init(&ste->miss_list_node);
@@ -436,7 +248,8 @@ dr_ste_replace_head_ste(struct mlx5dr_ste *ste, struct mlx5dr_ste *next_ste,
 /* Free ste that is located in the middle of the miss list:
  * |__| -->|_prev_ste_|->|_ste_|-->|_next_ste_|
  */
-static void dr_ste_remove_middle_ste(struct mlx5dr_ste *ste,
+static void dr_ste_remove_middle_ste(struct mlx5dr_ste_ctx *ste_ctx,
+                                    struct mlx5dr_ste *ste,
                                     struct mlx5dr_ste_send_info *ste_info,
                                     struct list_head *send_ste_list,
                                     struct mlx5dr_ste_htbl *stats_tbl)
@@ -448,8 +261,8 @@ static void dr_ste_remove_middle_ste(struct mlx5dr_ste *ste,
        if (WARN_ON(!prev_ste))
                return;
 
-       miss_addr = mlx5dr_ste_get_miss_addr(ste->hw_ste);
-       mlx5dr_ste_set_miss_addr(prev_ste->hw_ste, miss_addr);
+       miss_addr = ste_ctx->get_miss_addr(ste->hw_ste);
+       ste_ctx->set_miss_addr(prev_ste->hw_ste, miss_addr);
 
        mlx5dr_send_fill_and_append_ste_send_info(prev_ste, DR_STE_SIZE_REDUCED, 0,
                                                  prev_ste->hw_ste, ste_info,
@@ -467,6 +280,7 @@ void mlx5dr_ste_free(struct mlx5dr_ste *ste,
 {
        struct mlx5dr_ste_send_info *cur_ste_info, *tmp_ste_info;
        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        struct mlx5dr_ste_send_info ste_info_head;
        struct mlx5dr_ste *next_ste, *first_ste;
        bool put_on_origin_table = true;
@@ -495,7 +309,8 @@ void mlx5dr_ste_free(struct mlx5dr_ste *ste,
 
                if (!next_ste) {
                        /* One and only entry in the list */
-                       dr_ste_remove_head_ste(ste, nic_matcher,
+                       dr_ste_remove_head_ste(ste_ctx, ste,
+                                              nic_matcher,
                                               &ste_info_head,
                                               &send_ste_list,
                                               stats_tbl);
@@ -506,7 +321,9 @@ void mlx5dr_ste_free(struct mlx5dr_ste *ste,
                        put_on_origin_table = false;
                }
        } else { /* Ste in the middle of the list */
-               dr_ste_remove_middle_ste(ste, &ste_info_head, &send_ste_list, stats_tbl);
+               dr_ste_remove_middle_ste(ste_ctx, ste,
+                                        &ste_info_head, &send_ste_list,
+                                        stats_tbl);
        }
 
        /* Update HW */
@@ -530,34 +347,18 @@ bool mlx5dr_ste_equal_tag(void *src, void *dst)
        return !memcmp(s_hw_ste->tag, d_hw_ste->tag, DR_STE_SIZE_TAG);
 }
 
-void mlx5dr_ste_set_hit_addr_by_next_htbl(u8 *hw_ste,
+void mlx5dr_ste_set_hit_addr_by_next_htbl(struct mlx5dr_ste_ctx *ste_ctx,
+                                         u8 *hw_ste,
                                          struct mlx5dr_ste_htbl *next_htbl)
 {
        struct mlx5dr_icm_chunk *chunk = next_htbl->chunk;
 
-       mlx5dr_ste_set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries);
-}
-
-void mlx5dr_ste_set_miss_addr(u8 *hw_ste_p, u64 miss_addr)
-{
-       u64 index = miss_addr >> 6;
-
-       /* Miss address for TX and RX STEs located in the same offsets */
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_39_32, index >> 26);
-       MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_31_6, index);
-}
-
-void mlx5dr_ste_always_miss_addr(struct mlx5dr_ste *ste, u64 miss_addr)
-{
-       u8 *hw_ste = ste->hw_ste;
-
-       MLX5_SET(ste_rx_steering_mult, hw_ste, next_lu_type, MLX5DR_STE_LU_TYPE_DONT_CARE);
-       mlx5dr_ste_set_miss_addr(hw_ste, miss_addr);
-       dr_ste_set_always_miss((struct dr_hw_ste_format *)ste->hw_ste);
+       ste_ctx->set_hit_addr(hw_ste, chunk->icm_addr, chunk->num_of_entries);
 }
 
 /* Init one ste as a pattern for ste data array */
-void mlx5dr_ste_set_formatted_ste(u16 gvmi,
+void mlx5dr_ste_set_formatted_ste(struct mlx5dr_ste_ctx *ste_ctx,
+                                 u16 gvmi,
                                  struct mlx5dr_domain_rx_tx *nic_dmn,
                                  struct mlx5dr_ste_htbl *htbl,
                                  u8 *formatted_ste,
@@ -565,13 +366,13 @@ void mlx5dr_ste_set_formatted_ste(u16 gvmi,
 {
        struct mlx5dr_ste ste = {};
 
-       mlx5dr_ste_init(formatted_ste, htbl->lu_type, nic_dmn->ste_type, gvmi);
+       ste_ctx->ste_init(formatted_ste, htbl->lu_type, nic_dmn->ste_type, gvmi);
        ste.hw_ste = formatted_ste;
 
        if (connect_info->type == CONNECT_HIT)
-               dr_ste_always_hit_htbl(&ste, connect_info->hit_next_htbl);
+               dr_ste_always_hit_htbl(ste_ctx, &ste, connect_info->hit_next_htbl);
        else
-               mlx5dr_ste_always_miss_addr(&ste, connect_info->miss_icm_addr);
+               dr_ste_always_miss_addr(ste_ctx, &ste, connect_info->miss_icm_addr);
 }
 
 int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn,
@@ -582,7 +383,8 @@ int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn,
 {
        u8 formatted_ste[DR_STE_SIZE] = {};
 
-       mlx5dr_ste_set_formatted_ste(dmn->info.caps.gvmi,
+       mlx5dr_ste_set_formatted_ste(dmn->ste_ctx,
+                                    dmn->info.caps.gvmi,
                                     nic_dmn,
                                     htbl,
                                     formatted_ste,
@@ -597,18 +399,18 @@ int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher,
                                u8 *cur_hw_ste,
                                enum mlx5dr_icm_chunk_size log_table_size)
 {
-       struct dr_hw_ste_format *hw_ste = (struct dr_hw_ste_format *)cur_hw_ste;
        struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn;
        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        struct mlx5dr_htbl_connect_info info;
        struct mlx5dr_ste_htbl *next_htbl;
 
        if (!mlx5dr_ste_is_last_in_rule(nic_matcher, ste->ste_chain_location)) {
-               u8 next_lu_type;
+               u16 next_lu_type;
                u16 byte_mask;
 
-               next_lu_type = MLX5_GET(ste_general, hw_ste, next_lu_type);
-               byte_mask = MLX5_GET(ste_general, hw_ste, byte_mask);
+               next_lu_type = ste_ctx->get_next_lu_type(cur_hw_ste);
+               byte_mask = ste_ctx->get_byte_mask(cur_hw_ste);
 
                next_htbl = mlx5dr_ste_htbl_alloc(dmn->ste_icm_pool,
                                                  log_table_size,
@@ -628,7 +430,8 @@ int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher,
                        goto free_table;
                }
 
-               mlx5dr_ste_set_hit_addr_by_next_htbl(cur_hw_ste, next_htbl);
+               mlx5dr_ste_set_hit_addr_by_next_htbl(ste_ctx,
+                                                    cur_hw_ste, next_htbl);
                ste->next_htbl = next_htbl;
                next_htbl->pointing_ste = ste;
        }
@@ -657,7 +460,7 @@ static void dr_ste_set_ctrl(struct mlx5dr_ste_htbl *htbl)
 
 struct mlx5dr_ste_htbl *mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool,
                                              enum mlx5dr_icm_chunk_size chunk_size,
-                                             u8 lu_type, u16 byte_mask)
+                                             u16 lu_type, u16 byte_mask)
 {
        struct mlx5dr_icm_chunk *chunk;
        struct mlx5dr_ste_htbl *htbl;
@@ -709,6 +512,92 @@ int mlx5dr_ste_htbl_free(struct mlx5dr_ste_htbl *htbl)
        return 0;
 }
 
+void mlx5dr_ste_set_actions_tx(struct mlx5dr_ste_ctx *ste_ctx,
+                              struct mlx5dr_domain *dmn,
+                              u8 *action_type_set,
+                              u8 *hw_ste_arr,
+                              struct mlx5dr_ste_actions_attr *attr,
+                              u32 *added_stes)
+{
+       ste_ctx->set_actions_tx(dmn, action_type_set, hw_ste_arr,
+                               attr, added_stes);
+}
+
+void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx,
+                              struct mlx5dr_domain *dmn,
+                              u8 *action_type_set,
+                              u8 *hw_ste_arr,
+                              struct mlx5dr_ste_actions_attr *attr,
+                              u32 *added_stes)
+{
+       ste_ctx->set_actions_rx(dmn, action_type_set, hw_ste_arr,
+                               attr, added_stes);
+}
+
+const struct mlx5dr_ste_action_modify_field *
+mlx5dr_ste_conv_modify_hdr_sw_field(struct mlx5dr_ste_ctx *ste_ctx, u16 sw_field)
+{
+       const struct mlx5dr_ste_action_modify_field *hw_field;
+
+       if (sw_field >= ste_ctx->modify_field_arr_sz)
+               return NULL;
+
+       hw_field = &ste_ctx->modify_field_arr[sw_field];
+       if (!hw_field->end && !hw_field->start)
+               return NULL;
+
+       return hw_field;
+}
+
+void mlx5dr_ste_set_action_set(struct mlx5dr_ste_ctx *ste_ctx,
+                              __be64 *hw_action,
+                              u8 hw_field,
+                              u8 shifter,
+                              u8 length,
+                              u32 data)
+{
+       ste_ctx->set_action_set((u8 *)hw_action,
+                               hw_field, shifter, length, data);
+}
+
+void mlx5dr_ste_set_action_add(struct mlx5dr_ste_ctx *ste_ctx,
+                              __be64 *hw_action,
+                              u8 hw_field,
+                              u8 shifter,
+                              u8 length,
+                              u32 data)
+{
+       ste_ctx->set_action_add((u8 *)hw_action,
+                               hw_field, shifter, length, data);
+}
+
+void mlx5dr_ste_set_action_copy(struct mlx5dr_ste_ctx *ste_ctx,
+                               __be64 *hw_action,
+                               u8 dst_hw_field,
+                               u8 dst_shifter,
+                               u8 dst_len,
+                               u8 src_hw_field,
+                               u8 src_shifter)
+{
+       ste_ctx->set_action_copy((u8 *)hw_action,
+                                dst_hw_field, dst_shifter, dst_len,
+                                src_hw_field, src_shifter);
+}
+
+int mlx5dr_ste_set_action_decap_l3_list(struct mlx5dr_ste_ctx *ste_ctx,
+                                       void *data, u32 data_sz,
+                                       u8 *hw_action, u32 hw_action_sz,
+                                       u16 *used_hw_action_num)
+{
+       /* Only Ethernet frame is supported, with VLAN (18) or without (14) */
+       if (data_sz != HDR_LEN_L2 && data_sz != HDR_LEN_L2_W_VLAN)
+               return -EINVAL;
+
+       return ste_ctx->set_action_decap_l3_list(data, data_sz,
+                                                hw_action, hw_action_sz,
+                                                used_hw_action_num);
+}
+
 int mlx5dr_ste_build_pre_check(struct mlx5dr_domain *dmn,
                               u8 match_criteria,
                               struct mlx5dr_match_param *mask,
@@ -738,6 +627,7 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher,
 {
        struct mlx5dr_domain_rx_tx *nic_dmn = nic_matcher->nic_tbl->nic_dmn;
        struct mlx5dr_domain *dmn = matcher->tbl->dmn;
+       struct mlx5dr_ste_ctx *ste_ctx = dmn->ste_ctx;
        struct mlx5dr_ste_build *sb;
        int ret, i;
 
@@ -748,14 +638,14 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher,
 
        sb = nic_matcher->ste_builder;
        for (i = 0; i < nic_matcher->num_of_builders; i++) {
-               mlx5dr_ste_init(ste_arr,
-                               sb->lu_type,
-                               nic_dmn->ste_type,
-                               dmn->info.caps.gvmi);
+               ste_ctx->ste_init(ste_arr,
+                                 sb->lu_type,
+                                 nic_dmn->ste_type,
+                                 dmn->info.caps.gvmi);
 
                mlx5dr_ste_set_bit_mask(ste_arr, sb->bit_mask);
 
-               ret = sb->ste_build_tag_func(value, sb, mlx5dr_ste_get_tag(ste_arr));
+               ret = sb->ste_build_tag_func(value, sb, dr_ste_get_tag(ste_arr));
                if (ret)
                        return ret;
 
@@ -765,45 +655,14 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher,
                         * not relevant for the last ste in the chain.
                         */
                        sb++;
-                       MLX5_SET(ste_general, ste_arr, next_lu_type, sb->lu_type);
-                       MLX5_SET(ste_general, ste_arr, byte_mask, sb->byte_mask);
+                       ste_ctx->set_next_lu_type(ste_arr, sb->lu_type);
+                       ste_ctx->set_byte_mask(ste_arr, sb->byte_mask);
                }
                ste_arr += DR_STE_SIZE;
        }
        return 0;
 }
 
-static void dr_ste_build_eth_l2_src_des_bit_mask(struct mlx5dr_match_param *value,
-                                                bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, dmac_47_16, mask, dmac_47_16);
-       DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, dmac_15_0, mask, dmac_15_0);
-
-       if (mask->smac_47_16 || mask->smac_15_0) {
-               MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_47_32,
-                        mask->smac_47_16 >> 16);
-               MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_31_0,
-                        mask->smac_47_16 << 16 | mask->smac_15_0);
-               mask->smac_47_16 = 0;
-               mask->smac_15_0 = 0;
-       }
-
-       DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, first_vlan_id, mask, first_vid);
-       DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, first_cfi, mask, first_cfi);
-       DR_STE_SET_MASK_V(eth_l2_src_dst, bit_mask, first_priority, mask, first_prio);
-       DR_STE_SET_MASK(eth_l2_src_dst, bit_mask, l3_type, mask, ip_version);
-
-       if (mask->cvlan_tag) {
-               MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1);
-               mask->cvlan_tag = 0;
-       } else if (mask->svlan_tag) {
-               MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1);
-               mask->svlan_tag = 0;
-       }
-}
-
 static void dr_ste_copy_mask_misc(char *mask, struct mlx5dr_match_misc *spec)
 {
        spec->gre_c_present = MLX5_GET(fte_match_set_misc, mask, gre_c_present);
@@ -1045,566 +904,93 @@ void mlx5dr_ste_copy_param(u8 match_criteria,
        }
 }
 
-static int dr_ste_build_eth_l2_src_des_tag(struct mlx5dr_match_param *value,
-                                          struct mlx5dr_ste_build *sb,
-                                          u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_47_16, spec, dmac_47_16);
-       DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_15_0, spec, dmac_15_0);
-
-       if (spec->smac_47_16 || spec->smac_15_0) {
-               MLX5_SET(ste_eth_l2_src_dst, tag, smac_47_32,
-                        spec->smac_47_16 >> 16);
-               MLX5_SET(ste_eth_l2_src_dst, tag, smac_31_0,
-                        spec->smac_47_16 << 16 | spec->smac_15_0);
-               spec->smac_47_16 = 0;
-               spec->smac_15_0 = 0;
-       }
-
-       if (spec->ip_version) {
-               if (spec->ip_version == IP_VERSION_IPV4) {
-                       MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV4);
-                       spec->ip_version = 0;
-               } else if (spec->ip_version == IP_VERSION_IPV6) {
-                       MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV6);
-                       spec->ip_version = 0;
-               } else {
-                       pr_info("Unsupported ip_version value\n");
-                       return -EINVAL;
-               }
-       }
-
-       DR_STE_SET_TAG(eth_l2_src_dst, tag, first_vlan_id, spec, first_vid);
-       DR_STE_SET_TAG(eth_l2_src_dst, tag, first_cfi, spec, first_cfi);
-       DR_STE_SET_TAG(eth_l2_src_dst, tag, first_priority, spec, first_prio);
-
-       if (spec->cvlan_tag) {
-               MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_CVLAN);
-               spec->cvlan_tag = 0;
-       } else if (spec->svlan_tag) {
-               MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_SVLAN);
-               spec->svlan_tag = 0;
-       }
-       return 0;
-}
-
-void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_ctx *ste_ctx,
+                                    struct mlx5dr_ste_build *sb,
                                     struct mlx5dr_match_param *mask,
                                     bool inner, bool rx)
 {
-       dr_ste_build_eth_l2_src_des_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC_DST, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l2_src_des_tag;
-}
-
-static void dr_ste_build_eth_l3_ipv6_dst_bit_mask(struct mlx5dr_match_param *value,
-                                                 bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_127_96, mask, dst_ip_127_96);
-       DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_95_64, mask, dst_ip_95_64);
-       DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_63_32, mask, dst_ip_63_32);
-       DR_STE_SET_MASK_V(eth_l3_ipv6_dst, bit_mask, dst_ip_31_0, mask, dst_ip_31_0);
-}
-
-static int dr_ste_build_eth_l3_ipv6_dst_tag(struct mlx5dr_match_param *value,
-                                           struct mlx5dr_ste_build *sb,
-                                           u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_127_96, spec, dst_ip_127_96);
-       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_95_64, spec, dst_ip_95_64);
-       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_63_32, spec, dst_ip_63_32);
-       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_31_0, spec, dst_ip_31_0);
-
-       return 0;
+       ste_ctx->build_eth_l2_src_dst_init(sb, mask);
 }
 
-void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_ctx *ste_ctx,
+                                     struct mlx5dr_ste_build *sb,
                                      struct mlx5dr_match_param *mask,
                                      bool inner, bool rx)
 {
-       dr_ste_build_eth_l3_ipv6_dst_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_DST, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv6_dst_tag;
-}
-
-static void dr_ste_build_eth_l3_ipv6_src_bit_mask(struct mlx5dr_match_param *value,
-                                                 bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_127_96, mask, src_ip_127_96);
-       DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_95_64, mask, src_ip_95_64);
-       DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_63_32, mask, src_ip_63_32);
-       DR_STE_SET_MASK_V(eth_l3_ipv6_src, bit_mask, src_ip_31_0, mask, src_ip_31_0);
-}
-
-static int dr_ste_build_eth_l3_ipv6_src_tag(struct mlx5dr_match_param *value,
-                                           struct mlx5dr_ste_build *sb,
-                                           u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_127_96, spec, src_ip_127_96);
-       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_95_64, spec, src_ip_95_64);
-       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_63_32, spec, src_ip_63_32);
-       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_31_0, spec, src_ip_31_0);
-
-       return 0;
+       ste_ctx->build_eth_l3_ipv6_dst_init(sb, mask);
 }
 
-void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_ctx *ste_ctx,
+                                     struct mlx5dr_ste_build *sb,
                                      struct mlx5dr_match_param *mask,
                                      bool inner, bool rx)
 {
-       dr_ste_build_eth_l3_ipv6_src_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_SRC, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv6_src_tag;
-}
-
-static void dr_ste_build_eth_l3_ipv4_5_tuple_bit_mask(struct mlx5dr_match_param *value,
-                                                     bool inner,
-                                                     u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         destination_address, mask, dst_ip_31_0);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         source_address, mask, src_ip_31_0);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         destination_port, mask, tcp_dport);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         destination_port, mask, udp_dport);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         source_port, mask, tcp_sport);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         source_port, mask, udp_sport);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         protocol, mask, ip_protocol);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         fragmented, mask, frag);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         dscp, mask, ip_dscp);
-       DR_STE_SET_MASK_V(eth_l3_ipv4_5_tuple, bit_mask,
-                         ecn, mask, ip_ecn);
-
-       if (mask->tcp_flags) {
-               DR_STE_SET_TCP_FLAGS(eth_l3_ipv4_5_tuple, bit_mask, mask);
-               mask->tcp_flags = 0;
-       }
-}
-
-static int dr_ste_build_eth_l3_ipv4_5_tuple_tag(struct mlx5dr_match_param *value,
-                                               struct mlx5dr_ste_build *sb,
-                                               u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_address, spec, dst_ip_31_0);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_address, spec, src_ip_31_0);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, tcp_dport);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, udp_dport);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, tcp_sport);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, udp_sport);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, protocol, spec, ip_protocol);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, fragmented, spec, frag);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, dscp, spec, ip_dscp);
-       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, ecn, spec, ip_ecn);
-
-       if (spec->tcp_flags) {
-               DR_STE_SET_TCP_FLAGS(eth_l3_ipv4_5_tuple, tag, spec);
-               spec->tcp_flags = 0;
-       }
-
-       return 0;
+       ste_ctx->build_eth_l3_ipv6_src_init(sb, mask);
 }
 
-void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_ctx *ste_ctx,
+                                         struct mlx5dr_ste_build *sb,
                                          struct mlx5dr_match_param *mask,
                                          bool inner, bool rx)
 {
-       dr_ste_build_eth_l3_ipv4_5_tuple_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_5_TUPLE, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv4_5_tuple_tag;
-}
-
-static void
-dr_ste_build_eth_l2_src_or_dst_bit_mask(struct mlx5dr_match_param *value,
-                                       bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-       struct mlx5dr_match_misc *misc_mask = &value->misc;
-
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, first_vlan_id, mask, first_vid);
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, first_cfi, mask, first_cfi);
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, first_priority, mask, first_prio);
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, ip_fragmented, mask, frag);
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, l3_ethertype, mask, ethertype);
-       DR_STE_SET_MASK(eth_l2_src, bit_mask, l3_type, mask, ip_version);
-
-       if (mask->svlan_tag || mask->cvlan_tag) {
-               MLX5_SET(ste_eth_l2_src, bit_mask, first_vlan_qualifier, -1);
-               mask->cvlan_tag = 0;
-               mask->svlan_tag = 0;
-       }
-
-       if (inner) {
-               if (misc_mask->inner_second_cvlan_tag ||
-                   misc_mask->inner_second_svlan_tag) {
-                       MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1);
-                       misc_mask->inner_second_cvlan_tag = 0;
-                       misc_mask->inner_second_svlan_tag = 0;
-               }
-
-               DR_STE_SET_MASK_V(eth_l2_src, bit_mask,
-                                 second_vlan_id, misc_mask, inner_second_vid);
-               DR_STE_SET_MASK_V(eth_l2_src, bit_mask,
-                                 second_cfi, misc_mask, inner_second_cfi);
-               DR_STE_SET_MASK_V(eth_l2_src, bit_mask,
-                                 second_priority, misc_mask, inner_second_prio);
-       } else {
-               if (misc_mask->outer_second_cvlan_tag ||
-                   misc_mask->outer_second_svlan_tag) {
-                       MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1);
-                       misc_mask->outer_second_cvlan_tag = 0;
-                       misc_mask->outer_second_svlan_tag = 0;
-               }
-
-               DR_STE_SET_MASK_V(eth_l2_src, bit_mask,
-                                 second_vlan_id, misc_mask, outer_second_vid);
-               DR_STE_SET_MASK_V(eth_l2_src, bit_mask,
-                                 second_cfi, misc_mask, outer_second_cfi);
-               DR_STE_SET_MASK_V(eth_l2_src, bit_mask,
-                                 second_priority, misc_mask, outer_second_prio);
-       }
-}
-
-static int dr_ste_build_eth_l2_src_or_dst_tag(struct mlx5dr_match_param *value,
-                                             bool inner, u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = inner ? &value->inner : &value->outer;
-       struct mlx5dr_match_misc *misc_spec = &value->misc;
-
-       DR_STE_SET_TAG(eth_l2_src, tag, first_vlan_id, spec, first_vid);
-       DR_STE_SET_TAG(eth_l2_src, tag, first_cfi, spec, first_cfi);
-       DR_STE_SET_TAG(eth_l2_src, tag, first_priority, spec, first_prio);
-       DR_STE_SET_TAG(eth_l2_src, tag, ip_fragmented, spec, frag);
-       DR_STE_SET_TAG(eth_l2_src, tag, l3_ethertype, spec, ethertype);
-
-       if (spec->ip_version) {
-               if (spec->ip_version == IP_VERSION_IPV4) {
-                       MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV4);
-                       spec->ip_version = 0;
-               } else if (spec->ip_version == IP_VERSION_IPV6) {
-                       MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV6);
-                       spec->ip_version = 0;
-               } else {
-                       pr_info("Unsupported ip_version value\n");
-                       return -EINVAL;
-               }
-       }
-
-       if (spec->cvlan_tag) {
-               MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_CVLAN);
-               spec->cvlan_tag = 0;
-       } else if (spec->svlan_tag) {
-               MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_SVLAN);
-               spec->svlan_tag = 0;
-       }
-
-       if (inner) {
-               if (misc_spec->inner_second_cvlan_tag) {
-                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN);
-                       misc_spec->inner_second_cvlan_tag = 0;
-               } else if (misc_spec->inner_second_svlan_tag) {
-                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN);
-                       misc_spec->inner_second_svlan_tag = 0;
-               }
-
-               DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, inner_second_vid);
-               DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, inner_second_cfi);
-               DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, inner_second_prio);
-       } else {
-               if (misc_spec->outer_second_cvlan_tag) {
-                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN);
-                       misc_spec->outer_second_cvlan_tag = 0;
-               } else if (misc_spec->outer_second_svlan_tag) {
-                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN);
-                       misc_spec->outer_second_svlan_tag = 0;
-               }
-               DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, outer_second_vid);
-               DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, outer_second_cfi);
-               DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, outer_second_prio);
-       }
-
-       return 0;
-}
-
-static void dr_ste_build_eth_l2_src_bit_mask(struct mlx5dr_match_param *value,
-                                            bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, smac_47_16, mask, smac_47_16);
-       DR_STE_SET_MASK_V(eth_l2_src, bit_mask, smac_15_0, mask, smac_15_0);
-
-       dr_ste_build_eth_l2_src_or_dst_bit_mask(value, inner, bit_mask);
-}
-
-static int dr_ste_build_eth_l2_src_tag(struct mlx5dr_match_param *value,
-                                      struct mlx5dr_ste_build *sb,
-                                      u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l2_src, tag, smac_47_16, spec, smac_47_16);
-       DR_STE_SET_TAG(eth_l2_src, tag, smac_15_0, spec, smac_15_0);
-
-       return dr_ste_build_eth_l2_src_or_dst_tag(value, sb->inner, tag);
+       ste_ctx->build_eth_l3_ipv4_5_tuple_init(sb, mask);
 }
 
-void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx)
 {
-       dr_ste_build_eth_l2_src_bit_mask(mask, inner, sb->bit_mask);
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l2_src_tag;
-}
-
-static void dr_ste_build_eth_l2_dst_bit_mask(struct mlx5dr_match_param *value,
-                                            bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l2_dst, bit_mask, dmac_47_16, mask, dmac_47_16);
-       DR_STE_SET_MASK_V(eth_l2_dst, bit_mask, dmac_15_0, mask, dmac_15_0);
-
-       dr_ste_build_eth_l2_src_or_dst_bit_mask(value, inner, bit_mask);
-}
-
-static int dr_ste_build_eth_l2_dst_tag(struct mlx5dr_match_param *value,
-                                      struct mlx5dr_ste_build *sb,
-                                      u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l2_dst, tag, dmac_47_16, spec, dmac_47_16);
-       DR_STE_SET_TAG(eth_l2_dst, tag, dmac_15_0, spec, dmac_15_0);
-
-       return dr_ste_build_eth_l2_src_or_dst_tag(value, sb->inner, tag);
+       ste_ctx->build_eth_l2_src_init(sb, mask);
 }
 
-void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx)
 {
-       dr_ste_build_eth_l2_dst_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_DST, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l2_dst_tag;
-}
-
-static void dr_ste_build_eth_l2_tnl_bit_mask(struct mlx5dr_match_param *value,
-                                            bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-       struct mlx5dr_match_misc *misc = &value->misc;
-
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, dmac_47_16, mask, dmac_47_16);
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, dmac_15_0, mask, dmac_15_0);
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, first_vlan_id, mask, first_vid);
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, first_cfi, mask, first_cfi);
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, first_priority, mask, first_prio);
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, ip_fragmented, mask, frag);
-       DR_STE_SET_MASK_V(eth_l2_tnl, bit_mask, l3_ethertype, mask, ethertype);
-       DR_STE_SET_MASK(eth_l2_tnl, bit_mask, l3_type, mask, ip_version);
-
-       if (misc->vxlan_vni) {
-               MLX5_SET(ste_eth_l2_tnl, bit_mask,
-                        l2_tunneling_network_id, (misc->vxlan_vni << 8));
-               misc->vxlan_vni = 0;
-       }
-
-       if (mask->svlan_tag || mask->cvlan_tag) {
-               MLX5_SET(ste_eth_l2_tnl, bit_mask, first_vlan_qualifier, -1);
-               mask->cvlan_tag = 0;
-               mask->svlan_tag = 0;
-       }
+       ste_ctx->build_eth_l2_dst_init(sb, mask);
 }
 
-static int dr_ste_build_eth_l2_tnl_tag(struct mlx5dr_match_param *value,
-                                      struct mlx5dr_ste_build *sb,
-                                      u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-       struct mlx5dr_match_misc *misc = &value->misc;
-
-       DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_47_16, spec, dmac_47_16);
-       DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_15_0, spec, dmac_15_0);
-       DR_STE_SET_TAG(eth_l2_tnl, tag, first_vlan_id, spec, first_vid);
-       DR_STE_SET_TAG(eth_l2_tnl, tag, first_cfi, spec, first_cfi);
-       DR_STE_SET_TAG(eth_l2_tnl, tag, ip_fragmented, spec, frag);
-       DR_STE_SET_TAG(eth_l2_tnl, tag, first_priority, spec, first_prio);
-       DR_STE_SET_TAG(eth_l2_tnl, tag, l3_ethertype, spec, ethertype);
-
-       if (misc->vxlan_vni) {
-               MLX5_SET(ste_eth_l2_tnl, tag, l2_tunneling_network_id,
-                        (misc->vxlan_vni << 8));
-               misc->vxlan_vni = 0;
-       }
-
-       if (spec->cvlan_tag) {
-               MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_CVLAN);
-               spec->cvlan_tag = 0;
-       } else if (spec->svlan_tag) {
-               MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_SVLAN);
-               spec->svlan_tag = 0;
-       }
-
-       if (spec->ip_version) {
-               if (spec->ip_version == IP_VERSION_IPV4) {
-                       MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV4);
-                       spec->ip_version = 0;
-               } else if (spec->ip_version == IP_VERSION_IPV6) {
-                       MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV6);
-                       spec->ip_version = 0;
-               } else {
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask, bool inner, bool rx)
 {
-       dr_ste_build_eth_l2_tnl_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_ETHL2_TUNNELING_I;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l2_tnl_tag;
-}
-
-static void dr_ste_build_eth_l3_ipv4_misc_bit_mask(struct mlx5dr_match_param *value,
-                                                  bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l3_ipv4_misc, bit_mask, time_to_live, mask, ttl_hoplimit);
-}
-
-static int dr_ste_build_eth_l3_ipv4_misc_tag(struct mlx5dr_match_param *value,
-                                            struct mlx5dr_ste_build *sb,
-                                            u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l3_ipv4_misc, tag, time_to_live, spec, ttl_hoplimit);
-
-       return 0;
+       ste_ctx->build_eth_l2_tnl_init(sb, mask);
 }
 
-void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_ctx *ste_ctx,
+                                      struct mlx5dr_ste_build *sb,
                                       struct mlx5dr_match_param *mask,
                                       bool inner, bool rx)
 {
-       dr_ste_build_eth_l3_ipv4_misc_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_MISC, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l3_ipv4_misc_tag;
-}
-
-static void dr_ste_build_ipv6_l3_l4_bit_mask(struct mlx5dr_match_param *value,
-                                            bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, dst_port, mask, tcp_dport);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, src_port, mask, tcp_sport);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, dst_port, mask, udp_dport);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, src_port, mask, udp_sport);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, protocol, mask, ip_protocol);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, fragmented, mask, frag);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, dscp, mask, ip_dscp);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, ecn, mask, ip_ecn);
-       DR_STE_SET_MASK_V(eth_l4, bit_mask, ipv6_hop_limit, mask, ttl_hoplimit);
-
-       if (mask->tcp_flags) {
-               DR_STE_SET_TCP_FLAGS(eth_l4, bit_mask, mask);
-               mask->tcp_flags = 0;
-       }
+       ste_ctx->build_eth_l3_ipv4_misc_init(sb, mask);
 }
 
-static int dr_ste_build_ipv6_l3_l4_tag(struct mlx5dr_match_param *value,
-                                      struct mlx5dr_ste_build *sb,
-                                      u8 *tag)
-{
-       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
-
-       DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, tcp_dport);
-       DR_STE_SET_TAG(eth_l4, tag, src_port, spec, tcp_sport);
-       DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, udp_dport);
-       DR_STE_SET_TAG(eth_l4, tag, src_port, spec, udp_sport);
-       DR_STE_SET_TAG(eth_l4, tag, protocol, spec, ip_protocol);
-       DR_STE_SET_TAG(eth_l4, tag, fragmented, spec, frag);
-       DR_STE_SET_TAG(eth_l4, tag, dscp, spec, ip_dscp);
-       DR_STE_SET_TAG(eth_l4, tag, ecn, spec, ip_ecn);
-       DR_STE_SET_TAG(eth_l4, tag, ipv6_hop_limit, spec, ttl_hoplimit);
-
-       if (spec->tcp_flags) {
-               DR_STE_SET_TCP_FLAGS(eth_l4, tag, spec);
-               spec->tcp_flags = 0;
-       }
-
-       return 0;
-}
-
-void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_ctx *ste_ctx,
+                                    struct mlx5dr_ste_build *sb,
                                     struct mlx5dr_match_param *mask,
                                     bool inner, bool rx)
 {
-       dr_ste_build_ipv6_l3_l4_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_ipv6_l3_l4_tag;
+       ste_ctx->build_eth_ipv6_l3_l4_init(sb, mask);
 }
 
 static int dr_ste_build_empty_always_hit_tag(struct mlx5dr_match_param *value,
@@ -1622,653 +1008,110 @@ void mlx5dr_ste_build_empty_always_hit(struct mlx5dr_ste_build *sb, bool rx)
        sb->ste_build_tag_func = &dr_ste_build_empty_always_hit_tag;
 }
 
-static void dr_ste_build_mpls_bit_mask(struct mlx5dr_match_param *value,
-                                      bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_misc2 *misc2_mask = &value->misc2;
-
-       if (inner)
-               DR_STE_SET_MPLS_MASK(mpls, misc2_mask, inner, bit_mask);
-       else
-               DR_STE_SET_MPLS_MASK(mpls, misc2_mask, outer, bit_mask);
-}
-
-static int dr_ste_build_mpls_tag(struct mlx5dr_match_param *value,
-                                struct mlx5dr_ste_build *sb,
-                                u8 *tag)
-{
-       struct mlx5dr_match_misc2 *misc2_mask = &value->misc2;
-
-       if (sb->inner)
-               DR_STE_SET_MPLS_TAG(mpls, misc2_mask, inner, tag);
-       else
-               DR_STE_SET_MPLS_TAG(mpls, misc2_mask, outer, tag);
-
-       return 0;
-}
-
-void mlx5dr_ste_build_mpls(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_mpls(struct mlx5dr_ste_ctx *ste_ctx,
+                          struct mlx5dr_ste_build *sb,
                           struct mlx5dr_match_param *mask,
                           bool inner, bool rx)
 {
-       dr_ste_build_mpls_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(MPLS_FIRST, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_mpls_tag;
-}
-
-static void dr_ste_build_gre_bit_mask(struct mlx5dr_match_param *value,
-                                     bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_misc *misc_mask = &value->misc;
-
-       DR_STE_SET_MASK_V(gre, bit_mask, gre_protocol, misc_mask, gre_protocol);
-       DR_STE_SET_MASK_V(gre, bit_mask, gre_k_present, misc_mask, gre_k_present);
-       DR_STE_SET_MASK_V(gre, bit_mask, gre_key_h, misc_mask, gre_key_h);
-       DR_STE_SET_MASK_V(gre, bit_mask, gre_key_l, misc_mask, gre_key_l);
-
-       DR_STE_SET_MASK_V(gre, bit_mask, gre_c_present, misc_mask, gre_c_present);
-       DR_STE_SET_MASK_V(gre, bit_mask, gre_s_present, misc_mask, gre_s_present);
+       ste_ctx->build_mpls_init(sb, mask);
 }
 
-static int dr_ste_build_gre_tag(struct mlx5dr_match_param *value,
-                               struct mlx5dr_ste_build *sb,
-                               u8 *tag)
+void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_ctx *ste_ctx,
+                             struct mlx5dr_ste_build *sb,
+                             struct mlx5dr_match_param *mask,
+                             bool inner, bool rx)
 {
-       struct  mlx5dr_match_misc *misc = &value->misc;
-
-       DR_STE_SET_TAG(gre, tag, gre_protocol, misc, gre_protocol);
-
-       DR_STE_SET_TAG(gre, tag, gre_k_present, misc, gre_k_present);
-       DR_STE_SET_TAG(gre, tag, gre_key_h, misc, gre_key_h);
-       DR_STE_SET_TAG(gre, tag, gre_key_l, misc, gre_key_l);
-
-       DR_STE_SET_TAG(gre, tag, gre_c_present, misc, gre_c_present);
-
-       DR_STE_SET_TAG(gre, tag, gre_s_present, misc, gre_s_present);
-
-       return 0;
-}
-
-void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_build *sb,
-                             struct mlx5dr_match_param *mask, bool inner, bool rx)
-{
-       dr_ste_build_gre_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_GRE;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_gre_tag;
-}
-
-static void dr_ste_build_flex_parser_0_bit_mask(struct mlx5dr_match_param *value,
-                                               bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2;
-
-       if (DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(misc_2_mask)) {
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_label,
-                                 misc_2_mask, outer_first_mpls_over_gre_label);
-
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_exp,
-                                 misc_2_mask, outer_first_mpls_over_gre_exp);
-
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_s_bos,
-                                 misc_2_mask, outer_first_mpls_over_gre_s_bos);
-
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_ttl,
-                                 misc_2_mask, outer_first_mpls_over_gre_ttl);
-       } else {
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_label,
-                                 misc_2_mask, outer_first_mpls_over_udp_label);
-
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_exp,
-                                 misc_2_mask, outer_first_mpls_over_udp_exp);
-
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_s_bos,
-                                 misc_2_mask, outer_first_mpls_over_udp_s_bos);
-
-               DR_STE_SET_MASK_V(flex_parser_0, bit_mask, parser_3_ttl,
-                                 misc_2_mask, outer_first_mpls_over_udp_ttl);
-       }
-}
-
-static int dr_ste_build_flex_parser_0_tag(struct mlx5dr_match_param *value,
-                                         struct mlx5dr_ste_build *sb,
-                                         u8 *tag)
-{
-       struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2;
-
-       if (DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(misc_2_mask)) {
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label,
-                              misc_2_mask, outer_first_mpls_over_gre_label);
-
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp,
-                              misc_2_mask, outer_first_mpls_over_gre_exp);
-
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos,
-                              misc_2_mask, outer_first_mpls_over_gre_s_bos);
-
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl,
-                              misc_2_mask, outer_first_mpls_over_gre_ttl);
-       } else {
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label,
-                              misc_2_mask, outer_first_mpls_over_udp_label);
-
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp,
-                              misc_2_mask, outer_first_mpls_over_udp_exp);
-
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos,
-                              misc_2_mask, outer_first_mpls_over_udp_s_bos);
-
-               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl,
-                              misc_2_mask, outer_first_mpls_over_udp_ttl);
-       }
-       return 0;
+       ste_ctx->build_tnl_gre_init(sb, mask);
 }
 
-void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_ctx *ste_ctx,
+                              struct mlx5dr_ste_build *sb,
                               struct mlx5dr_match_param *mask,
                               bool inner, bool rx)
 {
-       dr_ste_build_flex_parser_0_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_0;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_flex_parser_0_tag;
-}
-
-#define ICMP_TYPE_OFFSET_FIRST_DW              24
-#define ICMP_CODE_OFFSET_FIRST_DW              16
-#define ICMP_HEADER_DATA_OFFSET_SECOND_DW      0
-
-static int dr_ste_build_flex_parser_1_bit_mask(struct mlx5dr_match_param *mask,
-                                              struct mlx5dr_cmd_caps *caps,
-                                              u8 *bit_mask)
-{
-       bool is_ipv4_mask = DR_MASK_IS_ICMPV4_SET(&mask->misc3);
-       struct mlx5dr_match_misc3 *misc_3_mask = &mask->misc3;
-       u32 icmp_header_data_mask;
-       u32 icmp_type_mask;
-       u32 icmp_code_mask;
-       int dw0_location;
-       int dw1_location;
-
-       if (is_ipv4_mask) {
-               icmp_header_data_mask   = misc_3_mask->icmpv4_header_data;
-               icmp_type_mask          = misc_3_mask->icmpv4_type;
-               icmp_code_mask          = misc_3_mask->icmpv4_code;
-               dw0_location            = caps->flex_parser_id_icmp_dw0;
-               dw1_location            = caps->flex_parser_id_icmp_dw1;
-       } else {
-               icmp_header_data_mask   = misc_3_mask->icmpv6_header_data;
-               icmp_type_mask          = misc_3_mask->icmpv6_type;
-               icmp_code_mask          = misc_3_mask->icmpv6_code;
-               dw0_location            = caps->flex_parser_id_icmpv6_dw0;
-               dw1_location            = caps->flex_parser_id_icmpv6_dw1;
-       }
-
-       switch (dw0_location) {
-       case 4:
-               if (icmp_type_mask) {
-                       MLX5_SET(ste_flex_parser_1, bit_mask, flex_parser_4,
-                                (icmp_type_mask << ICMP_TYPE_OFFSET_FIRST_DW));
-                       if (is_ipv4_mask)
-                               misc_3_mask->icmpv4_type = 0;
-                       else
-                               misc_3_mask->icmpv6_type = 0;
-               }
-               if (icmp_code_mask) {
-                       u32 cur_val = MLX5_GET(ste_flex_parser_1, bit_mask,
-                                              flex_parser_4);
-                       MLX5_SET(ste_flex_parser_1, bit_mask, flex_parser_4,
-                                cur_val | (icmp_code_mask << ICMP_CODE_OFFSET_FIRST_DW));
-                       if (is_ipv4_mask)
-                               misc_3_mask->icmpv4_code = 0;
-                       else
-                               misc_3_mask->icmpv6_code = 0;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       switch (dw1_location) {
-       case 5:
-               if (icmp_header_data_mask) {
-                       MLX5_SET(ste_flex_parser_1, bit_mask, flex_parser_5,
-                                (icmp_header_data_mask << ICMP_HEADER_DATA_OFFSET_SECOND_DW));
-                       if (is_ipv4_mask)
-                               misc_3_mask->icmpv4_header_data = 0;
-                       else
-                               misc_3_mask->icmpv6_header_data = 0;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
+       ste_ctx->build_tnl_mpls_init(sb, mask);
 }
 
-static int dr_ste_build_flex_parser_1_tag(struct mlx5dr_match_param *value,
-                                         struct mlx5dr_ste_build *sb,
-                                         u8 *tag)
-{
-       struct mlx5dr_match_misc3 *misc_3 = &value->misc3;
-       u32 icmp_header_data;
-       int dw0_location;
-       int dw1_location;
-       u32 icmp_type;
-       u32 icmp_code;
-       bool is_ipv4;
-
-       is_ipv4 = DR_MASK_IS_ICMPV4_SET(misc_3);
-       if (is_ipv4) {
-               icmp_header_data        = misc_3->icmpv4_header_data;
-               icmp_type               = misc_3->icmpv4_type;
-               icmp_code               = misc_3->icmpv4_code;
-               dw0_location            = sb->caps->flex_parser_id_icmp_dw0;
-               dw1_location            = sb->caps->flex_parser_id_icmp_dw1;
-       } else {
-               icmp_header_data        = misc_3->icmpv6_header_data;
-               icmp_type               = misc_3->icmpv6_type;
-               icmp_code               = misc_3->icmpv6_code;
-               dw0_location            = sb->caps->flex_parser_id_icmpv6_dw0;
-               dw1_location            = sb->caps->flex_parser_id_icmpv6_dw1;
-       }
-
-       switch (dw0_location) {
-       case 4:
-               if (icmp_type) {
-                       MLX5_SET(ste_flex_parser_1, tag, flex_parser_4,
-                                (icmp_type << ICMP_TYPE_OFFSET_FIRST_DW));
-                       if (is_ipv4)
-                               misc_3->icmpv4_type = 0;
-                       else
-                               misc_3->icmpv6_type = 0;
-               }
-
-               if (icmp_code) {
-                       u32 cur_val = MLX5_GET(ste_flex_parser_1, tag,
-                                              flex_parser_4);
-                       MLX5_SET(ste_flex_parser_1, tag, flex_parser_4,
-                                cur_val | (icmp_code << ICMP_CODE_OFFSET_FIRST_DW));
-                       if (is_ipv4)
-                               misc_3->icmpv4_code = 0;
-                       else
-                               misc_3->icmpv6_code = 0;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       switch (dw1_location) {
-       case 5:
-               if (icmp_header_data) {
-                       MLX5_SET(ste_flex_parser_1, tag, flex_parser_5,
-                                (icmp_header_data << ICMP_HEADER_DATA_OFFSET_SECOND_DW));
-                       if (is_ipv4)
-                               misc_3->icmpv4_header_data = 0;
-                       else
-                               misc_3->icmpv6_header_data = 0;
-               }
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-int mlx5dr_ste_build_icmp(struct mlx5dr_ste_build *sb,
+int mlx5dr_ste_build_icmp(struct mlx5dr_ste_ctx *ste_ctx,
+                         struct mlx5dr_ste_build *sb,
                          struct mlx5dr_match_param *mask,
                          struct mlx5dr_cmd_caps *caps,
                          bool inner, bool rx)
 {
-       int ret;
-
-       ret = dr_ste_build_flex_parser_1_bit_mask(mask, caps, sb->bit_mask);
-       if (ret)
-               return ret;
-
        sb->rx = rx;
        sb->inner = inner;
        sb->caps = caps;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_1;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_flex_parser_1_tag;
-
-       return 0;
-}
-
-static void dr_ste_build_general_purpose_bit_mask(struct mlx5dr_match_param *value,
-                                                 bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2;
-
-       DR_STE_SET_MASK_V(general_purpose, bit_mask,
-                         general_purpose_lookup_field, misc_2_mask,
-                         metadata_reg_a);
-}
-
-static int dr_ste_build_general_purpose_tag(struct mlx5dr_match_param *value,
-                                           struct mlx5dr_ste_build *sb,
-                                           u8 *tag)
-{
-       struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2;
-
-       DR_STE_SET_TAG(general_purpose, tag, general_purpose_lookup_field,
-                      misc_2_mask, metadata_reg_a);
-
-       return 0;
+       return ste_ctx->build_icmp_init(sb, mask);
 }
 
-void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_ctx *ste_ctx,
+                                     struct mlx5dr_ste_build *sb,
                                      struct mlx5dr_match_param *mask,
                                      bool inner, bool rx)
 {
-       dr_ste_build_general_purpose_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_GENERAL_PURPOSE;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_general_purpose_tag;
-}
-
-static void dr_ste_build_eth_l4_misc_bit_mask(struct mlx5dr_match_param *value,
-                                             bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_misc3 *misc_3_mask = &value->misc3;
-
-       if (inner) {
-               DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, seq_num, misc_3_mask,
-                                 inner_tcp_seq_num);
-               DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, ack_num, misc_3_mask,
-                                 inner_tcp_ack_num);
-       } else {
-               DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, seq_num, misc_3_mask,
-                                 outer_tcp_seq_num);
-               DR_STE_SET_MASK_V(eth_l4_misc, bit_mask, ack_num, misc_3_mask,
-                                 outer_tcp_ack_num);
-       }
+       ste_ctx->build_general_purpose_init(sb, mask);
 }
 
-static int dr_ste_build_eth_l4_misc_tag(struct mlx5dr_match_param *value,
-                                       struct mlx5dr_ste_build *sb,
-                                       u8 *tag)
-{
-       struct mlx5dr_match_misc3 *misc3 = &value->misc3;
-
-       if (sb->inner) {
-               DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, inner_tcp_seq_num);
-               DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, inner_tcp_ack_num);
-       } else {
-               DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, outer_tcp_seq_num);
-               DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, outer_tcp_ack_num);
-       }
-
-       return 0;
-}
-
-void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_ctx *ste_ctx,
+                                 struct mlx5dr_ste_build *sb,
                                  struct mlx5dr_match_param *mask,
                                  bool inner, bool rx)
 {
-       dr_ste_build_eth_l4_misc_bit_mask(mask, inner, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4_MISC, rx, inner);
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_eth_l4_misc_tag;
+       ste_ctx->build_eth_l4_misc_init(sb, mask);
 }
 
-static void
-dr_ste_build_flex_parser_tnl_vxlan_gpe_bit_mask(struct mlx5dr_match_param *value,
-                                               bool inner, u8 *bit_mask)
-{
-       struct mlx5dr_match_misc3 *misc_3_mask = &value->misc3;
-
-       DR_STE_SET_MASK_V(flex_parser_tnl_vxlan_gpe, bit_mask,
-                         outer_vxlan_gpe_flags,
-                         misc_3_mask, outer_vxlan_gpe_flags);
-       DR_STE_SET_MASK_V(flex_parser_tnl_vxlan_gpe, bit_mask,
-                         outer_vxlan_gpe_next_protocol,
-                         misc_3_mask, outer_vxlan_gpe_next_protocol);
-       DR_STE_SET_MASK_V(flex_parser_tnl_vxlan_gpe, bit_mask,
-                         outer_vxlan_gpe_vni,
-                         misc_3_mask, outer_vxlan_gpe_vni);
-}
-
-static int
-dr_ste_build_flex_parser_tnl_vxlan_gpe_tag(struct mlx5dr_match_param *value,
-                                          struct mlx5dr_ste_build *sb,
-                                          u8 *tag)
-{
-       struct mlx5dr_match_misc3 *misc3 = &value->misc3;
-
-       DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag,
-                      outer_vxlan_gpe_flags, misc3,
-                      outer_vxlan_gpe_flags);
-       DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag,
-                      outer_vxlan_gpe_next_protocol, misc3,
-                      outer_vxlan_gpe_next_protocol);
-       DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag,
-                      outer_vxlan_gpe_vni, misc3,
-                      outer_vxlan_gpe_vni);
-
-       return 0;
-}
-
-void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_ctx *ste_ctx,
+                                   struct mlx5dr_ste_build *sb,
                                    struct mlx5dr_match_param *mask,
                                    bool inner, bool rx)
 {
-       dr_ste_build_flex_parser_tnl_vxlan_gpe_bit_mask(mask, inner,
-                                                       sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_TNL_HEADER;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_flex_parser_tnl_vxlan_gpe_tag;
+       ste_ctx->build_tnl_vxlan_gpe_init(sb, mask);
 }
 
-static void
-dr_ste_build_flex_parser_tnl_geneve_bit_mask(struct mlx5dr_match_param *value,
-                                            u8 *bit_mask)
-{
-       struct mlx5dr_match_misc *misc_mask = &value->misc;
-
-       DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask,
-                         geneve_protocol_type,
-                         misc_mask, geneve_protocol_type);
-       DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask,
-                         geneve_oam,
-                         misc_mask, geneve_oam);
-       DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask,
-                         geneve_opt_len,
-                         misc_mask, geneve_opt_len);
-       DR_STE_SET_MASK_V(flex_parser_tnl_geneve, bit_mask,
-                         geneve_vni,
-                         misc_mask, geneve_vni);
-}
-
-static int
-dr_ste_build_flex_parser_tnl_geneve_tag(struct mlx5dr_match_param *value,
-                                       struct mlx5dr_ste_build *sb,
-                                       u8 *tag)
-{
-       struct mlx5dr_match_misc *misc = &value->misc;
-
-       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
-                      geneve_protocol_type, misc, geneve_protocol_type);
-       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
-                      geneve_oam, misc, geneve_oam);
-       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
-                      geneve_opt_len, misc, geneve_opt_len);
-       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
-                      geneve_vni, misc, geneve_vni);
-
-       return 0;
-}
-
-void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx)
 {
-       dr_ste_build_flex_parser_tnl_geneve_bit_mask(mask, sb->bit_mask);
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_FLEX_PARSER_TNL_HEADER;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_flex_parser_tnl_geneve_tag;
+       ste_ctx->build_tnl_geneve_init(sb, mask);
 }
 
-static void dr_ste_build_register_0_bit_mask(struct mlx5dr_match_param *value,
-                                            u8 *bit_mask)
-{
-       struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2;
-
-       DR_STE_SET_MASK_V(register_0, bit_mask, register_0_h,
-                         misc_2_mask, metadata_reg_c_0);
-       DR_STE_SET_MASK_V(register_0, bit_mask, register_0_l,
-                         misc_2_mask, metadata_reg_c_1);
-       DR_STE_SET_MASK_V(register_0, bit_mask, register_1_h,
-                         misc_2_mask, metadata_reg_c_2);
-       DR_STE_SET_MASK_V(register_0, bit_mask, register_1_l,
-                         misc_2_mask, metadata_reg_c_3);
-}
-
-static int dr_ste_build_register_0_tag(struct mlx5dr_match_param *value,
-                                      struct mlx5dr_ste_build *sb,
-                                      u8 *tag)
-{
-       struct mlx5dr_match_misc2 *misc2 = &value->misc2;
-
-       DR_STE_SET_TAG(register_0, tag, register_0_h, misc2, metadata_reg_c_0);
-       DR_STE_SET_TAG(register_0, tag, register_0_l, misc2, metadata_reg_c_1);
-       DR_STE_SET_TAG(register_0, tag, register_1_h, misc2, metadata_reg_c_2);
-       DR_STE_SET_TAG(register_0, tag, register_1_l, misc2, metadata_reg_c_3);
-
-       return 0;
-}
-
-void mlx5dr_ste_build_register_0(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_register_0(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx)
 {
-       dr_ste_build_register_0_bit_mask(mask, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_0;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_register_0_tag;
-}
-
-static void dr_ste_build_register_1_bit_mask(struct mlx5dr_match_param *value,
-                                            u8 *bit_mask)
-{
-       struct mlx5dr_match_misc2 *misc_2_mask = &value->misc2;
-
-       DR_STE_SET_MASK_V(register_1, bit_mask, register_2_h,
-                         misc_2_mask, metadata_reg_c_4);
-       DR_STE_SET_MASK_V(register_1, bit_mask, register_2_l,
-                         misc_2_mask, metadata_reg_c_5);
-       DR_STE_SET_MASK_V(register_1, bit_mask, register_3_h,
-                         misc_2_mask, metadata_reg_c_6);
-       DR_STE_SET_MASK_V(register_1, bit_mask, register_3_l,
-                         misc_2_mask, metadata_reg_c_7);
+       ste_ctx->build_register_0_init(sb, mask);
 }
 
-static int dr_ste_build_register_1_tag(struct mlx5dr_match_param *value,
-                                      struct mlx5dr_ste_build *sb,
-                                      u8 *tag)
-{
-       struct mlx5dr_match_misc2 *misc2 = &value->misc2;
-
-       DR_STE_SET_TAG(register_1, tag, register_2_h, misc2, metadata_reg_c_4);
-       DR_STE_SET_TAG(register_1, tag, register_2_l, misc2, metadata_reg_c_5);
-       DR_STE_SET_TAG(register_1, tag, register_3_h, misc2, metadata_reg_c_6);
-       DR_STE_SET_TAG(register_1, tag, register_3_l, misc2, metadata_reg_c_7);
-
-       return 0;
-}
-
-void mlx5dr_ste_build_register_1(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_register_1(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx)
 {
-       dr_ste_build_register_1_bit_mask(mask, sb->bit_mask);
-
        sb->rx = rx;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_1;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_register_1_tag;
+       ste_ctx->build_register_1_init(sb, mask);
 }
 
-static void dr_ste_build_src_gvmi_qpn_bit_mask(struct mlx5dr_match_param *value,
-                                              u8 *bit_mask)
-{
-       struct mlx5dr_match_misc *misc_mask = &value->misc;
-
-       DR_STE_SET_MASK(src_gvmi_qp, bit_mask, source_gvmi, misc_mask, source_port);
-       DR_STE_SET_MASK(src_gvmi_qp, bit_mask, source_qp, misc_mask, source_sqn);
-       misc_mask->source_eswitch_owner_vhca_id = 0;
-}
-
-static int dr_ste_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value,
-                                        struct mlx5dr_ste_build *sb,
-                                        u8 *tag)
-{
-       struct mlx5dr_match_misc *misc = &value->misc;
-       struct mlx5dr_cmd_vport_cap *vport_cap;
-       struct mlx5dr_domain *dmn = sb->dmn;
-       struct mlx5dr_cmd_caps *caps;
-       u8 *bit_mask = sb->bit_mask;
-       bool source_gvmi_set;
-
-       DR_STE_SET_TAG(src_gvmi_qp, tag, source_qp, misc, source_sqn);
-
-       if (sb->vhca_id_valid) {
-               /* Find port GVMI based on the eswitch_owner_vhca_id */
-               if (misc->source_eswitch_owner_vhca_id == dmn->info.caps.gvmi)
-                       caps = &dmn->info.caps;
-               else if (dmn->peer_dmn && (misc->source_eswitch_owner_vhca_id ==
-                                          dmn->peer_dmn->info.caps.gvmi))
-                       caps = &dmn->peer_dmn->info.caps;
-               else
-                       return -EINVAL;
-       } else {
-               caps = &dmn->info.caps;
-       }
-
-       vport_cap = mlx5dr_get_vport_cap(caps, misc->source_port);
-       if (!vport_cap)
-               return -EINVAL;
-
-       source_gvmi_set = MLX5_GET(ste_src_gvmi_qp, bit_mask, source_gvmi);
-       if (vport_cap->vport_gvmi && source_gvmi_set)
-               MLX5_SET(ste_src_gvmi_qp, tag, source_gvmi, vport_cap->vport_gvmi);
-
-       misc->source_eswitch_owner_vhca_id = 0;
-       misc->source_port = 0;
-
-       return 0;
-}
-
-void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_ctx *ste_ctx,
+                                  struct mlx5dr_ste_build *sb,
                                   struct mlx5dr_match_param *mask,
                                   struct mlx5dr_domain *dmn,
                                   bool inner, bool rx)
@@ -2276,12 +1119,21 @@ void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_build *sb,
        /* Set vhca_id_valid before we reset source_eswitch_owner_vhca_id */
        sb->vhca_id_valid = mask->misc.source_eswitch_owner_vhca_id;
 
-       dr_ste_build_src_gvmi_qpn_bit_mask(mask, sb->bit_mask);
-
        sb->rx = rx;
        sb->dmn = dmn;
        sb->inner = inner;
-       sb->lu_type = MLX5DR_STE_LU_TYPE_SRC_GVMI_AND_QP;
-       sb->byte_mask = dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
-       sb->ste_build_tag_func = &dr_ste_build_src_gvmi_qpn_tag;
+       ste_ctx->build_src_gvmi_qpn_init(sb, mask);
+}
+
+static struct mlx5dr_ste_ctx *mlx5dr_ste_ctx_arr[] = {
+       [MLX5_STEERING_FORMAT_CONNECTX_5] = &ste_ctx_v0,
+       [MLX5_STEERING_FORMAT_CONNECTX_6DX] = NULL,
+};
+
+struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx(u8 version)
+{
+       if (version > MLX5_STEERING_FORMAT_CONNECTX_6DX)
+               return NULL;
+
+       return mlx5dr_ste_ctx_arr[version];
 }
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste.h
new file mode 100644 (file)
index 0000000..4a3d6a8
--- /dev/null
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB */
+/* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. */
+
+#ifndef        _DR_STE_
+#define        _DR_STE_
+
+#include "dr_types.h"
+
+#define STE_IPV4 0x1
+#define STE_IPV6 0x2
+#define STE_TCP 0x1
+#define STE_UDP 0x2
+#define STE_SPI 0x3
+#define IP_VERSION_IPV4 0x4
+#define IP_VERSION_IPV6 0x6
+#define STE_SVLAN 0x1
+#define STE_CVLAN 0x2
+#define HDR_LEN_L2_MACS   0xC
+#define HDR_LEN_L2_VLAN   0x4
+#define HDR_LEN_L2_ETHER  0x2
+#define HDR_LEN_L2        (HDR_LEN_L2_MACS + HDR_LEN_L2_ETHER)
+#define HDR_LEN_L2_W_VLAN (HDR_LEN_L2 + HDR_LEN_L2_VLAN)
+
+/* Set to STE a specific value using DR_STE_SET */
+#define DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, value) do { \
+       if ((spec)->s_fname) { \
+               MLX5_SET(ste_##lookup_type, tag, t_fname, value); \
+               (spec)->s_fname = 0; \
+       } \
+} while (0)
+
+/* Set to STE spec->s_fname to tag->t_fname set spec->s_fname as used */
+#define DR_STE_SET_TAG(lookup_type, tag, t_fname, spec, s_fname) \
+       DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, spec->s_fname)
+
+/* Set to STE -1 to tag->t_fname and set spec->s_fname as used */
+#define DR_STE_SET_ONES(lookup_type, tag, t_fname, spec, s_fname) \
+       DR_STE_SET_VAL(lookup_type, tag, t_fname, spec, s_fname, -1)
+
+#define DR_STE_SET_TCP_FLAGS(lookup_type, tag, spec) do { \
+       MLX5_SET(ste_##lookup_type, tag, tcp_ns, !!((spec)->tcp_flags & (1 << 8))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_cwr, !!((spec)->tcp_flags & (1 << 7))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_ece, !!((spec)->tcp_flags & (1 << 6))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_urg, !!((spec)->tcp_flags & (1 << 5))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_ack, !!((spec)->tcp_flags & (1 << 4))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_psh, !!((spec)->tcp_flags & (1 << 3))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_rst, !!((spec)->tcp_flags & (1 << 2))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_syn, !!((spec)->tcp_flags & (1 << 1))); \
+       MLX5_SET(ste_##lookup_type, tag, tcp_fin, !!((spec)->tcp_flags & (1 << 0))); \
+} while (0)
+
+#define DR_STE_SET_MPLS(lookup_type, mask, in_out, tag) do { \
+       struct mlx5dr_match_misc2 *_mask = mask; \
+       u8 *_tag = tag; \
+       DR_STE_SET_TAG(lookup_type, _tag, mpls0_label, _mask, \
+                      in_out##_first_mpls_label);\
+       DR_STE_SET_TAG(lookup_type, _tag, mpls0_s_bos, _mask, \
+                      in_out##_first_mpls_s_bos); \
+       DR_STE_SET_TAG(lookup_type, _tag, mpls0_exp, _mask, \
+                      in_out##_first_mpls_exp); \
+       DR_STE_SET_TAG(lookup_type, _tag, mpls0_ttl, _mask, \
+                      in_out##_first_mpls_ttl); \
+} while (0)
+
+#define DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(_misc) (\
+       (_misc)->outer_first_mpls_over_gre_label || \
+       (_misc)->outer_first_mpls_over_gre_exp || \
+       (_misc)->outer_first_mpls_over_gre_s_bos || \
+       (_misc)->outer_first_mpls_over_gre_ttl)
+
+#define DR_STE_IS_OUTER_MPLS_OVER_UDP_SET(_misc) (\
+       (_misc)->outer_first_mpls_over_udp_label || \
+       (_misc)->outer_first_mpls_over_udp_exp || \
+       (_misc)->outer_first_mpls_over_udp_s_bos || \
+       (_misc)->outer_first_mpls_over_udp_ttl)
+
+enum dr_ste_action_modify_type_l3 {
+       DR_STE_ACTION_MDFY_TYPE_L3_NONE = 0x0,
+       DR_STE_ACTION_MDFY_TYPE_L3_IPV4 = 0x1,
+       DR_STE_ACTION_MDFY_TYPE_L3_IPV6 = 0x2,
+};
+
+enum dr_ste_action_modify_type_l4 {
+       DR_STE_ACTION_MDFY_TYPE_L4_NONE = 0x0,
+       DR_STE_ACTION_MDFY_TYPE_L4_TCP  = 0x1,
+       DR_STE_ACTION_MDFY_TYPE_L4_UDP  = 0x2,
+};
+
+u16 mlx5dr_ste_conv_bit_to_byte_mask(u8 *bit_mask);
+
+#define DR_STE_CTX_BUILDER(fname) \
+       ((*build_##fname##_init)(struct mlx5dr_ste_build *sb, \
+                                struct mlx5dr_match_param *mask))
+
+struct mlx5dr_ste_ctx {
+       /* Builders */
+       void DR_STE_CTX_BUILDER(eth_l2_src_dst);
+       void DR_STE_CTX_BUILDER(eth_l3_ipv6_src);
+       void DR_STE_CTX_BUILDER(eth_l3_ipv6_dst);
+       void DR_STE_CTX_BUILDER(eth_l3_ipv4_5_tuple);
+       void DR_STE_CTX_BUILDER(eth_l2_src);
+       void DR_STE_CTX_BUILDER(eth_l2_dst);
+       void DR_STE_CTX_BUILDER(eth_l2_tnl);
+       void DR_STE_CTX_BUILDER(eth_l3_ipv4_misc);
+       void DR_STE_CTX_BUILDER(eth_ipv6_l3_l4);
+       void DR_STE_CTX_BUILDER(mpls);
+       void DR_STE_CTX_BUILDER(tnl_gre);
+       void DR_STE_CTX_BUILDER(tnl_mpls);
+       int  DR_STE_CTX_BUILDER(icmp);
+       void DR_STE_CTX_BUILDER(general_purpose);
+       void DR_STE_CTX_BUILDER(eth_l4_misc);
+       void DR_STE_CTX_BUILDER(tnl_vxlan_gpe);
+       void DR_STE_CTX_BUILDER(tnl_geneve);
+       void DR_STE_CTX_BUILDER(register_0);
+       void DR_STE_CTX_BUILDER(register_1);
+       void DR_STE_CTX_BUILDER(src_gvmi_qpn);
+
+       /* Getters and Setters */
+       void (*ste_init)(u8 *hw_ste_p, u16 lu_type,
+                        u8 entry_type, u16 gvmi);
+       void (*set_next_lu_type)(u8 *hw_ste_p, u16 lu_type);
+       u16  (*get_next_lu_type)(u8 *hw_ste_p);
+       void (*set_miss_addr)(u8 *hw_ste_p, u64 miss_addr);
+       u64  (*get_miss_addr)(u8 *hw_ste_p);
+       void (*set_hit_addr)(u8 *hw_ste_p, u64 icm_addr, u32 ht_size);
+       void (*set_byte_mask)(u8 *hw_ste_p, u16 byte_mask);
+       u16  (*get_byte_mask)(u8 *hw_ste_p);
+
+       /* Actions */
+       void (*set_actions_rx)(struct mlx5dr_domain *dmn,
+                              u8 *action_type_set,
+                              u8 *hw_ste_arr,
+                              struct mlx5dr_ste_actions_attr *attr,
+                              u32 *added_stes);
+       void (*set_actions_tx)(struct mlx5dr_domain *dmn,
+                              u8 *action_type_set,
+                              u8 *hw_ste_arr,
+                              struct mlx5dr_ste_actions_attr *attr,
+                              u32 *added_stes);
+       u32 modify_field_arr_sz;
+       const struct mlx5dr_ste_action_modify_field *modify_field_arr;
+       void (*set_action_set)(u8 *hw_action,
+                              u8 hw_field,
+                              u8 shifter,
+                              u8 length,
+                              u32 data);
+       void (*set_action_add)(u8 *hw_action,
+                              u8 hw_field,
+                              u8 shifter,
+                              u8 length,
+                              u32 data);
+       void (*set_action_copy)(u8 *hw_action,
+                               u8 dst_hw_field,
+                               u8 dst_shifter,
+                               u8 dst_len,
+                               u8 src_hw_field,
+                               u8 src_shifter);
+       int (*set_action_decap_l3_list)(void *data,
+                                       u32 data_sz,
+                                       u8 *hw_action,
+                                       u32 hw_action_sz,
+                                       u16 *used_hw_action_num);
+};
+
+extern struct mlx5dr_ste_ctx ste_ctx_v0;
+
+#endif  /* _DR_STE_ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_ste_v0.c
new file mode 100644 (file)
index 0000000..b76fdff
--- /dev/null
@@ -0,0 +1,1640 @@
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+/* Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. */
+
+#include <linux/types.h>
+#include <linux/crc32.h>
+#include "dr_ste.h"
+
+#define SVLAN_ETHERTYPE                0x88a8
+#define DR_STE_ENABLE_FLOW_TAG BIT(31)
+
+enum dr_ste_v0_action_tunl {
+       DR_STE_TUNL_ACTION_NONE         = 0,
+       DR_STE_TUNL_ACTION_ENABLE       = 1,
+       DR_STE_TUNL_ACTION_DECAP        = 2,
+       DR_STE_TUNL_ACTION_L3_DECAP     = 3,
+       DR_STE_TUNL_ACTION_POP_VLAN     = 4,
+};
+
+enum dr_ste_v0_action_type {
+       DR_STE_ACTION_TYPE_PUSH_VLAN    = 1,
+       DR_STE_ACTION_TYPE_ENCAP_L3     = 3,
+       DR_STE_ACTION_TYPE_ENCAP        = 4,
+};
+
+enum dr_ste_v0_action_mdfy_op {
+       DR_STE_ACTION_MDFY_OP_COPY      = 0x1,
+       DR_STE_ACTION_MDFY_OP_SET       = 0x2,
+       DR_STE_ACTION_MDFY_OP_ADD       = 0x3,
+};
+
+#define DR_STE_CALC_LU_TYPE(lookup_type, rx, inner) \
+       ((inner) ? DR_STE_V0_LU_TYPE_##lookup_type##_I : \
+                  (rx) ? DR_STE_V0_LU_TYPE_##lookup_type##_D : \
+                         DR_STE_V0_LU_TYPE_##lookup_type##_O)
+
+enum {
+       DR_STE_V0_LU_TYPE_NOP                           = 0x00,
+       DR_STE_V0_LU_TYPE_SRC_GVMI_AND_QP               = 0x05,
+       DR_STE_V0_LU_TYPE_ETHL2_TUNNELING_I             = 0x0a,
+       DR_STE_V0_LU_TYPE_ETHL2_DST_O                   = 0x06,
+       DR_STE_V0_LU_TYPE_ETHL2_DST_I                   = 0x07,
+       DR_STE_V0_LU_TYPE_ETHL2_DST_D                   = 0x1b,
+       DR_STE_V0_LU_TYPE_ETHL2_SRC_O                   = 0x08,
+       DR_STE_V0_LU_TYPE_ETHL2_SRC_I                   = 0x09,
+       DR_STE_V0_LU_TYPE_ETHL2_SRC_D                   = 0x1c,
+       DR_STE_V0_LU_TYPE_ETHL2_SRC_DST_O               = 0x36,
+       DR_STE_V0_LU_TYPE_ETHL2_SRC_DST_I               = 0x37,
+       DR_STE_V0_LU_TYPE_ETHL2_SRC_DST_D               = 0x38,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV6_DST_O              = 0x0d,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV6_DST_I              = 0x0e,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV6_DST_D              = 0x1e,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV6_SRC_O              = 0x0f,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV6_SRC_I              = 0x10,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV6_SRC_D              = 0x1f,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV4_5_TUPLE_O          = 0x11,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV4_5_TUPLE_I          = 0x12,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV4_5_TUPLE_D          = 0x20,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV4_MISC_O             = 0x29,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV4_MISC_I             = 0x2a,
+       DR_STE_V0_LU_TYPE_ETHL3_IPV4_MISC_D             = 0x2b,
+       DR_STE_V0_LU_TYPE_ETHL4_O                       = 0x13,
+       DR_STE_V0_LU_TYPE_ETHL4_I                       = 0x14,
+       DR_STE_V0_LU_TYPE_ETHL4_D                       = 0x21,
+       DR_STE_V0_LU_TYPE_ETHL4_MISC_O                  = 0x2c,
+       DR_STE_V0_LU_TYPE_ETHL4_MISC_I                  = 0x2d,
+       DR_STE_V0_LU_TYPE_ETHL4_MISC_D                  = 0x2e,
+       DR_STE_V0_LU_TYPE_MPLS_FIRST_O                  = 0x15,
+       DR_STE_V0_LU_TYPE_MPLS_FIRST_I                  = 0x24,
+       DR_STE_V0_LU_TYPE_MPLS_FIRST_D                  = 0x25,
+       DR_STE_V0_LU_TYPE_GRE                           = 0x16,
+       DR_STE_V0_LU_TYPE_FLEX_PARSER_0                 = 0x22,
+       DR_STE_V0_LU_TYPE_FLEX_PARSER_1                 = 0x23,
+       DR_STE_V0_LU_TYPE_FLEX_PARSER_TNL_HEADER        = 0x19,
+       DR_STE_V0_LU_TYPE_GENERAL_PURPOSE               = 0x18,
+       DR_STE_V0_LU_TYPE_STEERING_REGISTERS_0          = 0x2f,
+       DR_STE_V0_LU_TYPE_STEERING_REGISTERS_1          = 0x30,
+       DR_STE_V0_LU_TYPE_DONT_CARE                     = MLX5DR_STE_LU_TYPE_DONT_CARE,
+};
+
+enum {
+       DR_STE_V0_ACTION_MDFY_FLD_L2_0          = 0,
+       DR_STE_V0_ACTION_MDFY_FLD_L2_1          = 1,
+       DR_STE_V0_ACTION_MDFY_FLD_L2_2          = 2,
+       DR_STE_V0_ACTION_MDFY_FLD_L3_0          = 3,
+       DR_STE_V0_ACTION_MDFY_FLD_L3_1          = 4,
+       DR_STE_V0_ACTION_MDFY_FLD_L3_2          = 5,
+       DR_STE_V0_ACTION_MDFY_FLD_L3_3          = 6,
+       DR_STE_V0_ACTION_MDFY_FLD_L3_4          = 7,
+       DR_STE_V0_ACTION_MDFY_FLD_L4_0          = 8,
+       DR_STE_V0_ACTION_MDFY_FLD_L4_1          = 9,
+       DR_STE_V0_ACTION_MDFY_FLD_MPLS          = 10,
+       DR_STE_V0_ACTION_MDFY_FLD_L2_TNL_0      = 11,
+       DR_STE_V0_ACTION_MDFY_FLD_REG_0         = 12,
+       DR_STE_V0_ACTION_MDFY_FLD_REG_1         = 13,
+       DR_STE_V0_ACTION_MDFY_FLD_REG_2         = 14,
+       DR_STE_V0_ACTION_MDFY_FLD_REG_3         = 15,
+       DR_STE_V0_ACTION_MDFY_FLD_L4_2          = 16,
+       DR_STE_V0_ACTION_MDFY_FLD_FLEX_0        = 17,
+       DR_STE_V0_ACTION_MDFY_FLD_FLEX_1        = 18,
+       DR_STE_V0_ACTION_MDFY_FLD_FLEX_2        = 19,
+       DR_STE_V0_ACTION_MDFY_FLD_FLEX_3        = 20,
+       DR_STE_V0_ACTION_MDFY_FLD_L2_TNL_1      = 21,
+       DR_STE_V0_ACTION_MDFY_FLD_METADATA      = 22,
+       DR_STE_V0_ACTION_MDFY_FLD_RESERVED      = 23,
+};
+
+static const struct mlx5dr_ste_action_modify_field dr_ste_v0_action_modify_field_arr[] = {
+       [MLX5_ACTION_IN_FIELD_OUT_SMAC_47_16] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_1, .start = 16, .end = 47,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_SMAC_15_0] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_1, .start = 0, .end = 15,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_ETHERTYPE] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_2, .start = 32, .end = 47,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DMAC_47_16] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_0, .start = 16, .end = 47,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DMAC_15_0] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_0, .start = 0, .end = 15,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_IP_DSCP] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_1, .start = 0, .end = 5,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_TCP_FLAGS] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 48, .end = 56,
+               .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_TCP_SPORT] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 0, .end = 15,
+               .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_TCP_DPORT] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 16, .end = 31,
+               .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_TCP,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_IP_TTL] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_1, .start = 8, .end = 15,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_IPV6_HOPLIMIT] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_1, .start = 8, .end = 15,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_UDP_SPORT] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 0, .end = 15,
+               .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_UDP,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_UDP_DPORT] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_0, .start = 16, .end = 31,
+               .l4_type = DR_STE_ACTION_MDFY_TYPE_L4_UDP,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_127_96] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_3, .start = 32, .end = 63,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_95_64] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_3, .start = 0, .end = 31,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_63_32] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_4, .start = 32, .end = 63,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_SIPV6_31_0] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_4, .start = 0, .end = 31,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_127_96] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 32, .end = 63,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_95_64] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 0, .end = 31,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_63_32] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_2, .start = 32, .end = 63,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DIPV6_31_0] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_2, .start = 0, .end = 31,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV6,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_SIPV4] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 0, .end = 31,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_DIPV4] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L3_0, .start = 32, .end = 63,
+               .l3_type = DR_STE_ACTION_MDFY_TYPE_L3_IPV4,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_A] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_METADATA, .start = 0, .end = 31,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_B] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_METADATA, .start = 32, .end = 63,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_0] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_0, .start = 32, .end = 63,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_1] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_0, .start = 0, .end = 31,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_2] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_1, .start = 32, .end = 63,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_3] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_1, .start = 0, .end = 31,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_4] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_2, .start = 32, .end = 63,
+       },
+       [MLX5_ACTION_IN_FIELD_METADATA_REG_C_5] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_REG_2, .start = 0, .end = 31,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_TCP_SEQ_NUM] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_1, .start = 32, .end = 63,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_TCP_ACK_NUM] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L4_1, .start = 0, .end = 31,
+       },
+       [MLX5_ACTION_IN_FIELD_OUT_FIRST_VID] = {
+               .hw_field = DR_STE_V0_ACTION_MDFY_FLD_L2_2, .start = 0, .end = 15,
+       },
+};
+
+static void dr_ste_v0_set_entry_type(u8 *hw_ste_p, u8 entry_type)
+{
+       MLX5_SET(ste_general, hw_ste_p, entry_type, entry_type);
+}
+
+static u8 dr_ste_v0_get_entry_type(u8 *hw_ste_p)
+{
+       return MLX5_GET(ste_general, hw_ste_p, entry_type);
+}
+
+static void dr_ste_v0_set_miss_addr(u8 *hw_ste_p, u64 miss_addr)
+{
+       u64 index = miss_addr >> 6;
+
+       /* Miss address for TX and RX STEs located in the same offsets */
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_39_32, index >> 26);
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_31_6, index);
+}
+
+static u64 dr_ste_v0_get_miss_addr(u8 *hw_ste_p)
+{
+       u64 index =
+               (MLX5_GET(ste_rx_steering_mult, hw_ste_p, miss_address_31_6) |
+                MLX5_GET(ste_rx_steering_mult, hw_ste_p, miss_address_39_32) << 26);
+
+       return index << 6;
+}
+
+static void dr_ste_v0_set_byte_mask(u8 *hw_ste_p, u16 byte_mask)
+{
+       MLX5_SET(ste_general, hw_ste_p, byte_mask, byte_mask);
+}
+
+static u16 dr_ste_v0_get_byte_mask(u8 *hw_ste_p)
+{
+       return MLX5_GET(ste_general, hw_ste_p, byte_mask);
+}
+
+static void dr_ste_v0_set_lu_type(u8 *hw_ste_p, u16 lu_type)
+{
+       MLX5_SET(ste_general, hw_ste_p, entry_sub_type, lu_type);
+}
+
+static void dr_ste_v0_set_next_lu_type(u8 *hw_ste_p, u16 lu_type)
+{
+       MLX5_SET(ste_general, hw_ste_p, next_lu_type, lu_type);
+}
+
+static u16 dr_ste_v0_get_next_lu_type(u8 *hw_ste_p)
+{
+       return MLX5_GET(ste_general, hw_ste_p, next_lu_type);
+}
+
+static void dr_ste_v0_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi)
+{
+       MLX5_SET(ste_general, hw_ste_p, next_table_base_63_48, gvmi);
+}
+
+static void dr_ste_v0_set_hit_addr(u8 *hw_ste_p, u64 icm_addr, u32 ht_size)
+{
+       u64 index = (icm_addr >> 5) | ht_size;
+
+       MLX5_SET(ste_general, hw_ste_p, next_table_base_39_32_size, index >> 27);
+       MLX5_SET(ste_general, hw_ste_p, next_table_base_31_5_size, index);
+}
+
+static void dr_ste_v0_init(u8 *hw_ste_p, u16 lu_type,
+                          u8 entry_type, u16 gvmi)
+{
+       dr_ste_v0_set_entry_type(hw_ste_p, entry_type);
+       dr_ste_v0_set_lu_type(hw_ste_p, lu_type);
+       dr_ste_v0_set_next_lu_type(hw_ste_p, MLX5DR_STE_LU_TYPE_DONT_CARE);
+
+       /* Set GVMI once, this is the same for RX/TX
+        * bits 63_48 of next table base / miss address encode the next GVMI
+        */
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, gvmi, gvmi);
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, next_table_base_63_48, gvmi);
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, miss_address_63_48, gvmi);
+}
+
+static void dr_ste_v0_rx_set_flow_tag(u8 *hw_ste_p, u32 flow_tag)
+{
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, qp_list_pointer,
+                DR_STE_ENABLE_FLOW_TAG | flow_tag);
+}
+
+static void dr_ste_v0_set_counter_id(u8 *hw_ste_p, u32 ctr_id)
+{
+       /* This can be used for both rx_steering_mult and for sx_transmit */
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_15_0, ctr_id);
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, counter_trigger_23_16, ctr_id >> 16);
+}
+
+static void dr_ste_v0_set_go_back_bit(u8 *hw_ste_p)
+{
+       MLX5_SET(ste_sx_transmit, hw_ste_p, go_back, 1);
+}
+
+static void dr_ste_v0_set_tx_push_vlan(u8 *hw_ste_p, u32 vlan_hdr,
+                                      bool go_back)
+{
+       MLX5_SET(ste_sx_transmit, hw_ste_p, action_type,
+                DR_STE_ACTION_TYPE_PUSH_VLAN);
+       MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, vlan_hdr);
+       /* Due to HW limitation we need to set this bit, otherwise reforamt +
+        * push vlan will not work.
+        */
+       if (go_back)
+               dr_ste_v0_set_go_back_bit(hw_ste_p);
+}
+
+static void dr_ste_v0_set_tx_encap(void *hw_ste_p, u32 reformat_id,
+                                  int size, bool encap_l3)
+{
+       MLX5_SET(ste_sx_transmit, hw_ste_p, action_type,
+                encap_l3 ? DR_STE_ACTION_TYPE_ENCAP_L3 : DR_STE_ACTION_TYPE_ENCAP);
+       /* The hardware expects here size in words (2 byte) */
+       MLX5_SET(ste_sx_transmit, hw_ste_p, action_description, size / 2);
+       MLX5_SET(ste_sx_transmit, hw_ste_p, encap_pointer_vlan_data, reformat_id);
+}
+
+static void dr_ste_v0_set_rx_decap(u8 *hw_ste_p)
+{
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action,
+                DR_STE_TUNL_ACTION_DECAP);
+}
+
+static void dr_ste_v0_set_rx_pop_vlan(u8 *hw_ste_p)
+{
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action,
+                DR_STE_TUNL_ACTION_POP_VLAN);
+}
+
+static void dr_ste_v0_set_rx_decap_l3(u8 *hw_ste_p, bool vlan)
+{
+       MLX5_SET(ste_rx_steering_mult, hw_ste_p, tunneling_action,
+                DR_STE_TUNL_ACTION_L3_DECAP);
+       MLX5_SET(ste_modify_packet, hw_ste_p, action_description, vlan ? 1 : 0);
+}
+
+static void dr_ste_v0_set_rewrite_actions(u8 *hw_ste_p, u16 num_of_actions,
+                                         u32 re_write_index)
+{
+       MLX5_SET(ste_modify_packet, hw_ste_p, number_of_re_write_actions,
+                num_of_actions);
+       MLX5_SET(ste_modify_packet, hw_ste_p, header_re_write_actions_pointer,
+                re_write_index);
+}
+
+static void dr_ste_v0_arr_init_next(u8 **last_ste,
+                                   u32 *added_stes,
+                                   enum mlx5dr_ste_entry_type entry_type,
+                                   u16 gvmi)
+{
+       (*added_stes)++;
+       *last_ste += DR_STE_SIZE;
+       dr_ste_v0_init(*last_ste, MLX5DR_STE_LU_TYPE_DONT_CARE,
+                      entry_type, gvmi);
+}
+
+static void
+dr_ste_v0_set_actions_tx(struct mlx5dr_domain *dmn,
+                        u8 *action_type_set,
+                        u8 *last_ste,
+                        struct mlx5dr_ste_actions_attr *attr,
+                        u32 *added_stes)
+{
+       bool encap = action_type_set[DR_ACTION_TYP_L2_TO_TNL_L2] ||
+               action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3];
+
+       /* We want to make sure the modify header comes before L2
+        * encapsulation. The reason for that is that we support
+        * modify headers for outer headers only
+        */
+       if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) {
+               dr_ste_v0_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT);
+               dr_ste_v0_set_rewrite_actions(last_ste,
+                                             attr->modify_actions,
+                                             attr->modify_index);
+       }
+
+       if (action_type_set[DR_ACTION_TYP_PUSH_VLAN]) {
+               int i;
+
+               for (i = 0; i < attr->vlans.count; i++) {
+                       if (i || action_type_set[DR_ACTION_TYP_MODIFY_HDR])
+                               dr_ste_v0_arr_init_next(&last_ste,
+                                                       added_stes,
+                                                       MLX5DR_STE_TYPE_TX,
+                                                       attr->gvmi);
+
+                       dr_ste_v0_set_tx_push_vlan(last_ste,
+                                                  attr->vlans.headers[i],
+                                                  encap);
+               }
+       }
+
+       if (encap) {
+               /* Modify header and encapsulation require a different STEs.
+                * Since modify header STE format doesn't support encapsulation
+                * tunneling_action.
+                */
+               if (action_type_set[DR_ACTION_TYP_MODIFY_HDR] ||
+                   action_type_set[DR_ACTION_TYP_PUSH_VLAN])
+                       dr_ste_v0_arr_init_next(&last_ste,
+                                               added_stes,
+                                               MLX5DR_STE_TYPE_TX,
+                                               attr->gvmi);
+
+               dr_ste_v0_set_tx_encap(last_ste,
+                                      attr->reformat_id,
+                                      attr->reformat_size,
+                                      action_type_set[DR_ACTION_TYP_L2_TO_TNL_L3]);
+               /* Whenever prio_tag_required enabled, we can be sure that the
+                * previous table (ACL) already push vlan to our packet,
+                * And due to HW limitation we need to set this bit, otherwise
+                * push vlan + reformat will not work.
+                */
+               if (MLX5_CAP_GEN(dmn->mdev, prio_tag_required))
+                       dr_ste_v0_set_go_back_bit(last_ste);
+       }
+
+       if (action_type_set[DR_ACTION_TYP_CTR])
+               dr_ste_v0_set_counter_id(last_ste, attr->ctr_id);
+
+       dr_ste_v0_set_hit_gvmi(last_ste, attr->hit_gvmi);
+       dr_ste_v0_set_hit_addr(last_ste, attr->final_icm_addr, 1);
+}
+
+static void
+dr_ste_v0_set_actions_rx(struct mlx5dr_domain *dmn,
+                        u8 *action_type_set,
+                        u8 *last_ste,
+                        struct mlx5dr_ste_actions_attr *attr,
+                        u32 *added_stes)
+{
+       if (action_type_set[DR_ACTION_TYP_CTR])
+               dr_ste_v0_set_counter_id(last_ste, attr->ctr_id);
+
+       if (action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2]) {
+               dr_ste_v0_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT);
+               dr_ste_v0_set_rx_decap_l3(last_ste, attr->decap_with_vlan);
+               dr_ste_v0_set_rewrite_actions(last_ste,
+                                             attr->decap_actions,
+                                             attr->decap_index);
+       }
+
+       if (action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2])
+               dr_ste_v0_set_rx_decap(last_ste);
+
+       if (action_type_set[DR_ACTION_TYP_POP_VLAN]) {
+               int i;
+
+               for (i = 0; i < attr->vlans.count; i++) {
+                       if (i ||
+                           action_type_set[DR_ACTION_TYP_TNL_L2_TO_L2] ||
+                           action_type_set[DR_ACTION_TYP_TNL_L3_TO_L2])
+                               dr_ste_v0_arr_init_next(&last_ste,
+                                                       added_stes,
+                                                       MLX5DR_STE_TYPE_RX,
+                                                       attr->gvmi);
+
+                       dr_ste_v0_set_rx_pop_vlan(last_ste);
+               }
+       }
+
+       if (action_type_set[DR_ACTION_TYP_MODIFY_HDR]) {
+               if (dr_ste_v0_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT)
+                       dr_ste_v0_arr_init_next(&last_ste,
+                                               added_stes,
+                                               MLX5DR_STE_TYPE_MODIFY_PKT,
+                                               attr->gvmi);
+               else
+                       dr_ste_v0_set_entry_type(last_ste, MLX5DR_STE_TYPE_MODIFY_PKT);
+
+               dr_ste_v0_set_rewrite_actions(last_ste,
+                                             attr->modify_actions,
+                                             attr->modify_index);
+       }
+
+       if (action_type_set[DR_ACTION_TYP_TAG]) {
+               if (dr_ste_v0_get_entry_type(last_ste) == MLX5DR_STE_TYPE_MODIFY_PKT)
+                       dr_ste_v0_arr_init_next(&last_ste,
+                                               added_stes,
+                                               MLX5DR_STE_TYPE_RX,
+                                               attr->gvmi);
+
+               dr_ste_v0_rx_set_flow_tag(last_ste, attr->flow_tag);
+       }
+
+       dr_ste_v0_set_hit_gvmi(last_ste, attr->hit_gvmi);
+       dr_ste_v0_set_hit_addr(last_ste, attr->final_icm_addr, 1);
+}
+
+static void dr_ste_v0_set_action_set(u8 *hw_action,
+                                    u8 hw_field,
+                                    u8 shifter,
+                                    u8 length,
+                                    u32 data)
+{
+       length = (length == 32) ? 0 : length;
+       MLX5_SET(dr_action_hw_set, hw_action, opcode, DR_STE_ACTION_MDFY_OP_SET);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_field_code, hw_field);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, shifter);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_length, length);
+       MLX5_SET(dr_action_hw_set, hw_action, inline_data, data);
+}
+
+static void dr_ste_v0_set_action_add(u8 *hw_action,
+                                    u8 hw_field,
+                                    u8 shifter,
+                                    u8 length,
+                                    u32 data)
+{
+       length = (length == 32) ? 0 : length;
+       MLX5_SET(dr_action_hw_set, hw_action, opcode, DR_STE_ACTION_MDFY_OP_ADD);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_field_code, hw_field);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, shifter);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_length, length);
+       MLX5_SET(dr_action_hw_set, hw_action, inline_data, data);
+}
+
+static void dr_ste_v0_set_action_copy(u8 *hw_action,
+                                     u8 dst_hw_field,
+                                     u8 dst_shifter,
+                                     u8 dst_len,
+                                     u8 src_hw_field,
+                                     u8 src_shifter)
+{
+       MLX5_SET(dr_action_hw_copy, hw_action, opcode, DR_STE_ACTION_MDFY_OP_COPY);
+       MLX5_SET(dr_action_hw_copy, hw_action, destination_field_code, dst_hw_field);
+       MLX5_SET(dr_action_hw_copy, hw_action, destination_left_shifter, dst_shifter);
+       MLX5_SET(dr_action_hw_copy, hw_action, destination_length, dst_len);
+       MLX5_SET(dr_action_hw_copy, hw_action, source_field_code, src_hw_field);
+       MLX5_SET(dr_action_hw_copy, hw_action, source_left_shifter, src_shifter);
+}
+
+#define DR_STE_DECAP_L3_MIN_ACTION_NUM 5
+
+static int
+dr_ste_v0_set_action_decap_l3_list(void *data, u32 data_sz,
+                                  u8 *hw_action, u32 hw_action_sz,
+                                  u16 *used_hw_action_num)
+{
+       struct mlx5_ifc_l2_hdr_bits *l2_hdr = data;
+       u32 hw_action_num;
+       int required_actions;
+       u32 hdr_fld_4b;
+       u16 hdr_fld_2b;
+       u16 vlan_type;
+       bool vlan;
+
+       vlan = (data_sz != HDR_LEN_L2);
+       hw_action_num = hw_action_sz / MLX5_ST_SZ_BYTES(dr_action_hw_set);
+       required_actions = DR_STE_DECAP_L3_MIN_ACTION_NUM + !!vlan;
+
+       if (hw_action_num < required_actions)
+               return -ENOMEM;
+
+       /* dmac_47_16 */
+       MLX5_SET(dr_action_hw_set, hw_action,
+                opcode, DR_STE_ACTION_MDFY_OP_SET);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_length, 0);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_0);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_left_shifter, 16);
+       hdr_fld_4b = MLX5_GET(l2_hdr, l2_hdr, dmac_47_16);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                inline_data, hdr_fld_4b);
+       hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set);
+
+       /* smac_47_16 */
+       MLX5_SET(dr_action_hw_set, hw_action,
+                opcode, DR_STE_ACTION_MDFY_OP_SET);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_length, 0);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_1);
+       MLX5_SET(dr_action_hw_set, hw_action, destination_left_shifter, 16);
+       hdr_fld_4b = (MLX5_GET(l2_hdr, l2_hdr, smac_31_0) >> 16 |
+                     MLX5_GET(l2_hdr, l2_hdr, smac_47_32) << 16);
+       MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_4b);
+       hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set);
+
+       /* dmac_15_0 */
+       MLX5_SET(dr_action_hw_set, hw_action,
+                opcode, DR_STE_ACTION_MDFY_OP_SET);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_length, 16);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_0);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_left_shifter, 0);
+       hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, dmac_15_0);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                inline_data, hdr_fld_2b);
+       hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set);
+
+       /* ethertype + (optional) vlan */
+       MLX5_SET(dr_action_hw_set, hw_action,
+                opcode, DR_STE_ACTION_MDFY_OP_SET);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_2);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_left_shifter, 32);
+       if (!vlan) {
+               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype);
+               MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_2b);
+               MLX5_SET(dr_action_hw_set, hw_action, destination_length, 16);
+       } else {
+               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, ethertype);
+               vlan_type = hdr_fld_2b == SVLAN_ETHERTYPE ? DR_STE_SVLAN : DR_STE_CVLAN;
+               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan);
+               hdr_fld_4b = (vlan_type << 16) | hdr_fld_2b;
+               MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_4b);
+               MLX5_SET(dr_action_hw_set, hw_action, destination_length, 18);
+       }
+       hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set);
+
+       /* smac_15_0 */
+       MLX5_SET(dr_action_hw_set, hw_action,
+                opcode, DR_STE_ACTION_MDFY_OP_SET);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_length, 16);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_1);
+       MLX5_SET(dr_action_hw_set, hw_action,
+                destination_left_shifter, 0);
+       hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, smac_31_0);
+       MLX5_SET(dr_action_hw_set, hw_action, inline_data, hdr_fld_2b);
+       hw_action += MLX5_ST_SZ_BYTES(dr_action_hw_set);
+
+       if (vlan) {
+               MLX5_SET(dr_action_hw_set, hw_action,
+                        opcode, DR_STE_ACTION_MDFY_OP_SET);
+               hdr_fld_2b = MLX5_GET(l2_hdr, l2_hdr, vlan_type);
+               MLX5_SET(dr_action_hw_set, hw_action,
+                        inline_data, hdr_fld_2b);
+               MLX5_SET(dr_action_hw_set, hw_action,
+                        destination_length, 16);
+               MLX5_SET(dr_action_hw_set, hw_action,
+                        destination_field_code, DR_STE_V0_ACTION_MDFY_FLD_L2_2);
+               MLX5_SET(dr_action_hw_set, hw_action,
+                        destination_left_shifter, 0);
+       }
+
+       *used_hw_action_num = required_actions;
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l2_src_dst_bit_mask(struct mlx5dr_match_param *value,
+                                       bool inner, u8 *bit_mask)
+{
+       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, dmac_47_16, mask, dmac_47_16);
+       DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, dmac_15_0, mask, dmac_15_0);
+
+       if (mask->smac_47_16 || mask->smac_15_0) {
+               MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_47_32,
+                        mask->smac_47_16 >> 16);
+               MLX5_SET(ste_eth_l2_src_dst, bit_mask, smac_31_0,
+                        mask->smac_47_16 << 16 | mask->smac_15_0);
+               mask->smac_47_16 = 0;
+               mask->smac_15_0 = 0;
+       }
+
+       DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, first_vlan_id, mask, first_vid);
+       DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, first_cfi, mask, first_cfi);
+       DR_STE_SET_TAG(eth_l2_src_dst, bit_mask, first_priority, mask, first_prio);
+       DR_STE_SET_ONES(eth_l2_src_dst, bit_mask, l3_type, mask, ip_version);
+
+       if (mask->cvlan_tag) {
+               MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1);
+               mask->cvlan_tag = 0;
+       } else if (mask->svlan_tag) {
+               MLX5_SET(ste_eth_l2_src_dst, bit_mask, first_vlan_qualifier, -1);
+               mask->svlan_tag = 0;
+       }
+}
+
+static int
+dr_ste_v0_build_eth_l2_src_dst_tag(struct mlx5dr_match_param *value,
+                                  struct mlx5dr_ste_build *sb,
+                                  u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_47_16, spec, dmac_47_16);
+       DR_STE_SET_TAG(eth_l2_src_dst, tag, dmac_15_0, spec, dmac_15_0);
+
+       if (spec->smac_47_16 || spec->smac_15_0) {
+               MLX5_SET(ste_eth_l2_src_dst, tag, smac_47_32,
+                        spec->smac_47_16 >> 16);
+               MLX5_SET(ste_eth_l2_src_dst, tag, smac_31_0,
+                        spec->smac_47_16 << 16 | spec->smac_15_0);
+               spec->smac_47_16 = 0;
+               spec->smac_15_0 = 0;
+       }
+
+       if (spec->ip_version) {
+               if (spec->ip_version == IP_VERSION_IPV4) {
+                       MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV4);
+                       spec->ip_version = 0;
+               } else if (spec->ip_version == IP_VERSION_IPV6) {
+                       MLX5_SET(ste_eth_l2_src_dst, tag, l3_type, STE_IPV6);
+                       spec->ip_version = 0;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       DR_STE_SET_TAG(eth_l2_src_dst, tag, first_vlan_id, spec, first_vid);
+       DR_STE_SET_TAG(eth_l2_src_dst, tag, first_cfi, spec, first_cfi);
+       DR_STE_SET_TAG(eth_l2_src_dst, tag, first_priority, spec, first_prio);
+
+       if (spec->cvlan_tag) {
+               MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_CVLAN);
+               spec->cvlan_tag = 0;
+       } else if (spec->svlan_tag) {
+               MLX5_SET(ste_eth_l2_src_dst, tag, first_vlan_qualifier, DR_STE_SVLAN);
+               spec->svlan_tag = 0;
+       }
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l2_src_dst_init(struct mlx5dr_ste_build *sb,
+                                   struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l2_src_dst_bit_mask(mask, sb->inner, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC_DST, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_src_dst_tag;
+}
+
+static int
+dr_ste_v0_build_eth_l3_ipv6_dst_tag(struct mlx5dr_match_param *value,
+                                   struct mlx5dr_ste_build *sb,
+                                   u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_127_96, spec, dst_ip_127_96);
+       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_95_64, spec, dst_ip_95_64);
+       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_63_32, spec, dst_ip_63_32);
+       DR_STE_SET_TAG(eth_l3_ipv6_dst, tag, dst_ip_31_0, spec, dst_ip_31_0);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l3_ipv6_dst_init(struct mlx5dr_ste_build *sb,
+                                    struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l3_ipv6_dst_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_DST, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv6_dst_tag;
+}
+
+static int
+dr_ste_v0_build_eth_l3_ipv6_src_tag(struct mlx5dr_match_param *value,
+                                   struct mlx5dr_ste_build *sb,
+                                   u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_127_96, spec, src_ip_127_96);
+       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_95_64, spec, src_ip_95_64);
+       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_63_32, spec, src_ip_63_32);
+       DR_STE_SET_TAG(eth_l3_ipv6_src, tag, src_ip_31_0, spec, src_ip_31_0);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l3_ipv6_src_init(struct mlx5dr_ste_build *sb,
+                                    struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l3_ipv6_src_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV6_SRC, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv6_src_tag;
+}
+
+static int
+dr_ste_v0_build_eth_l3_ipv4_5_tuple_tag(struct mlx5dr_match_param *value,
+                                       struct mlx5dr_ste_build *sb,
+                                       u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_address, spec, dst_ip_31_0);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_address, spec, src_ip_31_0);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, tcp_dport);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, destination_port, spec, udp_dport);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, tcp_sport);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, source_port, spec, udp_sport);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, protocol, spec, ip_protocol);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, fragmented, spec, frag);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, dscp, spec, ip_dscp);
+       DR_STE_SET_TAG(eth_l3_ipv4_5_tuple, tag, ecn, spec, ip_ecn);
+
+       if (spec->tcp_flags) {
+               DR_STE_SET_TCP_FLAGS(eth_l3_ipv4_5_tuple, tag, spec);
+               spec->tcp_flags = 0;
+       }
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l3_ipv4_5_tuple_init(struct mlx5dr_ste_build *sb,
+                                        struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l3_ipv4_5_tuple_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_5_TUPLE, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv4_5_tuple_tag;
+}
+
+static void
+dr_ste_v0_build_eth_l2_src_or_dst_bit_mask(struct mlx5dr_match_param *value,
+                                          bool inner, u8 *bit_mask)
+{
+       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
+       struct mlx5dr_match_misc *misc_mask = &value->misc;
+
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, first_vlan_id, mask, first_vid);
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, first_cfi, mask, first_cfi);
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, first_priority, mask, first_prio);
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, ip_fragmented, mask, frag);
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, l3_ethertype, mask, ethertype);
+       DR_STE_SET_ONES(eth_l2_src, bit_mask, l3_type, mask, ip_version);
+
+       if (mask->svlan_tag || mask->cvlan_tag) {
+               MLX5_SET(ste_eth_l2_src, bit_mask, first_vlan_qualifier, -1);
+               mask->cvlan_tag = 0;
+               mask->svlan_tag = 0;
+       }
+
+       if (inner) {
+               if (misc_mask->inner_second_cvlan_tag ||
+                   misc_mask->inner_second_svlan_tag) {
+                       MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1);
+                       misc_mask->inner_second_cvlan_tag = 0;
+                       misc_mask->inner_second_svlan_tag = 0;
+               }
+
+               DR_STE_SET_TAG(eth_l2_src, bit_mask,
+                              second_vlan_id, misc_mask, inner_second_vid);
+               DR_STE_SET_TAG(eth_l2_src, bit_mask,
+                              second_cfi, misc_mask, inner_second_cfi);
+               DR_STE_SET_TAG(eth_l2_src, bit_mask,
+                              second_priority, misc_mask, inner_second_prio);
+       } else {
+               if (misc_mask->outer_second_cvlan_tag ||
+                   misc_mask->outer_second_svlan_tag) {
+                       MLX5_SET(ste_eth_l2_src, bit_mask, second_vlan_qualifier, -1);
+                       misc_mask->outer_second_cvlan_tag = 0;
+                       misc_mask->outer_second_svlan_tag = 0;
+               }
+
+               DR_STE_SET_TAG(eth_l2_src, bit_mask,
+                              second_vlan_id, misc_mask, outer_second_vid);
+               DR_STE_SET_TAG(eth_l2_src, bit_mask,
+                              second_cfi, misc_mask, outer_second_cfi);
+               DR_STE_SET_TAG(eth_l2_src, bit_mask,
+                              second_priority, misc_mask, outer_second_prio);
+       }
+}
+
+static int
+dr_ste_v0_build_eth_l2_src_or_dst_tag(struct mlx5dr_match_param *value,
+                                     bool inner, u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = inner ? &value->inner : &value->outer;
+       struct mlx5dr_match_misc *misc_spec = &value->misc;
+
+       DR_STE_SET_TAG(eth_l2_src, tag, first_vlan_id, spec, first_vid);
+       DR_STE_SET_TAG(eth_l2_src, tag, first_cfi, spec, first_cfi);
+       DR_STE_SET_TAG(eth_l2_src, tag, first_priority, spec, first_prio);
+       DR_STE_SET_TAG(eth_l2_src, tag, ip_fragmented, spec, frag);
+       DR_STE_SET_TAG(eth_l2_src, tag, l3_ethertype, spec, ethertype);
+
+       if (spec->ip_version) {
+               if (spec->ip_version == IP_VERSION_IPV4) {
+                       MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV4);
+                       spec->ip_version = 0;
+               } else if (spec->ip_version == IP_VERSION_IPV6) {
+                       MLX5_SET(ste_eth_l2_src, tag, l3_type, STE_IPV6);
+                       spec->ip_version = 0;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       if (spec->cvlan_tag) {
+               MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_CVLAN);
+               spec->cvlan_tag = 0;
+       } else if (spec->svlan_tag) {
+               MLX5_SET(ste_eth_l2_src, tag, first_vlan_qualifier, DR_STE_SVLAN);
+               spec->svlan_tag = 0;
+       }
+
+       if (inner) {
+               if (misc_spec->inner_second_cvlan_tag) {
+                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN);
+                       misc_spec->inner_second_cvlan_tag = 0;
+               } else if (misc_spec->inner_second_svlan_tag) {
+                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN);
+                       misc_spec->inner_second_svlan_tag = 0;
+               }
+
+               DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, inner_second_vid);
+               DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, inner_second_cfi);
+               DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, inner_second_prio);
+       } else {
+               if (misc_spec->outer_second_cvlan_tag) {
+                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_CVLAN);
+                       misc_spec->outer_second_cvlan_tag = 0;
+               } else if (misc_spec->outer_second_svlan_tag) {
+                       MLX5_SET(ste_eth_l2_src, tag, second_vlan_qualifier, DR_STE_SVLAN);
+                       misc_spec->outer_second_svlan_tag = 0;
+               }
+               DR_STE_SET_TAG(eth_l2_src, tag, second_vlan_id, misc_spec, outer_second_vid);
+               DR_STE_SET_TAG(eth_l2_src, tag, second_cfi, misc_spec, outer_second_cfi);
+               DR_STE_SET_TAG(eth_l2_src, tag, second_priority, misc_spec, outer_second_prio);
+       }
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l2_src_bit_mask(struct mlx5dr_match_param *value,
+                                   bool inner, u8 *bit_mask)
+{
+       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, smac_47_16, mask, smac_47_16);
+       DR_STE_SET_TAG(eth_l2_src, bit_mask, smac_15_0, mask, smac_15_0);
+
+       dr_ste_v0_build_eth_l2_src_or_dst_bit_mask(value, inner, bit_mask);
+}
+
+static int
+dr_ste_v0_build_eth_l2_src_tag(struct mlx5dr_match_param *value,
+                              struct mlx5dr_ste_build *sb,
+                              u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l2_src, tag, smac_47_16, spec, smac_47_16);
+       DR_STE_SET_TAG(eth_l2_src, tag, smac_15_0, spec, smac_15_0);
+
+       return dr_ste_v0_build_eth_l2_src_or_dst_tag(value, sb->inner, tag);
+}
+
+static void
+dr_ste_v0_build_eth_l2_src_init(struct mlx5dr_ste_build *sb,
+                               struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l2_src_bit_mask(mask, sb->inner, sb->bit_mask);
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_SRC, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_src_tag;
+}
+
+static void
+dr_ste_v0_build_eth_l2_dst_bit_mask(struct mlx5dr_match_param *value,
+                                   struct mlx5dr_ste_build *sb,
+                                   u8 *bit_mask)
+{
+       struct mlx5dr_match_spec *mask = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l2_dst, bit_mask, dmac_47_16, mask, dmac_47_16);
+       DR_STE_SET_TAG(eth_l2_dst, bit_mask, dmac_15_0, mask, dmac_15_0);
+
+       dr_ste_v0_build_eth_l2_src_or_dst_bit_mask(value, sb->inner, bit_mask);
+}
+
+static int
+dr_ste_v0_build_eth_l2_dst_tag(struct mlx5dr_match_param *value,
+                              struct mlx5dr_ste_build *sb,
+                              u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l2_dst, tag, dmac_47_16, spec, dmac_47_16);
+       DR_STE_SET_TAG(eth_l2_dst, tag, dmac_15_0, spec, dmac_15_0);
+
+       return dr_ste_v0_build_eth_l2_src_or_dst_tag(value, sb->inner, tag);
+}
+
+static void
+dr_ste_v0_build_eth_l2_dst_init(struct mlx5dr_ste_build *sb,
+                               struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l2_dst_bit_mask(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL2_DST, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_dst_tag;
+}
+
+static void
+dr_ste_v0_build_eth_l2_tnl_bit_mask(struct mlx5dr_match_param *value,
+                                   bool inner, u8 *bit_mask)
+{
+       struct mlx5dr_match_spec *mask = inner ? &value->inner : &value->outer;
+       struct mlx5dr_match_misc *misc = &value->misc;
+
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, dmac_47_16, mask, dmac_47_16);
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, dmac_15_0, mask, dmac_15_0);
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, first_vlan_id, mask, first_vid);
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, first_cfi, mask, first_cfi);
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, first_priority, mask, first_prio);
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, ip_fragmented, mask, frag);
+       DR_STE_SET_TAG(eth_l2_tnl, bit_mask, l3_ethertype, mask, ethertype);
+       DR_STE_SET_ONES(eth_l2_tnl, bit_mask, l3_type, mask, ip_version);
+
+       if (misc->vxlan_vni) {
+               MLX5_SET(ste_eth_l2_tnl, bit_mask,
+                        l2_tunneling_network_id, (misc->vxlan_vni << 8));
+               misc->vxlan_vni = 0;
+       }
+
+       if (mask->svlan_tag || mask->cvlan_tag) {
+               MLX5_SET(ste_eth_l2_tnl, bit_mask, first_vlan_qualifier, -1);
+               mask->cvlan_tag = 0;
+               mask->svlan_tag = 0;
+       }
+}
+
+static int
+dr_ste_v0_build_eth_l2_tnl_tag(struct mlx5dr_match_param *value,
+                              struct mlx5dr_ste_build *sb,
+                              u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+       struct mlx5dr_match_misc *misc = &value->misc;
+
+       DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_47_16, spec, dmac_47_16);
+       DR_STE_SET_TAG(eth_l2_tnl, tag, dmac_15_0, spec, dmac_15_0);
+       DR_STE_SET_TAG(eth_l2_tnl, tag, first_vlan_id, spec, first_vid);
+       DR_STE_SET_TAG(eth_l2_tnl, tag, first_cfi, spec, first_cfi);
+       DR_STE_SET_TAG(eth_l2_tnl, tag, ip_fragmented, spec, frag);
+       DR_STE_SET_TAG(eth_l2_tnl, tag, first_priority, spec, first_prio);
+       DR_STE_SET_TAG(eth_l2_tnl, tag, l3_ethertype, spec, ethertype);
+
+       if (misc->vxlan_vni) {
+               MLX5_SET(ste_eth_l2_tnl, tag, l2_tunneling_network_id,
+                        (misc->vxlan_vni << 8));
+               misc->vxlan_vni = 0;
+       }
+
+       if (spec->cvlan_tag) {
+               MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_CVLAN);
+               spec->cvlan_tag = 0;
+       } else if (spec->svlan_tag) {
+               MLX5_SET(ste_eth_l2_tnl, tag, first_vlan_qualifier, DR_STE_SVLAN);
+               spec->svlan_tag = 0;
+       }
+
+       if (spec->ip_version) {
+               if (spec->ip_version == IP_VERSION_IPV4) {
+                       MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV4);
+                       spec->ip_version = 0;
+               } else if (spec->ip_version == IP_VERSION_IPV6) {
+                       MLX5_SET(ste_eth_l2_tnl, tag, l3_type, STE_IPV6);
+                       spec->ip_version = 0;
+               } else {
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l2_tnl_init(struct mlx5dr_ste_build *sb,
+                               struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l2_tnl_bit_mask(mask, sb->inner, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_ETHL2_TUNNELING_I;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l2_tnl_tag;
+}
+
+static int
+dr_ste_v0_build_eth_l3_ipv4_misc_tag(struct mlx5dr_match_param *value,
+                                    struct mlx5dr_ste_build *sb,
+                                    u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l3_ipv4_misc, tag, time_to_live, spec, ttl_hoplimit);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l3_ipv4_misc_init(struct mlx5dr_ste_build *sb,
+                                     struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l3_ipv4_misc_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL3_IPV4_MISC, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l3_ipv4_misc_tag;
+}
+
+static int
+dr_ste_v0_build_eth_ipv6_l3_l4_tag(struct mlx5dr_match_param *value,
+                                  struct mlx5dr_ste_build *sb,
+                                  u8 *tag)
+{
+       struct mlx5dr_match_spec *spec = sb->inner ? &value->inner : &value->outer;
+
+       DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, tcp_dport);
+       DR_STE_SET_TAG(eth_l4, tag, src_port, spec, tcp_sport);
+       DR_STE_SET_TAG(eth_l4, tag, dst_port, spec, udp_dport);
+       DR_STE_SET_TAG(eth_l4, tag, src_port, spec, udp_sport);
+       DR_STE_SET_TAG(eth_l4, tag, protocol, spec, ip_protocol);
+       DR_STE_SET_TAG(eth_l4, tag, fragmented, spec, frag);
+       DR_STE_SET_TAG(eth_l4, tag, dscp, spec, ip_dscp);
+       DR_STE_SET_TAG(eth_l4, tag, ecn, spec, ip_ecn);
+       DR_STE_SET_TAG(eth_l4, tag, ipv6_hop_limit, spec, ttl_hoplimit);
+
+       if (spec->tcp_flags) {
+               DR_STE_SET_TCP_FLAGS(eth_l4, tag, spec);
+               spec->tcp_flags = 0;
+       }
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_ipv6_l3_l4_init(struct mlx5dr_ste_build *sb,
+                                   struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_ipv6_l3_l4_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_ipv6_l3_l4_tag;
+}
+
+static int
+dr_ste_v0_build_mpls_tag(struct mlx5dr_match_param *value,
+                        struct mlx5dr_ste_build *sb,
+                        u8 *tag)
+{
+       struct mlx5dr_match_misc2 *misc2 = &value->misc2;
+
+       if (sb->inner)
+               DR_STE_SET_MPLS(mpls, misc2, inner, tag);
+       else
+               DR_STE_SET_MPLS(mpls, misc2, outer, tag);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_mpls_init(struct mlx5dr_ste_build *sb,
+                         struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_mpls_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(MPLS_FIRST, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_mpls_tag;
+}
+
+static int
+dr_ste_v0_build_tnl_gre_tag(struct mlx5dr_match_param *value,
+                           struct mlx5dr_ste_build *sb,
+                           u8 *tag)
+{
+       struct  mlx5dr_match_misc *misc = &value->misc;
+
+       DR_STE_SET_TAG(gre, tag, gre_protocol, misc, gre_protocol);
+
+       DR_STE_SET_TAG(gre, tag, gre_k_present, misc, gre_k_present);
+       DR_STE_SET_TAG(gre, tag, gre_key_h, misc, gre_key_h);
+       DR_STE_SET_TAG(gre, tag, gre_key_l, misc, gre_key_l);
+
+       DR_STE_SET_TAG(gre, tag, gre_c_present, misc, gre_c_present);
+
+       DR_STE_SET_TAG(gre, tag, gre_s_present, misc, gre_s_present);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_tnl_gre_init(struct mlx5dr_ste_build *sb,
+                            struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_tnl_gre_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_GRE;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_tnl_gre_tag;
+}
+
+static int
+dr_ste_v0_build_tnl_mpls_tag(struct mlx5dr_match_param *value,
+                            struct mlx5dr_ste_build *sb,
+                            u8 *tag)
+{
+       struct mlx5dr_match_misc2 *misc_2 = &value->misc2;
+
+       if (DR_STE_IS_OUTER_MPLS_OVER_GRE_SET(misc_2)) {
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label,
+                              misc_2, outer_first_mpls_over_gre_label);
+
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp,
+                              misc_2, outer_first_mpls_over_gre_exp);
+
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos,
+                              misc_2, outer_first_mpls_over_gre_s_bos);
+
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl,
+                              misc_2, outer_first_mpls_over_gre_ttl);
+       } else {
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_label,
+                              misc_2, outer_first_mpls_over_udp_label);
+
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_exp,
+                              misc_2, outer_first_mpls_over_udp_exp);
+
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_s_bos,
+                              misc_2, outer_first_mpls_over_udp_s_bos);
+
+               DR_STE_SET_TAG(flex_parser_0, tag, parser_3_ttl,
+                              misc_2, outer_first_mpls_over_udp_ttl);
+       }
+       return 0;
+}
+
+static void
+dr_ste_v0_build_tnl_mpls_init(struct mlx5dr_ste_build *sb,
+                             struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_tnl_mpls_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_0;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_tnl_mpls_tag;
+}
+
+#define ICMP_TYPE_OFFSET_FIRST_DW      24
+#define ICMP_CODE_OFFSET_FIRST_DW      16
+
+static int
+dr_ste_v0_build_icmp_tag(struct mlx5dr_match_param *value,
+                        struct mlx5dr_ste_build *sb,
+                        u8 *tag)
+{
+       struct mlx5dr_match_misc3 *misc_3 = &value->misc3;
+       u32 *icmp_header_data;
+       int dw0_location;
+       int dw1_location;
+       u8 *icmp_type;
+       u8 *icmp_code;
+       bool is_ipv4;
+
+       is_ipv4 = DR_MASK_IS_ICMPV4_SET(misc_3);
+       if (is_ipv4) {
+               icmp_header_data        = &misc_3->icmpv4_header_data;
+               icmp_type               = &misc_3->icmpv4_type;
+               icmp_code               = &misc_3->icmpv4_code;
+               dw0_location            = sb->caps->flex_parser_id_icmp_dw0;
+               dw1_location            = sb->caps->flex_parser_id_icmp_dw1;
+       } else {
+               icmp_header_data        = &misc_3->icmpv6_header_data;
+               icmp_type               = &misc_3->icmpv6_type;
+               icmp_code               = &misc_3->icmpv6_code;
+               dw0_location            = sb->caps->flex_parser_id_icmpv6_dw0;
+               dw1_location            = sb->caps->flex_parser_id_icmpv6_dw1;
+       }
+
+       switch (dw0_location) {
+       case 4:
+               MLX5_SET(ste_flex_parser_1, tag, flex_parser_4,
+                        (*icmp_type << ICMP_TYPE_OFFSET_FIRST_DW) |
+                        (*icmp_code << ICMP_TYPE_OFFSET_FIRST_DW));
+
+               *icmp_type = 0;
+               *icmp_code = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (dw1_location) {
+       case 5:
+               MLX5_SET(ste_flex_parser_1, tag, flex_parser_5,
+                        *icmp_header_data);
+               *icmp_header_data = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+dr_ste_v0_build_icmp_init(struct mlx5dr_ste_build *sb,
+                         struct mlx5dr_match_param *mask)
+{
+       int ret;
+
+       ret = dr_ste_v0_build_icmp_tag(mask, sb, sb->bit_mask);
+       if (ret)
+               return ret;
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_1;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_icmp_tag;
+
+       return 0;
+}
+
+static int
+dr_ste_v0_build_general_purpose_tag(struct mlx5dr_match_param *value,
+                                   struct mlx5dr_ste_build *sb,
+                                   u8 *tag)
+{
+       struct mlx5dr_match_misc2 *misc_2 = &value->misc2;
+
+       DR_STE_SET_TAG(general_purpose, tag, general_purpose_lookup_field,
+                      misc_2, metadata_reg_a);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_general_purpose_init(struct mlx5dr_ste_build *sb,
+                                    struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_general_purpose_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_GENERAL_PURPOSE;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_general_purpose_tag;
+}
+
+static int
+dr_ste_v0_build_eth_l4_misc_tag(struct mlx5dr_match_param *value,
+                               struct mlx5dr_ste_build *sb,
+                               u8 *tag)
+{
+       struct mlx5dr_match_misc3 *misc3 = &value->misc3;
+
+       if (sb->inner) {
+               DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, inner_tcp_seq_num);
+               DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, inner_tcp_ack_num);
+       } else {
+               DR_STE_SET_TAG(eth_l4_misc, tag, seq_num, misc3, outer_tcp_seq_num);
+               DR_STE_SET_TAG(eth_l4_misc, tag, ack_num, misc3, outer_tcp_ack_num);
+       }
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_eth_l4_misc_init(struct mlx5dr_ste_build *sb,
+                                struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_eth_l4_misc_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_CALC_LU_TYPE(ETHL4_MISC, sb->rx, sb->inner);
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_eth_l4_misc_tag;
+}
+
+static int
+dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_tag(struct mlx5dr_match_param *value,
+                                             struct mlx5dr_ste_build *sb,
+                                             u8 *tag)
+{
+       struct mlx5dr_match_misc3 *misc3 = &value->misc3;
+
+       DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag,
+                      outer_vxlan_gpe_flags, misc3,
+                      outer_vxlan_gpe_flags);
+       DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag,
+                      outer_vxlan_gpe_next_protocol, misc3,
+                      outer_vxlan_gpe_next_protocol);
+       DR_STE_SET_TAG(flex_parser_tnl_vxlan_gpe, tag,
+                      outer_vxlan_gpe_vni, misc3,
+                      outer_vxlan_gpe_vni);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_init(struct mlx5dr_ste_build *sb,
+                                              struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_tag(mask, sb, sb->bit_mask);
+       sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_TNL_HEADER;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_tag;
+}
+
+static int
+dr_ste_v0_build_flex_parser_tnl_geneve_tag(struct mlx5dr_match_param *value,
+                                          struct mlx5dr_ste_build *sb,
+                                          u8 *tag)
+{
+       struct mlx5dr_match_misc *misc = &value->misc;
+
+       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
+                      geneve_protocol_type, misc, geneve_protocol_type);
+       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
+                      geneve_oam, misc, geneve_oam);
+       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
+                      geneve_opt_len, misc, geneve_opt_len);
+       DR_STE_SET_TAG(flex_parser_tnl_geneve, tag,
+                      geneve_vni, misc, geneve_vni);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_flex_parser_tnl_geneve_init(struct mlx5dr_ste_build *sb,
+                                           struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_flex_parser_tnl_geneve_tag(mask, sb, sb->bit_mask);
+       sb->lu_type = DR_STE_V0_LU_TYPE_FLEX_PARSER_TNL_HEADER;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_flex_parser_tnl_geneve_tag;
+}
+
+static int
+dr_ste_v0_build_register_0_tag(struct mlx5dr_match_param *value,
+                              struct mlx5dr_ste_build *sb,
+                              u8 *tag)
+{
+       struct mlx5dr_match_misc2 *misc2 = &value->misc2;
+
+       DR_STE_SET_TAG(register_0, tag, register_0_h, misc2, metadata_reg_c_0);
+       DR_STE_SET_TAG(register_0, tag, register_0_l, misc2, metadata_reg_c_1);
+       DR_STE_SET_TAG(register_0, tag, register_1_h, misc2, metadata_reg_c_2);
+       DR_STE_SET_TAG(register_0, tag, register_1_l, misc2, metadata_reg_c_3);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_register_0_init(struct mlx5dr_ste_build *sb,
+                               struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_register_0_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_STEERING_REGISTERS_0;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_register_0_tag;
+}
+
+static int
+dr_ste_v0_build_register_1_tag(struct mlx5dr_match_param *value,
+                              struct mlx5dr_ste_build *sb,
+                              u8 *tag)
+{
+       struct mlx5dr_match_misc2 *misc2 = &value->misc2;
+
+       DR_STE_SET_TAG(register_1, tag, register_2_h, misc2, metadata_reg_c_4);
+       DR_STE_SET_TAG(register_1, tag, register_2_l, misc2, metadata_reg_c_5);
+       DR_STE_SET_TAG(register_1, tag, register_3_h, misc2, metadata_reg_c_6);
+       DR_STE_SET_TAG(register_1, tag, register_3_l, misc2, metadata_reg_c_7);
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_register_1_init(struct mlx5dr_ste_build *sb,
+                               struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_register_1_tag(mask, sb, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_STEERING_REGISTERS_1;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_register_1_tag;
+}
+
+static void
+dr_ste_v0_build_src_gvmi_qpn_bit_mask(struct mlx5dr_match_param *value,
+                                     u8 *bit_mask)
+{
+       struct mlx5dr_match_misc *misc_mask = &value->misc;
+
+       DR_STE_SET_ONES(src_gvmi_qp, bit_mask, source_gvmi, misc_mask, source_port);
+       DR_STE_SET_ONES(src_gvmi_qp, bit_mask, source_qp, misc_mask, source_sqn);
+       misc_mask->source_eswitch_owner_vhca_id = 0;
+}
+
+static int
+dr_ste_v0_build_src_gvmi_qpn_tag(struct mlx5dr_match_param *value,
+                                struct mlx5dr_ste_build *sb,
+                                u8 *tag)
+{
+       struct mlx5dr_match_misc *misc = &value->misc;
+       struct mlx5dr_cmd_vport_cap *vport_cap;
+       struct mlx5dr_domain *dmn = sb->dmn;
+       struct mlx5dr_cmd_caps *caps;
+       u8 *bit_mask = sb->bit_mask;
+       bool source_gvmi_set;
+
+       DR_STE_SET_TAG(src_gvmi_qp, tag, source_qp, misc, source_sqn);
+
+       if (sb->vhca_id_valid) {
+               /* Find port GVMI based on the eswitch_owner_vhca_id */
+               if (misc->source_eswitch_owner_vhca_id == dmn->info.caps.gvmi)
+                       caps = &dmn->info.caps;
+               else if (dmn->peer_dmn && (misc->source_eswitch_owner_vhca_id ==
+                                          dmn->peer_dmn->info.caps.gvmi))
+                       caps = &dmn->peer_dmn->info.caps;
+               else
+                       return -EINVAL;
+
+               misc->source_eswitch_owner_vhca_id = 0;
+       } else {
+               caps = &dmn->info.caps;
+       }
+
+       source_gvmi_set = MLX5_GET(ste_src_gvmi_qp, bit_mask, source_gvmi);
+       if (source_gvmi_set) {
+               vport_cap = mlx5dr_get_vport_cap(caps, misc->source_port);
+               if (!vport_cap) {
+                       mlx5dr_err(dmn, "Vport 0x%x is invalid\n",
+                                  misc->source_port);
+                       return -EINVAL;
+               }
+
+               if (vport_cap->vport_gvmi)
+                       MLX5_SET(ste_src_gvmi_qp, tag, source_gvmi, vport_cap->vport_gvmi);
+
+               misc->source_port = 0;
+       }
+
+       return 0;
+}
+
+static void
+dr_ste_v0_build_src_gvmi_qpn_init(struct mlx5dr_ste_build *sb,
+                                 struct mlx5dr_match_param *mask)
+{
+       dr_ste_v0_build_src_gvmi_qpn_bit_mask(mask, sb->bit_mask);
+
+       sb->lu_type = DR_STE_V0_LU_TYPE_SRC_GVMI_AND_QP;
+       sb->byte_mask = mlx5dr_ste_conv_bit_to_byte_mask(sb->bit_mask);
+       sb->ste_build_tag_func = &dr_ste_v0_build_src_gvmi_qpn_tag;
+}
+
+struct mlx5dr_ste_ctx ste_ctx_v0 = {
+       /* Builders */
+       .build_eth_l2_src_dst_init      = &dr_ste_v0_build_eth_l2_src_dst_init,
+       .build_eth_l3_ipv6_src_init     = &dr_ste_v0_build_eth_l3_ipv6_src_init,
+       .build_eth_l3_ipv6_dst_init     = &dr_ste_v0_build_eth_l3_ipv6_dst_init,
+       .build_eth_l3_ipv4_5_tuple_init = &dr_ste_v0_build_eth_l3_ipv4_5_tuple_init,
+       .build_eth_l2_src_init          = &dr_ste_v0_build_eth_l2_src_init,
+       .build_eth_l2_dst_init          = &dr_ste_v0_build_eth_l2_dst_init,
+       .build_eth_l2_tnl_init          = &dr_ste_v0_build_eth_l2_tnl_init,
+       .build_eth_l3_ipv4_misc_init    = &dr_ste_v0_build_eth_l3_ipv4_misc_init,
+       .build_eth_ipv6_l3_l4_init      = &dr_ste_v0_build_eth_ipv6_l3_l4_init,
+       .build_mpls_init                = &dr_ste_v0_build_mpls_init,
+       .build_tnl_gre_init             = &dr_ste_v0_build_tnl_gre_init,
+       .build_tnl_mpls_init            = &dr_ste_v0_build_tnl_mpls_init,
+       .build_icmp_init                = &dr_ste_v0_build_icmp_init,
+       .build_general_purpose_init     = &dr_ste_v0_build_general_purpose_init,
+       .build_eth_l4_misc_init         = &dr_ste_v0_build_eth_l4_misc_init,
+       .build_tnl_vxlan_gpe_init       = &dr_ste_v0_build_flex_parser_tnl_vxlan_gpe_init,
+       .build_tnl_geneve_init          = &dr_ste_v0_build_flex_parser_tnl_geneve_init,
+       .build_register_0_init          = &dr_ste_v0_build_register_0_init,
+       .build_register_1_init          = &dr_ste_v0_build_register_1_init,
+       .build_src_gvmi_qpn_init        = &dr_ste_v0_build_src_gvmi_qpn_init,
+
+       /* Getters and Setters */
+       .ste_init                       = &dr_ste_v0_init,
+       .set_next_lu_type               = &dr_ste_v0_set_next_lu_type,
+       .get_next_lu_type               = &dr_ste_v0_get_next_lu_type,
+       .set_miss_addr                  = &dr_ste_v0_set_miss_addr,
+       .get_miss_addr                  = &dr_ste_v0_get_miss_addr,
+       .set_hit_addr                   = &dr_ste_v0_set_hit_addr,
+       .set_byte_mask                  = &dr_ste_v0_set_byte_mask,
+       .get_byte_mask                  = &dr_ste_v0_get_byte_mask,
+
+       /* Actions */
+       .set_actions_rx                 = &dr_ste_v0_set_actions_rx,
+       .set_actions_tx                 = &dr_ste_v0_set_actions_tx,
+       .modify_field_arr_sz            = ARRAY_SIZE(dr_ste_v0_action_modify_field_arr),
+       .modify_field_arr               = dr_ste_v0_action_modify_field_arr,
+       .set_action_set                 = &dr_ste_v0_set_action_set,
+       .set_action_add                 = &dr_ste_v0_set_action_add,
+       .set_action_copy                = &dr_ste_v0_set_action_copy,
+       .set_action_decap_l3_list       = &dr_ste_v0_set_action_decap_l3_list,
+};
index 51880df..8d2c3b6 100644 (file)
@@ -120,6 +120,7 @@ struct mlx5dr_ste_htbl;
 struct mlx5dr_match_param;
 struct mlx5dr_cmd_caps;
 struct mlx5dr_matcher_rx_tx;
+struct mlx5dr_ste_ctx;
 
 struct mlx5dr_ste {
        u8 *hw_ste;
@@ -154,7 +155,7 @@ struct mlx5dr_ste_htbl_ctrl {
 };
 
 struct mlx5dr_ste_htbl {
-       u8 lu_type;
+       u16 lu_type;
        u16 byte_mask;
        u32 refcount;
        struct mlx5dr_icm_chunk *chunk;
@@ -190,7 +191,7 @@ struct mlx5dr_ste_build {
        u8 vhca_id_valid:1;
        struct mlx5dr_domain *dmn;
        struct mlx5dr_cmd_caps *caps;
-       u8 lu_type;
+       u16 lu_type;
        u16 byte_mask;
        u8 bit_mask[DR_STE_SIZE_MASK];
        int (*ste_build_tag_func)(struct mlx5dr_match_param *spec,
@@ -201,7 +202,7 @@ struct mlx5dr_ste_build {
 struct mlx5dr_ste_htbl *
 mlx5dr_ste_htbl_alloc(struct mlx5dr_icm_pool *pool,
                      enum mlx5dr_icm_chunk_size chunk_size,
-                     u8 lu_type, u16 byte_mask);
+                     u16 lu_type, u16 byte_mask);
 
 int mlx5dr_ste_htbl_free(struct mlx5dr_ste_htbl *htbl);
 
@@ -219,35 +220,84 @@ static inline void mlx5dr_htbl_get(struct mlx5dr_ste_htbl *htbl)
 
 /* STE utils */
 u32 mlx5dr_ste_calc_hash_index(u8 *hw_ste_p, struct mlx5dr_ste_htbl *htbl);
-void mlx5dr_ste_init(u8 *hw_ste_p, u8 lu_type, u8 entry_type, u16 gvmi);
-void mlx5dr_ste_always_hit_htbl(struct mlx5dr_ste *ste,
-                               struct mlx5dr_ste_htbl *next_htbl);
-void mlx5dr_ste_set_miss_addr(u8 *hw_ste, u64 miss_addr);
-u64 mlx5dr_ste_get_miss_addr(u8 *hw_ste);
-void mlx5dr_ste_set_hit_gvmi(u8 *hw_ste_p, u16 gvmi);
-void mlx5dr_ste_set_hit_addr(u8 *hw_ste, u64 icm_addr, u32 ht_size);
-void mlx5dr_ste_always_miss_addr(struct mlx5dr_ste *ste, u64 miss_addr);
+void mlx5dr_ste_set_miss_addr(struct mlx5dr_ste_ctx *ste_ctx,
+                             u8 *hw_ste, u64 miss_addr);
+void mlx5dr_ste_set_hit_addr(struct mlx5dr_ste_ctx *ste_ctx,
+                            u8 *hw_ste, u64 icm_addr, u32 ht_size);
+void mlx5dr_ste_set_hit_addr_by_next_htbl(struct mlx5dr_ste_ctx *ste_ctx,
+                                         u8 *hw_ste,
+                                         struct mlx5dr_ste_htbl *next_htbl);
 void mlx5dr_ste_set_bit_mask(u8 *hw_ste_p, u8 *bit_mask);
 bool mlx5dr_ste_is_last_in_rule(struct mlx5dr_matcher_rx_tx *nic_matcher,
                                u8 ste_location);
-void mlx5dr_ste_rx_set_flow_tag(u8 *hw_ste_p, u32 flow_tag);
-void mlx5dr_ste_set_counter_id(u8 *hw_ste_p, u32 ctr_id);
-void mlx5dr_ste_set_tx_encap(void *hw_ste_p, u32 reformat_id,
-                            int size, bool encap_l3);
-void mlx5dr_ste_set_rx_decap(u8 *hw_ste_p);
-void mlx5dr_ste_set_rx_decap_l3(u8 *hw_ste_p, bool vlan);
-void mlx5dr_ste_set_rx_pop_vlan(u8 *hw_ste_p);
-void mlx5dr_ste_set_tx_push_vlan(u8 *hw_ste_p, u32 vlan_tpid_pcp_dei_vid,
-                                bool go_back);
-void mlx5dr_ste_set_entry_type(u8 *hw_ste_p, u8 entry_type);
-u8 mlx5dr_ste_get_entry_type(u8 *hw_ste_p);
-void mlx5dr_ste_set_rewrite_actions(u8 *hw_ste_p, u16 num_of_actions,
-                                   u32 re_write_index);
-void mlx5dr_ste_set_go_back_bit(u8 *hw_ste_p);
 u64 mlx5dr_ste_get_icm_addr(struct mlx5dr_ste *ste);
 u64 mlx5dr_ste_get_mr_addr(struct mlx5dr_ste *ste);
 struct list_head *mlx5dr_ste_get_miss_list(struct mlx5dr_ste *ste);
 
+#define MLX5DR_MAX_VLANS 2
+
+struct mlx5dr_ste_actions_attr {
+       u32     modify_index;
+       u16     modify_actions;
+       u32     decap_index;
+       u16     decap_actions;
+       u8      decap_with_vlan:1;
+       u64     final_icm_addr;
+       u32     flow_tag;
+       u32     ctr_id;
+       u16     gvmi;
+       u16     hit_gvmi;
+       u32     reformat_id;
+       u32     reformat_size;
+       struct {
+               int     count;
+               u32     headers[MLX5DR_MAX_VLANS];
+       } vlans;
+};
+
+void mlx5dr_ste_set_actions_rx(struct mlx5dr_ste_ctx *ste_ctx,
+                              struct mlx5dr_domain *dmn,
+                              u8 *action_type_set,
+                              u8 *last_ste,
+                              struct mlx5dr_ste_actions_attr *attr,
+                              u32 *added_stes);
+void mlx5dr_ste_set_actions_tx(struct mlx5dr_ste_ctx *ste_ctx,
+                              struct mlx5dr_domain *dmn,
+                              u8 *action_type_set,
+                              u8 *last_ste,
+                              struct mlx5dr_ste_actions_attr *attr,
+                              u32 *added_stes);
+
+void mlx5dr_ste_set_action_set(struct mlx5dr_ste_ctx *ste_ctx,
+                              __be64 *hw_action,
+                              u8 hw_field,
+                              u8 shifter,
+                              u8 length,
+                              u32 data);
+void mlx5dr_ste_set_action_add(struct mlx5dr_ste_ctx *ste_ctx,
+                              __be64 *hw_action,
+                              u8 hw_field,
+                              u8 shifter,
+                              u8 length,
+                              u32 data);
+void mlx5dr_ste_set_action_copy(struct mlx5dr_ste_ctx *ste_ctx,
+                               __be64 *hw_action,
+                               u8 dst_hw_field,
+                               u8 dst_shifter,
+                               u8 dst_len,
+                               u8 src_hw_field,
+                               u8 src_shifter);
+int mlx5dr_ste_set_action_decap_l3_list(struct mlx5dr_ste_ctx *ste_ctx,
+                                       void *data,
+                                       u32 data_sz,
+                                       u8 *hw_action,
+                                       u32 hw_action_sz,
+                                       u16 *used_hw_action_num);
+
+const struct mlx5dr_ste_action_modify_field *
+mlx5dr_ste_conv_modify_hdr_sw_field(struct mlx5dr_ste_ctx *ste_ctx, u16 sw_field);
+
+struct mlx5dr_ste_ctx *mlx5dr_ste_get_ctx(u8 version);
 void mlx5dr_ste_free(struct mlx5dr_ste *ste,
                     struct mlx5dr_matcher *matcher,
                     struct mlx5dr_matcher_rx_tx *nic_matcher);
@@ -271,8 +321,6 @@ static inline bool mlx5dr_ste_is_not_used(struct mlx5dr_ste *ste)
        return !ste->refcount;
 }
 
-void mlx5dr_ste_set_hit_addr_by_next_htbl(u8 *hw_ste,
-                                         struct mlx5dr_ste_htbl *next_htbl);
 bool mlx5dr_ste_equal_tag(void *src, void *dst);
 int mlx5dr_ste_create_next_htbl(struct mlx5dr_matcher *matcher,
                                struct mlx5dr_matcher_rx_tx *nic_matcher,
@@ -289,65 +337,85 @@ int mlx5dr_ste_build_ste_arr(struct mlx5dr_matcher *matcher,
                             struct mlx5dr_matcher_rx_tx *nic_matcher,
                             struct mlx5dr_match_param *value,
                             u8 *ste_arr);
-void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_build *builder,
+void mlx5dr_ste_build_eth_l2_src_dst(struct mlx5dr_ste_ctx *ste_ctx,
+                                    struct mlx5dr_ste_build *builder,
                                     struct mlx5dr_match_param *mask,
                                     bool inner, bool rx);
-void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv4_5_tuple(struct mlx5dr_ste_ctx *ste_ctx,
+                                         struct mlx5dr_ste_build *sb,
                                          struct mlx5dr_match_param *mask,
                                          bool inner, bool rx);
-void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv4_misc(struct mlx5dr_ste_ctx *ste_ctx,
+                                      struct mlx5dr_ste_build *sb,
                                       struct mlx5dr_match_param *mask,
                                       bool inner, bool rx);
-void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv6_dst(struct mlx5dr_ste_ctx *ste_ctx,
+                                     struct mlx5dr_ste_build *sb,
                                      struct mlx5dr_match_param *mask,
                                      bool inner, bool rx);
-void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l3_ipv6_src(struct mlx5dr_ste_ctx *ste_ctx,
+                                     struct mlx5dr_ste_build *sb,
                                      struct mlx5dr_match_param *mask,
                                      bool inner, bool rx);
-void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_src(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx);
-void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_dst(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx);
-void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l2_tnl(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx);
-void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_ipv6_l3_l4(struct mlx5dr_ste_ctx *ste_ctx,
+                                    struct mlx5dr_ste_build *sb,
                                     struct mlx5dr_match_param *mask,
                                     bool inner, bool rx);
-void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_eth_l4_misc(struct mlx5dr_ste_ctx *ste_ctx,
+                                 struct mlx5dr_ste_build *sb,
                                  struct mlx5dr_match_param *mask,
                                  bool inner, bool rx);
-void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_gre(struct mlx5dr_ste_ctx *ste_ctx,
+                             struct mlx5dr_ste_build *sb,
                              struct mlx5dr_match_param *mask,
                              bool inner, bool rx);
-void mlx5dr_ste_build_mpls(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_mpls(struct mlx5dr_ste_ctx *ste_ctx,
+                          struct mlx5dr_ste_build *sb,
                           struct mlx5dr_match_param *mask,
                           bool inner, bool rx);
-void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_mpls(struct mlx5dr_ste_ctx *ste_ctx,
+                              struct mlx5dr_ste_build *sb,
                               struct mlx5dr_match_param *mask,
                               bool inner, bool rx);
-int mlx5dr_ste_build_icmp(struct mlx5dr_ste_build *sb,
+int mlx5dr_ste_build_icmp(struct mlx5dr_ste_ctx *ste_ctx,
+                         struct mlx5dr_ste_build *sb,
                          struct mlx5dr_match_param *mask,
                          struct mlx5dr_cmd_caps *caps,
                          bool inner, bool rx);
-void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_vxlan_gpe(struct mlx5dr_ste_ctx *ste_ctx,
+                                   struct mlx5dr_ste_build *sb,
                                    struct mlx5dr_match_param *mask,
                                    bool inner, bool rx);
-void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_tnl_geneve(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx);
-void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_general_purpose(struct mlx5dr_ste_ctx *ste_ctx,
+                                     struct mlx5dr_ste_build *sb,
                                      struct mlx5dr_match_param *mask,
                                      bool inner, bool rx);
-void mlx5dr_ste_build_register_0(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_register_0(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx);
-void mlx5dr_ste_build_register_1(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_register_1(struct mlx5dr_ste_ctx *ste_ctx,
+                                struct mlx5dr_ste_build *sb,
                                 struct mlx5dr_match_param *mask,
                                 bool inner, bool rx);
-void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_build *sb,
+void mlx5dr_ste_build_src_gvmi_qpn(struct mlx5dr_ste_ctx *ste_ctx,
+                                  struct mlx5dr_ste_build *sb,
                                   struct mlx5dr_match_param *mask,
                                   struct mlx5dr_domain *dmn,
                                   bool inner, bool rx);
@@ -574,10 +642,10 @@ struct mlx5dr_match_misc3 {
        u32 outer_vxlan_gpe_next_protocol:8;
        u32 icmpv4_header_data;
        u32 icmpv6_header_data;
-       u32 icmpv6_code:8;
-       u32 icmpv6_type:8;
-       u32 icmpv4_code:8;
-       u32 icmpv4_type:8;
+       u8 icmpv6_code;
+       u8 icmpv6_type;
+       u8 icmpv4_code;
+       u8 icmpv4_type;
        u8 reserved_auto3[0x1c];
 };
 
@@ -671,6 +739,7 @@ struct mlx5dr_domain {
        struct mlx5dr_send_ring *send_ring;
        struct mlx5dr_domain_info info;
        struct mlx5dr_domain_cache cache;
+       struct mlx5dr_ste_ctx *ste_ctx;
 };
 
 struct mlx5dr_table_rx_tx {
@@ -725,6 +794,14 @@ struct mlx5dr_rule_member {
        struct list_head use_ste_list;
 };
 
+struct mlx5dr_ste_action_modify_field {
+       u16 hw_field;
+       u8 start;
+       u8 end;
+       u8 l3_type;
+       u8 l4_type;
+};
+
 struct mlx5dr_action {
        enum mlx5dr_action_type action_type;
        refcount_t refcount;
@@ -1000,7 +1077,8 @@ int mlx5dr_ste_htbl_init_and_postsend(struct mlx5dr_domain *dmn,
                                      struct mlx5dr_ste_htbl *htbl,
                                      struct mlx5dr_htbl_connect_info *connect_info,
                                      bool update_hw_ste);
-void mlx5dr_ste_set_formatted_ste(u16 gvmi,
+void mlx5dr_ste_set_formatted_ste(struct mlx5dr_ste_ctx *ste_ctx,
+                                 u16 gvmi,
                                  struct mlx5dr_domain_rx_tx *nic_dmn,
                                  struct mlx5dr_ste_htbl *htbl,
                                  u8 *formatted_ste,
index e01c376..83df6df 100644 (file)
@@ -5,91 +5,6 @@
 #define MLX5_IFC_DR_H
 
 enum {
-       MLX5DR_ACTION_MDFY_HW_FLD_L2_0          = 0,
-       MLX5DR_ACTION_MDFY_HW_FLD_L2_1          = 1,
-       MLX5DR_ACTION_MDFY_HW_FLD_L2_2          = 2,
-       MLX5DR_ACTION_MDFY_HW_FLD_L3_0          = 3,
-       MLX5DR_ACTION_MDFY_HW_FLD_L3_1          = 4,
-       MLX5DR_ACTION_MDFY_HW_FLD_L3_2          = 5,
-       MLX5DR_ACTION_MDFY_HW_FLD_L3_3          = 6,
-       MLX5DR_ACTION_MDFY_HW_FLD_L3_4          = 7,
-       MLX5DR_ACTION_MDFY_HW_FLD_L4_0          = 8,
-       MLX5DR_ACTION_MDFY_HW_FLD_L4_1          = 9,
-       MLX5DR_ACTION_MDFY_HW_FLD_MPLS          = 10,
-       MLX5DR_ACTION_MDFY_HW_FLD_L2_TNL_0      = 11,
-       MLX5DR_ACTION_MDFY_HW_FLD_REG_0         = 12,
-       MLX5DR_ACTION_MDFY_HW_FLD_REG_1         = 13,
-       MLX5DR_ACTION_MDFY_HW_FLD_REG_2         = 14,
-       MLX5DR_ACTION_MDFY_HW_FLD_REG_3         = 15,
-       MLX5DR_ACTION_MDFY_HW_FLD_L4_2          = 16,
-       MLX5DR_ACTION_MDFY_HW_FLD_FLEX_0        = 17,
-       MLX5DR_ACTION_MDFY_HW_FLD_FLEX_1        = 18,
-       MLX5DR_ACTION_MDFY_HW_FLD_FLEX_2        = 19,
-       MLX5DR_ACTION_MDFY_HW_FLD_FLEX_3        = 20,
-       MLX5DR_ACTION_MDFY_HW_FLD_L2_TNL_1      = 21,
-       MLX5DR_ACTION_MDFY_HW_FLD_METADATA      = 22,
-       MLX5DR_ACTION_MDFY_HW_FLD_RESERVED      = 23,
-};
-
-enum {
-       MLX5DR_ACTION_MDFY_HW_OP_COPY           = 0x1,
-       MLX5DR_ACTION_MDFY_HW_OP_SET            = 0x2,
-       MLX5DR_ACTION_MDFY_HW_OP_ADD            = 0x3,
-};
-
-enum {
-       MLX5DR_ACTION_MDFY_HW_HDR_L3_NONE       = 0x0,
-       MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV4       = 0x1,
-       MLX5DR_ACTION_MDFY_HW_HDR_L3_IPV6       = 0x2,
-};
-
-enum {
-       MLX5DR_ACTION_MDFY_HW_HDR_L4_NONE       = 0x0,
-       MLX5DR_ACTION_MDFY_HW_HDR_L4_TCP        = 0x1,
-       MLX5DR_ACTION_MDFY_HW_HDR_L4_UDP        = 0x2,
-};
-
-enum {
-       MLX5DR_STE_LU_TYPE_NOP                          = 0x00,
-       MLX5DR_STE_LU_TYPE_SRC_GVMI_AND_QP              = 0x05,
-       MLX5DR_STE_LU_TYPE_ETHL2_TUNNELING_I            = 0x0a,
-       MLX5DR_STE_LU_TYPE_ETHL2_DST_O                  = 0x06,
-       MLX5DR_STE_LU_TYPE_ETHL2_DST_I                  = 0x07,
-       MLX5DR_STE_LU_TYPE_ETHL2_DST_D                  = 0x1b,
-       MLX5DR_STE_LU_TYPE_ETHL2_SRC_O                  = 0x08,
-       MLX5DR_STE_LU_TYPE_ETHL2_SRC_I                  = 0x09,
-       MLX5DR_STE_LU_TYPE_ETHL2_SRC_D                  = 0x1c,
-       MLX5DR_STE_LU_TYPE_ETHL2_SRC_DST_O              = 0x36,
-       MLX5DR_STE_LU_TYPE_ETHL2_SRC_DST_I              = 0x37,
-       MLX5DR_STE_LU_TYPE_ETHL2_SRC_DST_D              = 0x38,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV6_DST_O             = 0x0d,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV6_DST_I             = 0x0e,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV6_DST_D             = 0x1e,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV6_SRC_O             = 0x0f,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV6_SRC_I             = 0x10,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV6_SRC_D             = 0x1f,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV4_5_TUPLE_O         = 0x11,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV4_5_TUPLE_I         = 0x12,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV4_5_TUPLE_D         = 0x20,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV4_MISC_O            = 0x29,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV4_MISC_I            = 0x2a,
-       MLX5DR_STE_LU_TYPE_ETHL3_IPV4_MISC_D            = 0x2b,
-       MLX5DR_STE_LU_TYPE_ETHL4_O                      = 0x13,
-       MLX5DR_STE_LU_TYPE_ETHL4_I                      = 0x14,
-       MLX5DR_STE_LU_TYPE_ETHL4_D                      = 0x21,
-       MLX5DR_STE_LU_TYPE_ETHL4_MISC_O                 = 0x2c,
-       MLX5DR_STE_LU_TYPE_ETHL4_MISC_I                 = 0x2d,
-       MLX5DR_STE_LU_TYPE_ETHL4_MISC_D                 = 0x2e,
-       MLX5DR_STE_LU_TYPE_MPLS_FIRST_O                 = 0x15,
-       MLX5DR_STE_LU_TYPE_MPLS_FIRST_I                 = 0x24,
-       MLX5DR_STE_LU_TYPE_MPLS_FIRST_D                 = 0x25,
-       MLX5DR_STE_LU_TYPE_GRE                          = 0x16,
-       MLX5DR_STE_LU_TYPE_FLEX_PARSER_0                = 0x22,
-       MLX5DR_STE_LU_TYPE_FLEX_PARSER_1                = 0x23,
-       MLX5DR_STE_LU_TYPE_FLEX_PARSER_TNL_HEADER       = 0x19,
-       MLX5DR_STE_LU_TYPE_GENERAL_PURPOSE              = 0x18,
-       MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_0         = 0x2f,
-       MLX5DR_STE_LU_TYPE_STEERING_REGISTERS_1         = 0x30,
        MLX5DR_STE_LU_TYPE_DONT_CARE                    = 0x0f,
 };
 
index bdafc85..ba78e06 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/mlx5/vport.h>
 #include <linux/mlx5/eswitch.h>
 #include "mlx5_core.h"
+#include "sf/sf.h"
 
 /* Mutex to hold while enabling or disabling RoCE */
 static DEFINE_MUTEX(mlx5_roce_en_lock);
@@ -1160,6 +1161,6 @@ EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid);
  */
 u16 mlx5_eswitch_get_total_vports(const struct mlx5_core_dev *dev)
 {
-       return MLX5_SPECIAL_VPORTS(dev) + mlx5_core_max_vfs(dev);
+       return MLX5_SPECIAL_VPORTS(dev) + mlx5_core_max_vfs(dev) + mlx5_sf_max_functions(dev);
 }
 EXPORT_SYMBOL_GPL(mlx5_eswitch_get_total_vports);
index 5d9ddf3..e6f677e 100644 (file)
@@ -267,7 +267,7 @@ struct mlxfw_mfa2_file *mlxfw_mfa2_file_init(const struct firmware *fw)
        const void *first_tlv_ptr;
        const void *cb_top_ptr;
 
-       mfa2_file = kcalloc(1, sizeof(*mfa2_file), GFP_KERNEL);
+       mfa2_file = kzalloc(sizeof(*mfa2_file), GFP_KERNEL);
        if (!mfa2_file)
                return ERR_PTR(-ENOMEM);
 
index 685037e..52fdc34 100644 (file)
@@ -84,6 +84,7 @@ struct mlxsw_core {
        struct mlxsw_thermal *thermal;
        struct mlxsw_core_port *ports;
        unsigned int max_ports;
+       atomic_t active_ports_count;
        bool fw_flash_in_progress;
        struct {
                struct devlink_health_reporter *fw_fatal;
@@ -96,8 +97,36 @@ struct mlxsw_core {
 
 #define MLXSW_PORT_MAX_PORTS_DEFAULT   0x40
 
-static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core)
+static u64 mlxsw_ports_occ_get(void *priv)
 {
+       struct mlxsw_core *mlxsw_core = priv;
+
+       return atomic_read(&mlxsw_core->active_ports_count);
+}
+
+static int mlxsw_core_resources_ports_register(struct mlxsw_core *mlxsw_core)
+{
+       struct devlink *devlink = priv_to_devlink(mlxsw_core);
+       struct devlink_resource_size_params ports_num_params;
+       u32 max_ports;
+
+       max_ports = mlxsw_core->max_ports - 1;
+       devlink_resource_size_params_init(&ports_num_params, max_ports,
+                                         max_ports, 1,
+                                         DEVLINK_RESOURCE_UNIT_ENTRY);
+
+       return devlink_resource_register(devlink,
+                                        DEVLINK_RESOURCE_GENERIC_NAME_PORTS,
+                                        max_ports, MLXSW_CORE_RESOURCE_PORTS,
+                                        DEVLINK_RESOURCE_ID_PARENT_TOP,
+                                        &ports_num_params);
+}
+
+static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core, bool reload)
+{
+       struct devlink *devlink = priv_to_devlink(mlxsw_core);
+       int err;
+
        /* Switch ports are numbered from 1 to queried value */
        if (MLXSW_CORE_RES_VALID(mlxsw_core, MAX_SYSTEM_PORT))
                mlxsw_core->max_ports = MLXSW_CORE_RES_GET(mlxsw_core,
@@ -110,11 +139,30 @@ static int mlxsw_ports_init(struct mlxsw_core *mlxsw_core)
        if (!mlxsw_core->ports)
                return -ENOMEM;
 
+       if (!reload) {
+               err = mlxsw_core_resources_ports_register(mlxsw_core);
+               if (err)
+                       goto err_resources_ports_register;
+       }
+       atomic_set(&mlxsw_core->active_ports_count, 0);
+       devlink_resource_occ_get_register(devlink, MLXSW_CORE_RESOURCE_PORTS,
+                                         mlxsw_ports_occ_get, mlxsw_core);
+
        return 0;
+
+err_resources_ports_register:
+       kfree(mlxsw_core->ports);
+       return err;
 }
 
-static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core)
+static void mlxsw_ports_fini(struct mlxsw_core *mlxsw_core, bool reload)
 {
+       struct devlink *devlink = priv_to_devlink(mlxsw_core);
+
+       devlink_resource_occ_get_unregister(devlink, MLXSW_CORE_RESOURCE_PORTS);
+       if (!reload)
+               devlink_resources_unregister(priv_to_devlink(mlxsw_core), NULL);
+
        kfree(mlxsw_core->ports);
 }
 
@@ -1897,7 +1945,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
                        goto err_register_resources;
        }
 
-       err = mlxsw_ports_init(mlxsw_core);
+       err = mlxsw_ports_init(mlxsw_core, reload);
        if (err)
                goto err_ports_init;
 
@@ -1986,7 +2034,7 @@ err_devlink_register:
 err_emad_init:
        kfree(mlxsw_core->lag.mapping);
 err_alloc_lag_mapping:
-       mlxsw_ports_fini(mlxsw_core);
+       mlxsw_ports_fini(mlxsw_core, reload);
 err_ports_init:
        if (!reload)
                devlink_resources_unregister(devlink, NULL);
@@ -2056,7 +2104,7 @@ void mlxsw_core_bus_device_unregister(struct mlxsw_core *mlxsw_core,
                devlink_unregister(devlink);
        mlxsw_emad_fini(mlxsw_core);
        kfree(mlxsw_core->lag.mapping);
-       mlxsw_ports_fini(mlxsw_core);
+       mlxsw_ports_fini(mlxsw_core, reload);
        if (!reload)
                devlink_resources_unregister(devlink, NULL);
        mlxsw_core->bus->fini(mlxsw_core->bus_priv);
@@ -2755,16 +2803,25 @@ int mlxsw_core_port_init(struct mlxsw_core *mlxsw_core, u8 local_port,
                         const unsigned char *switch_id,
                         unsigned char switch_id_len)
 {
-       return __mlxsw_core_port_init(mlxsw_core, local_port,
-                                     DEVLINK_PORT_FLAVOUR_PHYSICAL,
-                                     port_number, split, split_port_subnumber,
-                                     splittable, lanes,
-                                     switch_id, switch_id_len);
+       int err;
+
+       err = __mlxsw_core_port_init(mlxsw_core, local_port,
+                                    DEVLINK_PORT_FLAVOUR_PHYSICAL,
+                                    port_number, split, split_port_subnumber,
+                                    splittable, lanes,
+                                    switch_id, switch_id_len);
+       if (err)
+               return err;
+
+       atomic_inc(&mlxsw_core->active_ports_count);
+       return 0;
 }
 EXPORT_SYMBOL(mlxsw_core_port_init);
 
 void mlxsw_core_port_fini(struct mlxsw_core *mlxsw_core, u8 local_port)
 {
+       atomic_dec(&mlxsw_core->active_ports_count);
+
        __mlxsw_core_port_fini(mlxsw_core, local_port);
 }
 EXPORT_SYMBOL(mlxsw_core_port_fini);
index 6b3ccbf..8af7d9d 100644 (file)
 #include "cmd.h"
 #include "resources.h"
 
+enum mlxsw_core_resource_id {
+       MLXSW_CORE_RESOURCE_PORTS = 1,
+       MLXSW_CORE_RESOURCE_MAX,
+};
+
 struct mlxsw_core;
 struct mlxsw_core_port;
 struct mlxsw_driver;
index 8fa286c..bf85ce9 100644 (file)
@@ -19,7 +19,7 @@
 #define MLXSW_THERMAL_ASIC_TEMP_NORM   75000   /* 75C */
 #define MLXSW_THERMAL_ASIC_TEMP_HIGH   85000   /* 85C */
 #define MLXSW_THERMAL_ASIC_TEMP_HOT    105000  /* 105C */
-#define MLXSW_THERMAL_ASIC_TEMP_CRIT   110000  /* 110C */
+#define MLXSW_THERMAL_ASIC_TEMP_CRIT   140000  /* 140C */
 #define MLXSW_THERMAL_HYSTERESIS_TEMP  5000    /* 5C */
 #define MLXSW_THERMAL_MODULE_TEMP_SHIFT        (MLXSW_THERMAL_HYSTERESIS_TEMP * 2)
 #define MLXSW_THERMAL_ZONE_MAX_NAME    16
@@ -176,6 +176,12 @@ mlxsw_thermal_module_trips_update(struct device *dev, struct mlxsw_core *core,
        if (err)
                return err;
 
+       if (crit_temp > emerg_temp) {
+               dev_warn(dev, "%s : Critical threshold %d is above emergency threshold %d\n",
+                        tz->tzdev->type, crit_temp, emerg_temp);
+               return 0;
+       }
+
        /* According to the system thermal requirements, the thermal zones are
         * defined with four trip points. The critical and emergency
         * temperature thresholds, provided by QSFP module are set as "active"
@@ -190,11 +196,8 @@ mlxsw_thermal_module_trips_update(struct device *dev, struct mlxsw_core *core,
                tz->trips[MLXSW_THERMAL_TEMP_TRIP_NORM].temp = crit_temp;
        tz->trips[MLXSW_THERMAL_TEMP_TRIP_HIGH].temp = crit_temp;
        tz->trips[MLXSW_THERMAL_TEMP_TRIP_HOT].temp = emerg_temp;
-       if (emerg_temp > crit_temp)
-               tz->trips[MLXSW_THERMAL_TEMP_TRIP_CRIT].temp = emerg_temp +
+       tz->trips[MLXSW_THERMAL_TEMP_TRIP_CRIT].temp = emerg_temp +
                                        MLXSW_THERMAL_MODULE_TEMP_SHIFT;
-       else
-               tz->trips[MLXSW_THERMAL_TEMP_TRIP_CRIT].temp = emerg_temp;
 
        return 0;
 }
index 4eeae8d..d005253 100644 (file)
@@ -323,8 +323,8 @@ static int mlxsw_pci_wqe_frag_map(struct mlxsw_pci *mlxsw_pci, char *wqe,
        struct pci_dev *pdev = mlxsw_pci->pdev;
        dma_addr_t mapaddr;
 
-       mapaddr = pci_map_single(pdev, frag_data, frag_len, direction);
-       if (unlikely(pci_dma_mapping_error(pdev, mapaddr))) {
+       mapaddr = dma_map_single(&pdev->dev, frag_data, frag_len, direction);
+       if (unlikely(dma_mapping_error(&pdev->dev, mapaddr))) {
                dev_err_ratelimited(&pdev->dev, "failed to dma map tx frag\n");
                return -EIO;
        }
@@ -342,7 +342,7 @@ static void mlxsw_pci_wqe_frag_unmap(struct mlxsw_pci *mlxsw_pci, char *wqe,
 
        if (!frag_len)
                return;
-       pci_unmap_single(pdev, mapaddr, frag_len, direction);
+       dma_unmap_single(&pdev->dev, mapaddr, frag_len, direction);
 }
 
 static int mlxsw_pci_rdq_skb_alloc(struct mlxsw_pci *mlxsw_pci,
@@ -858,9 +858,9 @@ static int mlxsw_pci_queue_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
                tasklet_setup(&q->tasklet, q_ops->tasklet);
 
        mem_item->size = MLXSW_PCI_AQ_SIZE;
-       mem_item->buf = pci_alloc_consistent(mlxsw_pci->pdev,
-                                            mem_item->size,
-                                            &mem_item->mapaddr);
+       mem_item->buf = dma_alloc_coherent(&mlxsw_pci->pdev->dev,
+                                          mem_item->size, &mem_item->mapaddr,
+                                          GFP_KERNEL);
        if (!mem_item->buf)
                return -ENOMEM;
 
@@ -890,8 +890,8 @@ static int mlxsw_pci_queue_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
 err_q_ops_init:
        kfree(q->elem_info);
 err_elem_info_alloc:
-       pci_free_consistent(mlxsw_pci->pdev, mem_item->size,
-                           mem_item->buf, mem_item->mapaddr);
+       dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
+                         mem_item->buf, mem_item->mapaddr);
        return err;
 }
 
@@ -903,8 +903,8 @@ static void mlxsw_pci_queue_fini(struct mlxsw_pci *mlxsw_pci,
 
        q_ops->fini(mlxsw_pci, q);
        kfree(q->elem_info);
-       pci_free_consistent(mlxsw_pci->pdev, mem_item->size,
-                           mem_item->buf, mem_item->mapaddr);
+       dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
+                         mem_item->buf, mem_item->mapaddr);
 }
 
 static int mlxsw_pci_queue_group_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
@@ -1273,9 +1273,9 @@ static int mlxsw_pci_fw_area_init(struct mlxsw_pci *mlxsw_pci, char *mbox,
                mem_item = &mlxsw_pci->fw_area.items[i];
 
                mem_item->size = MLXSW_PCI_PAGE_SIZE;
-               mem_item->buf = pci_alloc_consistent(mlxsw_pci->pdev,
-                                                    mem_item->size,
-                                                    &mem_item->mapaddr);
+               mem_item->buf = dma_alloc_coherent(&mlxsw_pci->pdev->dev,
+                                                  mem_item->size,
+                                                  &mem_item->mapaddr, GFP_KERNEL);
                if (!mem_item->buf) {
                        err = -ENOMEM;
                        goto err_alloc;
@@ -1304,8 +1304,8 @@ err_alloc:
        for (i--; i >= 0; i--) {
                mem_item = &mlxsw_pci->fw_area.items[i];
 
-               pci_free_consistent(mlxsw_pci->pdev, mem_item->size,
-                                   mem_item->buf, mem_item->mapaddr);
+               dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
+                                 mem_item->buf, mem_item->mapaddr);
        }
        kfree(mlxsw_pci->fw_area.items);
        return err;
@@ -1321,8 +1321,8 @@ static void mlxsw_pci_fw_area_fini(struct mlxsw_pci *mlxsw_pci)
        for (i = 0; i < mlxsw_pci->fw_area.count; i++) {
                mem_item = &mlxsw_pci->fw_area.items[i];
 
-               pci_free_consistent(mlxsw_pci->pdev, mem_item->size,
-                                   mem_item->buf, mem_item->mapaddr);
+               dma_free_coherent(&mlxsw_pci->pdev->dev, mem_item->size,
+                                 mem_item->buf, mem_item->mapaddr);
        }
        kfree(mlxsw_pci->fw_area.items);
 }
@@ -1347,8 +1347,8 @@ static int mlxsw_pci_mbox_alloc(struct mlxsw_pci *mlxsw_pci,
        int err = 0;
 
        mbox->size = MLXSW_CMD_MBOX_SIZE;
-       mbox->buf = pci_alloc_consistent(pdev, MLXSW_CMD_MBOX_SIZE,
-                                        &mbox->mapaddr);
+       mbox->buf = dma_alloc_coherent(&pdev->dev, MLXSW_CMD_MBOX_SIZE,
+                                      &mbox->mapaddr, GFP_KERNEL);
        if (!mbox->buf) {
                dev_err(&pdev->dev, "Failed allocating memory for mailbox\n");
                err = -ENOMEM;
@@ -1362,8 +1362,8 @@ static void mlxsw_pci_mbox_free(struct mlxsw_pci *mlxsw_pci,
 {
        struct pci_dev *pdev = mlxsw_pci->pdev;
 
-       pci_free_consistent(pdev, MLXSW_CMD_MBOX_SIZE, mbox->buf,
-                           mbox->mapaddr);
+       dma_free_coherent(&pdev->dev, MLXSW_CMD_MBOX_SIZE, mbox->buf,
+                         mbox->mapaddr);
 }
 
 static int mlxsw_pci_sys_ready_wait(struct mlxsw_pci *mlxsw_pci,
@@ -1848,17 +1848,11 @@ static int mlxsw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_pci_request_regions;
        }
 
-       err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
-       if (!err) {
-               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
-               if (err) {
-                       dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed\n");
-                       goto err_pci_set_dma_mask;
-               }
-       } else {
-               err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+       err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+       if (err) {
+               err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
                if (err) {
-                       dev_err(&pdev->dev, "pci_set_dma_mask failed\n");
+                       dev_err(&pdev->dev, "dma_set_mask failed\n");
                        goto err_pci_set_dma_mask;
                }
        }
index a6956cf..a3769f9 100644 (file)
@@ -52,7 +52,7 @@
 #define MLXSW_SP_RESOURCE_NAME_COUNTERS_RIF "rif"
 
 enum mlxsw_sp_resource_id {
-       MLXSW_SP_RESOURCE_KVD = 1,
+       MLXSW_SP_RESOURCE_KVD = MLXSW_CORE_RESOURCE_MAX,
        MLXSW_SP_RESOURCE_KVD_LINEAR,
        MLXSW_SP_RESOURCE_KVD_HASH_SINGLE,
        MLXSW_SP_RESOURCE_KVD_HASH_DOUBLE,
index 41424ee..0ac7014 100644 (file)
@@ -4309,11 +4309,18 @@ static int mlxsw_sp_nexthop_obj_validate(struct mlxsw_sp *mlxsw_sp,
        if (event != NEXTHOP_EVENT_REPLACE)
                return 0;
 
-       if (!info->is_grp)
+       switch (info->type) {
+       case NH_NOTIFIER_INFO_TYPE_SINGLE:
                return mlxsw_sp_nexthop_obj_single_validate(mlxsw_sp, info->nh,
                                                            info->extack);
-       return mlxsw_sp_nexthop_obj_group_validate(mlxsw_sp, info->nh_grp,
-                                                  info->extack);
+       case NH_NOTIFIER_INFO_TYPE_GRP:
+               return mlxsw_sp_nexthop_obj_group_validate(mlxsw_sp,
+                                                          info->nh_grp,
+                                                          info->extack);
+       default:
+               NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
+               return -EOPNOTSUPP;
+       }
 }
 
 static bool mlxsw_sp_nexthop_obj_is_gateway(struct mlxsw_sp *mlxsw_sp,
@@ -4321,13 +4328,17 @@ static bool mlxsw_sp_nexthop_obj_is_gateway(struct mlxsw_sp *mlxsw_sp,
 {
        const struct net_device *dev;
 
-       if (info->is_grp)
+       switch (info->type) {
+       case NH_NOTIFIER_INFO_TYPE_SINGLE:
+               dev = info->nh->dev;
+               return info->nh->gw_family || info->nh->is_reject ||
+                      mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);
+       case NH_NOTIFIER_INFO_TYPE_GRP:
                /* Already validated earlier. */
                return true;
-
-       dev = info->nh->dev;
-       return info->nh->gw_family || info->nh->is_reject ||
-              mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL);
+       default:
+               return false;
+       }
 }
 
 static void mlxsw_sp_nexthop_obj_blackhole_init(struct mlxsw_sp *mlxsw_sp,
@@ -4410,11 +4421,22 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp,
                                     struct mlxsw_sp_nexthop_group *nh_grp,
                                     struct nh_notifier_info *info)
 {
-       unsigned int nhs = info->is_grp ? info->nh_grp->num_nh : 1;
        struct mlxsw_sp_nexthop_group_info *nhgi;
        struct mlxsw_sp_nexthop *nh;
+       unsigned int nhs;
        int err, i;
 
+       switch (info->type) {
+       case NH_NOTIFIER_INFO_TYPE_SINGLE:
+               nhs = 1;
+               break;
+       case NH_NOTIFIER_INFO_TYPE_GRP:
+               nhs = info->nh_grp->num_nh;
+               break;
+       default:
+               return -EINVAL;
+       }
+
        nhgi = kzalloc(struct_size(nhgi, nexthops, nhs), GFP_KERNEL);
        if (!nhgi)
                return -ENOMEM;
@@ -4427,12 +4449,18 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp,
                int weight;
 
                nh = &nhgi->nexthops[i];
-               if (info->is_grp) {
-                       nh_obj = &info->nh_grp->nh_entries[i].nh;
-                       weight = info->nh_grp->nh_entries[i].weight;
-               } else {
+               switch (info->type) {
+               case NH_NOTIFIER_INFO_TYPE_SINGLE:
                        nh_obj = info->nh;
                        weight = 1;
+                       break;
+               case NH_NOTIFIER_INFO_TYPE_GRP:
+                       nh_obj = &info->nh_grp->nh_entries[i].nh;
+                       weight = info->nh_grp->nh_entries[i].weight;
+                       break;
+               default:
+                       err = -EINVAL;
+                       goto err_nexthop_obj_init;
                }
                err = mlxsw_sp_nexthop_obj_init(mlxsw_sp, nh_grp, nh, nh_obj,
                                                weight);
index c6c5826..1892cea 100644 (file)
@@ -157,6 +157,7 @@ mlxsw_sp1_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp1_span_entry_ops_cpu = {
+       .is_static = true,
        .can_handle = mlxsw_sp1_span_cpu_can_handle,
        .parms_set = mlxsw_sp1_span_entry_cpu_parms,
        .configure = mlxsw_sp1_span_entry_cpu_configure,
@@ -214,6 +215,7 @@ mlxsw_sp_span_entry_phys_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp_span_entry_ops_phys = {
+       .is_static = true,
        .can_handle = mlxsw_sp_port_dev_check,
        .parms_set = mlxsw_sp_span_entry_phys_parms,
        .configure = mlxsw_sp_span_entry_phys_configure,
@@ -721,6 +723,7 @@ mlxsw_sp2_span_entry_cpu_deconfigure(struct mlxsw_sp_span_entry *span_entry)
 
 static const
 struct mlxsw_sp_span_entry_ops mlxsw_sp2_span_entry_ops_cpu = {
+       .is_static = true,
        .can_handle = mlxsw_sp2_span_cpu_can_handle,
        .parms_set = mlxsw_sp2_span_entry_cpu_parms,
        .configure = mlxsw_sp2_span_entry_cpu_configure,
@@ -1036,6 +1039,9 @@ static void mlxsw_sp_span_respin_work(struct work_struct *work)
                if (!refcount_read(&curr->ref_count))
                        continue;
 
+               if (curr->ops->is_static)
+                       continue;
+
                err = curr->ops->parms_set(mlxsw_sp, curr->to_dev, &sparms);
                if (err)
                        continue;
index d907718..aa1cd40 100644 (file)
@@ -60,6 +60,7 @@ struct mlxsw_sp_span_entry {
 };
 
 struct mlxsw_sp_span_entry_ops {
+       bool is_static;
        bool (*can_handle)(const struct net_device *to_dev);
        int (*parms_set)(struct mlxsw_sp *mlxsw_sp,
                         const struct net_device *to_dev,
index cea42f6..20c4f3c 100644 (file)
@@ -527,7 +527,6 @@ mlxsw_sp_port_bridge_vlan_stp_set(struct mlxsw_sp_port *mlxsw_sp_port,
 }
 
 static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                           struct switchdev_trans *trans,
                                            struct net_device *orig_dev,
                                            u8 state)
 {
@@ -535,9 +534,6 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
        struct mlxsw_sp_bridge_vlan *bridge_vlan;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        /* It's possible we failed to enslave the port, yet this
         * operation is executed due to it being deferred.
         */
@@ -659,7 +655,6 @@ err_port_bridge_vlan_learning_set:
 
 static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port
                                               *mlxsw_sp_port,
-                                              struct switchdev_trans *trans,
                                               unsigned long brport_flags)
 {
        if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
@@ -669,16 +664,12 @@ static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port
 }
 
 static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                          struct switchdev_trans *trans,
                                           struct net_device *orig_dev,
                                           unsigned long brport_flags)
 {
        struct mlxsw_sp_bridge_port *bridge_port;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
                                                orig_dev);
        if (!bridge_port)
@@ -724,35 +715,26 @@ static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
 }
 
 static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                           struct switchdev_trans *trans,
                                            unsigned long ageing_clock_t)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
        u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
 
-       if (switchdev_trans_ph_prepare(trans)) {
-               if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
-                   ageing_time > MLXSW_SP_MAX_AGEING_TIME)
-                       return -ERANGE;
-               else
-                       return 0;
-       }
+       if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
+           ageing_time > MLXSW_SP_MAX_AGEING_TIME)
+               return -ERANGE;
 
        return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
 }
 
 static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                         struct switchdev_trans *trans,
                                          struct net_device *orig_dev,
                                          bool vlan_enabled)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct mlxsw_sp_bridge_device *bridge_device;
 
-       if (!switchdev_trans_ph_prepare(trans))
-               return 0;
-
        bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
        if (WARN_ON(!bridge_device))
                return -EINVAL;
@@ -765,16 +747,12 @@ static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
 }
 
 static int mlxsw_sp_port_attr_br_vlan_proto_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                               struct switchdev_trans *trans,
                                                struct net_device *orig_dev,
                                                u16 vlan_proto)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct mlxsw_sp_bridge_device *bridge_device;
 
-       if (!switchdev_trans_ph_prepare(trans))
-               return 0;
-
        bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, orig_dev);
        if (WARN_ON(!bridge_device))
                return -EINVAL;
@@ -784,16 +762,12 @@ static int mlxsw_sp_port_attr_br_vlan_proto_set(struct mlxsw_sp_port *mlxsw_sp_p
 }
 
 static int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                         struct switchdev_trans *trans,
                                          struct net_device *orig_dev,
                                          bool is_port_mrouter)
 {
        struct mlxsw_sp_bridge_port *bridge_port;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
                                                orig_dev);
        if (!bridge_port)
@@ -825,7 +799,6 @@ static bool mlxsw_sp_mc_flood(const struct mlxsw_sp_bridge_port *bridge_port)
 }
 
 static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                        struct switchdev_trans *trans,
                                         struct net_device *orig_dev,
                                         bool mc_disabled)
 {
@@ -834,9 +807,6 @@ static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
        struct mlxsw_sp_bridge_port *bridge_port;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        /* It's possible we failed to enslave the port, yet this
         * operation is executed due to it being deferred.
         */
@@ -896,16 +866,12 @@ mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp,
 
 static int
 mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
-                                 struct switchdev_trans *trans,
                                  struct net_device *orig_dev,
                                  bool is_mrouter)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct mlxsw_sp_bridge_device *bridge_device;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        /* It's possible we failed to enslave the port, yet this
         * operation is executed due to it being deferred.
         */
@@ -921,54 +887,52 @@ mlxsw_sp_port_attr_br_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port,
 }
 
 static int mlxsw_sp_port_attr_set(struct net_device *dev,
-                                 const struct switchdev_attr *attr,
-                                 struct switchdev_trans *trans)
+                                 const struct switchdev_attr *attr)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
        int err;
 
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-               err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port,
                                                       attr->orig_dev,
                                                       attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
                err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port,
-                                                         trans,
                                                          attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port,
                                                      attr->orig_dev,
                                                      attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
-               err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port,
                                                       attr->u.ageing_time);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
-               err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port,
                                                     attr->orig_dev,
                                                     attr->u.vlan_filtering);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_PROTOCOL:
-               err = mlxsw_sp_port_attr_br_vlan_proto_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_br_vlan_proto_set(mlxsw_sp_port,
                                                           attr->orig_dev,
                                                           attr->u.vlan_protocol);
                break;
        case SWITCHDEV_ATTR_ID_PORT_MROUTER:
-               err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_mrouter_set(mlxsw_sp_port,
                                                     attr->orig_dev,
                                                     attr->u.mrouter);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
-               err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port,
                                                    attr->orig_dev,
                                                    attr->u.mc_disabled);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
-               err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port, trans,
+               err = mlxsw_sp_port_attr_br_mrouter_set(mlxsw_sp_port,
                                                        attr->orig_dev,
                                                        attr->u.mrouter);
                break;
@@ -977,8 +941,7 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
                break;
        }
 
-       if (switchdev_trans_ph_commit(trans))
-               mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
+       mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
 
        return err;
 }
@@ -1211,23 +1174,20 @@ mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
                                const struct switchdev_obj_port_vlan *vlan)
 {
        u16 pvid;
-       u16 vid;
 
        pvid = mlxsw_sp_rif_vid(mlxsw_sp, br_dev);
        if (!pvid)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
-               if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
-                       if (vid != pvid) {
-                               netdev_err(br_dev, "Can't change PVID, it's used by router interface\n");
-                               return -EBUSY;
-                       }
-               } else {
-                       if (vid == pvid) {
-                               netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n");
-                               return -EBUSY;
-                       }
+       if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+               if (vlan->vid != pvid) {
+                       netdev_err(br_dev, "Can't change PVID, it's used by router interface\n");
+                       return -EBUSY;
+               }
+       } else {
+               if (vlan->vid == pvid) {
+                       netdev_err(br_dev, "Can't remove PVID, it's used by router interface\n");
+                       return -EBUSY;
                }
        }
 
@@ -1236,7 +1196,6 @@ mlxsw_sp_br_ban_rif_pvid_change(struct mlxsw_sp *mlxsw_sp,
 
 static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                                   const struct switchdev_obj_port_vlan *vlan,
-                                  struct switchdev_trans *trans,
                                   struct netlink_ext_ack *extack)
 {
        bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
@@ -1244,14 +1203,12 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct net_device *orig_dev = vlan->obj.orig_dev;
        struct mlxsw_sp_bridge_port *bridge_port;
-       u16 vid;
 
        if (netif_is_bridge_master(orig_dev)) {
                int err = 0;
 
                if ((vlan->flags & BRIDGE_VLAN_INFO_BRENTRY) &&
-                   br_vlan_enabled(orig_dev) &&
-                   switchdev_trans_ph_prepare(trans))
+                   br_vlan_enabled(orig_dev))
                        err = mlxsw_sp_br_ban_rif_pvid_change(mlxsw_sp,
                                                              orig_dev, vlan);
                if (!err)
@@ -1259,9 +1216,6 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
                return err;
        }
 
-       if (switchdev_trans_ph_commit(trans))
-               return 0;
-
        bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
        if (WARN_ON(!bridge_port))
                return -EINVAL;
@@ -1269,17 +1223,9 @@ static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
        if (!bridge_port->bridge_device->vlan_enabled)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               int err;
-
-               err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
-                                                   vid, flag_untagged,
-                                                   flag_pvid, extack);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port,
+                                            vlan->vid, flag_untagged,
+                                            flag_pvid, extack);
 }
 
 static enum mlxsw_reg_sfdf_flush_type mlxsw_sp_fdb_flush_type(bool lagged)
@@ -1716,8 +1662,7 @@ static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port,
 }
 
 static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
-                                const struct switchdev_obj_port_mdb *mdb,
-                                struct switchdev_trans *trans)
+                                const struct switchdev_obj_port_mdb *mdb)
 {
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct net_device *orig_dev = mdb->obj.orig_dev;
@@ -1729,9 +1674,6 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
        u16 fid_index;
        int err = 0;
 
-       if (switchdev_trans_ph_commit(trans))
-               return 0;
-
        bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
        if (!bridge_port)
                return 0;
@@ -1813,7 +1755,6 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port,
 
 static int mlxsw_sp_port_obj_add(struct net_device *dev,
                                 const struct switchdev_obj *obj,
-                                struct switchdev_trans *trans,
                                 struct netlink_ext_ack *extack)
 {
        struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
@@ -1823,22 +1764,19 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
-               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans,
-                                             extack);
 
-               if (switchdev_trans_ph_prepare(trans)) {
-                       /* The event is emitted before the changes are actually
-                        * applied to the bridge. Therefore schedule the respin
-                        * call for later, so that the respin logic sees the
-                        * updated bridge state.
-                        */
-                       mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
-               }
+               err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, extack);
+
+               /* The event is emitted before the changes are actually
+                * applied to the bridge. Therefore schedule the respin
+                * call for later, so that the respin logic sees the
+                * updated bridge state.
+                */
+               mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp);
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
                err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
-                                           SWITCHDEV_OBJ_PORT_MDB(obj),
-                                           trans);
+                                           SWITCHDEV_OBJ_PORT_MDB(obj));
                break;
        default:
                err = -EOPNOTSUPP;
@@ -1873,7 +1811,6 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
        struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
        struct net_device *orig_dev = vlan->obj.orig_dev;
        struct mlxsw_sp_bridge_port *bridge_port;
-       u16 vid;
 
        if (netif_is_bridge_master(orig_dev))
                return -EOPNOTSUPP;
@@ -1885,8 +1822,7 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
        if (!bridge_port->bridge_device->vlan_enabled)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
-               mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vid);
+       mlxsw_sp_bridge_port_vlan_del(mlxsw_sp_port, bridge_port, vlan->vid);
 
        return 0;
 }
@@ -3406,12 +3342,10 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
                SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj);
        bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-       struct switchdev_trans *trans = port_obj_info->trans;
        struct mlxsw_sp_bridge_device *bridge_device;
        struct netlink_ext_ack *extack;
        struct mlxsw_sp *mlxsw_sp;
        struct net_device *br_dev;
-       u16 vid;
 
        extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
        br_dev = netdev_master_upper_dev_get(vxlan_dev);
@@ -3424,9 +3358,6 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
 
        port_obj_info->handled = true;
 
-       if (switchdev_trans_ph_commit(trans))
-               return 0;
-
        bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev);
        if (!bridge_device)
                return -EINVAL;
@@ -3434,18 +3365,10 @@ mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev,
        if (!bridge_device->vlan_enabled)
                return 0;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               int err;
-
-               err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
-                                                       vxlan_dev, vid,
-                                                       flag_untagged,
-                                                       flag_pvid, extack);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device,
+                                                vxlan_dev, vlan->vid,
+                                                flag_untagged,
+                                                flag_pvid, extack);
 }
 
 static void
@@ -3458,7 +3381,6 @@ mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev,
        struct mlxsw_sp_bridge_device *bridge_device;
        struct mlxsw_sp *mlxsw_sp;
        struct net_device *br_dev;
-       u16 vid;
 
        br_dev = netdev_master_upper_dev_get(vxlan_dev);
        if (!br_dev)
@@ -3477,9 +3399,8 @@ mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev,
        if (!bridge_device->vlan_enabled)
                return;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
-               mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device,
-                                                 vxlan_dev, vid);
+       mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, vxlan_dev,
+                                         vlan->vid);
 }
 
 static int
index 42bc014..93df304 100644 (file)
@@ -31,6 +31,8 @@ config KS8851
        select MII
        select CRC32
        select EEPROM_93CX6
+       select PHYLIB
+       select MICREL_PHY
        help
          SPI driver for Micrel KS8851 SPI attached network chip.
 
@@ -40,6 +42,8 @@ config KS8851_MLL
        select MII
        select CRC32
        select EEPROM_93CX6
+       select PHYLIB
+       select MICREL_PHY
        help
          This platform driver is for Micrel KS8851 Address/data bus
          multiplexed network chip.
index 2b319e4..e2eb0ca 100644 (file)
@@ -358,6 +358,7 @@ union ks8851_tx_hdr {
  * @vdd_reg:   Optional regulator supplying the chip
  * @vdd_io: Optional digital power supply for IO
  * @gpio: Optional reset_n gpio
+ * @mii_bus: Pointer to MII bus structure
  * @lock: Bus access lock callback
  * @unlock: Bus access unlock callback
  * @rdreg16: 16bit register read callback
@@ -403,6 +404,7 @@ struct ks8851_net {
        struct regulator        *vdd_reg;
        struct regulator        *vdd_io;
        int                     gpio;
+       struct mii_bus          *mii_bus;
 
        void                    (*lock)(struct ks8851_net *ks,
                                        unsigned long *flags);
index 6fc7483..2feed6c 100644 (file)
@@ -8,8 +8,6 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#define DEBUG
-
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -23,6 +21,7 @@
 
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
+#include <linux/of_mdio.h>
 #include <linux/of_net.h>
 
 #include "ks8851.h"
@@ -932,7 +931,25 @@ static int ks8851_phy_reg(int reg)
                return KS_P1ANLPR;
        }
 
-       return 0x0;
+       return -EOPNOTSUPP;
+}
+
+static int ks8851_phy_read_common(struct net_device *dev, int phy_addr, int reg)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       unsigned long flags;
+       int result;
+       int ksreg;
+
+       ksreg = ks8851_phy_reg(reg);
+       if (ksreg < 0)
+               return ksreg;
+
+       ks8851_lock(ks, &flags);
+       result = ks8851_rdreg16(ks, ksreg);
+       ks8851_unlock(ks, &flags);
+
+       return result;
 }
 
 /**
@@ -952,20 +969,13 @@ static int ks8851_phy_reg(int reg)
  */
 static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg)
 {
-       struct ks8851_net *ks = netdev_priv(dev);
-       unsigned long flags;
-       int ksreg;
-       int result;
+       int ret;
 
-       ksreg = ks8851_phy_reg(reg);
-       if (!ksreg)
+       ret = ks8851_phy_read_common(dev, phy_addr, reg);
+       if (ret < 0)
                return 0x0;     /* no error return allowed, so use zero */
 
-       ks8851_lock(ks, &flags);
-       result = ks8851_rdreg16(ks, ksreg);
-       ks8851_unlock(ks, &flags);
-
-       return result;
+       return ret;
 }
 
 static void ks8851_phy_write(struct net_device *dev,
@@ -976,13 +986,37 @@ static void ks8851_phy_write(struct net_device *dev,
        int ksreg;
 
        ksreg = ks8851_phy_reg(reg);
-       if (ksreg) {
+       if (ksreg >= 0) {
                ks8851_lock(ks, &flags);
                ks8851_wrreg16(ks, ksreg, value);
                ks8851_unlock(ks, &flags);
        }
 }
 
+static int ks8851_mdio_read(struct mii_bus *bus, int phy_id, int reg)
+{
+       struct ks8851_net *ks = bus->priv;
+
+       if (phy_id != 0)
+               return -EOPNOTSUPP;
+
+       /* KS8851 PHY ID registers are swapped in HW, swap them back. */
+       if (reg == MII_PHYSID1)
+               reg = MII_PHYSID2;
+       else if (reg == MII_PHYSID2)
+               reg = MII_PHYSID1;
+
+       return ks8851_phy_read_common(ks->netdev, phy_id, reg);
+}
+
+static int ks8851_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
+{
+       struct ks8851_net *ks = bus->priv;
+
+       ks8851_phy_write(ks->netdev, phy_id, reg, val);
+       return 0;
+}
+
 /**
  * ks8851_read_selftest - read the selftest memory info.
  * @ks: The device state
@@ -1046,6 +1080,42 @@ int ks8851_resume(struct device *dev)
 }
 #endif
 
+static int ks8851_register_mdiobus(struct ks8851_net *ks, struct device *dev)
+{
+       struct mii_bus *mii_bus;
+       int ret;
+
+       mii_bus = mdiobus_alloc();
+       if (!mii_bus)
+               return -ENOMEM;
+
+       mii_bus->name = "ks8851_eth_mii";
+       mii_bus->read = ks8851_mdio_read;
+       mii_bus->write = ks8851_mdio_write;
+       mii_bus->priv = ks;
+       mii_bus->parent = dev;
+       mii_bus->phy_mask = ~((u32)BIT(0));
+       snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+
+       ret = mdiobus_register(mii_bus);
+       if (ret)
+               goto err_mdiobus_register;
+
+       ks->mii_bus = mii_bus;
+
+       return 0;
+
+err_mdiobus_register:
+       mdiobus_free(mii_bus);
+       return ret;
+}
+
+static void ks8851_unregister_mdiobus(struct ks8851_net *ks)
+{
+       mdiobus_unregister(ks->mii_bus);
+       mdiobus_free(ks->mii_bus);
+}
+
 int ks8851_probe_common(struct net_device *netdev, struct device *dev,
                        int msg_en)
 {
@@ -1104,6 +1174,8 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
 
        INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work);
 
+       SET_NETDEV_DEV(netdev, dev);
+
        /* setup EEPROM state */
        ks->eeprom.data = ks;
        ks->eeprom.width = PCI_EEPROM_WIDTH_93C46;
@@ -1120,6 +1192,10 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
 
        dev_info(dev, "message enable is %d\n", msg_en);
 
+       ret = ks8851_register_mdiobus(ks, dev);
+       if (ret)
+               goto err_mdio;
+
        /* set the default message enable */
        ks->msg_enable = netif_msg_init(msg_en, NETIF_MSG_DRV |
                                                NETIF_MSG_PROBE |
@@ -1128,7 +1204,6 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
        skb_queue_head_init(&ks->txq);
 
        netdev->ethtool_ops = &ks8851_ethtool_ops;
-       SET_NETDEV_DEV(netdev, dev);
 
        dev_set_drvdata(dev, ks);
 
@@ -1156,7 +1231,7 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
        ret = register_netdev(netdev);
        if (ret) {
                dev_err(dev, "failed to register network device\n");
-               goto err_netdev;
+               goto err_id;
        }
 
        netdev_info(netdev, "revision %d, MAC %pM, IRQ %d, %s EEPROM\n",
@@ -1165,8 +1240,9 @@ int ks8851_probe_common(struct net_device *netdev, struct device *dev,
 
        return 0;
 
-err_netdev:
 err_id:
+       ks8851_unregister_mdiobus(ks);
+err_mdio:
        if (gpio_is_valid(gpio))
                gpio_set_value(gpio, 0);
        regulator_disable(ks->vdd_reg);
@@ -1180,6 +1256,8 @@ int ks8851_remove_common(struct device *dev)
 {
        struct ks8851_net *priv = dev_get_drvdata(dev);
 
+       ks8851_unregister_mdiobus(priv);
+
        if (netif_msg_drv(priv))
                dev_info(dev, "remove\n");
 
index 3bab0cb..2e8fcce 100644 (file)
@@ -8,8 +8,6 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#define DEBUG
-
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
index 4ec7f16..479406e 100644 (file)
@@ -8,8 +8,6 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#define DEBUG
-
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
index 3804310..51359ce 100644 (file)
@@ -1253,7 +1253,7 @@ static void lan743x_tx_release_desc(struct lan743x_tx *tx,
        if (!(buffer_info->flags & TX_BUFFER_INFO_FLAG_ACTIVE))
                goto done;
 
-       descriptor_type = (descriptor->data0) &
+       descriptor_type = le32_to_cpu(descriptor->data0) &
                          TX_DESC_DATA0_DTYPE_MASK_;
        if (descriptor_type == TX_DESC_DATA0_DTYPE_DATA_)
                goto clean_up_data_descriptor;
@@ -1313,7 +1313,7 @@ static int lan743x_tx_next_index(struct lan743x_tx *tx, int index)
 
 static void lan743x_tx_release_completed_descriptors(struct lan743x_tx *tx)
 {
-       while ((*tx->head_cpu_ptr) != (tx->last_head)) {
+       while (le32_to_cpu(*tx->head_cpu_ptr) != (tx->last_head)) {
                lan743x_tx_release_desc(tx, tx->last_head, false);
                tx->last_head = lan743x_tx_next_index(tx, tx->last_head);
        }
@@ -1399,10 +1399,10 @@ static int lan743x_tx_frame_start(struct lan743x_tx *tx,
        if (dma_mapping_error(dev, dma_ptr))
                return -ENOMEM;
 
-       tx_descriptor->data1 = DMA_ADDR_LOW32(dma_ptr);
-       tx_descriptor->data2 = DMA_ADDR_HIGH32(dma_ptr);
-       tx_descriptor->data3 = (frame_length << 16) &
-               TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_;
+       tx_descriptor->data1 = cpu_to_le32(DMA_ADDR_LOW32(dma_ptr));
+       tx_descriptor->data2 = cpu_to_le32(DMA_ADDR_HIGH32(dma_ptr));
+       tx_descriptor->data3 = cpu_to_le32((frame_length << 16) &
+               TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_);
 
        buffer_info->skb = NULL;
        buffer_info->dma_ptr = dma_ptr;
@@ -1443,7 +1443,7 @@ static void lan743x_tx_frame_add_lso(struct lan743x_tx *tx,
                tx->frame_data0 |= TX_DESC_DATA0_IOC_;
        }
        tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
-       tx_descriptor->data0 = tx->frame_data0;
+       tx_descriptor->data0 = cpu_to_le32(tx->frame_data0);
 
        /* move to next descriptor */
        tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail);
@@ -1487,7 +1487,7 @@ static int lan743x_tx_frame_add_fragment(struct lan743x_tx *tx,
 
        /* wrap up previous descriptor */
        tx_descriptor = &tx->ring_cpu_ptr[tx->frame_tail];
-       tx_descriptor->data0 = tx->frame_data0;
+       tx_descriptor->data0 = cpu_to_le32(tx->frame_data0);
 
        /* move to next descriptor */
        tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail);
@@ -1513,10 +1513,10 @@ static int lan743x_tx_frame_add_fragment(struct lan743x_tx *tx,
                return -ENOMEM;
        }
 
-       tx_descriptor->data1 = DMA_ADDR_LOW32(dma_ptr);
-       tx_descriptor->data2 = DMA_ADDR_HIGH32(dma_ptr);
-       tx_descriptor->data3 = (frame_length << 16) &
-                              TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_;
+       tx_descriptor->data1 = cpu_to_le32(DMA_ADDR_LOW32(dma_ptr));
+       tx_descriptor->data2 = cpu_to_le32(DMA_ADDR_HIGH32(dma_ptr));
+       tx_descriptor->data3 = cpu_to_le32((frame_length << 16) &
+                              TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_);
 
        buffer_info->skb = NULL;
        buffer_info->dma_ptr = dma_ptr;
@@ -1560,7 +1560,7 @@ static void lan743x_tx_frame_end(struct lan743x_tx *tx,
        if (ignore_sync)
                buffer_info->flags |= TX_BUFFER_INFO_FLAG_IGNORE_SYNC;
 
-       tx_descriptor->data0 = tx->frame_data0;
+       tx_descriptor->data0 = cpu_to_le32(tx->frame_data0);
        tx->frame_tail = lan743x_tx_next_index(tx, tx->frame_tail);
        tx->last_tail = tx->frame_tail;
 
@@ -1967,11 +1967,11 @@ static int lan743x_rx_init_ring_element(struct lan743x_rx *rx, int index,
        }
 
        buffer_info->buffer_length = length;
-       descriptor->data1 = DMA_ADDR_LOW32(buffer_info->dma_ptr);
-       descriptor->data2 = DMA_ADDR_HIGH32(buffer_info->dma_ptr);
+       descriptor->data1 = cpu_to_le32(DMA_ADDR_LOW32(buffer_info->dma_ptr));
+       descriptor->data2 = cpu_to_le32(DMA_ADDR_HIGH32(buffer_info->dma_ptr));
        descriptor->data3 = 0;
-       descriptor->data0 = (RX_DESC_DATA0_OWN_ |
-                           (length & RX_DESC_DATA0_BUF_LENGTH_MASK_));
+       descriptor->data0 = cpu_to_le32((RX_DESC_DATA0_OWN_ |
+                           (length & RX_DESC_DATA0_BUF_LENGTH_MASK_)));
        skb_reserve(buffer_info->skb, RX_HEAD_PADDING);
        lan743x_rx_update_tail(rx, index);
 
@@ -1986,12 +1986,12 @@ static void lan743x_rx_reuse_ring_element(struct lan743x_rx *rx, int index)
        descriptor = &rx->ring_cpu_ptr[index];
        buffer_info = &rx->buffer_info[index];
 
-       descriptor->data1 = DMA_ADDR_LOW32(buffer_info->dma_ptr);
-       descriptor->data2 = DMA_ADDR_HIGH32(buffer_info->dma_ptr);
+       descriptor->data1 = cpu_to_le32(DMA_ADDR_LOW32(buffer_info->dma_ptr));
+       descriptor->data2 = cpu_to_le32(DMA_ADDR_HIGH32(buffer_info->dma_ptr));
        descriptor->data3 = 0;
-       descriptor->data0 = (RX_DESC_DATA0_OWN_ |
+       descriptor->data0 = cpu_to_le32((RX_DESC_DATA0_OWN_ |
                            ((buffer_info->buffer_length) &
-                           RX_DESC_DATA0_BUF_LENGTH_MASK_));
+                           RX_DESC_DATA0_BUF_LENGTH_MASK_)));
        lan743x_rx_update_tail(rx, index);
 }
 
@@ -2025,7 +2025,7 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
 {
        struct skb_shared_hwtstamps *hwtstamps = NULL;
        int result = RX_PROCESS_RESULT_NOTHING_TO_DO;
-       int current_head_index = *rx->head_cpu_ptr;
+       int current_head_index = le32_to_cpu(*rx->head_cpu_ptr);
        struct lan743x_rx_buffer_info *buffer_info;
        struct lan743x_rx_descriptor *descriptor;
        int extension_index = -1;
@@ -2040,14 +2040,14 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
 
        if (rx->last_head != current_head_index) {
                descriptor = &rx->ring_cpu_ptr[rx->last_head];
-               if (descriptor->data0 & RX_DESC_DATA0_OWN_)
+               if (le32_to_cpu(descriptor->data0) & RX_DESC_DATA0_OWN_)
                        goto done;
 
-               if (!(descriptor->data0 & RX_DESC_DATA0_FS_))
+               if (!(le32_to_cpu(descriptor->data0) & RX_DESC_DATA0_FS_))
                        goto done;
 
                first_index = rx->last_head;
-               if (descriptor->data0 & RX_DESC_DATA0_LS_) {
+               if (le32_to_cpu(descriptor->data0) & RX_DESC_DATA0_LS_) {
                        last_index = rx->last_head;
                } else {
                        int index;
@@ -2055,10 +2055,10 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
                        index = lan743x_rx_next_index(rx, first_index);
                        while (index != current_head_index) {
                                descriptor = &rx->ring_cpu_ptr[index];
-                               if (descriptor->data0 & RX_DESC_DATA0_OWN_)
+                               if (le32_to_cpu(descriptor->data0) & RX_DESC_DATA0_OWN_)
                                        goto done;
 
-                               if (descriptor->data0 & RX_DESC_DATA0_LS_) {
+                               if (le32_to_cpu(descriptor->data0) & RX_DESC_DATA0_LS_) {
                                        last_index = index;
                                        break;
                                }
@@ -2067,17 +2067,17 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
                }
                if (last_index >= 0) {
                        descriptor = &rx->ring_cpu_ptr[last_index];
-                       if (descriptor->data0 & RX_DESC_DATA0_EXT_) {
+                       if (le32_to_cpu(descriptor->data0) & RX_DESC_DATA0_EXT_) {
                                /* extension is expected to follow */
                                int index = lan743x_rx_next_index(rx,
                                                                  last_index);
                                if (index != current_head_index) {
                                        descriptor = &rx->ring_cpu_ptr[index];
-                                       if (descriptor->data0 &
+                                       if (le32_to_cpu(descriptor->data0) &
                                            RX_DESC_DATA0_OWN_) {
                                                goto done;
                                        }
-                                       if (descriptor->data0 &
+                                       if (le32_to_cpu(descriptor->data0) &
                                            RX_DESC_DATA0_EXT_) {
                                                extension_index = index;
                                        } else {
@@ -2129,7 +2129,7 @@ static int lan743x_rx_process_packet(struct lan743x_rx *rx)
                        }
                        buffer_info->skb = NULL;
                        packet_length = RX_DESC_DATA0_FRAME_LENGTH_GET_
-                                       (descriptor->data0);
+                                       (le32_to_cpu(descriptor->data0));
                        skb_put(skb, packet_length - 4);
                        skb->protocol = eth_type_trans(skb,
                                                       rx->adapter->netdev);
@@ -2167,8 +2167,8 @@ process_extension:
                        descriptor = &rx->ring_cpu_ptr[extension_index];
                        buffer_info = &rx->buffer_info[extension_index];
 
-                       ts_sec = descriptor->data1;
-                       ts_nsec = (descriptor->data2 &
+                       ts_sec = le32_to_cpu(descriptor->data1);
+                       ts_nsec = (le32_to_cpu(descriptor->data2) &
                                  RX_DESC_DATA2_TS_NS_MASK_);
                        lan743x_rx_reuse_ring_element(rx, extension_index);
                        real_last_index = extension_index;
index 404af3f..f3f7789 100644 (file)
@@ -661,7 +661,7 @@ struct lan743x_tx {
 
        struct lan743x_tx_buffer_info *buffer_info;
 
-       u32             *head_cpu_ptr;
+       __le32          *head_cpu_ptr;
        dma_addr_t      head_dma_ptr;
        int             last_head;
        int             last_tail;
@@ -691,7 +691,7 @@ struct lan743x_rx {
 
        struct lan743x_rx_buffer_info *buffer_info;
 
-       u32             *head_cpu_ptr;
+       __le32          *head_cpu_ptr;
        dma_addr_t      head_dma_ptr;
        u32             last_head;
        u32             last_tail;
@@ -775,10 +775,10 @@ struct lan743x_adapter {
 #define TX_DESC_DATA3_FRAME_LENGTH_MSS_MASK_   (0x3FFF0000)
 
 struct lan743x_tx_descriptor {
-       u32     data0;
-       u32     data1;
-       u32     data2;
-       u32     data3;
+       __le32     data0;
+       __le32     data1;
+       __le32     data2;
+       __le32     data3;
 } __aligned(DEFAULT_DMA_DESCRIPTOR_SPACING);
 
 #define TX_BUFFER_INFO_FLAG_ACTIVE             BIT(0)
@@ -813,10 +813,10 @@ struct lan743x_tx_buffer_info {
 #define RX_HEAD_PADDING                NET_IP_ALIGN
 
 struct lan743x_rx_descriptor {
-       u32     data0;
-       u32     data1;
-       u32     data2;
-       u32     data3;
+       __le32     data0;
+       __le32     data1;
+       __le32     data2;
+       __le32     data3;
 } __aligned(DEFAULT_DMA_DESCRIPTOR_SPACING);
 
 #define RX_BUFFER_INFO_FLAG_ACTIVE      BIT(0)
index 58f94c3..346bba2 100644 (file)
@@ -6,7 +6,8 @@ mscc_ocelot_switch_lib-y := \
        ocelot_police.o \
        ocelot_vcap.o \
        ocelot_flower.o \
-       ocelot_ptp.o
+       ocelot_ptp.o \
+       ocelot_devlink.o
 obj-$(CONFIG_MSCC_OCELOT_SWITCH) += mscc_ocelot.o
 mscc_ocelot-y := \
        ocelot_vsc7514.o \
index 0b9992b..5b2c0ce 100644 (file)
@@ -60,14 +60,27 @@ int ocelot_mact_learn(struct ocelot *ocelot, int port,
                      const unsigned char mac[ETH_ALEN],
                      unsigned int vid, enum macaccess_entry_type type)
 {
+       u32 cmd = ANA_TABLES_MACACCESS_VALID |
+               ANA_TABLES_MACACCESS_DEST_IDX(port) |
+               ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
+               ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN);
+       unsigned int mc_ports;
+
+       /* Set MAC_CPU_COPY if the CPU port is used by a multicast entry */
+       if (type == ENTRYTYPE_MACv4)
+               mc_ports = (mac[1] << 8) | mac[2];
+       else if (type == ENTRYTYPE_MACv6)
+               mc_ports = (mac[0] << 8) | mac[1];
+       else
+               mc_ports = 0;
+
+       if (mc_ports & BIT(ocelot->num_phys_ports))
+               cmd |= ANA_TABLES_MACACCESS_MAC_CPU_COPY;
+
        ocelot_mact_select(ocelot, mac, vid);
 
        /* Issue a write command */
-       ocelot_write(ocelot, ANA_TABLES_MACACCESS_VALID |
-                            ANA_TABLES_MACACCESS_DEST_IDX(port) |
-                            ANA_TABLES_MACACCESS_ENTRYTYPE(type) |
-                            ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN),
-                            ANA_TABLES_MACACCESS);
+       ocelot_write(ocelot, cmd, ANA_TABLES_MACACCESS);
 
        return ocelot_mact_wait_for_completion(ocelot);
 }
@@ -208,25 +221,20 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
 }
 
 int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
-                              bool vlan_aware, struct switchdev_trans *trans)
+                              bool vlan_aware)
 {
+       struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1];
        struct ocelot_port *ocelot_port = ocelot->ports[port];
+       struct ocelot_vcap_filter *filter;
        u32 val;
 
-       if (switchdev_trans_ph_prepare(trans)) {
-               struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1];
-               struct ocelot_vcap_filter *filter;
-
-               list_for_each_entry(filter, &block->rules, list) {
-                       if (filter->ingress_port_mask & BIT(port) &&
-                           filter->action.vid_replace_ena) {
-                               dev_err(ocelot->dev,
-                                       "Cannot change VLAN state with vlan modify rules active\n");
-                               return -EBUSY;
-                       }
+       list_for_each_entry(filter, &block->rules, list) {
+               if (filter->ingress_port_mask & BIT(port) &&
+                   filter->action.vid_replace_ena) {
+                       dev_err(ocelot->dev,
+                               "Cannot change VLAN state with vlan modify rules active\n");
+                       return -EBUSY;
                }
-
-               return 0;
        }
 
        ocelot_port->vlan_aware = vlan_aware;
@@ -1179,7 +1187,6 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
                             struct net_device *bridge)
 {
        struct ocelot_vlan pvid = {0}, native_vlan = {0};
-       struct switchdev_trans trans;
        int ret;
 
        ocelot->bridge_mask &= ~BIT(port);
@@ -1187,13 +1194,7 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
        if (!ocelot->bridge_mask)
                ocelot->hw_bridge_dev = NULL;
 
-       trans.ph_prepare = true;
-       ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans);
-       if (ret)
-               return ret;
-
-       trans.ph_prepare = false;
-       ret = ocelot_port_vlan_filtering(ocelot, port, false, &trans);
+       ret = ocelot_port_vlan_filtering(ocelot, port, false);
        if (ret)
                return ret;
 
@@ -1366,7 +1367,7 @@ void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu)
                            pause_stop);
 
        /* Tail dropping watermarks */
-       atop_tot = (ocelot->shared_queue_sz - 9 * maxlen) /
+       atop_tot = (ocelot->packet_buffer_size - 9 * maxlen) /
                   OCELOT_BUFFER_CELL_SZ;
        atop = (9 * maxlen) / OCELOT_BUFFER_CELL_SZ;
        ocelot_write_rix(ocelot, ocelot->ops->wm_enc(atop), SYS_ATOP, port);
@@ -1479,6 +1480,21 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot)
                         ANA_PORT_VLAN_CFG, cpu);
 }
 
+static void ocelot_detect_features(struct ocelot *ocelot)
+{
+       int mmgt, eq_ctrl;
+
+       /* For Ocelot, Felix, Seville, Serval etc, SYS:MMGT:MMGT:FREECNT holds
+        * the number of 240-byte free memory words (aka 4-cell chunks) and not
+        * 192 bytes as the documentation incorrectly says.
+        */
+       mmgt = ocelot_read(ocelot, SYS_MMGT);
+       ocelot->packet_buffer_size = 240 * SYS_MMGT_FREECNT(mmgt);
+
+       eq_ctrl = ocelot_read(ocelot, QSYS_EQ_CTRL);
+       ocelot->num_frame_refs = QSYS_MMGT_EQ_CTRL_FP_FREE_CNT(eq_ctrl);
+}
+
 int ocelot_init(struct ocelot *ocelot)
 {
        char queue_name[32];
@@ -1521,6 +1537,7 @@ int ocelot_init(struct ocelot *ocelot)
 
        INIT_LIST_HEAD(&ocelot->multicast);
        INIT_LIST_HEAD(&ocelot->pgids);
+       ocelot_detect_features(ocelot);
        ocelot_mact_init(ocelot);
        ocelot_vlan_init(ocelot);
        ocelot_vcap_init(ocelot);
index 291d39d..e8621db 100644 (file)
@@ -121,13 +121,15 @@ void ocelot_port_writel(struct ocelot_port *port, u32 val, u32 reg);
 
 int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,
                      struct phy_device *phy);
-
-void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu,
-                        enum ocelot_tag_prefix injection,
-                        enum ocelot_tag_prefix extraction);
+int ocelot_devlink_init(struct ocelot *ocelot);
+void ocelot_devlink_teardown(struct ocelot *ocelot);
+int ocelot_port_devlink_init(struct ocelot *ocelot, int port,
+                            enum devlink_port_flavour flavour);
+void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port);
 
 extern struct notifier_block ocelot_netdevice_nb;
 extern struct notifier_block ocelot_switchdev_nb;
 extern struct notifier_block ocelot_switchdev_blocking_nb;
+extern const struct devlink_ops ocelot_devlink_ops;
 
 #endif
diff --git a/drivers/net/ethernet/mscc/ocelot_devlink.c b/drivers/net/ethernet/mscc/ocelot_devlink.c
new file mode 100644 (file)
index 0000000..edafbd3
--- /dev/null
@@ -0,0 +1,885 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/* Copyright 2020-2021 NXP Semiconductors
+ */
+#include <net/devlink.h>
+#include "ocelot.h"
+
+/* The queue system tracks four resource consumptions:
+ * Resource 0: Memory tracked per source port
+ * Resource 1: Frame references tracked per source port
+ * Resource 2: Memory tracked per destination port
+ * Resource 3: Frame references tracked per destination port
+ */
+#define OCELOT_RESOURCE_SZ             256
+#define OCELOT_NUM_RESOURCES           4
+
+#define BUF_xxxx_I                     (0 * OCELOT_RESOURCE_SZ)
+#define REF_xxxx_I                     (1 * OCELOT_RESOURCE_SZ)
+#define BUF_xxxx_E                     (2 * OCELOT_RESOURCE_SZ)
+#define REF_xxxx_E                     (3 * OCELOT_RESOURCE_SZ)
+
+/* For each resource type there are 4 types of watermarks:
+ * Q_RSRV: reservation per QoS class per port
+ * PRIO_SHR: sharing watermark per QoS class across all ports
+ * P_RSRV: reservation per port
+ * COL_SHR: sharing watermark per color (drop precedence) across all ports
+ */
+#define xxx_Q_RSRV_x                   0
+#define xxx_PRIO_SHR_x                 216
+#define xxx_P_RSRV_x                   224
+#define xxx_COL_SHR_x                  254
+
+/* Reservation Watermarks
+ * ----------------------
+ *
+ * For setting up the reserved areas, egress watermarks exist per port and per
+ * QoS class for both ingress and egress.
+ */
+
+/*  Amount of packet buffer
+ *  |  per QoS class
+ *  |  |  reserved
+ *  |  |  |   per egress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * BUF_Q_RSRV_E
+ */
+#define BUF_Q_RSRV_E(port, prio) \
+       (BUF_xxxx_E + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio))
+
+/*  Amount of packet buffer
+ *  |  for all port's traffic classes
+ *  |  |  reserved
+ *  |  |  |   per egress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * BUF_P_RSRV_E
+ */
+#define BUF_P_RSRV_E(port) \
+       (BUF_xxxx_E + xxx_P_RSRV_x + (port))
+
+/*  Amount of packet buffer
+ *  |  per QoS class
+ *  |  |  reserved
+ *  |  |  |   per ingress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * BUF_Q_RSRV_I
+ */
+#define BUF_Q_RSRV_I(port, prio) \
+       (BUF_xxxx_I + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio))
+
+/*  Amount of packet buffer
+ *  |  for all port's traffic classes
+ *  |  |  reserved
+ *  |  |  |   per ingress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * BUF_P_RSRV_I
+ */
+#define BUF_P_RSRV_I(port) \
+       (BUF_xxxx_I + xxx_P_RSRV_x + (port))
+
+/*  Amount of frame references
+ *  |  per QoS class
+ *  |  |  reserved
+ *  |  |  |   per egress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * REF_Q_RSRV_E
+ */
+#define REF_Q_RSRV_E(port, prio) \
+       (REF_xxxx_E + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio))
+
+/*  Amount of frame references
+ *  |  for all port's traffic classes
+ *  |  |  reserved
+ *  |  |  |   per egress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * REF_P_RSRV_E
+ */
+#define REF_P_RSRV_E(port) \
+       (REF_xxxx_E + xxx_P_RSRV_x + (port))
+
+/*  Amount of frame references
+ *  |  per QoS class
+ *  |  |  reserved
+ *  |  |  |   per ingress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * REF_Q_RSRV_I
+ */
+#define REF_Q_RSRV_I(port, prio) \
+       (REF_xxxx_I + xxx_Q_RSRV_x + OCELOT_NUM_TC * (port) + (prio))
+
+/*  Amount of frame references
+ *  |  for all port's traffic classes
+ *  |  |  reserved
+ *  |  |  |   per ingress port
+ *  |  |  |   |
+ *  V  V  v   v
+ * REF_P_RSRV_I
+ */
+#define REF_P_RSRV_I(port) \
+       (REF_xxxx_I + xxx_P_RSRV_x + (port))
+
+/* Sharing Watermarks
+ * ------------------
+ *
+ * The shared memory area is shared between all ports.
+ */
+
+/* Amount of buffer
+ *  |   per QoS class
+ *  |   |    from the shared memory area
+ *  |   |    |  for egress traffic
+ *  |   |    |  |
+ *  V   V    v  v
+ * BUF_PRIO_SHR_E
+ */
+#define BUF_PRIO_SHR_E(prio) \
+       (BUF_xxxx_E + xxx_PRIO_SHR_x + (prio))
+
+/* Amount of buffer
+ *  |   per color (drop precedence level)
+ *  |   |   from the shared memory area
+ *  |   |   |  for egress traffic
+ *  |   |   |  |
+ *  V   V   v  v
+ * BUF_COL_SHR_E
+ */
+#define BUF_COL_SHR_E(dp) \
+       (BUF_xxxx_E + xxx_COL_SHR_x + (1 - (dp)))
+
+/* Amount of buffer
+ *  |   per QoS class
+ *  |   |    from the shared memory area
+ *  |   |    |  for ingress traffic
+ *  |   |    |  |
+ *  V   V    v  v
+ * BUF_PRIO_SHR_I
+ */
+#define BUF_PRIO_SHR_I(prio) \
+       (BUF_xxxx_I + xxx_PRIO_SHR_x + (prio))
+
+/* Amount of buffer
+ *  |   per color (drop precedence level)
+ *  |   |   from the shared memory area
+ *  |   |   |  for ingress traffic
+ *  |   |   |  |
+ *  V   V   v  v
+ * BUF_COL_SHR_I
+ */
+#define BUF_COL_SHR_I(dp) \
+       (BUF_xxxx_I + xxx_COL_SHR_x + (1 - (dp)))
+
+/* Amount of frame references
+ *  |   per QoS class
+ *  |   |    from the shared area
+ *  |   |    |  for egress traffic
+ *  |   |    |  |
+ *  V   V    v  v
+ * REF_PRIO_SHR_E
+ */
+#define REF_PRIO_SHR_E(prio) \
+       (REF_xxxx_E + xxx_PRIO_SHR_x + (prio))
+
+/* Amount of frame references
+ *  |   per color (drop precedence level)
+ *  |   |   from the shared area
+ *  |   |   |  for egress traffic
+ *  |   |   |  |
+ *  V   V   v  v
+ * REF_COL_SHR_E
+ */
+#define REF_COL_SHR_E(dp) \
+       (REF_xxxx_E + xxx_COL_SHR_x + (1 - (dp)))
+
+/* Amount of frame references
+ *  |   per QoS class
+ *  |   |    from the shared area
+ *  |   |    |  for ingress traffic
+ *  |   |    |  |
+ *  V   V    v  v
+ * REF_PRIO_SHR_I
+ */
+#define REF_PRIO_SHR_I(prio) \
+       (REF_xxxx_I + xxx_PRIO_SHR_x + (prio))
+
+/* Amount of frame references
+ *  |   per color (drop precedence level)
+ *  |   |   from the shared area
+ *  |   |   |  for ingress traffic
+ *  |   |   |  |
+ *  V   V   v  v
+ * REF_COL_SHR_I
+ */
+#define REF_COL_SHR_I(dp) \
+       (REF_xxxx_I + xxx_COL_SHR_x + (1 - (dp)))
+
+static u32 ocelot_wm_read(struct ocelot *ocelot, int index)
+{
+       int wm = ocelot_read_gix(ocelot, QSYS_RES_CFG, index);
+
+       return ocelot->ops->wm_dec(wm);
+}
+
+static void ocelot_wm_write(struct ocelot *ocelot, int index, u32 val)
+{
+       u32 wm = ocelot->ops->wm_enc(val);
+
+       ocelot_write_gix(ocelot, wm, QSYS_RES_CFG, index);
+}
+
+static void ocelot_wm_status(struct ocelot *ocelot, int index, u32 *inuse,
+                            u32 *maxuse)
+{
+       int res_stat = ocelot_read_gix(ocelot, QSYS_RES_STAT, index);
+
+       return ocelot->ops->wm_stat(res_stat, inuse, maxuse);
+}
+
+/* The hardware comes out of reset with strange defaults: the sum of all
+ * reservations for frame memory is larger than the total buffer size.
+ * One has to wonder how can the reservation watermarks still guarantee
+ * anything under congestion.
+ * Bring some sense into the hardware by changing the defaults to disable all
+ * reservations and rely only on the sharing watermark for frames with drop
+ * precedence 0. The user can still explicitly request reservations per port
+ * and per port-tc through devlink-sb.
+ */
+static void ocelot_disable_reservation_watermarks(struct ocelot *ocelot,
+                                                 int port)
+{
+       int prio;
+
+       for (prio = 0; prio < OCELOT_NUM_TC; prio++) {
+               ocelot_wm_write(ocelot, BUF_Q_RSRV_I(port, prio), 0);
+               ocelot_wm_write(ocelot, BUF_Q_RSRV_E(port, prio), 0);
+               ocelot_wm_write(ocelot, REF_Q_RSRV_I(port, prio), 0);
+               ocelot_wm_write(ocelot, REF_Q_RSRV_E(port, prio), 0);
+       }
+
+       ocelot_wm_write(ocelot, BUF_P_RSRV_I(port), 0);
+       ocelot_wm_write(ocelot, BUF_P_RSRV_E(port), 0);
+       ocelot_wm_write(ocelot, REF_P_RSRV_I(port), 0);
+       ocelot_wm_write(ocelot, REF_P_RSRV_E(port), 0);
+}
+
+/* We want the sharing watermarks to consume all nonreserved resources, for
+ * efficient resource utilization (a single traffic flow should be able to use
+ * up the entire buffer space and frame resources as long as there's no
+ * interference).
+ * The switch has 10 sharing watermarks per lookup: 8 per traffic class and 2
+ * per color (drop precedence).
+ * The trouble with configuring these sharing watermarks is that:
+ * (1) There's a risk that we overcommit the resources if we configure
+ *     (a) all 8 per-TC sharing watermarks to the max
+ *     (b) all 2 per-color sharing watermarks to the max
+ * (2) There's a risk that we undercommit the resources if we configure
+ *     (a) all 8 per-TC sharing watermarks to "max / 8"
+ *     (b) all 2 per-color sharing watermarks to "max / 2"
+ * So for Linux, let's just disable the sharing watermarks per traffic class
+ * (setting them to 0 will make them always exceeded), and rely only on the
+ * sharing watermark for drop priority 0. So frames with drop priority set to 1
+ * by QoS classification or policing will still be allowed, but only as long as
+ * the port and port-TC reservations are not exceeded.
+ */
+static void ocelot_disable_tc_sharing_watermarks(struct ocelot *ocelot)
+{
+       int prio;
+
+       for (prio = 0; prio < OCELOT_NUM_TC; prio++) {
+               ocelot_wm_write(ocelot, BUF_PRIO_SHR_I(prio), 0);
+               ocelot_wm_write(ocelot, BUF_PRIO_SHR_E(prio), 0);
+               ocelot_wm_write(ocelot, REF_PRIO_SHR_I(prio), 0);
+               ocelot_wm_write(ocelot, REF_PRIO_SHR_E(prio), 0);
+       }
+}
+
+static void ocelot_get_buf_rsrv(struct ocelot *ocelot, u32 *buf_rsrv_i,
+                               u32 *buf_rsrv_e)
+{
+       int port, prio;
+
+       *buf_rsrv_i = 0;
+       *buf_rsrv_e = 0;
+
+       for (port = 0; port <= ocelot->num_phys_ports; port++) {
+               for (prio = 0; prio < OCELOT_NUM_TC; prio++) {
+                       *buf_rsrv_i += ocelot_wm_read(ocelot,
+                                                     BUF_Q_RSRV_I(port, prio));
+                       *buf_rsrv_e += ocelot_wm_read(ocelot,
+                                                     BUF_Q_RSRV_E(port, prio));
+               }
+
+               *buf_rsrv_i += ocelot_wm_read(ocelot, BUF_P_RSRV_I(port));
+               *buf_rsrv_e += ocelot_wm_read(ocelot, BUF_P_RSRV_E(port));
+       }
+
+       *buf_rsrv_i *= OCELOT_BUFFER_CELL_SZ;
+       *buf_rsrv_e *= OCELOT_BUFFER_CELL_SZ;
+}
+
+static void ocelot_get_ref_rsrv(struct ocelot *ocelot, u32 *ref_rsrv_i,
+                               u32 *ref_rsrv_e)
+{
+       int port, prio;
+
+       *ref_rsrv_i = 0;
+       *ref_rsrv_e = 0;
+
+       for (port = 0; port <= ocelot->num_phys_ports; port++) {
+               for (prio = 0; prio < OCELOT_NUM_TC; prio++) {
+                       *ref_rsrv_i += ocelot_wm_read(ocelot,
+                                                     REF_Q_RSRV_I(port, prio));
+                       *ref_rsrv_e += ocelot_wm_read(ocelot,
+                                                     REF_Q_RSRV_E(port, prio));
+               }
+
+               *ref_rsrv_i += ocelot_wm_read(ocelot, REF_P_RSRV_I(port));
+               *ref_rsrv_e += ocelot_wm_read(ocelot, REF_P_RSRV_E(port));
+       }
+}
+
+/* Calculate all reservations, then set up the sharing watermark for DP=0 to
+ * consume the remaining resources up to the pool's configured size.
+ */
+static void ocelot_setup_sharing_watermarks(struct ocelot *ocelot)
+{
+       u32 buf_rsrv_i, buf_rsrv_e;
+       u32 ref_rsrv_i, ref_rsrv_e;
+       u32 buf_shr_i, buf_shr_e;
+       u32 ref_shr_i, ref_shr_e;
+
+       ocelot_get_buf_rsrv(ocelot, &buf_rsrv_i, &buf_rsrv_e);
+       ocelot_get_ref_rsrv(ocelot, &ref_rsrv_i, &ref_rsrv_e);
+
+       buf_shr_i = ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_ING] -
+                   buf_rsrv_i;
+       buf_shr_e = ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_EGR] -
+                   buf_rsrv_e;
+       ref_shr_i = ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_ING] -
+                   ref_rsrv_i;
+       ref_shr_e = ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_EGR] -
+                   ref_rsrv_e;
+
+       buf_shr_i /= OCELOT_BUFFER_CELL_SZ;
+       buf_shr_e /= OCELOT_BUFFER_CELL_SZ;
+
+       ocelot_wm_write(ocelot, BUF_COL_SHR_I(0), buf_shr_i);
+       ocelot_wm_write(ocelot, BUF_COL_SHR_E(0), buf_shr_e);
+       ocelot_wm_write(ocelot, REF_COL_SHR_E(0), ref_shr_e);
+       ocelot_wm_write(ocelot, REF_COL_SHR_I(0), ref_shr_i);
+       ocelot_wm_write(ocelot, BUF_COL_SHR_I(1), 0);
+       ocelot_wm_write(ocelot, BUF_COL_SHR_E(1), 0);
+       ocelot_wm_write(ocelot, REF_COL_SHR_E(1), 0);
+       ocelot_wm_write(ocelot, REF_COL_SHR_I(1), 0);
+}
+
+/* Ensure that all reservations can be enforced */
+static int ocelot_watermark_validate(struct ocelot *ocelot,
+                                    struct netlink_ext_ack *extack)
+{
+       u32 buf_rsrv_i, buf_rsrv_e;
+       u32 ref_rsrv_i, ref_rsrv_e;
+
+       ocelot_get_buf_rsrv(ocelot, &buf_rsrv_i, &buf_rsrv_e);
+       ocelot_get_ref_rsrv(ocelot, &ref_rsrv_i, &ref_rsrv_e);
+
+       if (buf_rsrv_i > ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_ING]) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Ingress frame reservations exceed pool size");
+               return -ERANGE;
+       }
+       if (buf_rsrv_e > ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_EGR]) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Egress frame reservations exceed pool size");
+               return -ERANGE;
+       }
+       if (ref_rsrv_i > ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_ING]) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Ingress reference reservations exceed pool size");
+               return -ERANGE;
+       }
+       if (ref_rsrv_e > ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_EGR]) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Egress reference reservations exceed pool size");
+               return -ERANGE;
+       }
+
+       return 0;
+}
+
+/* The hardware works like this:
+ *
+ *                         Frame forwarding decision taken
+ *                                       |
+ *                                       v
+ *       +--------------------+--------------------+--------------------+
+ *       |                    |                    |                    |
+ *       v                    v                    v                    v
+ * Ingress memory       Egress memory        Ingress frame        Egress frame
+ *     check                check           reference check      reference check
+ *       |                    |                    |                    |
+ *       v                    v                    v                    v
+ *  BUF_Q_RSRV_I   ok    BUF_Q_RSRV_E   ok    REF_Q_RSRV_I   ok     REF_Q_RSRV_E   ok
+ *(src port, prio) -+  (dst port, prio) -+  (src port, prio) -+   (dst port, prio) -+
+ *       |          |         |          |         |          |         |           |
+ *       |exceeded  |         |exceeded  |         |exceeded  |         |exceeded   |
+ *       v          |         v          |         v          |         v           |
+ *  BUF_P_RSRV_I  ok|    BUF_P_RSRV_E  ok|    REF_P_RSRV_I  ok|    REF_P_RSRV_E   ok|
+ *   (src port) ----+     (dst port) ----+     (src port) ----+     (dst port) -----+
+ *       |          |         |          |         |          |         |           |
+ *       |exceeded  |         |exceeded  |         |exceeded  |         |exceeded   |
+ *       v          |         v          |         v          |         v           |
+ * BUF_PRIO_SHR_I ok|   BUF_PRIO_SHR_E ok|   REF_PRIO_SHR_I ok|   REF_PRIO_SHR_E  ok|
+ *     (prio) ------+       (prio) ------+       (prio) ------+       (prio) -------+
+ *       |          |         |          |         |          |         |           |
+ *       |exceeded  |         |exceeded  |         |exceeded  |         |exceeded   |
+ *       v          |         v          |         v          |         v           |
+ * BUF_COL_SHR_I  ok|   BUF_COL_SHR_E  ok|   REF_COL_SHR_I  ok|   REF_COL_SHR_E   ok|
+ *      (dp) -------+        (dp) -------+        (dp) -------+        (dp) --------+
+ *       |          |         |          |         |          |         |           |
+ *       |exceeded  |         |exceeded  |         |exceeded  |         |exceeded   |
+ *       v          v         v          v         v          v         v           v
+ *      fail     success     fail     success     fail     success     fail      success
+ *       |          |         |          |         |          |         |           |
+ *       v          v         v          v         v          v         v           v
+ *       +-----+----+         +-----+----+         +-----+----+         +-----+-----+
+ *             |                    |                    |                    |
+ *             +-------> OR <-------+                    +-------> OR <-------+
+ *                        |                                        |
+ *                        v                                        v
+ *                        +----------------> AND <-----------------+
+ *                                            |
+ *                                            v
+ *                                    FIFO drop / accept
+ *
+ * We are modeling each of the 4 parallel lookups as a devlink-sb pool.
+ * At least one (ingress or egress) memory pool and one (ingress or egress)
+ * frame reference pool need to have resources for frame acceptance to succeed.
+ *
+ * The following watermarks are controlled explicitly through devlink-sb:
+ * BUF_Q_RSRV_I, BUF_Q_RSRV_E, REF_Q_RSRV_I, REF_Q_RSRV_E
+ * BUF_P_RSRV_I, BUF_P_RSRV_E, REF_P_RSRV_I, REF_P_RSRV_E
+ * The following watermarks are controlled implicitly through devlink-sb:
+ * BUF_COL_SHR_I, BUF_COL_SHR_E, REF_COL_SHR_I, REF_COL_SHR_E
+ * The following watermarks are unused and disabled:
+ * BUF_PRIO_SHR_I, BUF_PRIO_SHR_E, REF_PRIO_SHR_I, REF_PRIO_SHR_E
+ *
+ * This function overrides the hardware defaults with more sane ones (no
+ * reservations by default, let sharing use all resources) and disables the
+ * unused watermarks.
+ */
+static void ocelot_watermark_init(struct ocelot *ocelot)
+{
+       int all_tcs = GENMASK(OCELOT_NUM_TC - 1, 0);
+       int port;
+
+       ocelot_write(ocelot, all_tcs, QSYS_RES_QOS_MODE);
+
+       for (port = 0; port <= ocelot->num_phys_ports; port++)
+               ocelot_disable_reservation_watermarks(ocelot, port);
+
+       ocelot_disable_tc_sharing_watermarks(ocelot);
+       ocelot_setup_sharing_watermarks(ocelot);
+}
+
+/* Pool size and type are fixed up at runtime. Keeping this structure to
+ * look up the cell size multipliers.
+ */
+static const struct devlink_sb_pool_info ocelot_sb_pool[] = {
+       [OCELOT_SB_BUF] = {
+               .cell_size = OCELOT_BUFFER_CELL_SZ,
+               .threshold_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC,
+       },
+       [OCELOT_SB_REF] = {
+               .cell_size = 1,
+               .threshold_type = DEVLINK_SB_THRESHOLD_TYPE_STATIC,
+       },
+};
+
+/* Returns the pool size configured through ocelot_sb_pool_set */
+int ocelot_sb_pool_get(struct ocelot *ocelot, unsigned int sb_index,
+                      u16 pool_index,
+                      struct devlink_sb_pool_info *pool_info)
+{
+       if (sb_index >= OCELOT_SB_NUM)
+               return -ENODEV;
+       if (pool_index >= OCELOT_SB_POOL_NUM)
+               return -ENODEV;
+
+       *pool_info = ocelot_sb_pool[sb_index];
+       pool_info->size = ocelot->pool_size[sb_index][pool_index];
+       if (pool_index)
+               pool_info->pool_type = DEVLINK_SB_POOL_TYPE_INGRESS;
+       else
+               pool_info->pool_type = DEVLINK_SB_POOL_TYPE_EGRESS;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_pool_get);
+
+/* The pool size received here configures the total amount of resources used on
+ * ingress (or on egress, depending upon the pool index). The pool size, minus
+ * the values for the port and port-tc reservations, is written into the
+ * COL_SHR(dp=0) sharing watermark.
+ */
+int ocelot_sb_pool_set(struct ocelot *ocelot, unsigned int sb_index,
+                      u16 pool_index, u32 size,
+                      enum devlink_sb_threshold_type threshold_type,
+                      struct netlink_ext_ack *extack)
+{
+       u32 old_pool_size;
+       int err;
+
+       if (sb_index >= OCELOT_SB_NUM) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Invalid sb, use 0 for buffers and 1 for frame references");
+               return -ENODEV;
+       }
+       if (pool_index >= OCELOT_SB_POOL_NUM) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Invalid pool, use 0 for ingress and 1 for egress");
+               return -ENODEV;
+       }
+       if (threshold_type != DEVLINK_SB_THRESHOLD_TYPE_STATIC) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Only static threshold supported");
+               return -EOPNOTSUPP;
+       }
+
+       old_pool_size = ocelot->pool_size[sb_index][pool_index];
+       ocelot->pool_size[sb_index][pool_index] = size;
+
+       err = ocelot_watermark_validate(ocelot, extack);
+       if (err) {
+               ocelot->pool_size[sb_index][pool_index] = old_pool_size;
+               return err;
+       }
+
+       ocelot_setup_sharing_watermarks(ocelot);
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_pool_set);
+
+/* This retrieves the configuration made with ocelot_sb_port_pool_set */
+int ocelot_sb_port_pool_get(struct ocelot *ocelot, int port,
+                           unsigned int sb_index, u16 pool_index,
+                           u32 *p_threshold)
+{
+       int wm_index;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               if (pool_index == OCELOT_SB_POOL_ING)
+                       wm_index = BUF_P_RSRV_I(port);
+               else
+                       wm_index = BUF_P_RSRV_E(port);
+               break;
+       case OCELOT_SB_REF:
+               if (pool_index == OCELOT_SB_POOL_ING)
+                       wm_index = REF_P_RSRV_I(port);
+               else
+                       wm_index = REF_P_RSRV_E(port);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       *p_threshold = ocelot_wm_read(ocelot, wm_index);
+       *p_threshold *= ocelot_sb_pool[sb_index].cell_size;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_port_pool_get);
+
+/* This configures the P_RSRV per-port reserved resource watermark */
+int ocelot_sb_port_pool_set(struct ocelot *ocelot, int port,
+                           unsigned int sb_index, u16 pool_index,
+                           u32 threshold, struct netlink_ext_ack *extack)
+{
+       int wm_index, err;
+       u32 old_thr;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               if (pool_index == OCELOT_SB_POOL_ING)
+                       wm_index = BUF_P_RSRV_I(port);
+               else
+                       wm_index = BUF_P_RSRV_E(port);
+               break;
+       case OCELOT_SB_REF:
+               if (pool_index == OCELOT_SB_POOL_ING)
+                       wm_index = REF_P_RSRV_I(port);
+               else
+                       wm_index = REF_P_RSRV_E(port);
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(extack, "Invalid shared buffer");
+               return -ENODEV;
+       }
+
+       threshold /= ocelot_sb_pool[sb_index].cell_size;
+
+       old_thr = ocelot_wm_read(ocelot, wm_index);
+       ocelot_wm_write(ocelot, wm_index, threshold);
+
+       err = ocelot_watermark_validate(ocelot, extack);
+       if (err) {
+               ocelot_wm_write(ocelot, wm_index, old_thr);
+               return err;
+       }
+
+       ocelot_setup_sharing_watermarks(ocelot);
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_port_pool_set);
+
+/* This retrieves the configuration done by ocelot_sb_tc_pool_bind_set */
+int ocelot_sb_tc_pool_bind_get(struct ocelot *ocelot, int port,
+                              unsigned int sb_index, u16 tc_index,
+                              enum devlink_sb_pool_type pool_type,
+                              u16 *p_pool_index, u32 *p_threshold)
+{
+       int wm_index;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+                       wm_index = BUF_Q_RSRV_I(port, tc_index);
+               else
+                       wm_index = BUF_Q_RSRV_E(port, tc_index);
+               break;
+       case OCELOT_SB_REF:
+               if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+                       wm_index = REF_Q_RSRV_I(port, tc_index);
+               else
+                       wm_index = REF_Q_RSRV_E(port, tc_index);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       *p_threshold = ocelot_wm_read(ocelot, wm_index);
+       *p_threshold *= ocelot_sb_pool[sb_index].cell_size;
+
+       if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+               *p_pool_index = 0;
+       else
+               *p_pool_index = 1;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_tc_pool_bind_get);
+
+/* This configures the Q_RSRV per-port-tc reserved resource watermark */
+int ocelot_sb_tc_pool_bind_set(struct ocelot *ocelot, int port,
+                              unsigned int sb_index, u16 tc_index,
+                              enum devlink_sb_pool_type pool_type,
+                              u16 pool_index, u32 threshold,
+                              struct netlink_ext_ack *extack)
+{
+       int wm_index, err;
+       u32 old_thr;
+
+       /* Paranoid check? */
+       if (pool_index == OCELOT_SB_POOL_ING &&
+           pool_type != DEVLINK_SB_POOL_TYPE_INGRESS)
+               return -EINVAL;
+       if (pool_index == OCELOT_SB_POOL_EGR &&
+           pool_type != DEVLINK_SB_POOL_TYPE_EGRESS)
+               return -EINVAL;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+                       wm_index = BUF_Q_RSRV_I(port, tc_index);
+               else
+                       wm_index = BUF_Q_RSRV_E(port, tc_index);
+               break;
+       case OCELOT_SB_REF:
+               if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+                       wm_index = REF_Q_RSRV_I(port, tc_index);
+               else
+                       wm_index = REF_Q_RSRV_E(port, tc_index);
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(extack, "Invalid shared buffer");
+               return -ENODEV;
+       }
+
+       threshold /= ocelot_sb_pool[sb_index].cell_size;
+
+       old_thr = ocelot_wm_read(ocelot, wm_index);
+       ocelot_wm_write(ocelot, wm_index, threshold);
+       err = ocelot_watermark_validate(ocelot, extack);
+       if (err) {
+               ocelot_wm_write(ocelot, wm_index, old_thr);
+               return err;
+       }
+
+       ocelot_setup_sharing_watermarks(ocelot);
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_tc_pool_bind_set);
+
+/* The hardware does not support atomic snapshots, we'll read out the
+ * occupancy registers individually and have this as just a stub.
+ */
+int ocelot_sb_occ_snapshot(struct ocelot *ocelot, unsigned int sb_index)
+{
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_occ_snapshot);
+
+/* The watermark occupancy registers are cleared upon read,
+ * so let's read them.
+ */
+int ocelot_sb_occ_max_clear(struct ocelot *ocelot, unsigned int sb_index)
+{
+       u32 inuse, maxuse;
+       int port, prio;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               for (port = 0; port <= ocelot->num_phys_ports; port++) {
+                       for (prio = 0; prio < OCELOT_NUM_TC; prio++) {
+                               ocelot_wm_status(ocelot, BUF_Q_RSRV_I(port, prio),
+                                                &inuse, &maxuse);
+                               ocelot_wm_status(ocelot, BUF_Q_RSRV_E(port, prio),
+                                                &inuse, &maxuse);
+                       }
+                       ocelot_wm_status(ocelot, BUF_P_RSRV_I(port),
+                                        &inuse, &maxuse);
+                       ocelot_wm_status(ocelot, BUF_P_RSRV_E(port),
+                                        &inuse, &maxuse);
+               }
+               break;
+       case OCELOT_SB_REF:
+               for (port = 0; port <= ocelot->num_phys_ports; port++) {
+                       for (prio = 0; prio < OCELOT_NUM_TC; prio++) {
+                               ocelot_wm_status(ocelot, REF_Q_RSRV_I(port, prio),
+                                                &inuse, &maxuse);
+                               ocelot_wm_status(ocelot, REF_Q_RSRV_E(port, prio),
+                                                &inuse, &maxuse);
+                       }
+                       ocelot_wm_status(ocelot, REF_P_RSRV_I(port),
+                                        &inuse, &maxuse);
+                       ocelot_wm_status(ocelot, REF_P_RSRV_E(port),
+                                        &inuse, &maxuse);
+               }
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_occ_max_clear);
+
+/* This retrieves the watermark occupancy for per-port P_RSRV watermarks */
+int ocelot_sb_occ_port_pool_get(struct ocelot *ocelot, int port,
+                               unsigned int sb_index, u16 pool_index,
+                               u32 *p_cur, u32 *p_max)
+{
+       int wm_index;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               if (pool_index == OCELOT_SB_POOL_ING)
+                       wm_index = BUF_P_RSRV_I(port);
+               else
+                       wm_index = BUF_P_RSRV_E(port);
+               break;
+       case OCELOT_SB_REF:
+               if (pool_index == OCELOT_SB_POOL_ING)
+                       wm_index = REF_P_RSRV_I(port);
+               else
+                       wm_index = REF_P_RSRV_E(port);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       ocelot_wm_status(ocelot, wm_index, p_cur, p_max);
+       *p_cur *= ocelot_sb_pool[sb_index].cell_size;
+       *p_max *= ocelot_sb_pool[sb_index].cell_size;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_occ_port_pool_get);
+
+/* This retrieves the watermark occupancy for per-port-tc Q_RSRV watermarks */
+int ocelot_sb_occ_tc_port_bind_get(struct ocelot *ocelot, int port,
+                                  unsigned int sb_index, u16 tc_index,
+                                  enum devlink_sb_pool_type pool_type,
+                                  u32 *p_cur, u32 *p_max)
+{
+       int wm_index;
+
+       switch (sb_index) {
+       case OCELOT_SB_BUF:
+               if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+                       wm_index = BUF_Q_RSRV_I(port, tc_index);
+               else
+                       wm_index = BUF_Q_RSRV_E(port, tc_index);
+               break;
+       case OCELOT_SB_REF:
+               if (pool_type == DEVLINK_SB_POOL_TYPE_INGRESS)
+                       wm_index = REF_Q_RSRV_I(port, tc_index);
+               else
+                       wm_index = REF_Q_RSRV_E(port, tc_index);
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       ocelot_wm_status(ocelot, wm_index, p_cur, p_max);
+       *p_cur *= ocelot_sb_pool[sb_index].cell_size;
+       *p_max *= ocelot_sb_pool[sb_index].cell_size;
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_sb_occ_tc_port_bind_get);
+
+int ocelot_devlink_sb_register(struct ocelot *ocelot)
+{
+       int err;
+
+       err = devlink_sb_register(ocelot->devlink, OCELOT_SB_BUF,
+                                 ocelot->packet_buffer_size, 1, 1,
+                                 OCELOT_NUM_TC, OCELOT_NUM_TC);
+       if (err)
+               return err;
+
+       err = devlink_sb_register(ocelot->devlink, OCELOT_SB_REF,
+                                 ocelot->num_frame_refs, 1, 1,
+                                 OCELOT_NUM_TC, OCELOT_NUM_TC);
+       if (err) {
+               devlink_sb_unregister(ocelot->devlink, OCELOT_SB_BUF);
+               return err;
+       }
+
+       ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_ING] = ocelot->packet_buffer_size;
+       ocelot->pool_size[OCELOT_SB_BUF][OCELOT_SB_POOL_EGR] = ocelot->packet_buffer_size;
+       ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_ING] = ocelot->num_frame_refs;
+       ocelot->pool_size[OCELOT_SB_REF][OCELOT_SB_POOL_EGR] = ocelot->num_frame_refs;
+
+       ocelot_watermark_init(ocelot);
+
+       return 0;
+}
+EXPORT_SYMBOL(ocelot_devlink_sb_register);
+
+void ocelot_devlink_sb_unregister(struct ocelot *ocelot)
+{
+       devlink_sb_unregister(ocelot->devlink, OCELOT_SB_BUF);
+       devlink_sb_unregister(ocelot->devlink, OCELOT_SB_REF);
+}
+EXPORT_SYMBOL(ocelot_devlink_sb_unregister);
index 2bd2840..9553eb3 100644 (file)
 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
 /* Microsemi Ocelot Switch driver
+ *
+ * This contains glue logic between the switchdev driver operations and the
+ * mscc_ocelot_switch_lib.
  *
  * Copyright (c) 2017, 2019 Microsemi Corporation
+ * Copyright 2020-2021 NXP Semiconductors
  */
 
 #include <linux/if_bridge.h>
 #include "ocelot.h"
 #include "ocelot_vcap.h"
 
+static struct ocelot *devlink_port_to_ocelot(struct devlink_port *dlp)
+{
+       return devlink_priv(dlp->devlink);
+}
+
+static int devlink_port_to_port(struct devlink_port *dlp)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+
+       return dlp - ocelot->devlink_ports;
+}
+
+static int ocelot_devlink_sb_pool_get(struct devlink *dl,
+                                     unsigned int sb_index, u16 pool_index,
+                                     struct devlink_sb_pool_info *pool_info)
+{
+       struct ocelot *ocelot = devlink_priv(dl);
+
+       return ocelot_sb_pool_get(ocelot, sb_index, pool_index, pool_info);
+}
+
+static int ocelot_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index,
+                                     u16 pool_index, u32 size,
+                                     enum devlink_sb_threshold_type threshold_type,
+                                     struct netlink_ext_ack *extack)
+{
+       struct ocelot *ocelot = devlink_priv(dl);
+
+       return ocelot_sb_pool_set(ocelot, sb_index, pool_index, size,
+                                 threshold_type, extack);
+}
+
+static int ocelot_devlink_sb_port_pool_get(struct devlink_port *dlp,
+                                          unsigned int sb_index, u16 pool_index,
+                                          u32 *p_threshold)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+       int port = devlink_port_to_port(dlp);
+
+       return ocelot_sb_port_pool_get(ocelot, port, sb_index, pool_index,
+                                      p_threshold);
+}
+
+static int ocelot_devlink_sb_port_pool_set(struct devlink_port *dlp,
+                                          unsigned int sb_index, u16 pool_index,
+                                          u32 threshold,
+                                          struct netlink_ext_ack *extack)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+       int port = devlink_port_to_port(dlp);
+
+       return ocelot_sb_port_pool_set(ocelot, port, sb_index, pool_index,
+                                      threshold, extack);
+}
+
+static int
+ocelot_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp,
+                                  unsigned int sb_index, u16 tc_index,
+                                  enum devlink_sb_pool_type pool_type,
+                                  u16 *p_pool_index, u32 *p_threshold)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+       int port = devlink_port_to_port(dlp);
+
+       return ocelot_sb_tc_pool_bind_get(ocelot, port, sb_index, tc_index,
+                                         pool_type, p_pool_index,
+                                         p_threshold);
+}
+
+static int
+ocelot_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp,
+                                  unsigned int sb_index, u16 tc_index,
+                                  enum devlink_sb_pool_type pool_type,
+                                  u16 pool_index, u32 threshold,
+                                  struct netlink_ext_ack *extack)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+       int port = devlink_port_to_port(dlp);
+
+       return ocelot_sb_tc_pool_bind_set(ocelot, port, sb_index, tc_index,
+                                         pool_type, pool_index, threshold,
+                                         extack);
+}
+
+static int ocelot_devlink_sb_occ_snapshot(struct devlink *dl,
+                                         unsigned int sb_index)
+{
+       struct ocelot *ocelot = devlink_priv(dl);
+
+       return ocelot_sb_occ_snapshot(ocelot, sb_index);
+}
+
+static int ocelot_devlink_sb_occ_max_clear(struct devlink *dl,
+                                          unsigned int sb_index)
+{
+       struct ocelot *ocelot = devlink_priv(dl);
+
+       return ocelot_sb_occ_max_clear(ocelot, sb_index);
+}
+
+static int ocelot_devlink_sb_occ_port_pool_get(struct devlink_port *dlp,
+                                              unsigned int sb_index,
+                                              u16 pool_index, u32 *p_cur,
+                                              u32 *p_max)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+       int port = devlink_port_to_port(dlp);
+
+       return ocelot_sb_occ_port_pool_get(ocelot, port, sb_index, pool_index,
+                                          p_cur, p_max);
+}
+
+static int
+ocelot_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp,
+                                      unsigned int sb_index, u16 tc_index,
+                                      enum devlink_sb_pool_type pool_type,
+                                      u32 *p_cur, u32 *p_max)
+{
+       struct ocelot *ocelot = devlink_port_to_ocelot(dlp);
+       int port = devlink_port_to_port(dlp);
+
+       return ocelot_sb_occ_tc_port_bind_get(ocelot, port, sb_index,
+                                             tc_index, pool_type,
+                                             p_cur, p_max);
+}
+
+const struct devlink_ops ocelot_devlink_ops = {
+       .sb_pool_get                    = ocelot_devlink_sb_pool_get,
+       .sb_pool_set                    = ocelot_devlink_sb_pool_set,
+       .sb_port_pool_get               = ocelot_devlink_sb_port_pool_get,
+       .sb_port_pool_set               = ocelot_devlink_sb_port_pool_set,
+       .sb_tc_pool_bind_get            = ocelot_devlink_sb_tc_pool_bind_get,
+       .sb_tc_pool_bind_set            = ocelot_devlink_sb_tc_pool_bind_set,
+       .sb_occ_snapshot                = ocelot_devlink_sb_occ_snapshot,
+       .sb_occ_max_clear               = ocelot_devlink_sb_occ_max_clear,
+       .sb_occ_port_pool_get           = ocelot_devlink_sb_occ_port_pool_get,
+       .sb_occ_tc_port_bind_get        = ocelot_devlink_sb_occ_tc_port_bind_get,
+};
+
+int ocelot_port_devlink_init(struct ocelot *ocelot, int port,
+                            enum devlink_port_flavour flavour)
+{
+       struct devlink_port *dlp = &ocelot->devlink_ports[port];
+       int id_len = sizeof(ocelot->base_mac);
+       struct devlink *dl = ocelot->devlink;
+       struct devlink_port_attrs attrs = {};
+
+       memcpy(attrs.switch_id.id, &ocelot->base_mac, id_len);
+       attrs.switch_id.id_len = id_len;
+       attrs.phys.port_number = port;
+       attrs.flavour = flavour;
+
+       devlink_port_attrs_set(dlp, &attrs);
+
+       return devlink_port_register(dl, dlp, port);
+}
+
+void ocelot_port_devlink_teardown(struct ocelot *ocelot, int port)
+{
+       struct devlink_port *dlp = &ocelot->devlink_ports[port];
+
+       devlink_port_unregister(dlp);
+}
+
+static struct devlink_port *ocelot_get_devlink_port(struct net_device *dev)
+{
+       struct ocelot_port_private *priv = netdev_priv(dev);
+       struct ocelot *ocelot = priv->port.ocelot;
+       int port = priv->chip_port;
+
+       return &ocelot->devlink_ports[port];
+}
+
 int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv,
                               struct flow_cls_offload *f,
                               bool ingress)
@@ -457,7 +634,7 @@ static void ocelot_mact_work(struct work_struct *work)
                break;
        default:
                break;
-       };
+       }
 
        kfree(w);
 }
@@ -525,20 +702,6 @@ static void ocelot_set_rx_mode(struct net_device *dev)
        __dev_mc_sync(dev, ocelot_mc_sync, ocelot_mc_unsync);
 }
 
-static int ocelot_port_get_phys_port_name(struct net_device *dev,
-                                         char *buf, size_t len)
-{
-       struct ocelot_port_private *priv = netdev_priv(dev);
-       int port = priv->chip_port;
-       int ret;
-
-       ret = snprintf(buf, len, "p%d", port);
-       if (ret >= len)
-               return -EINVAL;
-
-       return 0;
-}
-
 static int ocelot_port_set_mac_address(struct net_device *dev, void *p)
 {
        struct ocelot_port_private *priv = netdev_priv(dev);
@@ -689,18 +852,6 @@ static int ocelot_set_features(struct net_device *dev,
        return 0;
 }
 
-static int ocelot_get_port_parent_id(struct net_device *dev,
-                                    struct netdev_phys_item_id *ppid)
-{
-       struct ocelot_port_private *priv = netdev_priv(dev);
-       struct ocelot *ocelot = priv->port.ocelot;
-
-       ppid->id_len = sizeof(ocelot->base_mac);
-       memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len);
-
-       return 0;
-}
-
 static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
        struct ocelot_port_private *priv = netdev_priv(dev);
@@ -727,7 +878,6 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
        .ndo_stop                       = ocelot_port_stop,
        .ndo_start_xmit                 = ocelot_port_xmit,
        .ndo_set_rx_mode                = ocelot_set_rx_mode,
-       .ndo_get_phys_port_name         = ocelot_port_get_phys_port_name,
        .ndo_set_mac_address            = ocelot_port_set_mac_address,
        .ndo_get_stats64                = ocelot_get_stats64,
        .ndo_fdb_add                    = ocelot_port_fdb_add,
@@ -736,9 +886,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = {
        .ndo_vlan_rx_add_vid            = ocelot_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid           = ocelot_vlan_rx_kill_vid,
        .ndo_set_features               = ocelot_set_features,
-       .ndo_get_port_parent_id         = ocelot_get_port_parent_id,
        .ndo_setup_tc                   = ocelot_setup_tc,
        .ndo_do_ioctl                   = ocelot_ioctl,
+       .ndo_get_devlink_port           = ocelot_get_devlink_port,
 };
 
 struct net_device *ocelot_port_to_netdev(struct ocelot *ocelot, int port)
@@ -825,12 +975,8 @@ static const struct ethtool_ops ocelot_ethtool_ops = {
 };
 
 static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port,
-                                          struct switchdev_trans *trans,
                                           u8 state)
 {
-       if (switchdev_trans_ph_prepare(trans))
-               return;
-
        ocelot_bridge_stp_state_set(ocelot, port, state);
 }
 
@@ -858,8 +1004,7 @@ static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc)
 }
 
 static int ocelot_port_attr_set(struct net_device *dev,
-                               const struct switchdev_attr *attr,
-                               struct switchdev_trans *trans)
+                               const struct switchdev_attr *attr)
 {
        struct ocelot_port_private *priv = netdev_priv(dev);
        struct ocelot *ocelot = priv->port.ocelot;
@@ -868,15 +1013,13 @@ static int ocelot_port_attr_set(struct net_device *dev,
 
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-               ocelot_port_attr_stp_state_set(ocelot, port, trans,
-                                              attr->u.stp_state);
+               ocelot_port_attr_stp_state_set(ocelot, port, attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
                ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
-               ocelot_port_vlan_filtering(ocelot, port,
-                                          attr->u.vlan_filtering, trans);
+               ocelot_port_vlan_filtering(ocelot, port, attr->u.vlan_filtering);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
                ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
@@ -890,56 +1033,27 @@ static int ocelot_port_attr_set(struct net_device *dev,
 }
 
 static int ocelot_port_obj_add_vlan(struct net_device *dev,
-                                   const struct switchdev_obj_port_vlan *vlan,
-                                   struct switchdev_trans *trans)
-{
-       int ret;
-       u16 vid;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-               bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
-
-               if (switchdev_trans_ph_prepare(trans))
-                       ret = ocelot_vlan_vid_prepare(dev, vid, pvid,
-                                                     untagged);
-               else
-                       ret = ocelot_vlan_vid_add(dev, vid, pvid, untagged);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static int ocelot_port_vlan_del_vlan(struct net_device *dev,
-                                    const struct switchdev_obj_port_vlan *vlan)
+                                   const struct switchdev_obj_port_vlan *vlan)
 {
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
        int ret;
-       u16 vid;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               ret = ocelot_vlan_vid_del(dev, vid);
 
-               if (ret)
-                       return ret;
-       }
+       ret = ocelot_vlan_vid_prepare(dev, vlan->vid, pvid, untagged);
+       if (ret)
+               return ret;
 
-       return 0;
+       return ocelot_vlan_vid_add(dev, vlan->vid, pvid, untagged);
 }
 
 static int ocelot_port_obj_add_mdb(struct net_device *dev,
-                                  const struct switchdev_obj_port_mdb *mdb,
-                                  struct switchdev_trans *trans)
+                                  const struct switchdev_obj_port_mdb *mdb)
 {
        struct ocelot_port_private *priv = netdev_priv(dev);
        struct ocelot_port *ocelot_port = &priv->port;
        struct ocelot *ocelot = ocelot_port->ocelot;
        int port = priv->chip_port;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        return ocelot_port_mdb_add(ocelot, port, mdb);
 }
 
@@ -956,7 +1070,6 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev,
 
 static int ocelot_port_obj_add(struct net_device *dev,
                               const struct switchdev_obj *obj,
-                              struct switchdev_trans *trans,
                               struct netlink_ext_ack *extack)
 {
        int ret = 0;
@@ -964,12 +1077,10 @@ static int ocelot_port_obj_add(struct net_device *dev,
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                ret = ocelot_port_obj_add_vlan(dev,
-                                              SWITCHDEV_OBJ_PORT_VLAN(obj),
-                                              trans);
+                                              SWITCHDEV_OBJ_PORT_VLAN(obj));
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
-               ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj),
-                                             trans);
+               ret = ocelot_port_obj_add_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
                break;
        default:
                return -EOPNOTSUPP;
@@ -985,8 +1096,8 @@ static int ocelot_port_obj_del(struct net_device *dev,
 
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
-               ret = ocelot_port_vlan_del_vlan(dev,
-                                               SWITCHDEV_OBJ_PORT_VLAN(obj));
+               ret = ocelot_vlan_vid_del(dev,
+                                         SWITCHDEV_OBJ_PORT_VLAN(obj)->vid);
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
                ret = ocelot_port_obj_del_mdb(dev, SWITCHDEV_OBJ_PORT_MDB(obj));
@@ -1042,10 +1153,8 @@ static int ocelot_netdevice_event(struct notifier_block *unused,
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        int ret = 0;
 
-       if (!ocelot_netdevice_dev_check(dev))
-               return 0;
-
        if (event == NETDEV_PRECHANGEUPPER &&
+           ocelot_netdevice_dev_check(dev) &&
            netif_is_lag_master(info->upper_dev)) {
                struct netdev_lag_upper_info *lag_upper_info = info->upper_info;
                struct netlink_ext_ack *extack;
index 9cf2bc5..30a38df 100644 (file)
@@ -517,7 +517,6 @@ static int ocelot_chip_init(struct ocelot *ocelot, const struct ocelot_ops *ops)
        ocelot->map = ocelot_regmap;
        ocelot->stats_layout = ocelot_stats_layout;
        ocelot->num_stats = ARRAY_SIZE(ocelot_stats_layout);
-       ocelot->shared_queue_sz = 224 * 1024;
        ocelot->num_mact_rows = 1024;
        ocelot->ops = ops;
 
@@ -764,9 +763,25 @@ static u16 ocelot_wm_enc(u16 value)
        return value;
 }
 
+static u16 ocelot_wm_dec(u16 wm)
+{
+       if (wm & BIT(8))
+               return (wm & GENMASK(7, 0)) * 16;
+
+       return wm;
+}
+
+static void ocelot_wm_stat(u32 val, u32 *inuse, u32 *maxuse)
+{
+       *inuse = (val & GENMASK(23, 12)) >> 12;
+       *maxuse = val & GENMASK(11, 0);
+}
+
 static const struct ocelot_ops ocelot_ops = {
        .reset                  = ocelot_reset,
        .wm_enc                 = ocelot_wm_enc,
+       .wm_dec                 = ocelot_wm_dec,
+       .wm_stat                = ocelot_wm_stat,
        .port_to_netdev         = ocelot_port_to_netdev,
        .netdev_to_port         = ocelot_netdev_to_port,
 };
@@ -1036,6 +1051,14 @@ static struct ptp_clock_info ocelot_ptp_clock_info = {
        .enable         = ocelot_ptp_enable,
 };
 
+static void mscc_ocelot_teardown_devlink_ports(struct ocelot *ocelot)
+{
+       int port;
+
+       for (port = 0; port < ocelot->num_phys_ports; port++)
+               ocelot_port_devlink_teardown(ocelot, port);
+}
+
 static void mscc_ocelot_release_ports(struct ocelot *ocelot)
 {
        int port;
@@ -1063,28 +1086,44 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
 {
        struct ocelot *ocelot = platform_get_drvdata(pdev);
        struct device_node *portnp;
-       int err;
+       bool *registered_ports;
+       int port, err;
+       u32 reg;
 
        ocelot->ports = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports,
                                     sizeof(struct ocelot_port *), GFP_KERNEL);
        if (!ocelot->ports)
                return -ENOMEM;
 
+       ocelot->devlink_ports = devm_kcalloc(ocelot->dev,
+                                            ocelot->num_phys_ports,
+                                            sizeof(*ocelot->devlink_ports),
+                                            GFP_KERNEL);
+       if (!ocelot->devlink_ports)
+               return -ENOMEM;
+
+       registered_ports = kcalloc(ocelot->num_phys_ports, sizeof(bool),
+                                  GFP_KERNEL);
+       if (!registered_ports)
+               return -ENOMEM;
+
        for_each_available_child_of_node(ports, portnp) {
                struct ocelot_port_private *priv;
                struct ocelot_port *ocelot_port;
                struct device_node *phy_node;
+               struct devlink_port *dlp;
                phy_interface_t phy_mode;
                struct phy_device *phy;
                struct regmap *target;
                struct resource *res;
                struct phy *serdes;
                char res_name[8];
-               u32 port;
 
-               if (of_property_read_u32(portnp, "reg", &port))
+               if (of_property_read_u32(portnp, "reg", &reg))
                        continue;
 
+               port = reg;
+
                snprintf(res_name, sizeof(res_name), "port%d", port);
 
                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
@@ -1102,15 +1141,26 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
                if (!phy)
                        continue;
 
+               err = ocelot_port_devlink_init(ocelot, port,
+                                              DEVLINK_PORT_FLAVOUR_PHYSICAL);
+               if (err) {
+                       of_node_put(portnp);
+                       goto out_teardown;
+               }
+
                err = ocelot_probe_port(ocelot, port, target, phy);
                if (err) {
                        of_node_put(portnp);
-                       return err;
+                       goto out_teardown;
                }
 
+               registered_ports[port] = true;
+
                ocelot_port = ocelot->ports[port];
                priv = container_of(ocelot_port, struct ocelot_port_private,
                                    port);
+               dlp = &ocelot->devlink_ports[port];
+               devlink_port_type_eth_set(dlp, priv->dev);
 
                of_get_phy_mode(portnp, &phy_mode);
 
@@ -1135,7 +1185,8 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
                                "invalid phy mode for port%d, (Q)SGMII only\n",
                                port);
                        of_node_put(portnp);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto out_teardown;
                }
 
                serdes = devm_of_phy_get(ocelot->dev, portnp, NULL);
@@ -1149,13 +1200,46 @@ static int mscc_ocelot_init_ports(struct platform_device *pdev,
                                        port);
 
                        of_node_put(portnp);
-                       return err;
+                       goto out_teardown;
                }
 
                priv->serdes = serdes;
        }
 
+       /* Initialize unused devlink ports at the end */
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               if (registered_ports[port])
+                       continue;
+
+               err = ocelot_port_devlink_init(ocelot, port,
+                                              DEVLINK_PORT_FLAVOUR_UNUSED);
+               if (err) {
+                       while (port-- >= 0) {
+                               if (!registered_ports[port])
+                                       continue;
+                               ocelot_port_devlink_teardown(ocelot, port);
+                       }
+
+                       goto out_teardown;
+               }
+       }
+
+       kfree(registered_ports);
+
        return 0;
+
+out_teardown:
+       /* Unregister the network interfaces */
+       mscc_ocelot_release_ports(ocelot);
+       /* Tear down devlink ports for the registered network interfaces */
+       for (port = 0; port < ocelot->num_phys_ports; port++) {
+               if (!registered_ports[port])
+                       continue;
+
+               ocelot_port_devlink_teardown(ocelot, port);
+       }
+       kfree(registered_ports);
+       return err;
 }
 
 static int mscc_ocelot_probe(struct platform_device *pdev)
@@ -1163,6 +1247,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        int err, irq_xtr, irq_ptp_rdy;
        struct device_node *ports;
+       struct devlink *devlink;
        struct ocelot *ocelot;
        struct regmap *hsio;
        unsigned int i;
@@ -1186,10 +1271,12 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        if (!np && !pdev->dev.platform_data)
                return -ENODEV;
 
-       ocelot = devm_kzalloc(&pdev->dev, sizeof(*ocelot), GFP_KERNEL);
-       if (!ocelot)
+       devlink = devlink_alloc(&ocelot_devlink_ops, sizeof(*ocelot));
+       if (!devlink)
                return -ENOMEM;
 
+       ocelot = devlink_priv(devlink);
+       ocelot->devlink = priv_to_devlink(ocelot);
        platform_set_drvdata(pdev, ocelot);
        ocelot->dev = &pdev->dev;
 
@@ -1206,7 +1293,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
                                ocelot->targets[io_target[i].id] = NULL;
                                continue;
                        }
-                       return PTR_ERR(target);
+                       err = PTR_ERR(target);
+                       goto out_free_devlink;
                }
 
                ocelot->targets[io_target[i].id] = target;
@@ -1215,24 +1303,25 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        hsio = syscon_regmap_lookup_by_compatible("mscc,ocelot-hsio");
        if (IS_ERR(hsio)) {
                dev_err(&pdev->dev, "missing hsio syscon\n");
-               return PTR_ERR(hsio);
+               err = PTR_ERR(hsio);
+               goto out_free_devlink;
        }
 
        ocelot->targets[HSIO] = hsio;
 
        err = ocelot_chip_init(ocelot, &ocelot_ops);
        if (err)
-               return err;
+               goto out_free_devlink;
 
        irq_xtr = platform_get_irq_byname(pdev, "xtr");
        if (irq_xtr < 0)
-               return -ENODEV;
+               goto out_free_devlink;
 
        err = devm_request_threaded_irq(&pdev->dev, irq_xtr, NULL,
                                        ocelot_xtr_irq_handler, IRQF_ONESHOT,
                                        "frame extraction", ocelot);
        if (err)
-               return err;
+               goto out_free_devlink;
 
        irq_ptp_rdy = platform_get_irq_byname(pdev, "ptp_rdy");
        if (irq_ptp_rdy > 0 && ocelot->targets[PTP]) {
@@ -1241,7 +1330,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
                                                IRQF_ONESHOT, "ptp ready",
                                                ocelot);
                if (err)
-                       return err;
+                       goto out_free_devlink;
 
                /* Both the PTP interrupt and the PTP bank are available */
                ocelot->ptp = 1;
@@ -1250,7 +1339,8 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        ports = of_get_child_by_name(np, "ethernet-ports");
        if (!ports) {
                dev_err(ocelot->dev, "no ethernet-ports child node found\n");
-               return -ENODEV;
+               err = -ENODEV;
+               goto out_free_devlink;
        }
 
        ocelot->num_phys_ports = of_get_child_count(ports);
@@ -1265,10 +1355,18 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
        if (err)
                goto out_put_ports;
 
-       err = mscc_ocelot_init_ports(pdev, ports);
+       err = devlink_register(devlink, ocelot->dev);
        if (err)
                goto out_ocelot_deinit;
 
+       err = mscc_ocelot_init_ports(pdev, ports);
+       if (err)
+               goto out_ocelot_devlink_unregister;
+
+       err = ocelot_devlink_sb_register(ocelot);
+       if (err)
+               goto out_ocelot_release_ports;
+
        if (ocelot->ptp) {
                err = ocelot_init_timestamp(ocelot, &ocelot_ptp_clock_info);
                if (err) {
@@ -1288,10 +1386,17 @@ static int mscc_ocelot_probe(struct platform_device *pdev)
 
        return 0;
 
+out_ocelot_release_ports:
+       mscc_ocelot_release_ports(ocelot);
+       mscc_ocelot_teardown_devlink_ports(ocelot);
+out_ocelot_devlink_unregister:
+       devlink_unregister(devlink);
 out_ocelot_deinit:
        ocelot_deinit(ocelot);
 out_put_ports:
        of_node_put(ports);
+out_free_devlink:
+       devlink_free(devlink);
        return err;
 }
 
@@ -1300,11 +1405,15 @@ static int mscc_ocelot_remove(struct platform_device *pdev)
        struct ocelot *ocelot = platform_get_drvdata(pdev);
 
        ocelot_deinit_timestamp(ocelot);
+       ocelot_devlink_sb_unregister(ocelot);
        mscc_ocelot_release_ports(ocelot);
+       mscc_ocelot_teardown_devlink_ports(ocelot);
+       devlink_unregister(ocelot->devlink);
        ocelot_deinit(ocelot);
        unregister_switchdev_blocking_notifier(&ocelot_switchdev_blocking_nb);
        unregister_switchdev_notifier(&ocelot_switchdev_nb);
        unregister_netdevice_notifier(&ocelot_netdevice_nb);
+       devlink_free(ocelot->devlink);
 
        return 0;
 }
index 776b7d2..2289e1f 100644 (file)
@@ -506,10 +506,14 @@ static int mac_sonic_platform_probe(struct platform_device *pdev)
 
        err = register_netdev(dev);
        if (err)
-               goto out;
+               goto undo_probe;
 
        return 0;
 
+undo_probe:
+       dma_free_coherent(lp->device,
+                         SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
 out:
        free_netdev(dev);
 
@@ -584,12 +588,16 @@ static int mac_sonic_nubus_probe(struct nubus_board *board)
 
        err = register_netdev(ndev);
        if (err)
-               goto out;
+               goto undo_probe;
 
        nubus_set_drvdata(board, ndev);
 
        return 0;
 
+undo_probe:
+       dma_free_coherent(lp->device,
+                         SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
 out:
        free_netdev(ndev);
        return err;
index afa166f..28d9e98 100644 (file)
@@ -229,11 +229,14 @@ int xtsonic_probe(struct platform_device *pdev)
        sonic_msg_init(dev);
 
        if ((err = register_netdev(dev)))
-               goto out1;
+               goto undo_probe1;
 
        return 0;
 
-out1:
+undo_probe1:
+       dma_free_coherent(lp->device,
+                         SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode),
+                         lp->descriptors, lp->descriptors_laddr);
        release_region(dev->base_addr, SONIC_MEM_SIZE);
 out:
        free_netdev(dev);
index 0a721f6..e31f8fb 100644 (file)
@@ -3109,13 +3109,19 @@ mem_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, bool is64)
        return 0;
 }
 
-static int mem_xadd4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+static int mem_atomic4(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
+       if (meta->insn.imm != BPF_ADD)
+               return -EOPNOTSUPP;
+
        return mem_xadd(nfp_prog, meta, false);
 }
 
-static int mem_xadd8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
+static int mem_atomic8(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta)
 {
+       if (meta->insn.imm != BPF_ADD)
+               return -EOPNOTSUPP;
+
        return mem_xadd(nfp_prog, meta, true);
 }
 
@@ -3475,8 +3481,8 @@ static const instr_cb_t instr_cb[256] = {
        [BPF_STX | BPF_MEM | BPF_H] =   mem_stx2,
        [BPF_STX | BPF_MEM | BPF_W] =   mem_stx4,
        [BPF_STX | BPF_MEM | BPF_DW] =  mem_stx8,
-       [BPF_STX | BPF_XADD | BPF_W] =  mem_xadd4,
-       [BPF_STX | BPF_XADD | BPF_DW] = mem_xadd8,
+       [BPF_STX | BPF_ATOMIC | BPF_W] =        mem_atomic4,
+       [BPF_STX | BPF_ATOMIC | BPF_DW] =       mem_atomic8,
        [BPF_ST | BPF_MEM | BPF_B] =    mem_st1,
        [BPF_ST | BPF_MEM | BPF_H] =    mem_st2,
        [BPF_ST | BPF_MEM | BPF_W] =    mem_st4,
index fac9c6f..d0e17ee 100644 (file)
@@ -428,9 +428,9 @@ static inline bool is_mbpf_classic_store_pkt(const struct nfp_insn_meta *meta)
        return is_mbpf_classic_store(meta) && meta->ptr.type == PTR_TO_PACKET;
 }
 
-static inline bool is_mbpf_xadd(const struct nfp_insn_meta *meta)
+static inline bool is_mbpf_atomic(const struct nfp_insn_meta *meta)
 {
-       return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_XADD);
+       return (meta->insn.code & ~BPF_SIZE_MASK) == (BPF_STX | BPF_ATOMIC);
 }
 
 static inline bool is_mbpf_mul(const struct nfp_insn_meta *meta)
index e92ee51..9d235c0 100644 (file)
@@ -479,7 +479,7 @@ nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
                        pr_vlog(env, "map writes not supported\n");
                        return -EOPNOTSUPP;
                }
-               if (is_mbpf_xadd(meta)) {
+               if (is_mbpf_atomic(meta)) {
                        err = nfp_bpf_map_mark_used(env, meta, reg,
                                                    NFP_MAP_USE_ATOMIC_CNT);
                        if (err)
@@ -523,12 +523,17 @@ exit_check_ptr:
 }
 
 static int
-nfp_bpf_check_xadd(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
-                  struct bpf_verifier_env *env)
+nfp_bpf_check_atomic(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta,
+                    struct bpf_verifier_env *env)
 {
        const struct bpf_reg_state *sreg = cur_regs(env) + meta->insn.src_reg;
        const struct bpf_reg_state *dreg = cur_regs(env) + meta->insn.dst_reg;
 
+       if (meta->insn.imm != BPF_ADD) {
+               pr_vlog(env, "atomic op not implemented: %d\n", meta->insn.imm);
+               return -EOPNOTSUPP;
+       }
+
        if (dreg->type != PTR_TO_MAP_VALUE) {
                pr_vlog(env, "atomic add not to a map value pointer: %d\n",
                        dreg->type);
@@ -655,8 +660,8 @@ int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx,
        if (is_mbpf_store(meta))
                return nfp_bpf_check_store(nfp_prog, meta, env);
 
-       if (is_mbpf_xadd(meta))
-               return nfp_bpf_check_xadd(nfp_prog, meta, env);
+       if (is_mbpf_atomic(meta))
+               return nfp_bpf_check_atomic(nfp_prog, meta, env);
 
        if (is_mbpf_alu(meta))
                return nfp_bpf_check_alu(nfp_prog, meta, env);
index f21fb57..eeb3068 100644 (file)
@@ -1822,8 +1822,8 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
        rcu_read_lock();
        xdp_prog = READ_ONCE(dp->xdp_prog);
        true_bufsz = xdp_prog ? PAGE_SIZE : dp->fl_bufsz;
-       xdp.frame_sz = PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM;
-       xdp.rxq = &rx_ring->xdp_rxq;
+       xdp_init_buff(&xdp, PAGE_SIZE - NFP_NET_RX_BUF_HEADROOM,
+                     &rx_ring->xdp_rxq);
        tx_ring = r_vec->xdp_ring;
 
        while (pkts_polled < budget) {
@@ -1914,10 +1914,10 @@ static int nfp_net_rx(struct nfp_net_rx_ring *rx_ring, int budget)
                        unsigned int dma_off;
                        int act;
 
-                       xdp.data_hard_start = rxbuf->frag + NFP_NET_RX_BUF_HEADROOM;
-                       xdp.data = orig_data;
-                       xdp.data_meta = orig_data;
-                       xdp.data_end = orig_data + pkt_len;
+                       xdp_prepare_buff(&xdp,
+                                        rxbuf->frag + NFP_NET_RX_BUF_HEADROOM,
+                                        pkt_off - NFP_NET_RX_BUF_HEADROOM,
+                                        pkt_len, true);
 
                        act = bpf_prog_run_xdp(xdp_prog, &xdp);
 
@@ -3656,8 +3656,6 @@ const struct net_device_ops nfp_net_netdev_ops = {
        .ndo_set_features       = nfp_net_set_features,
        .ndo_features_check     = nfp_net_features_check,
        .ndo_get_phys_port_name = nfp_net_get_phys_port_name,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_bpf                = nfp_net_xdp,
        .ndo_get_devlink_port   = nfp_devlink_get_devlink_port,
 };
index 9156c98..162a1ff 100644 (file)
@@ -337,7 +337,7 @@ void ionic_rx_fill(struct ionic_queue *q)
        unsigned int i, j;
        unsigned int len;
 
-       len = netdev->mtu + ETH_HLEN;
+       len = netdev->mtu + ETH_HLEN + VLAN_HLEN;
        nfrags = round_up(len, PAGE_SIZE) / PAGE_SIZE;
 
        for (i = ionic_q_space_avail(q); i; i--) {
@@ -979,7 +979,7 @@ static int ionic_tx_calc_csum(struct ionic_queue *q, struct sk_buff *skb)
                stats->vlan_inserted++;
        }
 
-       if (skb->csum_not_inet)
+       if (skb_csum_is_sctp(skb))
                stats->crc32_csum++;
        else
                stats->csum++;
index 4366c7a..6b5ddb0 100644 (file)
@@ -78,6 +78,7 @@ config QED
        depends on PCI
        select ZLIB_INFLATE
        select CRC8
+       select CRC32
        select NET_DEVLINK
        help
          This enables the support for Marvell FastLinQ adapters family.
index 5e9f8ee..2fcbcec 100644 (file)
@@ -113,7 +113,8 @@ netxen_get_minidump_template(struct netxen_adapter *adapter)
                return NX_RCODE_INVALID_ARGS;
        }
 
-       addr = pci_zalloc_consistent(adapter->pdev, size, &md_template_addr);
+       addr = dma_alloc_coherent(&adapter->pdev->dev, size,
+                                 &md_template_addr, GFP_KERNEL);
        if (!addr) {
                dev_err(&adapter->pdev->dev, "Unable to allocate dmable memory for template.\n");
                return -ENOMEM;
@@ -133,7 +134,7 @@ netxen_get_minidump_template(struct netxen_adapter *adapter)
                dev_err(&adapter->pdev->dev, "Failed to get minidump template, err_code : %d, requested_size : %d, actual_size : %d\n",
                        cmd.rsp.cmd, size, cmd.rsp.arg2);
        }
-       pci_free_consistent(adapter->pdev, size, addr, md_template_addr);
+       dma_free_coherent(&adapter->pdev->dev, size, addr, md_template_addr);
        return 0;
 }
 
@@ -281,14 +282,14 @@ nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter)
        rsp_size =
                SIZEOF_CARDRSP_RX(nx_cardrsp_rx_ctx_t, nrds_rings, nsds_rings);
 
-       addr = pci_alloc_consistent(adapter->pdev,
-                               rq_size, &hostrq_phys_addr);
+       addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
+                                 &hostrq_phys_addr, GFP_KERNEL);
        if (addr == NULL)
                return -ENOMEM;
        prq = addr;
 
-       addr = pci_alloc_consistent(adapter->pdev,
-                       rsp_size, &cardrsp_phys_addr);
+       addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
+                                 &cardrsp_phys_addr, GFP_KERNEL);
        if (addr == NULL) {
                err = -ENOMEM;
                goto out_free_rq;
@@ -387,9 +388,10 @@ nx_fw_cmd_create_rx_ctx(struct netxen_adapter *adapter)
        recv_ctx->virt_port = prsp->virt_port;
 
 out_free_rsp:
-       pci_free_consistent(adapter->pdev, rsp_size, prsp, cardrsp_phys_addr);
+       dma_free_coherent(&adapter->pdev->dev, rsp_size, prsp,
+                         cardrsp_phys_addr);
 out_free_rq:
-       pci_free_consistent(adapter->pdev, rq_size, prq, hostrq_phys_addr);
+       dma_free_coherent(&adapter->pdev->dev, rq_size, prq, hostrq_phys_addr);
        return err;
 }
 
@@ -429,14 +431,14 @@ nx_fw_cmd_create_tx_ctx(struct netxen_adapter *adapter)
        struct netxen_cmd_args cmd;
 
        rq_size = SIZEOF_HOSTRQ_TX(nx_hostrq_tx_ctx_t);
-       rq_addr = pci_alloc_consistent(adapter->pdev,
-               rq_size, &rq_phys_addr);
+       rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size,
+                                    &rq_phys_addr, GFP_KERNEL);
        if (!rq_addr)
                return -ENOMEM;
 
        rsp_size = SIZEOF_CARDRSP_TX(nx_cardrsp_tx_ctx_t);
-       rsp_addr = pci_alloc_consistent(adapter->pdev,
-               rsp_size, &rsp_phys_addr);
+       rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size,
+                                     &rsp_phys_addr, GFP_KERNEL);
        if (!rsp_addr) {
                err = -ENOMEM;
                goto out_free_rq;
@@ -491,10 +493,11 @@ nx_fw_cmd_create_tx_ctx(struct netxen_adapter *adapter)
                err = -EIO;
        }
 
-       pci_free_consistent(adapter->pdev, rsp_size, rsp_addr, rsp_phys_addr);
+       dma_free_coherent(&adapter->pdev->dev, rsp_size, rsp_addr,
+                         rsp_phys_addr);
 
 out_free_rq:
-       pci_free_consistent(adapter->pdev, rq_size, rq_addr, rq_phys_addr);
+       dma_free_coherent(&adapter->pdev->dev, rq_size, rq_addr, rq_phys_addr);
 
        return err;
 }
@@ -745,9 +748,9 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
        recv_ctx = &adapter->recv_ctx;
        tx_ring = adapter->tx_ring;
 
-       addr = pci_alloc_consistent(pdev,
-                       sizeof(struct netxen_ring_ctx) + sizeof(uint32_t),
-                       &recv_ctx->phys_addr);
+       addr = dma_alloc_coherent(&pdev->dev,
+                                 sizeof(struct netxen_ring_ctx) + sizeof(uint32_t),
+                                 &recv_ctx->phys_addr, GFP_KERNEL);
        if (addr == NULL) {
                dev_err(&pdev->dev, "failed to allocate hw context\n");
                return -ENOMEM;
@@ -762,8 +765,8 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
                (__le32 *)(((char *)addr) + sizeof(struct netxen_ring_ctx));
 
        /* cmd desc ring */
-       addr = pci_alloc_consistent(pdev, TX_DESC_RINGSIZE(tx_ring),
-                       &tx_ring->phys_addr);
+       addr = dma_alloc_coherent(&pdev->dev, TX_DESC_RINGSIZE(tx_ring),
+                                 &tx_ring->phys_addr, GFP_KERNEL);
 
        if (addr == NULL) {
                dev_err(&pdev->dev, "%s: failed to allocate tx desc ring\n",
@@ -776,9 +779,9 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
 
        for (ring = 0; ring < adapter->max_rds_rings; ring++) {
                rds_ring = &recv_ctx->rds_rings[ring];
-               addr = pci_alloc_consistent(adapter->pdev,
-                               RCV_DESC_RINGSIZE(rds_ring),
-                               &rds_ring->phys_addr);
+               addr = dma_alloc_coherent(&adapter->pdev->dev,
+                                         RCV_DESC_RINGSIZE(rds_ring),
+                                         &rds_ring->phys_addr, GFP_KERNEL);
                if (addr == NULL) {
                        dev_err(&pdev->dev,
                                "%s: failed to allocate rds ring [%d]\n",
@@ -797,9 +800,9 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
        for (ring = 0; ring < adapter->max_sds_rings; ring++) {
                sds_ring = &recv_ctx->sds_rings[ring];
 
-               addr = pci_alloc_consistent(adapter->pdev,
-                               STATUS_DESC_RINGSIZE(sds_ring),
-                               &sds_ring->phys_addr);
+               addr = dma_alloc_coherent(&adapter->pdev->dev,
+                                         STATUS_DESC_RINGSIZE(sds_ring),
+                                         &sds_ring->phys_addr, GFP_KERNEL);
                if (addr == NULL) {
                        dev_err(&pdev->dev,
                                "%s: failed to allocate sds ring [%d]\n",
@@ -874,19 +877,17 @@ done:
        recv_ctx = &adapter->recv_ctx;
 
        if (recv_ctx->hwctx != NULL) {
-               pci_free_consistent(adapter->pdev,
-                               sizeof(struct netxen_ring_ctx) +
-                               sizeof(uint32_t),
-                               recv_ctx->hwctx,
-                               recv_ctx->phys_addr);
+               dma_free_coherent(&adapter->pdev->dev,
+                                 sizeof(struct netxen_ring_ctx) + sizeof(uint32_t),
+                                 recv_ctx->hwctx, recv_ctx->phys_addr);
                recv_ctx->hwctx = NULL;
        }
 
        tx_ring = adapter->tx_ring;
        if (tx_ring->desc_head != NULL) {
-               pci_free_consistent(adapter->pdev,
-                               TX_DESC_RINGSIZE(tx_ring),
-                               tx_ring->desc_head, tx_ring->phys_addr);
+               dma_free_coherent(&adapter->pdev->dev,
+                                 TX_DESC_RINGSIZE(tx_ring),
+                                 tx_ring->desc_head, tx_ring->phys_addr);
                tx_ring->desc_head = NULL;
        }
 
@@ -894,10 +895,10 @@ done:
                rds_ring = &recv_ctx->rds_rings[ring];
 
                if (rds_ring->desc_head != NULL) {
-                       pci_free_consistent(adapter->pdev,
-                                       RCV_DESC_RINGSIZE(rds_ring),
-                                       rds_ring->desc_head,
-                                       rds_ring->phys_addr);
+                       dma_free_coherent(&adapter->pdev->dev,
+                                         RCV_DESC_RINGSIZE(rds_ring),
+                                         rds_ring->desc_head,
+                                         rds_ring->phys_addr);
                        rds_ring->desc_head = NULL;
                }
        }
@@ -906,10 +907,10 @@ done:
                sds_ring = &recv_ctx->sds_rings[ring];
 
                if (sds_ring->desc_head != NULL) {
-                       pci_free_consistent(adapter->pdev,
-                               STATUS_DESC_RINGSIZE(sds_ring),
-                               sds_ring->desc_head,
-                               sds_ring->phys_addr);
+                       dma_free_coherent(&adapter->pdev->dev,
+                                         STATUS_DESC_RINGSIZE(sds_ring),
+                                         sds_ring->desc_head,
+                                         sds_ring->phys_addr);
                        sds_ring->desc_head = NULL;
                }
        }
index 94546ed..08f9477 100644 (file)
@@ -102,10 +102,8 @@ void netxen_release_rx_buffers(struct netxen_adapter *adapter)
                        rx_buf = &(rds_ring->rx_buf_arr[i]);
                        if (rx_buf->state == NETXEN_BUFFER_FREE)
                                continue;
-                       pci_unmap_single(adapter->pdev,
-                                       rx_buf->dma,
-                                       rds_ring->dma_size,
-                                       PCI_DMA_FROMDEVICE);
+                       dma_unmap_single(&adapter->pdev->dev, rx_buf->dma,
+                                        rds_ring->dma_size, DMA_FROM_DEVICE);
                        if (rx_buf->skb != NULL)
                                dev_kfree_skb_any(rx_buf->skb);
                }
@@ -124,16 +122,16 @@ void netxen_release_tx_buffers(struct netxen_adapter *adapter)
        for (i = 0; i < tx_ring->num_desc; i++) {
                buffrag = cmd_buf->frag_array;
                if (buffrag->dma) {
-                       pci_unmap_single(adapter->pdev, buffrag->dma,
-                                        buffrag->length, PCI_DMA_TODEVICE);
+                       dma_unmap_single(&adapter->pdev->dev, buffrag->dma,
+                                        buffrag->length, DMA_TO_DEVICE);
                        buffrag->dma = 0ULL;
                }
                for (j = 1; j < cmd_buf->frag_count; j++) {
                        buffrag++;
                        if (buffrag->dma) {
-                               pci_unmap_page(adapter->pdev, buffrag->dma,
-                                              buffrag->length,
-                                              PCI_DMA_TODEVICE);
+                               dma_unmap_page(&adapter->pdev->dev,
+                                              buffrag->dma, buffrag->length,
+                                              DMA_TO_DEVICE);
                                buffrag->dma = 0ULL;
                        }
                }
@@ -1250,9 +1248,10 @@ int netxen_init_dummy_dma(struct netxen_adapter *adapter)
        if (!NX_IS_REVISION_P2(adapter->ahw.revision_id))
                return 0;
 
-       adapter->dummy_dma.addr = pci_alloc_consistent(adapter->pdev,
-                                NETXEN_HOST_DUMMY_DMA_SIZE,
-                                &adapter->dummy_dma.phys_addr);
+       adapter->dummy_dma.addr = dma_alloc_coherent(&adapter->pdev->dev,
+                                                    NETXEN_HOST_DUMMY_DMA_SIZE,
+                                                    &adapter->dummy_dma.phys_addr,
+                                                    GFP_KERNEL);
        if (adapter->dummy_dma.addr == NULL) {
                dev_err(&adapter->pdev->dev,
                        "ERROR: Could not allocate dummy DMA memory\n");
@@ -1304,10 +1303,10 @@ void netxen_free_dummy_dma(struct netxen_adapter *adapter)
        }
 
        if (i) {
-               pci_free_consistent(adapter->pdev,
-                           NETXEN_HOST_DUMMY_DMA_SIZE,
-                           adapter->dummy_dma.addr,
-                           adapter->dummy_dma.phys_addr);
+               dma_free_coherent(&adapter->pdev->dev,
+                                 NETXEN_HOST_DUMMY_DMA_SIZE,
+                                 adapter->dummy_dma.addr,
+                                 adapter->dummy_dma.phys_addr);
                adapter->dummy_dma.addr = NULL;
        } else
                dev_err(&adapter->pdev->dev, "dma_watchdog_shutdown failed\n");
@@ -1467,10 +1466,10 @@ netxen_alloc_rx_skb(struct netxen_adapter *adapter,
        if (!adapter->ahw.cut_through)
                skb_reserve(skb, 2);
 
-       dma = pci_map_single(pdev, skb->data,
-                       rds_ring->dma_size, PCI_DMA_FROMDEVICE);
+       dma = dma_map_single(&pdev->dev, skb->data, rds_ring->dma_size,
+                            DMA_FROM_DEVICE);
 
-       if (pci_dma_mapping_error(pdev, dma)) {
+       if (dma_mapping_error(&pdev->dev, dma)) {
                dev_kfree_skb_any(skb);
                buffer->skb = NULL;
                return 1;
@@ -1491,8 +1490,8 @@ static struct sk_buff *netxen_process_rxbuf(struct netxen_adapter *adapter,
 
        buffer = &rds_ring->rx_buf_arr[index];
 
-       pci_unmap_single(adapter->pdev, buffer->dma, rds_ring->dma_size,
-                       PCI_DMA_FROMDEVICE);
+       dma_unmap_single(&adapter->pdev->dev, buffer->dma, rds_ring->dma_size,
+                        DMA_FROM_DEVICE);
 
        skb = buffer->skb;
        if (!skb)
@@ -1754,13 +1753,13 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter)
                buffer = &tx_ring->cmd_buf_arr[sw_consumer];
                if (buffer->skb) {
                        frag = &buffer->frag_array[0];
-                       pci_unmap_single(pdev, frag->dma, frag->length,
-                                        PCI_DMA_TODEVICE);
+                       dma_unmap_single(&pdev->dev, frag->dma, frag->length,
+                                        DMA_TO_DEVICE);
                        frag->dma = 0ULL;
                        for (i = 1; i < buffer->frag_count; i++) {
                                frag++; /* Get the next frag */
-                               pci_unmap_page(pdev, frag->dma, frag->length,
-                                              PCI_DMA_TODEVICE);
+                               dma_unmap_page(&pdev->dev, frag->dma,
+                                              frag->length, DMA_TO_DEVICE);
                                frag->dma = 0ULL;
                        }
 
index f218477..7e6bac8 100644 (file)
@@ -243,8 +243,8 @@ static int nx_set_dma_mask(struct netxen_adapter *adapter)
                cmask = mask;
        }
 
-       if (pci_set_dma_mask(pdev, mask) == 0 &&
-               pci_set_consistent_dma_mask(pdev, cmask) == 0) {
+       if (dma_set_mask(&pdev->dev, mask) == 0 &&
+           dma_set_coherent_mask(&pdev->dev, cmask) == 0) {
                adapter->pci_using_dac = 1;
                return 0;
        }
@@ -277,13 +277,13 @@ nx_update_dma_mask(struct netxen_adapter *adapter)
 
                mask = DMA_BIT_MASK(32+shift);
 
-               err = pci_set_dma_mask(pdev, mask);
+               err = dma_set_mask(&pdev->dev, mask);
                if (err)
                        goto err_out;
 
                if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
 
-                       err = pci_set_consistent_dma_mask(pdev, mask);
+                       err = dma_set_coherent_mask(&pdev->dev, mask);
                        if (err)
                                goto err_out;
                }
@@ -293,8 +293,8 @@ nx_update_dma_mask(struct netxen_adapter *adapter)
        return 0;
 
 err_out:
-       pci_set_dma_mask(pdev, old_mask);
-       pci_set_consistent_dma_mask(pdev, old_cmask);
+       dma_set_mask(&pdev->dev, old_mask);
+       dma_set_coherent_mask(&pdev->dev, old_cmask);
        return err;
 }
 
@@ -564,11 +564,6 @@ static const struct net_device_ops netxen_netdev_ops = {
        .ndo_set_features = netxen_set_features,
 };
 
-static inline bool netxen_function_zero(struct pci_dev *pdev)
-{
-       return (PCI_FUNC(pdev->devfn) == 0) ? true : false;
-}
-
 static inline void netxen_set_interrupt_mode(struct netxen_adapter *adapter,
                                             u32 mode)
 {
@@ -664,7 +659,7 @@ static int netxen_setup_intr(struct netxen_adapter *adapter)
        netxen_initialize_interrupt_registers(adapter);
        netxen_set_msix_bit(pdev, 0);
 
-       if (netxen_function_zero(pdev)) {
+       if (adapter->portnum == 0) {
                if (!netxen_setup_msi_interrupts(adapter, num_msix))
                        netxen_set_interrupt_mode(adapter, NETXEN_MSI_MODE);
                else
@@ -1983,9 +1978,9 @@ netxen_map_tx_skb(struct pci_dev *pdev,
        nr_frags = skb_shinfo(skb)->nr_frags;
        nf = &pbuf->frag_array[0];
 
-       map = pci_map_single(pdev, skb->data,
-                       skb_headlen(skb), PCI_DMA_TODEVICE);
-       if (pci_dma_mapping_error(pdev, map))
+       map = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb),
+                            DMA_TO_DEVICE);
+       if (dma_mapping_error(&pdev->dev, map))
                goto out_err;
 
        nf->dma = map;
@@ -2009,12 +2004,12 @@ netxen_map_tx_skb(struct pci_dev *pdev,
 unwind:
        while (--i >= 0) {
                nf = &pbuf->frag_array[i+1];
-               pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE);
+               dma_unmap_page(&pdev->dev, nf->dma, nf->length, DMA_TO_DEVICE);
                nf->dma = 0ULL;
        }
 
        nf = &pbuf->frag_array[0];
-       pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE);
+       dma_unmap_single(&pdev->dev, nf->dma, skb_headlen(skb), DMA_TO_DEVICE);
        nf->dma = 0ULL;
 
 out_err:
index a2494bf..70c8d3c 100644 (file)
@@ -1090,12 +1090,9 @@ static bool qede_rx_xdp(struct qede_dev *edev,
        struct xdp_buff xdp;
        enum xdp_action act;
 
-       xdp.data_hard_start = page_address(bd->data);
-       xdp.data = xdp.data_hard_start + *data_offset;
-       xdp_set_data_meta_invalid(&xdp);
-       xdp.data_end = xdp.data + *len;
-       xdp.rxq = &rxq->xdp_rxq;
-       xdp.frame_sz = rxq->rx_buf_seg_size; /* PAGE_SIZE when XDP enabled */
+       xdp_init_buff(&xdp, rxq->rx_buf_seg_size, &rxq->xdp_rxq);
+       xdp_prepare_buff(&xdp, page_address(bd->data), *data_offset,
+                        *len, false);
 
        /* Queues always have a full reset currently, so for the time
         * being until there's atomic program replace just mark read
@@ -1799,6 +1796,11 @@ netdev_features_t qede_features_check(struct sk_buff *skb,
                              ntohs(udp_hdr(skb)->dest) != gnv_port))
                                return features & ~(NETIF_F_CSUM_MASK |
                                                    NETIF_F_GSO_MASK);
+               } else if (l4_proto == IPPROTO_IPIP) {
+                       /* IPIP tunnels are unknown to the device or at least unsupported natively,
+                        * offloads for them can't be done trivially, so disable them for such skb.
+                        */
+                       return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK);
                }
        }
 
index 9cf960a..4bf9479 100644 (file)
@@ -663,8 +663,6 @@ static const struct net_device_ops qede_netdev_ops = {
        .ndo_get_vf_config      = qede_get_vf_config,
        .ndo_set_vf_rate        = qede_set_vf_rate,
 #endif
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = qede_features_check,
        .ndo_bpf                = qede_xdp,
 #ifdef CONFIG_RFS_ACCEL
@@ -688,8 +686,6 @@ static const struct net_device_ops qede_netdev_vf_ops = {
        .ndo_fix_features       = qede_fix_features,
        .ndo_set_features       = qede_set_features,
        .ndo_get_stats64        = qede_get_stats64,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = qede_features_check,
 };
 
@@ -707,8 +703,6 @@ static const struct net_device_ops qede_netdev_vf_xdp_ops = {
        .ndo_fix_features       = qede_fix_features,
        .ndo_set_features       = qede_set_features,
        .ndo_get_stats64        = qede_get_stats64,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = qede_features_check,
        .ndo_bpf                = qede_xdp,
        .ndo_xdp_xmit           = qede_xdp_transmit,
index 27740c0..214e347 100644 (file)
@@ -315,12 +315,11 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev,
                         * buffer
                         */
                        skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE);
-                       map = pci_map_single(qdev->pdev,
+                       map = dma_map_single(&qdev->pdev->dev,
                                             lrg_buf_cb->skb->data,
-                                            qdev->lrg_buffer_len -
-                                            QL_HEADER_SPACE,
-                                            PCI_DMA_FROMDEVICE);
-                       err = pci_dma_mapping_error(qdev->pdev, map);
+                                            qdev->lrg_buffer_len - QL_HEADER_SPACE,
+                                            DMA_FROM_DEVICE);
+                       err = dma_mapping_error(&qdev->pdev->dev, map);
                        if (err) {
                                netdev_err(qdev->ndev,
                                           "PCI mapping failed with error: %d\n",
@@ -1802,13 +1801,12 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev)
                                 * first buffer
                                 */
                                skb_reserve(lrg_buf_cb->skb, QL_HEADER_SPACE);
-                               map = pci_map_single(qdev->pdev,
+                               map = dma_map_single(&qdev->pdev->dev,
                                                     lrg_buf_cb->skb->data,
-                                                    qdev->lrg_buffer_len -
-                                                    QL_HEADER_SPACE,
-                                                    PCI_DMA_FROMDEVICE);
+                                                    qdev->lrg_buffer_len - QL_HEADER_SPACE,
+                                                    DMA_FROM_DEVICE);
 
-                               err = pci_dma_mapping_error(qdev->pdev, map);
+                               err = dma_mapping_error(&qdev->pdev->dev, map);
                                if (err) {
                                        netdev_err(qdev->ndev,
                                                   "PCI mapping failed with error: %d\n",
@@ -1943,18 +1941,16 @@ static void ql_process_mac_tx_intr(struct ql3_adapter *qdev,
                goto invalid_seg_count;
        }
 
-       pci_unmap_single(qdev->pdev,
+       dma_unmap_single(&qdev->pdev->dev,
                         dma_unmap_addr(&tx_cb->map[0], mapaddr),
-                        dma_unmap_len(&tx_cb->map[0], maplen),
-                        PCI_DMA_TODEVICE);
+                        dma_unmap_len(&tx_cb->map[0], maplen), DMA_TO_DEVICE);
        tx_cb->seg_count--;
        if (tx_cb->seg_count) {
                for (i = 1; i < tx_cb->seg_count; i++) {
-                       pci_unmap_page(qdev->pdev,
-                                      dma_unmap_addr(&tx_cb->map[i],
-                                                     mapaddr),
+                       dma_unmap_page(&qdev->pdev->dev,
+                                      dma_unmap_addr(&tx_cb->map[i], mapaddr),
                                       dma_unmap_len(&tx_cb->map[i], maplen),
-                                      PCI_DMA_TODEVICE);
+                                      DMA_TO_DEVICE);
                }
        }
        qdev->ndev->stats.tx_packets++;
@@ -2021,10 +2017,9 @@ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev,
        qdev->ndev->stats.rx_bytes += length;
 
        skb_put(skb, length);
-       pci_unmap_single(qdev->pdev,
+       dma_unmap_single(&qdev->pdev->dev,
                         dma_unmap_addr(lrg_buf_cb2, mapaddr),
-                        dma_unmap_len(lrg_buf_cb2, maplen),
-                        PCI_DMA_FROMDEVICE);
+                        dma_unmap_len(lrg_buf_cb2, maplen), DMA_FROM_DEVICE);
        prefetch(skb->data);
        skb_checksum_none_assert(skb);
        skb->protocol = eth_type_trans(skb, qdev->ndev);
@@ -2067,10 +2062,9 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev,
        skb2 = lrg_buf_cb2->skb;
 
        skb_put(skb2, length);  /* Just the second buffer length here. */
-       pci_unmap_single(qdev->pdev,
+       dma_unmap_single(&qdev->pdev->dev,
                         dma_unmap_addr(lrg_buf_cb2, mapaddr),
-                        dma_unmap_len(lrg_buf_cb2, maplen),
-                        PCI_DMA_FROMDEVICE);
+                        dma_unmap_len(lrg_buf_cb2, maplen), DMA_FROM_DEVICE);
        prefetch(skb2->data);
 
        skb_checksum_none_assert(skb2);
@@ -2319,9 +2313,9 @@ static int ql_send_map(struct ql3_adapter *qdev,
        /*
         * Map the skb buffer first.
         */
-       map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE);
+       map = dma_map_single(&qdev->pdev->dev, skb->data, len, DMA_TO_DEVICE);
 
-       err = pci_dma_mapping_error(qdev->pdev, map);
+       err = dma_mapping_error(&qdev->pdev->dev, map);
        if (err) {
                netdev_err(qdev->ndev, "PCI mapping failed with error: %d\n",
                           err);
@@ -2357,11 +2351,11 @@ static int ql_send_map(struct ql3_adapter *qdev,
                    (seg == 7 && seg_cnt > 8) ||
                    (seg == 12 && seg_cnt > 13) ||
                    (seg == 17 && seg_cnt > 18)) {
-                       map = pci_map_single(qdev->pdev, oal,
+                       map = dma_map_single(&qdev->pdev->dev, oal,
                                             sizeof(struct oal),
-                                            PCI_DMA_TODEVICE);
+                                            DMA_TO_DEVICE);
 
-                       err = pci_dma_mapping_error(qdev->pdev, map);
+                       err = dma_mapping_error(&qdev->pdev->dev, map);
                        if (err) {
                                netdev_err(qdev->ndev,
                                           "PCI mapping outbound address list with error: %d\n",
@@ -2423,24 +2417,24 @@ map_error:
                    (seg == 7 && seg_cnt > 8) ||
                    (seg == 12 && seg_cnt > 13) ||
                    (seg == 17 && seg_cnt > 18)) {
-                       pci_unmap_single(qdev->pdev,
-                               dma_unmap_addr(&tx_cb->map[seg], mapaddr),
-                               dma_unmap_len(&tx_cb->map[seg], maplen),
-                                PCI_DMA_TODEVICE);
+                       dma_unmap_single(&qdev->pdev->dev,
+                                        dma_unmap_addr(&tx_cb->map[seg], mapaddr),
+                                        dma_unmap_len(&tx_cb->map[seg], maplen),
+                                        DMA_TO_DEVICE);
                        oal++;
                        seg++;
                }
 
-               pci_unmap_page(qdev->pdev,
+               dma_unmap_page(&qdev->pdev->dev,
                               dma_unmap_addr(&tx_cb->map[seg], mapaddr),
                               dma_unmap_len(&tx_cb->map[seg], maplen),
-                              PCI_DMA_TODEVICE);
+                              DMA_TO_DEVICE);
        }
 
-       pci_unmap_single(qdev->pdev,
+       dma_unmap_single(&qdev->pdev->dev,
                         dma_unmap_addr(&tx_cb->map[0], mapaddr),
                         dma_unmap_addr(&tx_cb->map[0], maplen),
-                        PCI_DMA_TODEVICE);
+                        DMA_TO_DEVICE);
 
        return NETDEV_TX_BUSY;
 
@@ -2525,9 +2519,8 @@ static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev)
        wmb();
 
        qdev->req_q_virt_addr =
-           pci_alloc_consistent(qdev->pdev,
-                                (size_t) qdev->req_q_size,
-                                &qdev->req_q_phy_addr);
+           dma_alloc_coherent(&qdev->pdev->dev, (size_t)qdev->req_q_size,
+                              &qdev->req_q_phy_addr, GFP_KERNEL);
 
        if ((qdev->req_q_virt_addr == NULL) ||
            LS_64BITS(qdev->req_q_phy_addr) & (qdev->req_q_size - 1)) {
@@ -2536,16 +2529,14 @@ static int ql_alloc_net_req_rsp_queues(struct ql3_adapter *qdev)
        }
 
        qdev->rsp_q_virt_addr =
-           pci_alloc_consistent(qdev->pdev,
-                                (size_t) qdev->rsp_q_size,
-                                &qdev->rsp_q_phy_addr);
+           dma_alloc_coherent(&qdev->pdev->dev, (size_t)qdev->rsp_q_size,
+                              &qdev->rsp_q_phy_addr, GFP_KERNEL);
 
        if ((qdev->rsp_q_virt_addr == NULL) ||
            LS_64BITS(qdev->rsp_q_phy_addr) & (qdev->rsp_q_size - 1)) {
                netdev_err(qdev->ndev, "rspQ allocation failed\n");
-               pci_free_consistent(qdev->pdev, (size_t) qdev->req_q_size,
-                                   qdev->req_q_virt_addr,
-                                   qdev->req_q_phy_addr);
+               dma_free_coherent(&qdev->pdev->dev, (size_t)qdev->req_q_size,
+                                 qdev->req_q_virt_addr, qdev->req_q_phy_addr);
                return -ENOMEM;
        }
 
@@ -2561,15 +2552,13 @@ static void ql_free_net_req_rsp_queues(struct ql3_adapter *qdev)
                return;
        }
 
-       pci_free_consistent(qdev->pdev,
-                           qdev->req_q_size,
-                           qdev->req_q_virt_addr, qdev->req_q_phy_addr);
+       dma_free_coherent(&qdev->pdev->dev, qdev->req_q_size,
+                         qdev->req_q_virt_addr, qdev->req_q_phy_addr);
 
        qdev->req_q_virt_addr = NULL;
 
-       pci_free_consistent(qdev->pdev,
-                           qdev->rsp_q_size,
-                           qdev->rsp_q_virt_addr, qdev->rsp_q_phy_addr);
+       dma_free_coherent(&qdev->pdev->dev, qdev->rsp_q_size,
+                         qdev->rsp_q_virt_addr, qdev->rsp_q_phy_addr);
 
        qdev->rsp_q_virt_addr = NULL;
 
@@ -2593,9 +2582,9 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev)
                return -ENOMEM;
 
        qdev->lrg_buf_q_alloc_virt_addr =
-               pci_alloc_consistent(qdev->pdev,
-                                    qdev->lrg_buf_q_alloc_size,
-                                    &qdev->lrg_buf_q_alloc_phy_addr);
+               dma_alloc_coherent(&qdev->pdev->dev,
+                                  qdev->lrg_buf_q_alloc_size,
+                                  &qdev->lrg_buf_q_alloc_phy_addr, GFP_KERNEL);
 
        if (qdev->lrg_buf_q_alloc_virt_addr == NULL) {
                netdev_err(qdev->ndev, "lBufQ failed\n");
@@ -2613,15 +2602,16 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev)
                qdev->small_buf_q_alloc_size = qdev->small_buf_q_size * 2;
 
        qdev->small_buf_q_alloc_virt_addr =
-               pci_alloc_consistent(qdev->pdev,
-                                    qdev->small_buf_q_alloc_size,
-                                    &qdev->small_buf_q_alloc_phy_addr);
+               dma_alloc_coherent(&qdev->pdev->dev,
+                                  qdev->small_buf_q_alloc_size,
+                                  &qdev->small_buf_q_alloc_phy_addr, GFP_KERNEL);
 
        if (qdev->small_buf_q_alloc_virt_addr == NULL) {
                netdev_err(qdev->ndev, "Small Buffer Queue allocation failed\n");
-               pci_free_consistent(qdev->pdev, qdev->lrg_buf_q_alloc_size,
-                                   qdev->lrg_buf_q_alloc_virt_addr,
-                                   qdev->lrg_buf_q_alloc_phy_addr);
+               dma_free_coherent(&qdev->pdev->dev,
+                                 qdev->lrg_buf_q_alloc_size,
+                                 qdev->lrg_buf_q_alloc_virt_addr,
+                                 qdev->lrg_buf_q_alloc_phy_addr);
                return -ENOMEM;
        }
 
@@ -2638,17 +2628,15 @@ static void ql_free_buffer_queues(struct ql3_adapter *qdev)
                return;
        }
        kfree(qdev->lrg_buf);
-       pci_free_consistent(qdev->pdev,
-                           qdev->lrg_buf_q_alloc_size,
-                           qdev->lrg_buf_q_alloc_virt_addr,
-                           qdev->lrg_buf_q_alloc_phy_addr);
+       dma_free_coherent(&qdev->pdev->dev, qdev->lrg_buf_q_alloc_size,
+                         qdev->lrg_buf_q_alloc_virt_addr,
+                         qdev->lrg_buf_q_alloc_phy_addr);
 
        qdev->lrg_buf_q_virt_addr = NULL;
 
-       pci_free_consistent(qdev->pdev,
-                           qdev->small_buf_q_alloc_size,
-                           qdev->small_buf_q_alloc_virt_addr,
-                           qdev->small_buf_q_alloc_phy_addr);
+       dma_free_coherent(&qdev->pdev->dev, qdev->small_buf_q_alloc_size,
+                         qdev->small_buf_q_alloc_virt_addr,
+                         qdev->small_buf_q_alloc_phy_addr);
 
        qdev->small_buf_q_virt_addr = NULL;
 
@@ -2666,9 +2654,9 @@ static int ql_alloc_small_buffers(struct ql3_adapter *qdev)
                 QL_SMALL_BUFFER_SIZE);
 
        qdev->small_buf_virt_addr =
-               pci_alloc_consistent(qdev->pdev,
-                                    qdev->small_buf_total_size,
-                                    &qdev->small_buf_phy_addr);
+               dma_alloc_coherent(&qdev->pdev->dev,
+                                  qdev->small_buf_total_size,
+                                  &qdev->small_buf_phy_addr, GFP_KERNEL);
 
        if (qdev->small_buf_virt_addr == NULL) {
                netdev_err(qdev->ndev, "Failed to get small buffer memory\n");
@@ -2701,10 +2689,10 @@ static void ql_free_small_buffers(struct ql3_adapter *qdev)
                return;
        }
        if (qdev->small_buf_virt_addr != NULL) {
-               pci_free_consistent(qdev->pdev,
-                                   qdev->small_buf_total_size,
-                                   qdev->small_buf_virt_addr,
-                                   qdev->small_buf_phy_addr);
+               dma_free_coherent(&qdev->pdev->dev,
+                                 qdev->small_buf_total_size,
+                                 qdev->small_buf_virt_addr,
+                                 qdev->small_buf_phy_addr);
 
                qdev->small_buf_virt_addr = NULL;
        }
@@ -2719,10 +2707,10 @@ static void ql_free_large_buffers(struct ql3_adapter *qdev)
                lrg_buf_cb = &qdev->lrg_buf[i];
                if (lrg_buf_cb->skb) {
                        dev_kfree_skb(lrg_buf_cb->skb);
-                       pci_unmap_single(qdev->pdev,
+                       dma_unmap_single(&qdev->pdev->dev,
                                         dma_unmap_addr(lrg_buf_cb, mapaddr),
                                         dma_unmap_len(lrg_buf_cb, maplen),
-                                        PCI_DMA_FROMDEVICE);
+                                        DMA_FROM_DEVICE);
                        memset(lrg_buf_cb, 0, sizeof(struct ql_rcv_buf_cb));
                } else {
                        break;
@@ -2774,13 +2762,11 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev)
                         * buffer
                         */
                        skb_reserve(skb, QL_HEADER_SPACE);
-                       map = pci_map_single(qdev->pdev,
-                                            skb->data,
-                                            qdev->lrg_buffer_len -
-                                            QL_HEADER_SPACE,
-                                            PCI_DMA_FROMDEVICE);
+                       map = dma_map_single(&qdev->pdev->dev, skb->data,
+                                            qdev->lrg_buffer_len - QL_HEADER_SPACE,
+                                            DMA_FROM_DEVICE);
 
-                       err = pci_dma_mapping_error(qdev->pdev, map);
+                       err = dma_mapping_error(&qdev->pdev->dev, map);
                        if (err) {
                                netdev_err(qdev->ndev,
                                           "PCI mapping failed with error: %d\n",
@@ -2865,8 +2851,8 @@ static int ql_alloc_mem_resources(struct ql3_adapter *qdev)
         * Network Completion Queue Producer Index Register
         */
        qdev->shadow_reg_virt_addr =
-               pci_alloc_consistent(qdev->pdev,
-                                    PAGE_SIZE, &qdev->shadow_reg_phy_addr);
+               dma_alloc_coherent(&qdev->pdev->dev, PAGE_SIZE,
+                                  &qdev->shadow_reg_phy_addr, GFP_KERNEL);
 
        if (qdev->shadow_reg_virt_addr != NULL) {
                qdev->preq_consumer_index = qdev->shadow_reg_virt_addr;
@@ -2921,10 +2907,9 @@ err_small_buffers:
 err_buffer_queues:
        ql_free_net_req_rsp_queues(qdev);
 err_req_rsp:
-       pci_free_consistent(qdev->pdev,
-                           PAGE_SIZE,
-                           qdev->shadow_reg_virt_addr,
-                           qdev->shadow_reg_phy_addr);
+       dma_free_coherent(&qdev->pdev->dev, PAGE_SIZE,
+                         qdev->shadow_reg_virt_addr,
+                         qdev->shadow_reg_phy_addr);
 
        return -ENOMEM;
 }
@@ -2937,10 +2922,9 @@ static void ql_free_mem_resources(struct ql3_adapter *qdev)
        ql_free_buffer_queues(qdev);
        ql_free_net_req_rsp_queues(qdev);
        if (qdev->shadow_reg_virt_addr != NULL) {
-               pci_free_consistent(qdev->pdev,
-                                   PAGE_SIZE,
-                                   qdev->shadow_reg_virt_addr,
-                                   qdev->shadow_reg_phy_addr);
+               dma_free_coherent(&qdev->pdev->dev, PAGE_SIZE,
+                                 qdev->shadow_reg_virt_addr,
+                                 qdev->shadow_reg_phy_addr);
                qdev->shadow_reg_virt_addr = NULL;
        }
 }
@@ -3641,18 +3625,15 @@ static void ql_reset_work(struct work_struct *work)
                        if (tx_cb->skb) {
                                netdev_printk(KERN_DEBUG, ndev,
                                              "Freeing lost SKB\n");
-                               pci_unmap_single(qdev->pdev,
-                                        dma_unmap_addr(&tx_cb->map[0],
-                                                       mapaddr),
-                                        dma_unmap_len(&tx_cb->map[0], maplen),
-                                        PCI_DMA_TODEVICE);
+                               dma_unmap_single(&qdev->pdev->dev,
+                                                dma_unmap_addr(&tx_cb->map[0], mapaddr),
+                                                dma_unmap_len(&tx_cb->map[0], maplen),
+                                                DMA_TO_DEVICE);
                                for (j = 1; j < tx_cb->seg_count; j++) {
-                                       pci_unmap_page(qdev->pdev,
-                                              dma_unmap_addr(&tx_cb->map[j],
-                                                             mapaddr),
-                                              dma_unmap_len(&tx_cb->map[j],
-                                                            maplen),
-                                              PCI_DMA_TODEVICE);
+                                       dma_unmap_page(&qdev->pdev->dev,
+                                                      dma_unmap_addr(&tx_cb->map[j], mapaddr),
+                                                      dma_unmap_len(&tx_cb->map[j], maplen),
+                                                      DMA_TO_DEVICE);
                                }
                                dev_kfree_skb(tx_cb->skb);
                                tx_cb->skb = NULL;
@@ -3784,13 +3765,10 @@ static int ql3xxx_probe(struct pci_dev *pdev,
 
        pci_set_master(pdev);
 
-       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
+       if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
                pci_using_dac = 1;
-               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
-       } else if (!(err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)))) {
+       else if (!(err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))))
                pci_using_dac = 0;
-               err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
-       }
 
        if (err) {
                pr_err("%s no usable DMA configuration\n", pci_name(pdev));
index c2faf96..96b947f 100644 (file)
@@ -520,8 +520,6 @@ static const struct net_device_ops qlcnic_netdev_ops = {
        .ndo_fdb_del            = qlcnic_fdb_del,
        .ndo_fdb_dump           = qlcnic_fdb_dump,
        .ndo_get_phys_port_id   = qlcnic_get_phys_port_id,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_features_check     = qlcnic_features_check,
 #ifdef CONFIG_QLCNIC_SRIOV
        .ndo_set_vf_mac         = qlcnic_sriov_set_vf_mac,
index 46d8510..475e6f0 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/bitfield.h>
 #include <linux/prefetch.h>
 #include <linux/ipv6.h>
+#include <asm/unaligned.h>
 #include <net/ip6_checksum.h>
 
 #include "r8169.h"
@@ -260,6 +261,9 @@ enum rtl8168_8101_registers {
 #define        CSIAR_BYTE_ENABLE               0x0000f000
 #define        CSIAR_ADDR_MASK                 0x00000fff
        PMCH                    = 0x6f,
+#define D3COLD_NO_PLL_DOWN             BIT(7)
+#define D3HOT_NO_PLL_DOWN              BIT(6)
+#define D3_NO_PLL_DOWN                 (BIT(7) | BIT(6))
        EPHYAR                  = 0x80,
 #define        EPHYAR_FLAG                     0x80000000
 #define        EPHYAR_WRITE_CMD                0x80000000
@@ -529,6 +533,9 @@ enum rtl_rx_desc_bit {
        IPFail          = (1 << 16), /* IP checksum failed */
        UDPFail         = (1 << 15), /* UDP/IP checksum failed */
        TCPFail         = (1 << 14), /* TCP/IP checksum failed */
+
+#define RxCSFailMask   (IPFail | UDPFail | TCPFail)
+
        RxVlanTag       = (1 << 16), /* VLAN tag available */
 };
 
@@ -584,6 +591,12 @@ enum rtl_flag {
        RTL_FLAG_MAX
 };
 
+enum rtl_dash_type {
+       RTL_DASH_NONE,
+       RTL_DASH_DP,
+       RTL_DASH_EP,
+};
+
 struct rtl8169_private {
        void __iomem *mmio_addr;        /* memory map physical address */
        struct pci_dev *pci_dev;
@@ -591,6 +604,7 @@ struct rtl8169_private {
        struct phy_device *phydev;
        struct napi_struct napi;
        enum mac_version mac_version;
+       enum rtl_dash_type dash_type;
        u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */
        u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */
        u32 dirty_tx;
@@ -746,14 +760,75 @@ static const struct rtl_cond name = {                     \
                                                        \
 static bool name ## _check(struct rtl8169_private *tp)
 
-static bool rtl_ocp_reg_failure(struct rtl8169_private *tp, u32 reg)
+static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type)
 {
-       if (reg & 0xffff0001) {
-               if (net_ratelimit())
-                       netdev_err(tp->dev, "Invalid ocp reg %x!\n", reg);
-               return true;
-       }
-       return false;
+       /* based on RTL8168FP_OOBMAC_BASE in vendor driver */
+       if (tp->mac_version == RTL_GIGA_MAC_VER_52 && type == ERIAR_OOB)
+               *cmd |= 0x7f0 << 18;
+}
+
+DECLARE_RTL_COND(rtl_eriar_cond)
+{
+       return RTL_R32(tp, ERIAR) & ERIAR_FLAG;
+}
+
+static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
+                          u32 val, int type)
+{
+       u32 cmd = ERIAR_WRITE_CMD | type | mask | addr;
+
+       if (WARN(addr & 3 || !mask, "addr: 0x%x, mask: 0x%08x\n", addr, mask))
+               return;
+
+       RTL_W32(tp, ERIDR, val);
+       r8168fp_adjust_ocp_cmd(tp, &cmd, type);
+       RTL_W32(tp, ERIAR, cmd);
+
+       rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
+}
+
+static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
+                         u32 val)
+{
+       _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC);
+}
+
+static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type)
+{
+       u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr;
+
+       r8168fp_adjust_ocp_cmd(tp, &cmd, type);
+       RTL_W32(tp, ERIAR, cmd);
+
+       return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
+               RTL_R32(tp, ERIDR) : ~0;
+}
+
+static u32 rtl_eri_read(struct rtl8169_private *tp, int addr)
+{
+       return _rtl_eri_read(tp, addr, ERIAR_EXGMAC);
+}
+
+static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m)
+{
+       u32 val = rtl_eri_read(tp, addr);
+
+       rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p);
+}
+
+static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p)
+{
+       rtl_w0w1_eri(tp, addr, p, 0);
+}
+
+static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m)
+{
+       rtl_w0w1_eri(tp, addr, 0, m);
+}
+
+static bool rtl_ocp_reg_failure(u32 reg)
+{
+       return WARN_ONCE(reg & 0xffff0001, "Invalid ocp reg %x!\n", reg);
 }
 
 DECLARE_RTL_COND(rtl_ocp_gphy_cond)
@@ -763,7 +838,7 @@ DECLARE_RTL_COND(rtl_ocp_gphy_cond)
 
 static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
 {
-       if (rtl_ocp_reg_failure(tp, reg))
+       if (rtl_ocp_reg_failure(reg))
                return;
 
        RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data);
@@ -773,7 +848,7 @@ static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
 
 static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
 {
-       if (rtl_ocp_reg_failure(tp, reg))
+       if (rtl_ocp_reg_failure(reg))
                return 0;
 
        RTL_W32(tp, GPHY_OCP, reg << 15);
@@ -784,7 +859,7 @@ static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg)
 
 static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
 {
-       if (rtl_ocp_reg_failure(tp, reg))
+       if (rtl_ocp_reg_failure(reg))
                return;
 
        RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data);
@@ -792,7 +867,7 @@ static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data)
 
 static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg)
 {
-       if (rtl_ocp_reg_failure(tp, reg))
+       if (rtl_ocp_reg_failure(reg))
                return 0;
 
        RTL_W32(tp, OCPDR, reg << 15);
@@ -808,6 +883,25 @@ static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask,
        r8168_mac_ocp_write(tp, reg, (data & ~mask) | set);
 }
 
+/* Work around a hw issue with RTL8168g PHY, the quirk disables
+ * PHY MCU interrupts before PHY power-down.
+ */
+static void rtl8168g_phy_suspend_quirk(struct rtl8169_private *tp, int value)
+{
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_40:
+       case RTL_GIGA_MAC_VER_41:
+       case RTL_GIGA_MAC_VER_49:
+               if (value & BMCR_RESET || !(value & BMCR_PDOWN))
+                       rtl_eri_set_bits(tp, 0x1a8, 0xfc000000);
+               else
+                       rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000);
+               break;
+       default:
+               break;
+       }
+};
+
 static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value)
 {
        if (reg == 0x1f) {
@@ -818,6 +912,9 @@ static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value)
        if (tp->ocp_base != OCP_STD_PHY_BASE)
                reg -= 0x10;
 
+       if (tp->ocp_base == OCP_STD_PHY_BASE && reg == MII_BMCR)
+               rtl8168g_phy_suspend_quirk(tp, value);
+
        r8168_phy_ocp_write(tp, tp->ocp_base + reg * 2, value);
 }
 
@@ -1009,70 +1106,6 @@ static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr)
                RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0;
 }
 
-static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type)
-{
-       /* based on RTL8168FP_OOBMAC_BASE in vendor driver */
-       if (tp->mac_version == RTL_GIGA_MAC_VER_52 && type == ERIAR_OOB)
-               *cmd |= 0x7f0 << 18;
-}
-
-DECLARE_RTL_COND(rtl_eriar_cond)
-{
-       return RTL_R32(tp, ERIAR) & ERIAR_FLAG;
-}
-
-static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
-                          u32 val, int type)
-{
-       u32 cmd = ERIAR_WRITE_CMD | type | mask | addr;
-
-       BUG_ON((addr & 3) || (mask == 0));
-       RTL_W32(tp, ERIDR, val);
-       r8168fp_adjust_ocp_cmd(tp, &cmd, type);
-       RTL_W32(tp, ERIAR, cmd);
-
-       rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100);
-}
-
-static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask,
-                         u32 val)
-{
-       _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC);
-}
-
-static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type)
-{
-       u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr;
-
-       r8168fp_adjust_ocp_cmd(tp, &cmd, type);
-       RTL_W32(tp, ERIAR, cmd);
-
-       return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ?
-               RTL_R32(tp, ERIDR) : ~0;
-}
-
-static u32 rtl_eri_read(struct rtl8169_private *tp, int addr)
-{
-       return _rtl_eri_read(tp, addr, ERIAR_EXGMAC);
-}
-
-static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m)
-{
-       u32 val = rtl_eri_read(tp, addr);
-
-       rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p);
-}
-
-static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p)
-{
-       rtl_w0w1_eri(tp, addr, p, 0);
-}
-
-static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m)
-{
-       rtl_w0w1_eri(tp, addr, 0, m);
-}
-
 static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u16 reg)
 {
        RTL_W32(tp, OCPAR, 0x0fu << 12 | (reg & 0x0fff));
@@ -1158,19 +1191,10 @@ static void rtl8168ep_driver_start(struct rtl8169_private *tp)
 
 static void rtl8168_driver_start(struct rtl8169_private *tp)
 {
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_27:
-       case RTL_GIGA_MAC_VER_28:
-       case RTL_GIGA_MAC_VER_31:
+       if (tp->dash_type == RTL_DASH_DP)
                rtl8168dp_driver_start(tp);
-               break;
-       case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52:
+       else
                rtl8168ep_driver_start(tp);
-               break;
-       default:
-               BUG();
-               break;
-       }
 }
 
 static void rtl8168dp_driver_stop(struct rtl8169_private *tp)
@@ -1189,44 +1213,51 @@ static void rtl8168ep_driver_stop(struct rtl8169_private *tp)
 
 static void rtl8168_driver_stop(struct rtl8169_private *tp)
 {
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_27:
-       case RTL_GIGA_MAC_VER_28:
-       case RTL_GIGA_MAC_VER_31:
+       if (tp->dash_type == RTL_DASH_DP)
                rtl8168dp_driver_stop(tp);
-               break;
-       case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52:
+       else
                rtl8168ep_driver_stop(tp);
-               break;
-       default:
-               BUG();
-               break;
-       }
 }
 
 static bool r8168dp_check_dash(struct rtl8169_private *tp)
 {
        u16 reg = rtl8168_get_ocp_reg(tp);
 
-       return !!(r8168dp_ocp_read(tp, reg) & 0x00008000);
+       return r8168dp_ocp_read(tp, reg) & BIT(15);
 }
 
 static bool r8168ep_check_dash(struct rtl8169_private *tp)
 {
-       return r8168ep_ocp_read(tp, 0x128) & 0x00000001;
+       return r8168ep_ocp_read(tp, 0x128) & BIT(0);
 }
 
-static bool r8168_check_dash(struct rtl8169_private *tp)
+static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp)
 {
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_27:
        case RTL_GIGA_MAC_VER_28:
        case RTL_GIGA_MAC_VER_31:
-               return r8168dp_check_dash(tp);
+               return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE;
        case RTL_GIGA_MAC_VER_49 ... RTL_GIGA_MAC_VER_52:
-               return r8168ep_check_dash(tp);
+               return r8168ep_check_dash(tp) ? RTL_DASH_EP : RTL_DASH_NONE;
        default:
-               return false;
+               return RTL_DASH_NONE;
+       }
+}
+
+static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable)
+{
+       switch (tp->mac_version) {
+       case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26:
+       case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37:
+       case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63:
+               if (enable)
+                       RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN);
+               else
+                       RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | D3_NO_PLL_DOWN);
+               break;
+       default:
+               break;
        }
 }
 
@@ -1396,6 +1427,7 @@ static void __rtl8169_set_wol(struct rtl8169_private *tp, u32 wolopts)
        rtl_lock_config_regs(tp);
 
        device_set_wakeup_enable(tp_to_dev(tp), wolopts);
+       rtl_set_d3_pll_down(tp, !wolopts);
        tp->dev->wol_enabled = wolopts ? 1 : 0;
 }
 
@@ -1962,7 +1994,11 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii)
                { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 },
 
                /* 8168DP family. */
-               { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 },
+               /* It seems this early RTL8168dp version never made it to
+                * the wild. Let's see whether somebody complains, if not
+                * we'll remove support for this chip version completely.
+                * { 0x7cf, 0x288,      RTL_GIGA_MAC_VER_27 },
+                */
                { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 },
                { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 },
 
@@ -2081,18 +2117,12 @@ static void rtl8125b_config_eee_mac(struct rtl8169_private *tp)
        r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
 }
 
-static void rtl_rar_exgmac_set(struct rtl8169_private *tp, u8 *addr)
+static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr)
 {
-       const u16 w[] = {
-               addr[0] | (addr[1] << 8),
-               addr[2] | (addr[3] << 8),
-               addr[4] | (addr[5] << 8)
-       };
-
-       rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, w[0] | (w[1] << 16));
-       rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, w[2]);
-       rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, w[0] << 16);
-       rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, w[1] | (w[2] << 16));
+       rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, get_unaligned_le32(addr));
+       rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, get_unaligned_le16(addr + 4));
+       rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, get_unaligned_le16(addr) << 16);
+       rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, get_unaligned_le32(addr + 2));
 }
 
 u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp)
@@ -2142,14 +2172,14 @@ static void rtl8169_init_phy(struct rtl8169_private *tp)
        genphy_soft_reset(tp->phydev);
 }
 
-static void rtl_rar_set(struct rtl8169_private *tp, u8 *addr)
+static void rtl_rar_set(struct rtl8169_private *tp, const u8 *addr)
 {
        rtl_unlock_config_regs(tp);
 
-       RTL_W32(tp, MAC4, addr[4] | addr[5] << 8);
+       RTL_W32(tp, MAC4, get_unaligned_le16(addr + 4));
        rtl_pci_commit(tp);
 
-       RTL_W32(tp, MAC0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24);
+       RTL_W32(tp, MAC0, get_unaligned_le32(addr));
        rtl_pci_commit(tp);
 
        if (tp->mac_version == RTL_GIGA_MAC_VER_34)
@@ -2172,28 +2202,16 @@ static int rtl_set_mac_address(struct net_device *dev, void *p)
        return 0;
 }
 
-static void rtl_wol_suspend_quirk(struct rtl8169_private *tp)
+static void rtl_wol_enable_rx(struct rtl8169_private *tp)
 {
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_25:
-       case RTL_GIGA_MAC_VER_26:
-       case RTL_GIGA_MAC_VER_29:
-       case RTL_GIGA_MAC_VER_30:
-       case RTL_GIGA_MAC_VER_32:
-       case RTL_GIGA_MAC_VER_33:
-       case RTL_GIGA_MAC_VER_34:
-       case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_63:
+       if (tp->mac_version >= RTL_GIGA_MAC_VER_25)
                RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) |
                        AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
-               break;
-       default:
-               break;
-       }
 }
 
-static void rtl_pll_power_down(struct rtl8169_private *tp)
+static void rtl_prepare_power_down(struct rtl8169_private *tp)
 {
-       if (r8168_check_dash(tp))
+       if (tp->dash_type != RTL_DASH_NONE)
                return;
 
        if (tp->mac_version == RTL_GIGA_MAC_VER_32 ||
@@ -2202,62 +2220,8 @@ static void rtl_pll_power_down(struct rtl8169_private *tp)
 
        if (device_may_wakeup(tp_to_dev(tp))) {
                phy_speed_down(tp->phydev, false);
-               rtl_wol_suspend_quirk(tp);
-               return;
+               rtl_wol_enable_rx(tp);
        }
-
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_33:
-       case RTL_GIGA_MAC_VER_37:
-       case RTL_GIGA_MAC_VER_39:
-       case RTL_GIGA_MAC_VER_43:
-       case RTL_GIGA_MAC_VER_44:
-       case RTL_GIGA_MAC_VER_45:
-       case RTL_GIGA_MAC_VER_46:
-       case RTL_GIGA_MAC_VER_47:
-       case RTL_GIGA_MAC_VER_48:
-       case RTL_GIGA_MAC_VER_50 ... RTL_GIGA_MAC_VER_63:
-               RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80);
-               break;
-       case RTL_GIGA_MAC_VER_40:
-       case RTL_GIGA_MAC_VER_41:
-       case RTL_GIGA_MAC_VER_49:
-               rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000);
-               RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~0x80);
-               break;
-       default:
-               break;
-       }
-}
-
-static void rtl_pll_power_up(struct rtl8169_private *tp)
-{
-       switch (tp->mac_version) {
-       case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_33:
-       case RTL_GIGA_MAC_VER_37:
-       case RTL_GIGA_MAC_VER_39:
-       case RTL_GIGA_MAC_VER_43:
-               RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0x80);
-               break;
-       case RTL_GIGA_MAC_VER_44:
-       case RTL_GIGA_MAC_VER_45:
-       case RTL_GIGA_MAC_VER_46:
-       case RTL_GIGA_MAC_VER_47:
-       case RTL_GIGA_MAC_VER_48:
-       case RTL_GIGA_MAC_VER_50 ... RTL_GIGA_MAC_VER_63:
-               RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0);
-               break;
-       case RTL_GIGA_MAC_VER_40:
-       case RTL_GIGA_MAC_VER_41:
-       case RTL_GIGA_MAC_VER_49:
-               RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | 0xc0);
-               rtl_eri_set_bits(tp, 0x1a8, 0xfc000000);
-               break;
-       default:
-               break;
-       }
-
-       phy_resume(tp->phydev);
 }
 
 static void rtl_init_rxcfg(struct rtl8169_private *tp)
@@ -2338,13 +2302,14 @@ static void r8168b_1_hw_jumbo_disable(struct rtl8169_private *tp)
 static void rtl_jumbo_config(struct rtl8169_private *tp)
 {
        bool jumbo = tp->dev->mtu > ETH_DATA_LEN;
+       int readrq = 4096;
 
        rtl_unlock_config_regs(tp);
        switch (tp->mac_version) {
        case RTL_GIGA_MAC_VER_12:
        case RTL_GIGA_MAC_VER_17:
                if (jumbo) {
-                       pcie_set_readrq(tp->pci_dev, 512);
+                       readrq = 512;
                        r8168b_1_hw_jumbo_enable(tp);
                } else {
                        r8168b_1_hw_jumbo_disable(tp);
@@ -2352,7 +2317,7 @@ static void rtl_jumbo_config(struct rtl8169_private *tp)
                break;
        case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_26:
                if (jumbo) {
-                       pcie_set_readrq(tp->pci_dev, 512);
+                       readrq = 512;
                        r8168c_hw_jumbo_enable(tp);
                } else {
                        r8168c_hw_jumbo_disable(tp);
@@ -2365,20 +2330,18 @@ static void rtl_jumbo_config(struct rtl8169_private *tp)
                        r8168dp_hw_jumbo_disable(tp);
                break;
        case RTL_GIGA_MAC_VER_31 ... RTL_GIGA_MAC_VER_33:
-               if (jumbo) {
-                       pcie_set_readrq(tp->pci_dev, 512);
+               if (jumbo)
                        r8168e_hw_jumbo_enable(tp);
-               } else {
+               else
                        r8168e_hw_jumbo_disable(tp);
-               }
                break;
        default:
                break;
        }
        rtl_lock_config_regs(tp);
 
-       if (!jumbo && pci_is_pcie(tp->pci_dev) && tp->supports_gmii)
-               pcie_set_readrq(tp->pci_dev, 4096);
+       if (pci_is_pcie(tp->pci_dev) && tp->supports_gmii)
+               pcie_set_readrq(tp->pci_dev, readrq);
 }
 
 DECLARE_RTL_COND(rtl_chipcmd_cond)
@@ -4406,10 +4369,9 @@ static inline int rtl8169_fragmented_frame(u32 status)
 
 static inline void rtl8169_rx_csum(struct sk_buff *skb, u32 opts1)
 {
-       u32 status = opts1 & RxProtoMask;
+       u32 status = opts1 & (RxProtoMask | RxCSFailMask);
 
-       if (((status == RxProtoTCP) && !(opts1 & TCPFail)) ||
-           ((status == RxProtoUDP) && !(opts1 & UDPFail)))
+       if (status == RxProtoTCP || status == RxProtoUDP)
                skb->ip_summed = CHECKSUM_UNNECESSARY;
        else
                skb_checksum_none_assert(skb);
@@ -4616,12 +4578,12 @@ static void rtl8169_down(struct rtl8169_private *tp)
 
        rtl8169_cleanup(tp, true);
 
-       rtl_pll_power_down(tp);
+       rtl_prepare_power_down(tp);
 }
 
 static void rtl8169_up(struct rtl8169_private *tp)
 {
-       rtl_pll_power_up(tp);
+       phy_resume(tp->phydev);
        rtl8169_init_phy(tp);
        napi_enable(&tp->napi);
        set_bit(RTL_FLAG_TASK_ENABLED, tp->wk.flags);
@@ -4888,12 +4850,10 @@ static void rtl_shutdown(struct pci_dev *pdev)
        rtl_rar_set(tp, tp->dev->perm_addr);
 
        if (system_state == SYSTEM_POWER_OFF) {
-               if (tp->saved_wolopts) {
-                       rtl_wol_suspend_quirk(tp);
+               if (tp->saved_wolopts)
                        rtl_wol_shutdown_quirk(tp);
-               }
 
-               pci_wake_from_d3(pdev, true);
+               pci_wake_from_d3(pdev, tp->saved_wolopts);
                pci_set_power_state(pdev, PCI_D3hot);
        }
 }
@@ -4907,7 +4867,7 @@ static void rtl_remove_one(struct pci_dev *pdev)
 
        unregister_netdev(tp->dev);
 
-       if (r8168_check_dash(tp))
+       if (tp->dash_type != RTL_DASH_NONE)
                rtl8168_driver_stop(tp);
 
        rtl_release_firmware(tp);
@@ -4975,16 +4935,12 @@ static void rtl_read_mac_address(struct rtl8169_private *tp,
 {
        /* Get MAC address */
        if (rtl_is_8168evl_up(tp) && tp->mac_version != RTL_GIGA_MAC_VER_34) {
-               u32 value = rtl_eri_read(tp, 0xe0);
-
-               mac_addr[0] = (value >>  0) & 0xff;
-               mac_addr[1] = (value >>  8) & 0xff;
-               mac_addr[2] = (value >> 16) & 0xff;
-               mac_addr[3] = (value >> 24) & 0xff;
+               u32 value;
 
+               value = rtl_eri_read(tp, 0xe0);
+               put_unaligned_le32(value, mac_addr);
                value = rtl_eri_read(tp, 0xe4);
-               mac_addr[4] = (value >>  0) & 0xff;
-               mac_addr[5] = (value >>  8) & 0xff;
+               put_unaligned_le16(value, mac_addr + 4);
        } else if (rtl_is_8125(tp)) {
                rtl_read_mac_from_reg(tp, mac_addr, MAC0_BKP);
        }
@@ -5265,12 +5221,14 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* Identify chip attached to board */
        chipset = rtl8169_get_mac_version(xid, tp->supports_gmii);
        if (chipset == RTL_GIGA_MAC_NONE) {
-               dev_err(&pdev->dev, "unknown chip XID %03x\n", xid);
+               dev_err(&pdev->dev, "unknown chip XID %03x, contact r8169 maintainers (see MAINTAINERS file)\n", xid);
                return -ENODEV;
        }
 
        tp->mac_version = chipset;
 
+       tp->dash_type = rtl_check_dash(tp);
+
        tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK;
 
        if (sizeof(dma_addr_t) > 4 && tp->mac_version >= RTL_GIGA_MAC_VER_18 &&
@@ -5340,6 +5298,8 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* configure chip for default features */
        rtl8169_set_features(dev, dev->features);
 
+       rtl_set_d3_pll_down(tp, true);
+
        jumbo_max = rtl_jumbo_max(tp);
        if (jumbo_max)
                dev->max_mtu = jumbo_max;
@@ -5360,9 +5320,6 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (rc)
                return rc;
 
-       /* chip gets powered up in rtl_open() */
-       rtl_pll_power_down(tp);
-
        rc = register_netdev(dev);
        if (rc)
                return rc;
@@ -5376,7 +5333,7 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                            jumbo_max, tp->mac_version <= RTL_GIGA_MAC_VER_06 ?
                            "ok" : "ko");
 
-       if (r8168_check_dash(tp)) {
+       if (tp->dash_type != RTL_DASH_NONE) {
                netdev_info(dev, "DASH enabled\n");
                rtl8168_driver_start(tp);
        }
index 7453b17..cb47e68 100644 (file)
@@ -165,7 +165,7 @@ enum ravb_reg {
        GTO2    = 0x03A8,
        GIC     = 0x03AC,
        GIS     = 0x03B0,
-       GCPT    = 0x03B4,       /* Undocumented? */
+       GCPT    = 0x03B4,       /* Documented for R-Car Gen3 only */
        GCT0    = 0x03B8,
        GCT1    = 0x03BC,
        GCT2    = 0x03C0,
@@ -225,7 +225,7 @@ enum CSR_BIT {
        CSR_OPS_RESET   = 0x00000001,
        CSR_OPS_CONFIG  = 0x00000002,
        CSR_OPS_OPERATION = 0x00000004,
-       CSR_OPS_STANDBY = 0x00000008,   /* Undocumented? */
+       CSR_OPS_STANDBY = 0x00000008,   /* Documented for R-Car Gen3 only */
        CSR_DTS         = 0x00000100,
        CSR_TPO0        = 0x00010000,
        CSR_TPO1        = 0x00020000,
@@ -241,13 +241,12 @@ enum ESR_BIT {
        ESR_EIL         = 0x00001000,
 };
 
-/* APSR */
+/* APSR (R-Car Gen3 only) */
 enum APSR_BIT {
-       APSR_MEMS               = 0x00000002,
-       APSR_CMSW               = 0x00000010,
-       APSR_DM                 = 0x00006000,   /* Undocumented? */
-       APSR_DM_RDM             = 0x00002000,
-       APSR_DM_TDM             = 0x00004000,
+       APSR_MEMS       = 0x00000002,   /* Undocumented */
+       APSR_CMSW       = 0x00000010,
+       APSR_RDM        = 0x00002000,
+       APSR_TDM        = 0x00004000,
 };
 
 /* RCR */
@@ -530,16 +529,16 @@ enum RIS2_BIT {
 
 /* TIC */
 enum TIC_BIT {
-       TIC_FTE0        = 0x00000001,   /* Undocumented? */
-       TIC_FTE1        = 0x00000002,   /* Undocumented? */
+       TIC_FTE0        = 0x00000001,   /* Documented for R-Car Gen3 only */
+       TIC_FTE1        = 0x00000002,   /* Documented for R-Car Gen3 only */
        TIC_TFUE        = 0x00000100,
        TIC_TFWE        = 0x00000200,
 };
 
 /* TIS */
 enum TIS_BIT {
-       TIS_FTF0        = 0x00000001,   /* Undocumented? */
-       TIS_FTF1        = 0x00000002,   /* Undocumented? */
+       TIS_FTF0        = 0x00000001,   /* Documented for R-Car Gen3 only */
+       TIS_FTF1        = 0x00000002,   /* Documented for R-Car Gen3 only */
        TIS_TFUF        = 0x00000100,
        TIS_TFWF        = 0x00000200,
        TIS_RESERVED    = (GENMASK(31, 20) | GENMASK(15, 12) | GENMASK(7, 4))
@@ -547,8 +546,8 @@ enum TIS_BIT {
 
 /* ISS */
 enum ISS_BIT {
-       ISS_FRS         = 0x00000001,   /* Undocumented? */
-       ISS_FTS         = 0x00000004,   /* Undocumented? */
+       ISS_FRS         = 0x00000001,   /* Documented for R-Car Gen3 only */
+       ISS_FTS         = 0x00000004,   /* Documented for R-Car Gen3 only */
        ISS_ES          = 0x00000040,
        ISS_MS          = 0x00000080,
        ISS_TFUS        = 0x00000100,
@@ -608,13 +607,13 @@ enum GTI_BIT {
 
 /* GIC */
 enum GIC_BIT {
-       GIC_PTCE        = 0x00000001,   /* Undocumented? */
+       GIC_PTCE        = 0x00000001,   /* Documented for R-Car Gen3 only */
        GIC_PTME        = 0x00000004,
 };
 
 /* GIS */
 enum GIS_BIT {
-       GIS_PTCF        = 0x00000001,   /* Undocumented? */
+       GIS_PTCF        = 0x00000001,   /* Documented for R-Car Gen3 only */
        GIS_PTMF        = 0x00000004,
        GIS_RESERVED    = GENMASK(15, 10),
 };
@@ -808,10 +807,10 @@ enum ECMR_BIT {
        ECMR_TE         = 0x00000020,
        ECMR_RE         = 0x00000040,
        ECMR_MPDE       = 0x00000200,
-       ECMR_TXF        = 0x00010000,   /* Undocumented? */
+       ECMR_TXF        = 0x00010000,   /* Documented for R-Car Gen3 only */
        ECMR_RXF        = 0x00020000,
        ECMR_PFR        = 0x00040000,
-       ECMR_ZPF        = 0x00080000,   /* Undocumented? */
+       ECMR_ZPF        = 0x00080000,   /* Documented for R-Car Gen3 only */
        ECMR_RZPF       = 0x00100000,
        ECMR_DPAD       = 0x00200000,
        ECMR_RCSC       = 0x00800000,
@@ -830,7 +829,7 @@ enum ECSR_BIT {
 enum ECSIPR_BIT {
        ECSIPR_ICDIP    = 0x00000001,
        ECSIPR_MPDIP    = 0x00000002,
-       ECSIPR_LCHNGIP  = 0x00000004,   /* Undocumented? */
+       ECSIPR_LCHNGIP  = 0x00000004,
 };
 
 /* PIR */
index bd30505..eb0c03b 100644 (file)
@@ -2034,10 +2034,10 @@ static void ravb_set_delay_mode(struct net_device *ndev)
        u32 set = 0;
 
        if (priv->rxcidm)
-               set |= APSR_DM_RDM;
+               set |= APSR_RDM;
        if (priv->txcidm)
-               set |= APSR_DM_TDM;
-       ravb_modify(ndev, APSR, APSR_DM, set);
+               set |= APSR_TDM;
+       ravb_modify(ndev, APSR, APSR_RDM | APSR_TDM, set);
 }
 
 static int ravb_probe(struct platform_device *pdev)
index c633046..590b088 100644 (file)
@@ -2606,10 +2606,10 @@ static int sh_eth_close(struct net_device *ndev)
        /* Free all the skbuffs in the Rx queue and the DMA buffer. */
        sh_eth_ring_free(ndev);
 
-       pm_runtime_put_sync(&mdp->pdev->dev);
-
        mdp->is_opened = 0;
 
+       pm_runtime_put(&mdp->pdev->dev);
+
        return 0;
 }
 
@@ -3034,6 +3034,28 @@ static int sh_mdio_release(struct sh_eth_private *mdp)
        return 0;
 }
 
+static int sh_mdiobb_read(struct mii_bus *bus, int phy, int reg)
+{
+       int res;
+
+       pm_runtime_get_sync(bus->parent);
+       res = mdiobb_read(bus, phy, reg);
+       pm_runtime_put(bus->parent);
+
+       return res;
+}
+
+static int sh_mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
+{
+       int res;
+
+       pm_runtime_get_sync(bus->parent);
+       res = mdiobb_write(bus, phy, reg, val);
+       pm_runtime_put(bus->parent);
+
+       return res;
+}
+
 /* MDIO bus init function */
 static int sh_mdio_init(struct sh_eth_private *mdp,
                        struct sh_eth_plat_data *pd)
@@ -3058,6 +3080,10 @@ static int sh_mdio_init(struct sh_eth_private *mdp,
        if (!mdp->mii_bus)
                return -ENOMEM;
 
+       /* Wrap accessors with Runtime PM-aware ops */
+       mdp->mii_bus->read = sh_mdiobb_read;
+       mdp->mii_bus->write = sh_mdiobb_write;
+
        /* Hook up MII support for ethtool */
        mdp->mii_bus->name = "sh_mii";
        mdp->mii_bus->parent = dev;
index 6fad253..315a6e5 100644 (file)
@@ -103,15 +103,13 @@ struct rocker_world_ops {
        int (*port_attr_stp_state_set)(struct rocker_port *rocker_port,
                                       u8 state);
        int (*port_attr_bridge_flags_set)(struct rocker_port *rocker_port,
-                                         unsigned long brport_flags,
-                                         struct switchdev_trans *trans);
+                                         unsigned long brport_flags);
        int (*port_attr_bridge_flags_support_get)(const struct rocker_port *
                                                  rocker_port,
                                                  unsigned long *
                                                  p_brport_flags);
        int (*port_attr_bridge_ageing_time_set)(struct rocker_port *rocker_port,
-                                               u32 ageing_time,
-                                               struct switchdev_trans *trans);
+                                               u32 ageing_time);
        int (*port_obj_vlan_add)(struct rocker_port *rocker_port,
                                 const struct switchdev_obj_port_vlan *vlan);
        int (*port_obj_vlan_del)(struct rocker_port *rocker_port,
index dd0bc7f..740a715 100644 (file)
@@ -1550,17 +1550,13 @@ static void rocker_world_port_stop(struct rocker_port *rocker_port)
 }
 
 static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port,
-                                               u8 state,
-                                               struct switchdev_trans *trans)
+                                               u8 state)
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
 
        if (!wops->port_attr_stp_state_set)
                return -EOPNOTSUPP;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        return wops->port_attr_stp_state_set(rocker_port, state);
 }
 
@@ -1580,8 +1576,7 @@ rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port *
 
 static int
 rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port,
-                                           unsigned long brport_flags,
-                                           struct switchdev_trans *trans)
+                                           unsigned long brport_flags)
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
        unsigned long brport_flags_s;
@@ -1603,52 +1598,37 @@ rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port,
 
 static int
 rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
-                                       unsigned long brport_flags,
-                                       struct switchdev_trans *trans)
+                                       unsigned long brport_flags)
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
 
        if (!wops->port_attr_bridge_flags_set)
                return -EOPNOTSUPP;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
-       return wops->port_attr_bridge_flags_set(rocker_port, brport_flags,
-                                               trans);
+       return wops->port_attr_bridge_flags_set(rocker_port, brport_flags);
 }
 
 static int
 rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
-                                             u32 ageing_time,
-                                             struct switchdev_trans *trans)
-
+                                             u32 ageing_time)
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
 
        if (!wops->port_attr_bridge_ageing_time_set)
                return -EOPNOTSUPP;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
-       return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time,
-                                                     trans);
+       return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time);
 }
 
 static int
 rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port,
-                              const struct switchdev_obj_port_vlan *vlan,
-                              struct switchdev_trans *trans)
+                              const struct switchdev_obj_port_vlan *vlan)
 {
        struct rocker_world_ops *wops = rocker_port->rocker->wops;
 
        if (!wops->port_obj_vlan_add)
                return -EOPNOTSUPP;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        return wops->port_obj_vlan_add(rocker_port, vlan);
 }
 
@@ -2066,8 +2046,7 @@ static const struct net_device_ops rocker_port_netdev_ops = {
  ********************/
 
 static int rocker_port_attr_set(struct net_device *dev,
-                               const struct switchdev_attr *attr,
-                               struct switchdev_trans *trans)
+                               const struct switchdev_attr *attr)
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
        int err = 0;
@@ -2075,23 +2054,19 @@ static int rocker_port_attr_set(struct net_device *dev,
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
                err = rocker_world_port_attr_stp_state_set(rocker_port,
-                                                          attr->u.stp_state,
-                                                          trans);
+                                                          attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
                err = rocker_world_port_attr_pre_bridge_flags_set(rocker_port,
-                                                             attr->u.brport_flags,
-                                                             trans);
+                                                             attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
                err = rocker_world_port_attr_bridge_flags_set(rocker_port,
-                                                             attr->u.brport_flags,
-                                                             trans);
+                                                             attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
                err = rocker_world_port_attr_bridge_ageing_time_set(rocker_port,
-                                                                   attr->u.ageing_time,
-                                                                   trans);
+                                                                   attr->u.ageing_time);
                break;
        default:
                err = -EOPNOTSUPP;
@@ -2102,8 +2077,7 @@ static int rocker_port_attr_set(struct net_device *dev,
 }
 
 static int rocker_port_obj_add(struct net_device *dev,
-                              const struct switchdev_obj *obj,
-                              struct switchdev_trans *trans)
+                              const struct switchdev_obj *obj)
 {
        struct rocker_port *rocker_port = netdev_priv(dev);
        int err = 0;
@@ -2111,8 +2085,7 @@ static int rocker_port_obj_add(struct net_device *dev,
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                err = rocker_world_port_obj_vlan_add(rocker_port,
-                                                    SWITCHDEV_OBJ_PORT_VLAN(obj),
-                                                    trans);
+                                                    SWITCHDEV_OBJ_PORT_VLAN(obj));
                break;
        default:
                err = -EOPNOTSUPP;
@@ -2725,8 +2698,7 @@ rocker_switchdev_port_attr_set_event(struct net_device *netdev,
 {
        int err;
 
-       err = rocker_port_attr_set(netdev, port_attr_info->attr,
-                                  port_attr_info->trans);
+       err = rocker_port_attr_set(netdev, port_attr_info->attr);
 
        port_attr_info->handled = true;
        return notifier_from_errno(err);
@@ -2847,8 +2819,7 @@ rocker_switchdev_port_obj_event(unsigned long event, struct net_device *netdev,
 
        switch (event) {
        case SWITCHDEV_PORT_OBJ_ADD:
-               err = rocker_port_obj_add(netdev, port_obj_info->obj,
-                                         port_obj_info->trans);
+               err = rocker_port_obj_add(netdev, port_obj_info->obj);
                break;
        case SWITCHDEV_PORT_OBJ_DEL:
                err = rocker_port_obj_del(netdev, port_obj_info->obj);
index 7072b24..967a634 100644 (file)
@@ -923,7 +923,7 @@ static int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port,
        struct ofdpa_flow_tbl_entry *entry;
        u32 priority;
        bool vlan_bridging = !!vlan_id;
-       bool dflt = !eth_dst || (eth_dst && eth_dst_mask);
+       bool dflt = !eth_dst || eth_dst_mask;
        bool wild = false;
 
        entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
@@ -2488,8 +2488,7 @@ static int ofdpa_port_attr_stp_state_set(struct rocker_port *rocker_port,
 }
 
 static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
-                                           unsigned long brport_flags,
-                                           struct switchdev_trans *trans)
+                                           unsigned long brport_flags)
 {
        struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
        unsigned long orig_flags;
@@ -2497,14 +2496,11 @@ static int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
 
        orig_flags = ofdpa_port->brport_flags;
        ofdpa_port->brport_flags = brport_flags;
-       if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING &&
-           !switchdev_trans_ph_prepare(trans))
+
+       if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING)
                err = rocker_port_set_learning(ofdpa_port->rocker_port,
                                               !!(ofdpa_port->brport_flags & BR_LEARNING));
 
-       if (switchdev_trans_ph_prepare(trans))
-               ofdpa_port->brport_flags = orig_flags;
-
        return err;
 }
 
@@ -2520,18 +2516,15 @@ ofdpa_port_attr_bridge_flags_support_get(const struct rocker_port *
 
 static int
 ofdpa_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port,
-                                      u32 ageing_time,
-                                      struct switchdev_trans *trans)
+                                      u32 ageing_time)
 {
        struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
        struct ofdpa *ofdpa = ofdpa_port->ofdpa;
 
-       if (!switchdev_trans_ph_prepare(trans)) {
-               ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time);
-               if (ofdpa_port->ageing_time < ofdpa->ageing_time)
-                       ofdpa->ageing_time = ofdpa_port->ageing_time;
-               mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies);
-       }
+       ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time);
+       if (ofdpa_port->ageing_time < ofdpa->ageing_time)
+               ofdpa->ageing_time = ofdpa_port->ageing_time;
+       mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies);
 
        return 0;
 }
@@ -2540,32 +2533,16 @@ static int ofdpa_port_obj_vlan_add(struct rocker_port *rocker_port,
                                   const struct switchdev_obj_port_vlan *vlan)
 {
        struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
-       u16 vid;
-       int err;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               err = ofdpa_port_vlan_add(ofdpa_port, vid, vlan->flags);
-               if (err)
-                       return err;
-       }
 
-       return 0;
+       return ofdpa_port_vlan_add(ofdpa_port, vlan->vid, vlan->flags);
 }
 
 static int ofdpa_port_obj_vlan_del(struct rocker_port *rocker_port,
                                   const struct switchdev_obj_port_vlan *vlan)
 {
        struct ofdpa_port *ofdpa_port = rocker_port->wpriv;
-       u16 vid;
-       int err;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               err = ofdpa_port_vlan_del(ofdpa_port, vid, vlan->flags);
-               if (err)
-                       return err;
-       }
 
-       return 0;
+       return ofdpa_port_vlan_del(ofdpa_port, vlan->vid, vlan->flags);
 }
 
 static int ofdpa_port_obj_fdb_add(struct rocker_port *rocker_port,
index 7183080..36c8625 100644 (file)
@@ -612,8 +612,6 @@ static const struct net_device_ops efx_netdev_ops = {
 #ifdef CONFIG_RFS_ACCEL
        .ndo_rx_flow_steer      = efx_filter_rfs,
 #endif
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_xdp_xmit           = efx_xdp_xmit,
        .ndo_bpf                = efx_xdp
 };
index a4a626e..1bfeee2 100644 (file)
@@ -17,6 +17,7 @@
 #include "rx_common.h"
 #include "nic.h"
 #include "sriov.h"
+#include "workarounds.h"
 
 /* This is the first interrupt mode to try out of:
  * 0 => MSI-X
@@ -137,6 +138,7 @@ static int efx_allocate_msix_channels(struct efx_nic *efx,
 {
        unsigned int n_channels = parallelism;
        int vec_count;
+       int tx_per_ev;
        int n_xdp_tx;
        int n_xdp_ev;
 
@@ -149,9 +151,9 @@ static int efx_allocate_msix_channels(struct efx_nic *efx,
         * multiple tx queues, assuming tx and ev queues are both
         * maximum size.
         */
-
+       tx_per_ev = EFX_MAX_EVQ_SIZE / EFX_TXQ_MAX_ENT(efx);
        n_xdp_tx = num_possible_cpus();
-       n_xdp_ev = DIV_ROUND_UP(n_xdp_tx, EFX_MAX_TXQ_PER_CHANNEL);
+       n_xdp_ev = DIV_ROUND_UP(n_xdp_tx, tx_per_ev);
 
        vec_count = pci_msix_vec_count(efx->pci_dev);
        if (vec_count < 0)
index aaa1128..89c5c75 100644 (file)
@@ -293,14 +293,10 @@ static bool efx_do_xdp(struct efx_nic *efx, struct efx_channel *channel,
        memcpy(rx_prefix, *ehp - efx->rx_prefix_size,
               efx->rx_prefix_size);
 
-       xdp.data = *ehp;
-       xdp.data_hard_start = xdp.data - EFX_XDP_HEADROOM;
-
+       xdp_init_buff(&xdp, efx->rx_page_buf_step, &rx_queue->xdp_rxq_info);
        /* No support yet for XDP metadata */
-       xdp_set_data_meta_invalid(&xdp);
-       xdp.data_end = xdp.data + rx_buf->len;
-       xdp.rxq = &rx_queue->xdp_rxq_info;
-       xdp.frame_sz = efx->rx_page_buf_step;
+       xdp_prepare_buff(&xdp, *ehp - EFX_XDP_HEADROOM, EFX_XDP_HEADROOM,
+                        rx_buf->len, false);
 
        xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp);
        rcu_read_unlock();
index 742a1f7..891b492 100644 (file)
@@ -2191,7 +2191,7 @@ static const struct of_device_id smc91x_match[] = {
 MODULE_DEVICE_TABLE(of, smc91x_match);
 
 /**
- * of_try_set_control_gpio - configure a gpio if it exists
+ * try_toggle_control_gpio - configure a gpio if it exists
  * @dev: net device
  * @desc: where to store the GPIO descriptor, if it exists
  * @name: name of the GPIO in DT
index 823d9a7..606c79d 100644 (file)
@@ -557,6 +557,7 @@ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
        unsigned int addr;
        int i, reg;
 
+       pm_runtime_get_sync(bus->parent);
        spin_lock_irqsave(&pdata->mac_lock, flags);
 
        /* Confirm MII not busy */
@@ -582,6 +583,7 @@ static int smsc911x_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
 
 out:
        spin_unlock_irqrestore(&pdata->mac_lock, flags);
+       pm_runtime_put(bus->parent);
        return reg;
 }
 
@@ -594,6 +596,7 @@ static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
        unsigned int addr;
        int i, reg;
 
+       pm_runtime_get_sync(bus->parent);
        spin_lock_irqsave(&pdata->mac_lock, flags);
 
        /* Confirm MII not busy */
@@ -623,6 +626,7 @@ static int smsc911x_mii_write(struct mii_bus *bus, int phyaddr, int regidx,
 
 out:
        spin_unlock_irqrestore(&pdata->mac_lock, flags);
+       pm_runtime_put(bus->parent);
        return reg;
 }
 
@@ -1589,6 +1593,8 @@ static int smsc911x_open(struct net_device *dev)
        int retval;
        int irq_flags;
 
+       pm_runtime_get_sync(dev->dev.parent);
+
        /* find and start the given phy */
        if (!dev->phydev) {
                retval = smsc911x_mii_probe(dev);
@@ -1735,6 +1741,7 @@ mii_free_out:
        phy_disconnect(dev->phydev);
        dev->phydev = NULL;
 out:
+       pm_runtime_put(dev->dev.parent);
        return retval;
 }
 
@@ -1766,6 +1773,7 @@ static int smsc911x_stop(struct net_device *dev)
                dev->phydev = NULL;
        }
        netif_carrier_off(dev);
+       pm_runtime_put(dev->dev.parent);
 
        SMSC_TRACE(pdata, ifdown, "Interface stopped");
        return 0;
@@ -2334,7 +2342,6 @@ static int smsc911x_drv_remove(struct platform_device *pdev)
 
        free_netdev(dev);
 
-       pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
        return 0;
@@ -2540,6 +2547,7 @@ static int smsc911x_drv_probe(struct platform_device *pdev)
        }
 
        spin_unlock_irq(&pdata->mac_lock);
+       pm_runtime_put(&pdev->dev);
 
        netdev_info(dev, "MAC Address: %pM\n", dev->dev_addr);
 
index 19d20a6..3c53051 100644 (file)
@@ -956,8 +956,7 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget)
        u32 xdp_act = 0;
        int done = 0;
 
-       xdp.rxq = &dring->xdp_rxq;
-       xdp.frame_sz = PAGE_SIZE;
+       xdp_init_buff(&xdp, PAGE_SIZE, &dring->xdp_rxq);
 
        rcu_read_lock();
        xdp_prog = READ_ONCE(priv->xdp_prog);
@@ -1016,10 +1015,8 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget)
                                        dma_dir);
                prefetch(desc->addr);
 
-               xdp.data_hard_start = desc->addr;
-               xdp.data = desc->addr + NETSEC_RXBUF_HEADROOM;
-               xdp_set_data_meta_invalid(&xdp);
-               xdp.data_end = xdp.data + pkt_len;
+               xdp_prepare_buff(&xdp, desc->addr, NETSEC_RXBUF_HEADROOM,
+                                pkt_len, false);
 
                if (xdp_prog) {
                        xdp_result = netsec_run_xdp(priv, xdp_prog, &xdp);
index 82b1c7a..ba0e4d2 100644 (file)
@@ -129,7 +129,7 @@ static int intel_eth_plat_probe(struct platform_device *pdev)
                                if (ret) {
                                        dev_err(&pdev->dev,
                                                "Failed to set tx_clk\n");
-                                       return ret;
+                                       goto err_remove_config_dt;
                                }
                        }
                }
@@ -143,7 +143,7 @@ static int intel_eth_plat_probe(struct platform_device *pdev)
                        if (ret) {
                                dev_err(&pdev->dev,
                                        "Failed to set clk_ptp_ref\n");
-                               return ret;
+                               goto err_remove_config_dt;
                        }
                }
        }
index a2e80c8..1c9c67b 100644 (file)
@@ -375,6 +375,7 @@ static int ehl_pse0_common_data(struct pci_dev *pdev,
                                struct plat_stmmacenet_data *plat)
 {
        plat->bus_id = 2;
+       plat->addr64 = 32;
        return ehl_common_data(pdev, plat);
 }
 
@@ -406,6 +407,7 @@ static int ehl_pse1_common_data(struct pci_dev *pdev,
                                struct plat_stmmacenet_data *plat)
 {
        plat->bus_id = 3;
+       plat->addr64 = 32;
        return ehl_common_data(pdev, plat);
 }
 
@@ -457,6 +459,21 @@ static struct stmmac_pci_info tgl_sgmii1g_info = {
        .setup = tgl_sgmii_data,
 };
 
+static int adls_sgmii_data(struct pci_dev *pdev,
+                          struct plat_stmmacenet_data *plat)
+{
+       plat->bus_id = 1;
+       plat->phy_interface = PHY_INTERFACE_MODE_SGMII;
+
+       /* SerDes power up and power down are done in BIOS for ADL */
+
+       return tgl_common_data(pdev, plat);
+}
+
+static struct stmmac_pci_info adls_sgmii1g_info = {
+       .setup = adls_sgmii_data,
+};
+
 static const struct stmmac_pci_func_data galileo_stmmac_func_data[] = {
        {
                .func = 6,
@@ -721,7 +738,11 @@ static SIMPLE_DEV_PM_OPS(intel_eth_pm_ops, intel_eth_pci_suspend,
 #define PCI_DEVICE_ID_INTEL_EHL_PSE1_RGMII1G_ID                0x4bb0
 #define PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII1G_ID                0x4bb1
 #define PCI_DEVICE_ID_INTEL_EHL_PSE1_SGMII2G5_ID       0x4bb2
+#define PCI_DEVICE_ID_INTEL_TGLH_SGMII1G_0_ID          0x43ac
+#define PCI_DEVICE_ID_INTEL_TGLH_SGMII1G_1_ID          0x43a2
 #define PCI_DEVICE_ID_INTEL_TGL_SGMII1G_ID             0xa0ac
+#define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_0_ID          0x7aac
+#define PCI_DEVICE_ID_INTEL_ADLS_SGMII1G_1_ID          0x7aad
 
 static const struct pci_device_id intel_eth_pci_id_table[] = {
        { PCI_DEVICE_DATA(INTEL, QUARK_ID, &quark_info) },
@@ -735,6 +756,10 @@ static const struct pci_device_id intel_eth_pci_id_table[] = {
        { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII1G_ID, &ehl_pse1_sgmii1g_info) },
        { PCI_DEVICE_DATA(INTEL, EHL_PSE1_SGMII2G5_ID, &ehl_pse1_sgmii1g_info) },
        { PCI_DEVICE_DATA(INTEL, TGL_SGMII1G_ID, &tgl_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_0_ID, &tgl_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, TGLH_SGMII1G_1_ID, &tgl_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_0_ID, &adls_sgmii1g_info) },
+       { PCI_DEVICE_DATA(INTEL, ADLS_SGMII1G_1_ID, &adls_sgmii1g_info) },
        {}
 };
 MODULE_DEVICE_TABLE(pci, intel_eth_pci_id_table);
index 459ae71..848e5c3 100644 (file)
  */
 #define PRG_ETH0_ADJ_SKEW              GENMASK(24, 20)
 
+#define PRG_ETH1                       0x4
+
+/* Defined for adding a delay to the input RX_CLK for better timing.
+ * Each step is 200ps. These bits are used with external RGMII PHYs
+ * because RGMII RX only has the small window. cfg_rxclk_dly can
+ * adjust the window between RX_CLK and RX_DATA and improve the stability
+ * of "rx data valid".
+ */
+#define PRG_ETH1_CFG_RXCLK_DLY         GENMASK(19, 16)
+
 struct meson8b_dwmac;
 
 struct meson8b_dwmac_data {
        int (*set_phy_mode)(struct meson8b_dwmac *dwmac);
+       bool has_prg_eth1_rgmii_rx_delay;
 };
 
 struct meson8b_dwmac {
@@ -82,7 +93,7 @@ struct meson8b_dwmac {
        phy_interface_t                 phy_mode;
        struct clk                      *rgmii_tx_clk;
        u32                             tx_delay_ns;
-       u32                             rx_delay_ns;
+       u32                             rx_delay_ps;
        struct clk                      *timing_adj_clk;
 };
 
@@ -135,7 +146,7 @@ static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
        struct device *dev = dwmac->dev;
        static const struct clk_parent_data mux_parents[] = {
                { .fw_name = "clkin0", },
-               { .fw_name = "clkin1", },
+               { .index = -1, },
        };
        static const struct clk_div_table div_table[] = {
                { .div = 2, .val = 2, },
@@ -268,32 +279,37 @@ static int meson8b_devm_clk_prepare_enable(struct meson8b_dwmac *dwmac,
        return 0;
 }
 
-static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
+static int meson8b_init_rgmii_delays(struct meson8b_dwmac *dwmac)
 {
-       u32 tx_dly_config, rx_dly_config, delay_config;
+       u32 tx_dly_config, rx_adj_config, cfg_rxclk_dly, delay_config;
        int ret;
 
+       rx_adj_config = 0;
+       cfg_rxclk_dly = 0;
        tx_dly_config = FIELD_PREP(PRG_ETH0_TXDLY_MASK,
                                   dwmac->tx_delay_ns >> 1);
 
-       if (dwmac->rx_delay_ns == 2)
-               rx_dly_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
-       else
-               rx_dly_config = 0;
+       if (dwmac->data->has_prg_eth1_rgmii_rx_delay)
+               cfg_rxclk_dly = FIELD_PREP(PRG_ETH1_CFG_RXCLK_DLY,
+                                          dwmac->rx_delay_ps / 200);
+       else if (dwmac->rx_delay_ps == 2000)
+               rx_adj_config = PRG_ETH0_ADJ_ENABLE | PRG_ETH0_ADJ_SETUP;
 
        switch (dwmac->phy_mode) {
        case PHY_INTERFACE_MODE_RGMII:
-               delay_config = tx_dly_config | rx_dly_config;
+               delay_config = tx_dly_config | rx_adj_config;
                break;
        case PHY_INTERFACE_MODE_RGMII_RXID:
                delay_config = tx_dly_config;
+               cfg_rxclk_dly = 0;
                break;
        case PHY_INTERFACE_MODE_RGMII_TXID:
-               delay_config = rx_dly_config;
+               delay_config = rx_adj_config;
                break;
        case PHY_INTERFACE_MODE_RGMII_ID:
        case PHY_INTERFACE_MODE_RMII:
                delay_config = 0;
+               cfg_rxclk_dly = 0;
                break;
        default:
                dev_err(dwmac->dev, "unsupported phy-mode %s\n",
@@ -301,7 +317,7 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
                return -EINVAL;
        }
 
-       if (rx_dly_config & PRG_ETH0_ADJ_ENABLE) {
+       if (delay_config & PRG_ETH0_ADJ_ENABLE) {
                if (!dwmac->timing_adj_clk) {
                        dev_err(dwmac->dev,
                                "The timing-adjustment clock is mandatory for the RX delay re-timing\n");
@@ -323,6 +339,16 @@ static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
                                PRG_ETH0_ADJ_DELAY | PRG_ETH0_ADJ_SKEW,
                                delay_config);
 
+       meson8b_dwmac_mask_bits(dwmac, PRG_ETH1, PRG_ETH1_CFG_RXCLK_DLY,
+                               cfg_rxclk_dly);
+
+       return 0;
+}
+
+static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
+{
+       int ret;
+
        if (phy_interface_mode_is_rgmii(dwmac->phy_mode)) {
                /* only relevant for RMII mode -> disable in RGMII mode */
                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
@@ -406,16 +432,30 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
                                 &dwmac->tx_delay_ns))
                dwmac->tx_delay_ns = 2;
 
-       /* use 0ns as fallback since this is what most boards actually use */
-       if (of_property_read_u32(pdev->dev.of_node, "amlogic,rx-delay-ns",
-                                &dwmac->rx_delay_ns))
-               dwmac->rx_delay_ns = 0;
+       /* RX delay defaults to 0ps since this is what many boards use */
+       if (of_property_read_u32(pdev->dev.of_node, "rx-internal-delay-ps",
+                                &dwmac->rx_delay_ps)) {
+               if (!of_property_read_u32(pdev->dev.of_node,
+                                         "amlogic,rx-delay-ns",
+                                         &dwmac->rx_delay_ps))
+                       /* convert ns to ps */
+                       dwmac->rx_delay_ps *= 1000;
+       }
 
-       if (dwmac->rx_delay_ns != 0 && dwmac->rx_delay_ns != 2) {
-               dev_err(&pdev->dev,
-                       "The only allowed RX delays values are: 0ns, 2ns");
-               ret = -EINVAL;
-               goto err_remove_config_dt;
+       if (dwmac->data->has_prg_eth1_rgmii_rx_delay) {
+               if (dwmac->rx_delay_ps > 3000 || dwmac->rx_delay_ps % 200) {
+                       dev_err(dwmac->dev,
+                               "The RGMII RX delay range is 0..3000ps in 200ps steps");
+                       ret = -EINVAL;
+                       goto err_remove_config_dt;
+               }
+       } else {
+               if (dwmac->rx_delay_ps != 0 && dwmac->rx_delay_ps != 2000) {
+                       dev_err(dwmac->dev,
+                               "The only allowed RGMII RX delays values are: 0ps, 2000ps");
+                       ret = -EINVAL;
+                       goto err_remove_config_dt;
+               }
        }
 
        dwmac->timing_adj_clk = devm_clk_get_optional(dwmac->dev,
@@ -425,6 +465,10 @@ static int meson8b_dwmac_probe(struct platform_device *pdev)
                goto err_remove_config_dt;
        }
 
+       ret = meson8b_init_rgmii_delays(dwmac);
+       if (ret)
+               goto err_remove_config_dt;
+
        ret = meson8b_init_rgmii_tx_clk(dwmac);
        if (ret)
                goto err_remove_config_dt;
@@ -453,10 +497,17 @@ err_remove_config_dt:
 
 static const struct meson8b_dwmac_data meson8b_dwmac_data = {
        .set_phy_mode = meson8b_set_phy_mode,
+       .has_prg_eth1_rgmii_rx_delay = false,
 };
 
 static const struct meson8b_dwmac_data meson_axg_dwmac_data = {
        .set_phy_mode = meson_axg_set_phy_mode,
+       .has_prg_eth1_rgmii_rx_delay = false,
+};
+
+static const struct meson8b_dwmac_data meson_g12a_dwmac_data = {
+       .set_phy_mode = meson_axg_set_phy_mode,
+       .has_prg_eth1_rgmii_rx_delay = true,
 };
 
 static const struct of_device_id meson8b_dwmac_match[] = {
@@ -478,7 +529,7 @@ static const struct of_device_id meson8b_dwmac_match[] = {
        },
        {
                .compatible = "amlogic,meson-g12a-dwmac",
-               .data = &meson_axg_dwmac_data,
+               .data = &meson_g12a_dwmac_data,
        },
        { }
 };
index 58e0511..a5e0eff 100644 (file)
@@ -64,6 +64,7 @@ struct emac_variant {
  * @variant:   reference to the current board variant
  * @regmap:    regmap for using the syscon
  * @internal_phy_powered: Does the internal PHY is enabled
+ * @use_internal_phy: Is the internal PHY selected for use
  * @mux_handle:        Internal pointer used by mdio-mux lib
  */
 struct sunxi_priv_data {
@@ -74,6 +75,7 @@ struct sunxi_priv_data {
        const struct emac_variant *variant;
        struct regmap_field *regmap_field;
        bool internal_phy_powered;
+       bool use_internal_phy;
        void *mux_handle;
 };
 
@@ -539,8 +541,11 @@ static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = {
        .dma_interrupt = sun8i_dwmac_dma_interrupt,
 };
 
+static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv);
+
 static int sun8i_dwmac_init(struct platform_device *pdev, void *priv)
 {
+       struct net_device *ndev = platform_get_drvdata(pdev);
        struct sunxi_priv_data *gmac = priv;
        int ret;
 
@@ -554,13 +559,25 @@ static int sun8i_dwmac_init(struct platform_device *pdev, void *priv)
 
        ret = clk_prepare_enable(gmac->tx_clk);
        if (ret) {
-               if (gmac->regulator)
-                       regulator_disable(gmac->regulator);
                dev_err(&pdev->dev, "Could not enable AHB clock\n");
-               return ret;
+               goto err_disable_regulator;
+       }
+
+       if (gmac->use_internal_phy) {
+               ret = sun8i_dwmac_power_internal_phy(netdev_priv(ndev));
+               if (ret)
+                       goto err_disable_clk;
        }
 
        return 0;
+
+err_disable_clk:
+       clk_disable_unprepare(gmac->tx_clk);
+err_disable_regulator:
+       if (gmac->regulator)
+               regulator_disable(gmac->regulator);
+
+       return ret;
 }
 
 static void sun8i_dwmac_core_init(struct mac_device_info *hw,
@@ -831,7 +848,6 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
        struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
        u32 reg, val;
        int ret = 0;
-       bool need_power_ephy = false;
 
        if (current_child ^ desired_child) {
                regmap_field_read(gmac->regmap_field, &reg);
@@ -839,13 +855,12 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
                case DWMAC_SUN8I_MDIO_MUX_INTERNAL_ID:
                        dev_info(priv->device, "Switch mux to internal PHY");
                        val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SELECT;
-
-                       need_power_ephy = true;
+                       gmac->use_internal_phy = true;
                        break;
                case DWMAC_SUN8I_MDIO_MUX_EXTERNAL_ID:
                        dev_info(priv->device, "Switch mux to external PHY");
                        val = (reg & ~H3_EPHY_MUX_MASK) | H3_EPHY_SHUTDOWN;
-                       need_power_ephy = false;
+                       gmac->use_internal_phy = false;
                        break;
                default:
                        dev_err(priv->device, "Invalid child ID %x\n",
@@ -853,7 +868,7 @@ static int mdio_mux_syscon_switch_fn(int current_child, int desired_child,
                        return -EINVAL;
                }
                regmap_field_write(gmac->regmap_field, val);
-               if (need_power_ephy) {
+               if (gmac->use_internal_phy) {
                        ret = sun8i_dwmac_power_internal_phy(priv);
                        if (ret)
                                return ret;
@@ -883,22 +898,23 @@ static int sun8i_dwmac_register_mdio_mux(struct stmmac_priv *priv)
        return ret;
 }
 
-static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
+static int sun8i_dwmac_set_syscon(struct device *dev,
+                                 struct plat_stmmacenet_data *plat)
 {
-       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
-       struct device_node *node = priv->device->of_node;
+       struct sunxi_priv_data *gmac = plat->bsp_priv;
+       struct device_node *node = dev->of_node;
        int ret;
        u32 reg, val;
 
        ret = regmap_field_read(gmac->regmap_field, &val);
        if (ret) {
-               dev_err(priv->device, "Fail to read from regmap field.\n");
+               dev_err(dev, "Fail to read from regmap field.\n");
                return ret;
        }
 
        reg = gmac->variant->default_syscon_value;
        if (reg != val)
-               dev_warn(priv->device,
+               dev_warn(dev,
                         "Current syscon value is not the default %x (expect %x)\n",
                         val, reg);
 
@@ -911,9 +927,9 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
                /* Force EPHY xtal frequency to 24MHz. */
                reg |= H3_EPHY_CLK_SEL;
 
-               ret = of_mdio_parse_addr(priv->device, priv->plat->phy_node);
+               ret = of_mdio_parse_addr(dev, plat->phy_node);
                if (ret < 0) {
-                       dev_err(priv->device, "Could not parse MDIO addr\n");
+                       dev_err(dev, "Could not parse MDIO addr\n");
                        return ret;
                }
                /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY
@@ -929,17 +945,17 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
 
        if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) {
                if (val % 100) {
-                       dev_err(priv->device, "tx-delay must be a multiple of 100\n");
+                       dev_err(dev, "tx-delay must be a multiple of 100\n");
                        return -EINVAL;
                }
                val /= 100;
-               dev_dbg(priv->device, "set tx-delay to %x\n", val);
+               dev_dbg(dev, "set tx-delay to %x\n", val);
                if (val <= gmac->variant->tx_delay_max) {
                        reg &= ~(gmac->variant->tx_delay_max <<
                                 SYSCON_ETXDC_SHIFT);
                        reg |= (val << SYSCON_ETXDC_SHIFT);
                } else {
-                       dev_err(priv->device, "Invalid TX clock delay: %d\n",
+                       dev_err(dev, "Invalid TX clock delay: %d\n",
                                val);
                        return -EINVAL;
                }
@@ -947,17 +963,17 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
 
        if (!of_property_read_u32(node, "allwinner,rx-delay-ps", &val)) {
                if (val % 100) {
-                       dev_err(priv->device, "rx-delay must be a multiple of 100\n");
+                       dev_err(dev, "rx-delay must be a multiple of 100\n");
                        return -EINVAL;
                }
                val /= 100;
-               dev_dbg(priv->device, "set rx-delay to %x\n", val);
+               dev_dbg(dev, "set rx-delay to %x\n", val);
                if (val <= gmac->variant->rx_delay_max) {
                        reg &= ~(gmac->variant->rx_delay_max <<
                                 SYSCON_ERXDC_SHIFT);
                        reg |= (val << SYSCON_ERXDC_SHIFT);
                } else {
-                       dev_err(priv->device, "Invalid RX clock delay: %d\n",
+                       dev_err(dev, "Invalid RX clock delay: %d\n",
                                val);
                        return -EINVAL;
                }
@@ -968,7 +984,7 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
        if (gmac->variant->support_rmii)
                reg &= ~SYSCON_RMII_EN;
 
-       switch (priv->plat->interface) {
+       switch (plat->interface) {
        case PHY_INTERFACE_MODE_MII:
                /* default */
                break;
@@ -982,8 +998,8 @@ static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
                reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII;
                break;
        default:
-               dev_err(priv->device, "Unsupported interface mode: %s",
-                       phy_modes(priv->plat->interface));
+               dev_err(dev, "Unsupported interface mode: %s",
+                       phy_modes(plat->interface));
                return -EINVAL;
        }
 
@@ -1004,17 +1020,10 @@ static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
        struct sunxi_priv_data *gmac = priv;
 
        if (gmac->variant->soc_has_internal_phy) {
-               /* sun8i_dwmac_exit could be called with mdiomux uninit */
-               if (gmac->mux_handle)
-                       mdio_mux_uninit(gmac->mux_handle);
                if (gmac->internal_phy_powered)
                        sun8i_dwmac_unpower_internal_phy(gmac);
        }
 
-       sun8i_dwmac_unset_syscon(gmac);
-
-       reset_control_put(gmac->rst_ephy);
-
        clk_disable_unprepare(gmac->tx_clk);
 
        if (gmac->regulator)
@@ -1049,16 +1058,11 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv)
 {
        struct mac_device_info *mac;
        struct stmmac_priv *priv = ppriv;
-       int ret;
 
        mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
        if (!mac)
                return NULL;
 
-       ret = sun8i_dwmac_set_syscon(priv);
-       if (ret)
-               return NULL;
-
        mac->pcsr = priv->ioaddr;
        mac->mac = &sun8i_dwmac_ops;
        mac->dma = &sun8i_dwmac_dma_ops;
@@ -1134,10 +1138,6 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
-       if (IS_ERR(plat_dat))
-               return PTR_ERR(plat_dat);
-
        gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
        if (!gmac)
                return -ENOMEM;
@@ -1201,11 +1201,15 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
        ret = of_get_phy_mode(dev->of_node, &interface);
        if (ret)
                return -EINVAL;
-       plat_dat->interface = interface;
+
+       plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
+       if (IS_ERR(plat_dat))
+               return PTR_ERR(plat_dat);
 
        /* platform data specifying hardware features and callbacks.
         * hardware features were copied from Allwinner drivers.
         */
+       plat_dat->interface = interface;
        plat_dat->rx_coe = STMMAC_RX_COE_TYPE2;
        plat_dat->tx_coe = 1;
        plat_dat->has_sun8i = true;
@@ -1214,9 +1218,13 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
        plat_dat->exit = sun8i_dwmac_exit;
        plat_dat->setup = sun8i_dwmac_setup;
 
+       ret = sun8i_dwmac_set_syscon(&pdev->dev, plat_dat);
+       if (ret)
+               goto dwmac_deconfig;
+
        ret = sun8i_dwmac_init(pdev, plat_dat->bsp_priv);
        if (ret)
-               return ret;
+               goto dwmac_syscon;
 
        ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
        if (ret)
@@ -1230,7 +1238,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
        if (gmac->variant->soc_has_internal_phy) {
                ret = get_ephy_nodes(priv);
                if (ret)
-                       goto dwmac_exit;
+                       goto dwmac_remove;
                ret = sun8i_dwmac_register_mdio_mux(priv);
                if (ret) {
                        dev_err(&pdev->dev, "Failed to register mux\n");
@@ -1239,15 +1247,42 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
        } else {
                ret = sun8i_dwmac_reset(priv);
                if (ret)
-                       goto dwmac_exit;
+                       goto dwmac_remove;
        }
 
        return ret;
 dwmac_mux:
-       sun8i_dwmac_unset_syscon(gmac);
+       reset_control_put(gmac->rst_ephy);
+       clk_put(gmac->ephy_clk);
+dwmac_remove:
+       stmmac_dvr_remove(&pdev->dev);
 dwmac_exit:
+       sun8i_dwmac_exit(pdev, gmac);
+dwmac_syscon:
+       sun8i_dwmac_unset_syscon(gmac);
+dwmac_deconfig:
+       stmmac_remove_config_dt(pdev, plat_dat);
+
+       return ret;
+}
+
+static int sun8i_dwmac_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct stmmac_priv *priv = netdev_priv(ndev);
+       struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
+
+       if (gmac->variant->soc_has_internal_phy) {
+               mdio_mux_uninit(gmac->mux_handle);
+               sun8i_dwmac_unpower_internal_phy(gmac);
+               reset_control_put(gmac->rst_ephy);
+               clk_put(gmac->ephy_clk);
+       }
+
        stmmac_pltfr_remove(pdev);
-return ret;
+       sun8i_dwmac_unset_syscon(gmac);
+
+       return 0;
 }
 
 static const struct of_device_id sun8i_dwmac_match[] = {
@@ -1269,7 +1304,7 @@ MODULE_DEVICE_TABLE(of, sun8i_dwmac_match);
 
 static struct platform_driver sun8i_dwmac_driver = {
        .probe  = sun8i_dwmac_probe,
-       .remove = stmmac_pltfr_remove,
+       .remove = sun8i_dwmac_remove,
        .driver = {
                .name           = "dwmac-sun8i",
                .pm             = &stmmac_pltfr_pm_ops,
index 03e79a6..8f7ac24 100644 (file)
@@ -568,68 +568,24 @@ static int dwmac5_est_write(void __iomem *ioaddr, u32 reg, u32 val, bool gcl)
 int dwmac5_est_configure(void __iomem *ioaddr, struct stmmac_est *cfg,
                         unsigned int ptp_rate)
 {
-       u32 speed, total_offset, offset, ctrl, ctr_low;
-       u32 extcfg = readl(ioaddr + GMAC_EXT_CONFIG);
-       u32 mac_cfg = readl(ioaddr + GMAC_CONFIG);
        int i, ret = 0x0;
-       u64 total_ctr;
-
-       if (extcfg & GMAC_CONFIG_EIPG_EN) {
-               offset = (extcfg & GMAC_CONFIG_EIPG) >> GMAC_CONFIG_EIPG_SHIFT;
-               offset = 104 + (offset * 8);
-       } else {
-               offset = (mac_cfg & GMAC_CONFIG_IPG) >> GMAC_CONFIG_IPG_SHIFT;
-               offset = 96 - (offset * 8);
-       }
-
-       speed = mac_cfg & (GMAC_CONFIG_PS | GMAC_CONFIG_FES);
-       speed = speed >> GMAC_CONFIG_FES_SHIFT;
-
-       switch (speed) {
-       case 0x0:
-               offset = offset * 1000; /* 1G */
-               break;
-       case 0x1:
-               offset = offset * 400; /* 2.5G */
-               break;
-       case 0x2:
-               offset = offset * 100000; /* 10M */
-               break;
-       case 0x3:
-               offset = offset * 10000; /* 100M */
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       offset = offset / 1000;
+       u32 ctrl;
 
        ret |= dwmac5_est_write(ioaddr, BTR_LOW, cfg->btr[0], false);
        ret |= dwmac5_est_write(ioaddr, BTR_HIGH, cfg->btr[1], false);
        ret |= dwmac5_est_write(ioaddr, TER, cfg->ter, false);
        ret |= dwmac5_est_write(ioaddr, LLR, cfg->gcl_size, false);
+       ret |= dwmac5_est_write(ioaddr, CTR_LOW, cfg->ctr[0], false);
+       ret |= dwmac5_est_write(ioaddr, CTR_HIGH, cfg->ctr[1], false);
        if (ret)
                return ret;
 
-       total_offset = 0;
        for (i = 0; i < cfg->gcl_size; i++) {
-               ret = dwmac5_est_write(ioaddr, i, cfg->gcl[i] + offset, true);
+               ret = dwmac5_est_write(ioaddr, i, cfg->gcl[i], true);
                if (ret)
                        return ret;
-
-               total_offset += offset;
        }
 
-       total_ctr = cfg->ctr[0] + cfg->ctr[1] * 1000000000ULL;
-       total_ctr += total_offset;
-
-       ctr_low = do_div(total_ctr, 1000000000);
-
-       ret |= dwmac5_est_write(ioaddr, CTR_LOW, ctr_low, false);
-       ret |= dwmac5_est_write(ioaddr, CTR_HIGH, total_ctr, false);
-       if (ret)
-               return ret;
-
        ctrl = readl(ioaddr + MTL_EST_CONTROL);
        ctrl &= ~PTOV;
        ctrl |= ((1000000000 / ptp_rate) * 6) << PTOV_SHIFT;
index 5b1c12f..26b971c 100644 (file)
@@ -2184,7 +2184,7 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan)
                        spin_lock_irqsave(&ch->lock, flags);
                        stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 1, 0);
                        spin_unlock_irqrestore(&ch->lock, flags);
-                       __napi_schedule_irqoff(&ch->rx_napi);
+                       __napi_schedule(&ch->rx_napi);
                }
        }
 
@@ -2193,7 +2193,7 @@ static int stmmac_napi_check(struct stmmac_priv *priv, u32 chan)
                        spin_lock_irqsave(&ch->lock, flags);
                        stmmac_disable_dma_irq(priv, priv->ioaddr, chan, 0, 1);
                        spin_unlock_irqrestore(&ch->lock, flags);
-                       __napi_schedule_irqoff(&ch->tx_napi);
+                       __napi_schedule(&ch->tx_napi);
                }
        }
 
@@ -4026,6 +4026,7 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
 {
        struct stmmac_priv *priv = netdev_priv(dev);
        int txfifosz = priv->plat->tx_fifo_size;
+       const int mtu = new_mtu;
 
        if (txfifosz == 0)
                txfifosz = priv->dma_cap.tx_fifo_size;
@@ -4043,7 +4044,7 @@ static int stmmac_change_mtu(struct net_device *dev, int new_mtu)
        if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB))
                return -EINVAL;
 
-       dev->mtu = new_mtu;
+       dev->mtu = mtu;
 
        netdev_update_features(dev);
 
index f5bed4d..8ed3b2c 100644 (file)
@@ -599,7 +599,8 @@ static int tc_setup_taprio(struct stmmac_priv *priv,
 {
        u32 size, wid = priv->dma_cap.estwid, dep = priv->dma_cap.estdep;
        struct plat_stmmacenet_data *plat = priv->plat;
-       struct timespec64 time;
+       struct timespec64 time, current_time;
+       ktime_t current_time_ns;
        bool fpe = false;
        int i, ret = 0;
        u64 ctr;
@@ -694,7 +695,22 @@ static int tc_setup_taprio(struct stmmac_priv *priv,
        }
 
        /* Adjust for real system time */
-       time = ktime_to_timespec64(qopt->base_time);
+       priv->ptp_clock_ops.gettime64(&priv->ptp_clock_ops, &current_time);
+       current_time_ns = timespec64_to_ktime(current_time);
+       if (ktime_after(qopt->base_time, current_time_ns)) {
+               time = ktime_to_timespec64(qopt->base_time);
+       } else {
+               ktime_t base_time;
+               s64 n;
+
+               n = div64_s64(ktime_sub_ns(current_time_ns, qopt->base_time),
+                             qopt->cycle_time);
+               base_time = ktime_add_ns(qopt->base_time,
+                                        (n + 1) * qopt->cycle_time);
+
+               time = ktime_to_timespec64(base_time);
+       }
+
        priv->plat->est->btr[0] = (u32)time.tv_nsec;
        priv->plat->est->btr[1] = (u32)time.tv_sec;
 
index 766e886..1850743 100644 (file)
@@ -366,8 +366,9 @@ static int am65_cpsw_nuss_rx_push(struct am65_cpsw_common *common,
        }
        desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx);
 
-       buf_dma = dma_map_single(dev, skb->data, pkt_len, DMA_FROM_DEVICE);
-       if (unlikely(dma_mapping_error(dev, buf_dma))) {
+       buf_dma = dma_map_single(rx_chn->dma_dev, skb->data, pkt_len,
+                                DMA_FROM_DEVICE);
+       if (unlikely(dma_mapping_error(rx_chn->dma_dev, buf_dma))) {
                k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
                dev_err(dev, "Failed to map rx skb buffer\n");
                return -EINVAL;
@@ -375,6 +376,7 @@ static int am65_cpsw_nuss_rx_push(struct am65_cpsw_common *common,
 
        cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT,
                         AM65_CPSW_NAV_PS_DATA_SIZE);
+       k3_udma_glue_rx_dma_to_cppi5_addr(rx_chn->rx_chn, &buf_dma);
        cppi5_hdesc_attach_buf(desc_rx, buf_dma, skb_tailroom(skb), buf_dma, skb_tailroom(skb));
        swdata = cppi5_hdesc_get_swdata(desc_rx);
        *((void **)swdata) = skb;
@@ -691,8 +693,9 @@ static void am65_cpsw_nuss_rx_cleanup(void *data, dma_addr_t desc_dma)
        swdata = cppi5_hdesc_get_swdata(desc_rx);
        skb = *swdata;
        cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len);
+       k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma);
 
-       dma_unmap_single(rx_chn->dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE);
+       dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE);
        k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
 
        dev_kfree_skb_any(skb);
@@ -779,6 +782,7 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common,
        swdata = cppi5_hdesc_get_swdata(desc_rx);
        skb = *swdata;
        cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len);
+       k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma);
        pkt_len = cppi5_hdesc_get_pktlen(desc_rx);
        cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
        dev_dbg(dev, "%s rx port_id:%d\n", __func__, port_id);
@@ -793,7 +797,7 @@ static int am65_cpsw_nuss_rx_packets(struct am65_cpsw_common *common,
        csum_info = psdata[2];
        dev_dbg(dev, "%s rx csum_info:%#x\n", __func__, csum_info);
 
-       dma_unmap_single(dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE);
+       dma_unmap_single(rx_chn->dma_dev, buf_dma, buf_dma_len, DMA_FROM_DEVICE);
 
        k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
 
@@ -864,7 +868,6 @@ static int am65_cpsw_nuss_rx_poll(struct napi_struct *napi_rx, int budget)
 }
 
 static void am65_cpsw_nuss_xmit_free(struct am65_cpsw_tx_chn *tx_chn,
-                                    struct device *dev,
                                     struct cppi5_host_desc_t *desc)
 {
        struct cppi5_host_desc_t *first_desc, *next_desc;
@@ -875,20 +878,23 @@ static void am65_cpsw_nuss_xmit_free(struct am65_cpsw_tx_chn *tx_chn,
        next_desc = first_desc;
 
        cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len);
+       k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma);
 
-       dma_unmap_single(dev, buf_dma, buf_dma_len,
-                        DMA_TO_DEVICE);
+       dma_unmap_single(tx_chn->dma_dev, buf_dma, buf_dma_len, DMA_TO_DEVICE);
 
        next_desc_dma = cppi5_hdesc_get_next_hbdesc(first_desc);
+       k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma);
        while (next_desc_dma) {
                next_desc = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool,
                                                       next_desc_dma);
                cppi5_hdesc_get_obuf(next_desc, &buf_dma, &buf_dma_len);
+               k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma);
 
-               dma_unmap_page(dev, buf_dma, buf_dma_len,
+               dma_unmap_page(tx_chn->dma_dev, buf_dma, buf_dma_len,
                               DMA_TO_DEVICE);
 
                next_desc_dma = cppi5_hdesc_get_next_hbdesc(next_desc);
+               k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &next_desc_dma);
 
                k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc);
        }
@@ -906,7 +912,7 @@ static void am65_cpsw_nuss_tx_cleanup(void *data, dma_addr_t desc_dma)
        desc_tx = k3_cppi_desc_pool_dma2virt(tx_chn->desc_pool, desc_dma);
        swdata = cppi5_hdesc_get_swdata(desc_tx);
        skb = *(swdata);
-       am65_cpsw_nuss_xmit_free(tx_chn, tx_chn->common->dev, desc_tx);
+       am65_cpsw_nuss_xmit_free(tx_chn, desc_tx);
 
        dev_kfree_skb_any(skb);
 }
@@ -926,7 +932,7 @@ am65_cpsw_nuss_tx_compl_packet(struct am65_cpsw_tx_chn *tx_chn,
                                             desc_dma);
        swdata = cppi5_hdesc_get_swdata(desc_tx);
        skb = *(swdata);
-       am65_cpsw_nuss_xmit_free(tx_chn, tx_chn->common->dev, desc_tx);
+       am65_cpsw_nuss_xmit_free(tx_chn, desc_tx);
 
        ndev = skb->dev;
 
@@ -1119,9 +1125,9 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
        netif_txq = netdev_get_tx_queue(ndev, q_idx);
 
        /* Map the linear buffer */
-       buf_dma = dma_map_single(dev, skb->data, pkt_len,
+       buf_dma = dma_map_single(tx_chn->dma_dev, skb->data, pkt_len,
                                 DMA_TO_DEVICE);
-       if (unlikely(dma_mapping_error(dev, buf_dma))) {
+       if (unlikely(dma_mapping_error(tx_chn->dma_dev, buf_dma))) {
                dev_err(dev, "Failed to map tx skb buffer\n");
                ndev->stats.tx_errors++;
                goto err_free_skb;
@@ -1130,7 +1136,8 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
        first_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool);
        if (!first_desc) {
                dev_dbg(dev, "Failed to allocate descriptor\n");
-               dma_unmap_single(dev, buf_dma, pkt_len, DMA_TO_DEVICE);
+               dma_unmap_single(tx_chn->dma_dev, buf_dma, pkt_len,
+                                DMA_TO_DEVICE);
                goto busy_stop_q;
        }
 
@@ -1140,6 +1147,7 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
        cppi5_hdesc_set_pkttype(first_desc, 0x7);
        cppi5_desc_set_tags_ids(&first_desc->hdr, 0, port->port_id);
 
+       k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
        cppi5_hdesc_attach_buf(first_desc, buf_dma, pkt_len, buf_dma, pkt_len);
        swdata = cppi5_hdesc_get_swdata(first_desc);
        *(swdata) = skb;
@@ -1175,9 +1183,9 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
                        goto busy_free_descs;
                }
 
-               buf_dma = skb_frag_dma_map(dev, frag, 0, frag_size,
+               buf_dma = skb_frag_dma_map(tx_chn->dma_dev, frag, 0, frag_size,
                                           DMA_TO_DEVICE);
-               if (unlikely(dma_mapping_error(dev, buf_dma))) {
+               if (unlikely(dma_mapping_error(tx_chn->dma_dev, buf_dma))) {
                        dev_err(dev, "Failed to map tx skb page\n");
                        k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc);
                        ndev->stats.tx_errors++;
@@ -1185,11 +1193,13 @@ static netdev_tx_t am65_cpsw_nuss_ndo_slave_xmit(struct sk_buff *skb,
                }
 
                cppi5_hdesc_reset_hbdesc(next_desc);
+               k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
                cppi5_hdesc_attach_buf(next_desc,
                                       buf_dma, frag_size, buf_dma, frag_size);
 
                desc_dma = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool,
                                                      next_desc);
+               k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &desc_dma);
                cppi5_hdesc_link_hbdesc(cur_desc, desc_dma);
 
                pkt_len += frag_size;
@@ -1237,14 +1247,14 @@ done_tx:
        return NETDEV_TX_OK;
 
 err_free_descs:
-       am65_cpsw_nuss_xmit_free(tx_chn, dev, first_desc);
+       am65_cpsw_nuss_xmit_free(tx_chn, first_desc);
 err_free_skb:
        ndev->stats.tx_dropped++;
        dev_kfree_skb_any(skb);
        return NETDEV_TX_OK;
 
 busy_free_descs:
-       am65_cpsw_nuss_xmit_free(tx_chn, dev, first_desc);
+       am65_cpsw_nuss_xmit_free(tx_chn, first_desc);
 busy_stop_q:
        netif_tx_stop_queue(netif_txq);
        return NETDEV_TX_BUSY;
@@ -1545,16 +1555,6 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common)
                tx_chn->common = common;
                tx_chn->id = i;
                tx_chn->descs_num = max_desc_num;
-               tx_chn->desc_pool =
-                       k3_cppi_desc_pool_create_name(dev,
-                                                     tx_chn->descs_num,
-                                                     hdesc_size,
-                                                     tx_chn->tx_chn_name);
-               if (IS_ERR(tx_chn->desc_pool)) {
-                       ret = PTR_ERR(tx_chn->desc_pool);
-                       dev_err(dev, "Failed to create poll %d\n", ret);
-                       goto err;
-               }
 
                tx_chn->tx_chn =
                        k3_udma_glue_request_tx_chn(dev,
@@ -1565,6 +1565,17 @@ static int am65_cpsw_nuss_init_tx_chns(struct am65_cpsw_common *common)
                                            "Failed to request tx dma channel\n");
                        goto err;
                }
+               tx_chn->dma_dev = k3_udma_glue_tx_get_dma_device(tx_chn->tx_chn);
+
+               tx_chn->desc_pool = k3_cppi_desc_pool_create_name(tx_chn->dma_dev,
+                                                                 tx_chn->descs_num,
+                                                                 hdesc_size,
+                                                                 tx_chn->tx_chn_name);
+               if (IS_ERR(tx_chn->desc_pool)) {
+                       ret = PTR_ERR(tx_chn->desc_pool);
+                       dev_err(dev, "Failed to create poll %d\n", ret);
+                       goto err;
+               }
 
                tx_chn->irq = k3_udma_glue_tx_get_irq(tx_chn->tx_chn);
                if (tx_chn->irq <= 0) {
@@ -1622,14 +1633,6 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common)
        /* init all flows */
        rx_chn->dev = dev;
        rx_chn->descs_num = max_desc_num;
-       rx_chn->desc_pool = k3_cppi_desc_pool_create_name(dev,
-                                                         rx_chn->descs_num,
-                                                         hdesc_size, "rx");
-       if (IS_ERR(rx_chn->desc_pool)) {
-               ret = PTR_ERR(rx_chn->desc_pool);
-               dev_err(dev, "Failed to create rx poll %d\n", ret);
-               goto err;
-       }
 
        rx_chn->rx_chn = k3_udma_glue_request_rx_chn(dev, "rx", &rx_cfg);
        if (IS_ERR(rx_chn->rx_chn)) {
@@ -1637,6 +1640,16 @@ static int am65_cpsw_nuss_init_rx_chns(struct am65_cpsw_common *common)
                                    "Failed to request rx dma channel\n");
                goto err;
        }
+       rx_chn->dma_dev = k3_udma_glue_rx_get_dma_device(rx_chn->rx_chn);
+
+       rx_chn->desc_pool = k3_cppi_desc_pool_create_name(rx_chn->dma_dev,
+                                                         rx_chn->descs_num,
+                                                         hdesc_size, "rx");
+       if (IS_ERR(rx_chn->desc_pool)) {
+               ret = PTR_ERR(rx_chn->desc_pool);
+               dev_err(dev, "Failed to create rx poll %d\n", ret);
+               goto err;
+       }
 
        common->rx_flow_id_base =
                        k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn);
@@ -2102,9 +2115,16 @@ static const struct am65_cpsw_pdata j721e_pdata = {
        .fdqring_mode = K3_RINGACC_RING_MODE_MESSAGE,
 };
 
+static const struct am65_cpsw_pdata am64x_cpswxg_pdata = {
+       .quirks = 0,
+       .ale_dev_id = "am64-cpswxg",
+       .fdqring_mode = K3_RINGACC_RING_MODE_RING,
+};
+
 static const struct of_device_id am65_cpsw_nuss_of_mtable[] = {
        { .compatible = "ti,am654-cpsw-nuss", .data = &am65x_sr1_0},
        { .compatible = "ti,j721e-cpsw-nuss", .data = &j721e_pdata},
+       { .compatible = "ti,am642-cpsw-nuss", .data = &am64x_cpswxg_pdata},
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, am65_cpsw_nuss_of_mtable);
@@ -2164,12 +2184,6 @@ static int am65_cpsw_nuss_probe(struct platform_device *pdev)
        common->tx_ch_num = 1;
        common->pf_p0_rx_ptype_rrobin = false;
 
-       ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(48));
-       if (ret) {
-               dev_err(dev, "error setting dma mask: %d\n", ret);
-               return ret;
-       }
-
        common->ports = devm_kcalloc(dev, common->port_num,
                                     sizeof(*common->ports),
                                     GFP_KERNEL);
index 02aed4c..d7f8a0f 100644 (file)
@@ -56,6 +56,7 @@ struct am65_cpsw_host {
 };
 
 struct am65_cpsw_tx_chn {
+       struct device *dma_dev;
        struct napi_struct napi_tx;
        struct am65_cpsw_common *common;
        struct k3_cppi_desc_pool *desc_pool;
@@ -69,6 +70,7 @@ struct am65_cpsw_tx_chn {
 
 struct am65_cpsw_rx_chn {
        struct device *dev;
+       struct device *dma_dev;
        struct k3_cppi_desc_pool *desc_pool;
        struct k3_udma_glue_rx_channel *rx_chn;
        u32 descs_num;
index 3bdd4db..ebcc638 100644 (file)
@@ -356,7 +356,7 @@ static void am65_cpsw_est_set_sched_list(struct net_device *ndev,
                writel(~all_fetch_allow & AM65_CPSW_FETCH_ALLOW_MSK, ram_addr);
 }
 
-/**
+/*
  * Enable ESTf periodic output, set cycle start time and interval.
  */
 static int am65_cpsw_timer_set(struct net_device *ndev,
index 5dc60ec..9caaae7 100644 (file)
@@ -727,7 +727,7 @@ static long am65_cpts_ts_work(struct ptp_clock_info *ptp)
 /**
  * am65_cpts_rx_enable - enable rx timestamping
  * @cpts: cpts handle
- * @skb: packet
+ * @en: enable
  *
  * This functions enables rx packets timestamping. The CPTS can timestamp all
  * rx packets.
index b0f00b4..5239318 100644 (file)
@@ -392,21 +392,15 @@ static void cpsw_rx_handler(void *token, int len, int status)
        }
 
        if (priv->xdp_prog) {
+               int headroom = CPSW_HEADROOM, size = len;
+
+               xdp_init_buff(&xdp, PAGE_SIZE, &priv->xdp_rxq[ch]);
                if (status & CPDMA_RX_VLAN_ENCAP) {
-                       xdp.data = pa + CPSW_HEADROOM +
-                                  CPSW_RX_VLAN_ENCAP_HDR_SIZE;
-                       xdp.data_end = xdp.data + len -
-                                      CPSW_RX_VLAN_ENCAP_HDR_SIZE;
-               } else {
-                       xdp.data = pa + CPSW_HEADROOM;
-                       xdp.data_end = xdp.data + len;
+                       headroom += CPSW_RX_VLAN_ENCAP_HDR_SIZE;
+                       size -= CPSW_RX_VLAN_ENCAP_HDR_SIZE;
                }
 
-               xdp_set_data_meta_invalid(&xdp);
-
-               xdp.data_hard_start = pa;
-               xdp.rxq = &priv->xdp_rxq[ch];
-               xdp.frame_sz = PAGE_SIZE;
+               xdp_prepare_buff(&xdp, pa, headroom, size, false);
 
                port = priv->emac_port + cpsw->data.dual_emac;
                ret = cpsw_run_xdp(priv, ch, &xdp, page, port);
index cdc308a..d828f85 100644 (file)
@@ -1256,6 +1256,13 @@ static const struct cpsw_ale_dev_id cpsw_ale_id_match[] = {
                .major_ver_mask = 0x7,
                .vlan_entry_tbl = vlan_entry_k3_cpswxg,
        },
+       {
+               .dev_id = "am64-cpswxg",
+               .features = CPSW_ALE_F_STATUS_REG | CPSW_ALE_F_HW_AUTOAGING,
+               .major_ver_mask = 0x7,
+               .vlan_entry_tbl = vlan_entry_k3_cpswxg,
+               .tbl_entries = 512,
+       },
        { },
 };
 
index 2f5e0ad..94747f8 100644 (file)
@@ -335,21 +335,15 @@ static void cpsw_rx_handler(void *token, int len, int status)
        }
 
        if (priv->xdp_prog) {
+               int headroom = CPSW_HEADROOM, size = len;
+
+               xdp_init_buff(&xdp, PAGE_SIZE, &priv->xdp_rxq[ch]);
                if (status & CPDMA_RX_VLAN_ENCAP) {
-                       xdp.data = pa + CPSW_HEADROOM +
-                                  CPSW_RX_VLAN_ENCAP_HDR_SIZE;
-                       xdp.data_end = xdp.data + len -
-                                      CPSW_RX_VLAN_ENCAP_HDR_SIZE;
-               } else {
-                       xdp.data = pa + CPSW_HEADROOM;
-                       xdp.data_end = xdp.data + len;
+                       headroom += CPSW_RX_VLAN_ENCAP_HDR_SIZE;
+                       size -= CPSW_RX_VLAN_ENCAP_HDR_SIZE;
                }
 
-               xdp_set_data_meta_invalid(&xdp);
-
-               xdp.data_hard_start = pa;
-               xdp.rxq = &priv->xdp_rxq[ch];
-               xdp.frame_sz = PAGE_SIZE;
+               xdp_prepare_buff(&xdp, pa, headroom, size, false);
 
                ret = cpsw_run_xdp(priv, ch, &xdp, page, priv->emac_port);
                if (ret != CPSW_XDP_PASS)
index 29747da..9967cf9 100644 (file)
@@ -24,16 +24,12 @@ struct cpsw_switchdev_event_work {
        unsigned long event;
 };
 
-static int cpsw_port_stp_state_set(struct cpsw_priv *priv,
-                                  struct switchdev_trans *trans, u8 state)
+static int cpsw_port_stp_state_set(struct cpsw_priv *priv, u8 state)
 {
        struct cpsw_common *cpsw = priv->cpsw;
        u8 cpsw_state;
        int ret = 0;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        switch (state) {
        case BR_STATE_FORWARDING:
                cpsw_state = ALE_PORT_STATE_FORWARD;
@@ -60,16 +56,12 @@ static int cpsw_port_stp_state_set(struct cpsw_priv *priv,
 }
 
 static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv,
-                                      struct switchdev_trans *trans,
                                       struct net_device *orig_dev,
                                       unsigned long brport_flags)
 {
        struct cpsw_common *cpsw = priv->cpsw;
        bool unreg_mcast_add = false;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        if (brport_flags & BR_MCAST_FLOOD)
                unreg_mcast_add = true;
        dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n",
@@ -82,7 +74,6 @@ static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv,
 }
 
 static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
-                                          struct switchdev_trans *trans,
                                           unsigned long flags)
 {
        if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD))
@@ -92,8 +83,7 @@ static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
 }
 
 static int cpsw_port_attr_set(struct net_device *ndev,
-                             const struct switchdev_attr *attr,
-                             struct switchdev_trans *trans)
+                             const struct switchdev_attr *attr)
 {
        struct cpsw_priv *priv = netdev_priv(ndev);
        int ret;
@@ -102,15 +92,15 @@ static int cpsw_port_attr_set(struct net_device *ndev,
 
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
-               ret = cpsw_port_attr_br_flags_pre_set(ndev, trans,
+               ret = cpsw_port_attr_br_flags_pre_set(ndev,
                                                      attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-               ret = cpsw_port_stp_state_set(priv, trans, attr->u.stp_state);
+               ret = cpsw_port_stp_state_set(priv, attr->u.stp_state);
                dev_dbg(priv->dev, "stp state: %u\n", attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               ret = cpsw_port_attr_br_flags_set(priv, trans, attr->orig_dev,
+               ret = cpsw_port_attr_br_flags_set(priv, attr->orig_dev,
                                                  attr->u.brport_flags);
                break;
        default:
@@ -253,56 +243,24 @@ static int cpsw_port_vlan_del(struct cpsw_priv *priv, u16 vid,
 }
 
 static int cpsw_port_vlans_add(struct cpsw_priv *priv,
-                              const struct switchdev_obj_port_vlan *vlan,
-                              struct switchdev_trans *trans)
+                              const struct switchdev_obj_port_vlan *vlan)
 {
        bool untag = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
        struct net_device *orig_dev = vlan->obj.orig_dev;
        bool cpu_port = netif_is_bridge_master(orig_dev);
        bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-       u16 vid;
 
        dev_dbg(priv->dev, "VID add: %s: vid:%u flags:%X\n",
-               priv->ndev->name, vlan->vid_begin, vlan->flags);
+               priv->ndev->name, vlan->vid, vlan->flags);
 
        if (cpu_port && !(vlan->flags & BRIDGE_VLAN_INFO_BRENTRY))
                return 0;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               int err;
-
-               err = cpsw_port_vlan_add(priv, untag, pvid, vid, orig_dev);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int cpsw_port_vlans_del(struct cpsw_priv *priv,
-                              const struct switchdev_obj_port_vlan *vlan)
-
-{
-       struct net_device *orig_dev = vlan->obj.orig_dev;
-       u16 vid;
-
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               int err;
-
-               err = cpsw_port_vlan_del(priv, vid, orig_dev);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return cpsw_port_vlan_add(priv, untag, pvid, vlan->vid, orig_dev);
 }
 
 static int cpsw_port_mdb_add(struct cpsw_priv *priv,
-                            struct switchdev_obj_port_mdb *mdb,
-                            struct switchdev_trans *trans)
+                            struct switchdev_obj_port_mdb *mdb)
 
 {
        struct net_device *orig_dev = mdb->obj.orig_dev;
@@ -311,9 +269,6 @@ static int cpsw_port_mdb_add(struct cpsw_priv *priv,
        int port_mask;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        if (cpu_port)
                port_mask = BIT(HOST_PORT_NUM);
        else
@@ -352,7 +307,6 @@ static int cpsw_port_mdb_del(struct cpsw_priv *priv,
 
 static int cpsw_port_obj_add(struct net_device *ndev,
                             const struct switchdev_obj *obj,
-                            struct switchdev_trans *trans,
                             struct netlink_ext_ack *extack)
 {
        struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj);
@@ -365,11 +319,11 @@ static int cpsw_port_obj_add(struct net_device *ndev,
 
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
-               err = cpsw_port_vlans_add(priv, vlan, trans);
+               err = cpsw_port_vlans_add(priv, vlan);
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
        case SWITCHDEV_OBJ_ID_HOST_MDB:
-               err = cpsw_port_mdb_add(priv, mdb, trans);
+               err = cpsw_port_mdb_add(priv, mdb);
                break;
        default:
                err = -EOPNOTSUPP;
@@ -392,7 +346,7 @@ static int cpsw_port_obj_del(struct net_device *ndev,
 
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
-               err = cpsw_port_vlans_del(priv, vlan);
+               err = cpsw_port_vlan_del(priv, vlan->vid, vlan->obj.orig_dev);
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
        case SWITCHDEV_OBJ_ID_HOST_MDB:
index d1fc795..43222a3 100644 (file)
@@ -599,6 +599,7 @@ void cpts_unregister(struct cpts *cpts)
 
        ptp_clock_unregister(cpts->clock);
        cpts->clock = NULL;
+       cpts->phc_index = -1;
 
        cpts_write32(cpts, 0, int_enable);
        cpts_write32(cpts, 0, control);
@@ -784,6 +785,7 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
        cpts->cc.read = cpts_systim_read;
        cpts->cc.mask = CLOCKSOURCE_MASK(32);
        cpts->info = cpts_info;
+       cpts->phc_index = -1;
 
        if (n_ext_ts)
                cpts->info.n_ext_ts = n_ext_ts;
index 3d1fc8d..55e6526 100644 (file)
@@ -1100,7 +1100,7 @@ static int gelic_net_poll(struct napi_struct *napi, int budget)
        return packets_done;
 }
 
-/**
+/*
  * gelic_card_interrupt - event handler for gelic_net
  */
 static irqreturn_t gelic_card_interrupt(int irq, void *ptr)
@@ -1400,6 +1400,7 @@ out:
 /**
  * gelic_net_tx_timeout - called when the tx timeout watchdog kicks in.
  * @netdev: interface device structure
+ * @txqueue: unused
  *
  * called, if tx hangs. Schedules a task that resets the interface
  */
@@ -1431,6 +1432,7 @@ static const struct net_device_ops gelic_netdevice_ops = {
 /**
  * gelic_ether_setup_netdev_ops - initialization of net_device operations
  * @netdev: net_device structure
+ * @napi: napi structure
  *
  * fills out function pointers in the net_device structure
  */
@@ -1632,7 +1634,7 @@ static void gelic_card_get_vlan_info(struct gelic_card *card)
        dev_info(ctodev(card), "internal vlan %s\n",
                 card->vlan_required? "enabled" : "disabled");
 }
-/**
+/*
  * ps3_gelic_driver_probe - add a device to the control of this driver
  */
 static int ps3_gelic_driver_probe(struct ps3_system_bus_device *dev)
@@ -1787,7 +1789,7 @@ fail_open:
        return result;
 }
 
-/**
+/*
  * ps3_gelic_driver_remove - remove a device from the control of this driver
  */
 
index 5f5b33e..d5a75ef 100644 (file)
@@ -254,7 +254,7 @@ spider_net_set_promisc(struct spider_net_card *card)
 
 /**
  * spider_net_get_descr_status -- returns the status of a descriptor
- * @descr: descriptor to look at
+ * @hwdescr: descriptor to look at
  *
  * returns the status as in the dmac_cmd_status field of the descriptor
  */
@@ -542,6 +542,7 @@ error:
 
 /**
  * spider_net_get_multicast_hash - generates hash for multicast filter table
+ * @netdev: interface device structure
  * @addr: multicast address
  *
  * returns the hash value.
@@ -890,7 +891,7 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
 
 /**
  * spider_net_cleanup_tx_ring - cleans up the TX ring
- * @card: card structure
+ * @t: timer context used to obtain the pointer to net card data structure
  *
  * spider_net_cleanup_tx_ring is called by either the tx_timer
  * or from the NAPI polling routine.
@@ -1063,6 +1064,7 @@ static void show_rx_chain(struct spider_net_card *card)
 
 /**
  * spider_net_resync_head_ptr - Advance head ptr past empty descrs
+ * @card: card structure
  *
  * If the driver fails to keep up and empty the queue, then the
  * hardware wil run out of room to put incoming packets. This
@@ -1220,7 +1222,7 @@ bad_desc:
 
 /**
  * spider_net_poll - NAPI poll function called by the stack to return packets
- * @netdev: interface device structure
+ * @napi: napi device structure
  * @budget: number of packets we can pass to the stack at most
  *
  * returns 0 if no more packets available to the driver/stack. Returns 1,
@@ -1268,7 +1270,7 @@ static int spider_net_poll(struct napi_struct *napi, int budget)
 /**
  * spider_net_set_mac - sets the MAC of an interface
  * @netdev: interface device structure
- * @ptr: pointer to new MAC address
+ * @p: pointer to new MAC address
  *
  * Returns 0 on success, <0 on failure. Currently, we don't support this
  * and will always return EOPNOTSUPP.
@@ -1340,6 +1342,8 @@ spider_net_link_reset(struct net_device *netdev)
  * spider_net_handle_error_irq - handles errors raised by an interrupt
  * @card: card structure
  * @status_reg: interrupt status register 0 (GHIINT0STS)
+ * @error_reg1: interrupt status register 1 (GHIINT1STS)
+ * @error_reg2: interrupt status register 2 (GHIINT2STS)
  *
  * spider_net_handle_error_irq treats or ignores all error conditions
  * found when an interrupt is presented
@@ -1961,8 +1965,7 @@ init_firmware_failed:
 
 /**
  * spider_net_link_phy
- * @data: used for pointer to card structure
- *
+ * @t: timer context used to obtain the pointer to net card data structure
  */
 static void spider_net_link_phy(struct timer_list *t)
 {
@@ -2140,7 +2143,7 @@ spider_net_stop(struct net_device *netdev)
 /**
  * spider_net_tx_timeout_task - task scheduled by the watchdog timeout
  * function (to be called not under interrupt status)
- * @data: data, is interface device structure
+ * @work: work context used to obtain the pointer to net card data structure
  *
  * called as task when tx hangs, resets interface (if interface is up)
  */
@@ -2174,6 +2177,7 @@ out:
 /**
  * spider_net_tx_timeout - called when the tx timeout watchdog kicks in.
  * @netdev: interface device structure
+ * @txqueue: unused
  *
  * called, if tx hangs. Schedules a task that resets the interface
  */
index 2e52029..0152f1e 100644 (file)
@@ -247,7 +247,7 @@ static inline void memcpy_swab32(u32 *dest, u32 *src, int cnt)
 }
 #endif
 
-static spinlock_t mdio_lock;
+static DEFINE_SPINLOCK(mdio_lock);
 static struct eth_regs __iomem *mdio_regs; /* mdio command and status only */
 static struct mii_bus *mdio_bus;
 static int ports_open;
@@ -528,7 +528,6 @@ static int ixp4xx_mdio_register(struct eth_regs __iomem *regs)
 
        mdio_regs = regs;
        __raw_writel(DEFAULT_CORE_CNTRL, &mdio_regs->core_control);
-       spin_lock_init(&mdio_lock);
        mdio_bus->name = "IXP4xx MII Bus";
        mdio_bus->read = &ixp4xx_mdio_read;
        mdio_bus->write = &ixp4xx_mdio_write;
index 5523f06..4ac0373 100644 (file)
@@ -1197,11 +1197,12 @@ static void geneve_setup(struct net_device *dev)
        SET_NETDEV_DEVTYPE(dev, &geneve_type);
 
        dev->features    |= NETIF_F_LLTX;
-       dev->features    |= NETIF_F_SG | NETIF_F_HW_CSUM;
+       dev->features    |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
        dev->features    |= NETIF_F_RXCSUM;
        dev->features    |= NETIF_F_GSO_SOFTWARE;
 
-       dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+       dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
+       dev->hw_features |= NETIF_F_RXCSUM;
        dev->hw_features |= NETIF_F_GSO_SOFTWARE;
 
        /* MTU range: 68 - (something less than 65535) */
@@ -1851,16 +1852,10 @@ static int geneve_netdevice_event(struct notifier_block *unused,
 {
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 
-       if (event == NETDEV_UDP_TUNNEL_PUSH_INFO ||
-           event == NETDEV_UDP_TUNNEL_DROP_INFO) {
-               geneve_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO);
-       } else if (event == NETDEV_UNREGISTER) {
-               if (!dev->udp_tunnel_nic_info)
-                       geneve_offload_rx_ports(dev, false);
-       } else if (event == NETDEV_REGISTER) {
-               if (!dev->udp_tunnel_nic_info)
-                       geneve_offload_rx_ports(dev, true);
-       }
+       if (event == NETDEV_UDP_TUNNEL_PUSH_INFO)
+               geneve_offload_rx_ports(dev, true);
+       else if (event == NETDEV_UDP_TUNNEL_DROP_INFO)
+               geneve_offload_rx_ports(dev, false);
 
        return NOTIFY_DONE;
 }
index 4c04e27..8513643 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/file.h>
 #include <linux/gtp.h>
 
+#include <net/dst_metadata.h>
 #include <net/net_namespace.h>
 #include <net/protocol.h>
 #include <net/ip.h>
@@ -73,6 +74,9 @@ struct gtp_dev {
        unsigned int            hash_size;
        struct hlist_head       *tid_hash;
        struct hlist_head       *addr_hash;
+       /* Used by LWT tunnel. */
+       bool                    collect_md;
+       struct socket           *collect_md_sock;
 };
 
 static unsigned int gtp_net_id __read_mostly;
@@ -179,33 +183,121 @@ static bool gtp_check_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
        return false;
 }
 
-static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
-                       unsigned int hdrlen, unsigned int role)
+static int gtp_set_tun_dst(struct gtp_dev *gtp, struct sk_buff *skb,
+                          unsigned int hdrlen, u8 gtp_version,
+                          __be64 tid, u8 flags)
 {
-       if (!gtp_check_ms(skb, pctx, hdrlen, role)) {
-               netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
-               return 1;
+       struct metadata_dst *tun_dst;
+       int opts_len = 0;
+
+       if (unlikely(flags & GTP1_F_MASK))
+               opts_len = sizeof(struct gtpu_metadata);
+
+       tun_dst = udp_tun_rx_dst(skb, gtp->sk1u->sk_family, TUNNEL_KEY, tid, opts_len);
+       if (!tun_dst) {
+               netdev_dbg(gtp->dev, "Failed to allocate tun_dst");
+               goto err;
        }
 
+       netdev_dbg(gtp->dev, "attaching metadata_dst to skb, gtp ver %d hdrlen %d\n",
+                  gtp_version, hdrlen);
+       if (unlikely(opts_len)) {
+               struct gtpu_metadata *opts;
+               struct gtp1_header *gtp1;
+
+               opts = ip_tunnel_info_opts(&tun_dst->u.tun_info);
+               gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
+               opts->ver = GTP_METADATA_V1;
+               opts->flags = gtp1->flags;
+               opts->type = gtp1->type;
+               netdev_dbg(gtp->dev, "recved control pkt: flag %x type: %d\n",
+                          opts->flags, opts->type);
+               tun_dst->u.tun_info.key.tun_flags |= TUNNEL_GTPU_OPT;
+               tun_dst->u.tun_info.options_len = opts_len;
+               skb->protocol = htons(0xffff);         /* Unknown */
+       }
        /* Get rid of the GTP + UDP headers. */
        if (iptunnel_pull_header(skb, hdrlen, skb->protocol,
-                                !net_eq(sock_net(pctx->sk), dev_net(pctx->dev))))
-               return -1;
+                                !net_eq(sock_net(gtp->sk1u), dev_net(gtp->dev)))) {
+               gtp->dev->stats.rx_length_errors++;
+               goto err;
+       }
+
+       skb_dst_set(skb, &tun_dst->dst);
+       return 0;
+err:
+       return -1;
+}
+
+static int gtp_rx(struct gtp_dev *gtp, struct sk_buff *skb,
+                 unsigned int hdrlen, u8 gtp_version, unsigned int role,
+                 __be64 tid, u8 flags, u8 type)
+{
+       if (ip_tunnel_collect_metadata() || gtp->collect_md) {
+               int err;
+
+               err = gtp_set_tun_dst(gtp, skb, hdrlen, gtp_version, tid, flags);
+               if (err)
+                       goto err;
+       } else {
+               struct pdp_ctx *pctx;
+
+               if (flags & GTP1_F_MASK)
+                       hdrlen += 4;
 
-       netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n");
+               if (type != GTP_TPDU)
+                       return 1;
+
+               if (gtp_version == GTP_V0)
+                       pctx = gtp0_pdp_find(gtp, be64_to_cpu(tid));
+               else
+                       pctx = gtp1_pdp_find(gtp, be64_to_cpu(tid));
+               if (!pctx) {
+                       netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
+                       return 1;
+               }
+
+               if (!gtp_check_ms(skb, pctx, hdrlen, role)) {
+                       netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
+                       return 1;
+               }
+               /* Get rid of the GTP + UDP headers. */
+               if (iptunnel_pull_header(skb, hdrlen, skb->protocol,
+                                        !net_eq(sock_net(pctx->sk), dev_net(gtp->dev)))) {
+                       gtp->dev->stats.rx_length_errors++;
+                       goto err;
+               }
+       }
+       netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n");
 
        /* Now that the UDP and the GTP header have been removed, set up the
         * new network header. This is required by the upper layer to
         * calculate the transport header.
         */
        skb_reset_network_header(skb);
+       if (pskb_may_pull(skb, sizeof(struct iphdr))) {
+               struct iphdr *iph;
+
+               iph = ip_hdr(skb);
+               if (iph->version == 4) {
+                       netdev_dbg(gtp->dev, "inner pkt: ipv4");
+                       skb->protocol = htons(ETH_P_IP);
+               } else if (iph->version == 6) {
+                       netdev_dbg(gtp->dev, "inner pkt: ipv6");
+                       skb->protocol = htons(ETH_P_IPV6);
+               } else {
+                       netdev_dbg(gtp->dev, "inner pkt error: Unknown type");
+               }
+       }
 
-       skb->dev = pctx->dev;
-
-       dev_sw_netstats_rx_add(pctx->dev, skb->len);
-
+       skb->dev = gtp->dev;
+       dev_sw_netstats_rx_add(gtp->dev, skb->len);
        netif_rx(skb);
        return 0;
+
+err:
+       gtp->dev->stats.rx_dropped++;
+       return -1;
 }
 
 /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
@@ -214,7 +306,6 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
        unsigned int hdrlen = sizeof(struct udphdr) +
                              sizeof(struct gtp0_header);
        struct gtp0_header *gtp0;
-       struct pdp_ctx *pctx;
 
        if (!pskb_may_pull(skb, hdrlen))
                return -1;
@@ -224,16 +315,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
        if ((gtp0->flags >> 5) != GTP_V0)
                return 1;
 
-       if (gtp0->type != GTP_TPDU)
-               return 1;
-
-       pctx = gtp0_pdp_find(gtp, be64_to_cpu(gtp0->tid));
-       if (!pctx) {
-               netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
-               return 1;
-       }
-
-       return gtp_rx(pctx, skb, hdrlen, gtp->role);
+       return gtp_rx(gtp, skb, hdrlen, GTP_V0, gtp->role, gtp0->tid, gtp0->flags, gtp0->type);
 }
 
 static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
@@ -241,41 +323,30 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb)
        unsigned int hdrlen = sizeof(struct udphdr) +
                              sizeof(struct gtp1_header);
        struct gtp1_header *gtp1;
-       struct pdp_ctx *pctx;
 
        if (!pskb_may_pull(skb, hdrlen))
                return -1;
 
        gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
 
+       netdev_dbg(gtp->dev, "GTPv1 recv: flags %x\n", gtp1->flags);
        if ((gtp1->flags >> 5) != GTP_V1)
                return 1;
 
-       if (gtp1->type != GTP_TPDU)
-               return 1;
-
        /* From 29.060: "This field shall be present if and only if any one or
         * more of the S, PN and E flags are set.".
         *
         * If any of the bit is set, then the remaining ones also have to be
         * set.
         */
-       if (gtp1->flags & GTP1_F_MASK)
-               hdrlen += 4;
-
        /* Make sure the header is larger enough, including extensions. */
        if (!pskb_may_pull(skb, hdrlen))
                return -1;
 
        gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
 
-       pctx = gtp1_pdp_find(gtp, ntohl(gtp1->tid));
-       if (!pctx) {
-               netdev_dbg(gtp->dev, "No PDP ctx to decap skb=%p\n", skb);
-               return 1;
-       }
-
-       return gtp_rx(pctx, skb, hdrlen, gtp->role);
+       return gtp_rx(gtp, skb, hdrlen, GTP_V1, gtp->role,
+                     key32_to_tunnel_id(gtp1->tid), gtp1->flags, gtp1->type);
 }
 
 static void __gtp_encap_destroy(struct sock *sk)
@@ -315,6 +386,11 @@ static void gtp_encap_disable(struct gtp_dev *gtp)
 {
        gtp_encap_disable_sock(gtp->sk0);
        gtp_encap_disable_sock(gtp->sk1u);
+       if (gtp->collect_md_sock) {
+               udp_tunnel_sock_release(gtp->collect_md_sock);
+               gtp->collect_md_sock = NULL;
+               netdev_dbg(gtp->dev, "GTP socket released.\n");
+       }
 }
 
 /* UDP encapsulation receive handler. See net/ipv4/udp.c.
@@ -329,7 +405,8 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
        if (!gtp)
                return 1;
 
-       netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk);
+       netdev_dbg(gtp->dev, "encap_recv sk=%p type %d\n",
+                  sk, udp_sk(sk)->encap_type);
 
        switch (udp_sk(sk)->encap_type) {
        case UDP_ENCAP_GTP0:
@@ -383,12 +460,13 @@ static void gtp_dev_uninit(struct net_device *dev)
 
 static struct rtable *ip4_route_output_gtp(struct flowi4 *fl4,
                                           const struct sock *sk,
-                                          __be32 daddr)
+                                          __be32 daddr,
+                                          __be32 saddr)
 {
        memset(fl4, 0, sizeof(*fl4));
        fl4->flowi4_oif         = sk->sk_bound_dev_if;
        fl4->daddr              = daddr;
-       fl4->saddr              = inet_sk(sk)->inet_saddr;
+       fl4->saddr              = saddr;
        fl4->flowi4_tos         = RT_CONN_FLAGS(sk);
        fl4->flowi4_proto       = sk->sk_protocol;
 
@@ -412,7 +490,7 @@ static inline void gtp0_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
        gtp0->tid       = cpu_to_be64(pctx->u.v0.tid);
 }
 
-static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
+static inline void gtp1_push_header(struct sk_buff *skb, __be32 tid)
 {
        int payload_len = skb->len;
        struct gtp1_header *gtp1;
@@ -428,46 +506,63 @@ static inline void gtp1_push_header(struct sk_buff *skb, struct pdp_ctx *pctx)
        gtp1->flags     = 0x30; /* v1, GTP-non-prime. */
        gtp1->type      = GTP_TPDU;
        gtp1->length    = htons(payload_len);
-       gtp1->tid       = htonl(pctx->u.v1.o_tei);
+       gtp1->tid       = tid;
 
        /* TODO: Suppport for extension header, sequence number and N-PDU.
         *       Update the length field if any of them is available.
         */
 }
 
-struct gtp_pktinfo {
-       struct sock             *sk;
-       struct iphdr            *iph;
-       struct flowi4           fl4;
-       struct rtable           *rt;
-       struct pdp_ctx          *pctx;
-       struct net_device       *dev;
-       __be16                  gtph_port;
-};
-
-static void gtp_push_header(struct sk_buff *skb, struct gtp_pktinfo *pktinfo)
+static inline int gtp1_push_control_header(struct sk_buff *skb,
+                                          __be32 tid,
+                                          struct gtpu_metadata *opts,
+                                          struct net_device *dev)
 {
-       switch (pktinfo->pctx->gtp_version) {
-       case GTP_V0:
-               pktinfo->gtph_port = htons(GTP0_PORT);
-               gtp0_push_header(skb, pktinfo->pctx);
-               break;
-       case GTP_V1:
-               pktinfo->gtph_port = htons(GTP1U_PORT);
-               gtp1_push_header(skb, pktinfo->pctx);
-               break;
+       struct gtp1_header *gtp1c;
+       int payload_len;
+
+       if (opts->ver != GTP_METADATA_V1)
+               return -ENOENT;
+
+       if (opts->type == 0xFE) {
+               /* for end marker ignore skb data. */
+               netdev_dbg(dev, "xmit pkt with null data");
+               pskb_trim(skb, 0);
        }
+       if (skb_cow_head(skb, sizeof(*gtp1c)) < 0)
+               return -ENOMEM;
+
+       payload_len = skb->len;
+
+       gtp1c = skb_push(skb, sizeof(*gtp1c));
+
+       gtp1c->flags    = opts->flags;
+       gtp1c->type     = opts->type;
+       gtp1c->length   = htons(payload_len);
+       gtp1c->tid      = tid;
+       netdev_dbg(dev, "xmit control pkt: ver %d flags %x type %x pkt len %d tid %x",
+                  opts->ver, opts->flags, opts->type, skb->len, tid);
+       return 0;
 }
 
+struct gtp_pktinfo {
+       struct sock             *sk;
+       __u8                    tos;
+       struct flowi4           fl4;
+       struct rtable           *rt;
+       struct net_device       *dev;
+       __be16                  gtph_port;
+};
+
 static inline void gtp_set_pktinfo_ipv4(struct gtp_pktinfo *pktinfo,
-                                       struct sock *sk, struct iphdr *iph,
-                                       struct pdp_ctx *pctx, struct rtable *rt,
+                                       struct sock *sk,
+                                       __u8 tos,
+                                       struct rtable *rt,
                                        struct flowi4 *fl4,
                                        struct net_device *dev)
 {
        pktinfo->sk     = sk;
-       pktinfo->iph    = iph;
-       pktinfo->pctx   = pctx;
+       pktinfo->tos    = tos;
        pktinfo->rt     = rt;
        pktinfo->fl4    = *fl4;
        pktinfo->dev    = dev;
@@ -477,40 +572,99 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
                             struct gtp_pktinfo *pktinfo)
 {
        struct gtp_dev *gtp = netdev_priv(dev);
+       struct gtpu_metadata *opts = NULL;
+       struct sock *sk = NULL;
        struct pdp_ctx *pctx;
        struct rtable *rt;
        struct flowi4 fl4;
-       struct iphdr *iph;
-       __be16 df;
+       u8 gtp_version;
+       __be16 df = 0;
+       __be32 tun_id;
+       __be32 daddr;
+       __be32 saddr;
+       __u8 tos;
        int mtu;
 
-       /* Read the IP destination address and resolve the PDP context.
-        * Prepend PDP header with TEI/TID from PDP ctx.
-        */
-       iph = ip_hdr(skb);
-       if (gtp->role == GTP_ROLE_SGSN)
-               pctx = ipv4_pdp_find(gtp, iph->saddr);
-       else
-               pctx = ipv4_pdp_find(gtp, iph->daddr);
+       if (gtp->collect_md) {
+               /* LWT GTP1U encap */
+               struct ip_tunnel_info *info = NULL;
 
-       if (!pctx) {
-               netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
-                          &iph->daddr);
-               return -ENOENT;
+               info = skb_tunnel_info(skb);
+               if (!info) {
+                       netdev_dbg(dev, "missing tunnel info");
+                       return -ENOENT;
+               }
+               if (info->key.tp_dst && ntohs(info->key.tp_dst) != GTP1U_PORT) {
+                       netdev_dbg(dev, "unexpected GTP dst port: %d", ntohs(info->key.tp_dst));
+                       return -EOPNOTSUPP;
+               }
+               pctx = NULL;
+               gtp_version = GTP_V1;
+               tun_id = tunnel_id_to_key32(info->key.tun_id);
+               daddr = info->key.u.ipv4.dst;
+               saddr = info->key.u.ipv4.src;
+               sk = gtp->sk1u;
+               if (!sk) {
+                       netdev_dbg(dev, "missing tunnel sock");
+                       return -EOPNOTSUPP;
+               }
+               tos = info->key.tos;
+               if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT)
+                       df = htons(IP_DF);
+
+               if (info->options_len != 0) {
+                       if (info->key.tun_flags & TUNNEL_GTPU_OPT) {
+                               opts = ip_tunnel_info_opts(info);
+                       } else {
+                               netdev_dbg(dev, "missing tunnel metadata for control pkt");
+                               return -EOPNOTSUPP;
+                       }
+               }
+               netdev_dbg(dev, "flow-based GTP1U encap: tunnel id %d\n",
+                          be32_to_cpu(tun_id));
+       } else {
+               struct iphdr *iph;
+
+               if (ntohs(skb->protocol) != ETH_P_IP)
+                       return -EOPNOTSUPP;
+
+               iph = ip_hdr(skb);
+
+               /* Read the IP destination address and resolve the PDP context.
+                * Prepend PDP header with TEI/TID from PDP ctx.
+                */
+               if (gtp->role == GTP_ROLE_SGSN)
+                       pctx = ipv4_pdp_find(gtp, iph->saddr);
+               else
+                       pctx = ipv4_pdp_find(gtp, iph->daddr);
+
+               if (!pctx) {
+                       netdev_dbg(dev, "no PDP ctx found for %pI4, skip\n",
+                                  &iph->daddr);
+                       return -ENOENT;
+               }
+               sk = pctx->sk;
+               netdev_dbg(dev, "found PDP context %p\n", pctx);
+
+               gtp_version = pctx->gtp_version;
+               tun_id  = htonl(pctx->u.v1.o_tei);
+               daddr = pctx->peer_addr_ip4.s_addr;
+               saddr = inet_sk(sk)->inet_saddr;
+               tos = iph->tos;
+               df = iph->frag_off;
+               netdev_dbg(dev, "gtp -> IP src: %pI4 dst: %pI4\n",
+                          &iph->saddr, &iph->daddr);
        }
-       netdev_dbg(dev, "found PDP context %p\n", pctx);
 
-       rt = ip4_route_output_gtp(&fl4, pctx->sk, pctx->peer_addr_ip4.s_addr);
+       rt = ip4_route_output_gtp(&fl4, sk, daddr, saddr);
        if (IS_ERR(rt)) {
-               netdev_dbg(dev, "no route to SSGN %pI4\n",
-                          &pctx->peer_addr_ip4.s_addr);
+               netdev_dbg(dev, "no route to SSGN %pI4\n", &daddr);
                dev->stats.tx_carrier_errors++;
                goto err;
        }
 
        if (rt->dst.dev == dev) {
-               netdev_dbg(dev, "circular route to SSGN %pI4\n",
-                          &pctx->peer_addr_ip4.s_addr);
+               netdev_dbg(dev, "circular route to SSGN %pI4\n", &daddr);
                dev->stats.collisions++;
                goto err_rt;
        }
@@ -518,11 +672,10 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
        skb_dst_drop(skb);
 
        /* This is similar to tnl_update_pmtu(). */
-       df = iph->frag_off;
        if (df) {
                mtu = dst_mtu(&rt->dst) - dev->hard_header_len -
                        sizeof(struct iphdr) - sizeof(struct udphdr);
-               switch (pctx->gtp_version) {
+               switch (gtp_version) {
                case GTP_V0:
                        mtu -= sizeof(struct gtp0_header);
                        break;
@@ -536,17 +689,38 @@ static int gtp_build_skb_ip4(struct sk_buff *skb, struct net_device *dev,
 
        rt->dst.ops->update_pmtu(&rt->dst, NULL, skb, mtu, false);
 
-       if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) &&
-           mtu < ntohs(iph->tot_len)) {
-               netdev_dbg(dev, "packet too big, fragmentation needed\n");
+       if (!skb_is_gso(skb) && (df & htons(IP_DF)) && mtu < skb->len) {
+               netdev_dbg(dev, "packet too big, fragmentation needed");
                memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
                icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
                              htonl(mtu));
                goto err_rt;
        }
 
-       gtp_set_pktinfo_ipv4(pktinfo, pctx->sk, iph, pctx, rt, &fl4, dev);
-       gtp_push_header(skb, pktinfo);
+       gtp_set_pktinfo_ipv4(pktinfo, sk, tos, rt, &fl4, dev);
+
+       if (unlikely(opts)) {
+               int err;
+
+               pktinfo->gtph_port = htons(GTP1U_PORT);
+               err = gtp1_push_control_header(skb, tun_id, opts, dev);
+               if (err) {
+                       netdev_info(dev, "cntr pkt error %d", err);
+                       goto err_rt;
+               }
+               return 0;
+       }
+
+       switch (gtp_version) {
+       case GTP_V0:
+               pktinfo->gtph_port = htons(GTP0_PORT);
+               gtp0_push_header(skb, pctx);
+               break;
+       case GTP_V1:
+               pktinfo->gtph_port = htons(GTP1U_PORT);
+               gtp1_push_header(skb, tun_id);
+               break;
+       }
 
        return 0;
 err_rt:
@@ -557,7 +731,6 @@ err:
 
 static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       unsigned int proto = ntohs(skb->protocol);
        struct gtp_pktinfo pktinfo;
        int err;
 
@@ -569,32 +742,22 @@ static netdev_tx_t gtp_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 
        /* PDP context lookups in gtp_build_skb_*() need rcu read-side lock. */
        rcu_read_lock();
-       switch (proto) {
-       case ETH_P_IP:
-               err = gtp_build_skb_ip4(skb, dev, &pktinfo);
-               break;
-       default:
-               err = -EOPNOTSUPP;
-               break;
-       }
+       err = gtp_build_skb_ip4(skb, dev, &pktinfo);
        rcu_read_unlock();
 
        if (err < 0)
                goto tx_err;
 
-       switch (proto) {
-       case ETH_P_IP:
-               netdev_dbg(pktinfo.dev, "gtp -> IP src: %pI4 dst: %pI4\n",
-                          &pktinfo.iph->saddr, &pktinfo.iph->daddr);
-               udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb,
-                                   pktinfo.fl4.saddr, pktinfo.fl4.daddr,
-                                   pktinfo.iph->tos,
-                                   ip4_dst_hoplimit(&pktinfo.rt->dst),
-                                   0,
-                                   pktinfo.gtph_port, pktinfo.gtph_port,
-                                   true, false);
-               break;
-       }
+       udp_tunnel_xmit_skb(pktinfo.rt, pktinfo.sk, skb,
+                           pktinfo.fl4.saddr,
+                           pktinfo.fl4.daddr,
+                           pktinfo.tos,
+                           ip4_dst_hoplimit(&pktinfo.rt->dst),
+                           0,
+                           pktinfo.gtph_port,
+                           pktinfo.gtph_port,
+                           true,
+                           false);
 
        return NETDEV_TX_OK;
 tx_err:
@@ -610,6 +773,19 @@ static const struct net_device_ops gtp_netdev_ops = {
        .ndo_get_stats64        = dev_get_tstats64,
 };
 
+static struct gtp_dev *gtp_find_flow_based_dev(struct net *net)
+{
+       struct gtp_net *gn = net_generic(net, gtp_net_id);
+       struct gtp_dev *gtp;
+
+       list_for_each_entry(gtp, &gn->gtp_dev_list, list) {
+               if (gtp->collect_md)
+                       return gtp;
+       }
+
+       return NULL;
+}
+
 static void gtp_link_setup(struct net_device *dev)
 {
        dev->netdev_ops         = &gtp_netdev_ops;
@@ -634,7 +810,7 @@ static void gtp_link_setup(struct net_device *dev)
 }
 
 static int gtp_hashtable_new(struct gtp_dev *gtp, int hsize);
-static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[]);
+static int gtp_encap_enable(struct gtp_dev *gtp, struct net_device *dev, struct nlattr *data[]);
 
 static void gtp_destructor(struct net_device *dev)
 {
@@ -652,11 +828,24 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
        struct gtp_net *gn;
        int hashsize, err;
 
-       if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1])
+       if (!data[IFLA_GTP_FD0] && !data[IFLA_GTP_FD1] &&
+           !data[IFLA_GTP_COLLECT_METADATA])
                return -EINVAL;
 
        gtp = netdev_priv(dev);
 
+       if (data[IFLA_GTP_COLLECT_METADATA]) {
+               if (data[IFLA_GTP_FD0]) {
+                       netdev_dbg(dev, "LWT device does not support setting v0 socket");
+                       return -EINVAL;
+               }
+               if (gtp_find_flow_based_dev(src_net)) {
+                       netdev_dbg(dev, "LWT device already exist");
+                       return -EBUSY;
+               }
+               gtp->collect_md = true;
+       }
+
        if (!data[IFLA_GTP_PDP_HASHSIZE]) {
                hashsize = 1024;
        } else {
@@ -669,7 +858,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
        if (err < 0)
                return err;
 
-       err = gtp_encap_enable(gtp, data);
+       err = gtp_encap_enable(gtp, dev, data);
        if (err < 0)
                goto out_hashtable;
 
@@ -683,7 +872,7 @@ static int gtp_newlink(struct net *src_net, struct net_device *dev,
        list_add_rcu(&gtp->list, &gn->gtp_dev_list);
        dev->priv_destructor = gtp_destructor;
 
-       netdev_dbg(dev, "registered new GTP interface\n");
+       netdev_dbg(dev, "registered new GTP interface %s\n", dev->name);
 
        return 0;
 
@@ -714,6 +903,7 @@ static const struct nla_policy gtp_policy[IFLA_GTP_MAX + 1] = {
        [IFLA_GTP_FD1]                  = { .type = NLA_U32 },
        [IFLA_GTP_PDP_HASHSIZE]         = { .type = NLA_U32 },
        [IFLA_GTP_ROLE]                 = { .type = NLA_U32 },
+       [IFLA_GTP_COLLECT_METADATA]     = { .type = NLA_FLAG },
 };
 
 static int gtp_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -737,6 +927,9 @@ static int gtp_fill_info(struct sk_buff *skb, const struct net_device *dev)
        if (nla_put_u32(skb, IFLA_GTP_PDP_HASHSIZE, gtp->hash_size))
                goto nla_put_failure;
 
+       if (gtp->collect_md && nla_put_flag(skb, IFLA_GTP_COLLECT_METADATA))
+               goto nla_put_failure;
+
        return 0;
 
 nla_put_failure:
@@ -782,35 +975,24 @@ err1:
        return -ENOMEM;
 }
 
-static struct sock *gtp_encap_enable_socket(int fd, int type,
-                                           struct gtp_dev *gtp)
+static int __gtp_encap_enable_socket(struct socket *sock, int type,
+                                    struct gtp_dev *gtp)
 {
        struct udp_tunnel_sock_cfg tuncfg = {NULL};
-       struct socket *sock;
        struct sock *sk;
-       int err;
-
-       pr_debug("enable gtp on %d, %d\n", fd, type);
-
-       sock = sockfd_lookup(fd, &err);
-       if (!sock) {
-               pr_debug("gtp socket fd=%d not found\n", fd);
-               return NULL;
-       }
 
        sk = sock->sk;
        if (sk->sk_protocol != IPPROTO_UDP ||
            sk->sk_type != SOCK_DGRAM ||
            (sk->sk_family != AF_INET && sk->sk_family != AF_INET6)) {
-               pr_debug("socket fd=%d not UDP\n", fd);
-               sk = ERR_PTR(-EINVAL);
-               goto out_sock;
+               pr_debug("socket not UDP\n");
+               return -EINVAL;
        }
 
        lock_sock(sk);
        if (sk->sk_user_data) {
-               sk = ERR_PTR(-EBUSY);
-               goto out_rel_sock;
+               release_sock(sock->sk);
+               return -EBUSY;
        }
 
        sock_hold(sk);
@@ -821,15 +1003,58 @@ static struct sock *gtp_encap_enable_socket(int fd, int type,
        tuncfg.encap_destroy = gtp_encap_destroy;
 
        setup_udp_tunnel_sock(sock_net(sock->sk), sock, &tuncfg);
-
-out_rel_sock:
        release_sock(sock->sk);
-out_sock:
+       return 0;
+}
+
+static struct sock *gtp_encap_enable_socket(int fd, int type,
+                                           struct gtp_dev *gtp)
+{
+       struct socket *sock;
+       int err;
+
+       pr_debug("enable gtp on %d, %d\n", fd, type);
+
+       sock = sockfd_lookup(fd, &err);
+       if (!sock) {
+               pr_debug("gtp socket fd=%d not found\n", fd);
+               return NULL;
+       }
+       err =  __gtp_encap_enable_socket(sock, type, gtp);
        sockfd_put(sock);
-       return sk;
+       if (err)
+               return ERR_PTR(err);
+
+       return sock->sk;
 }
 
-static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
+static struct socket *gtp_create_gtp_socket(struct gtp_dev *gtp, struct net_device *dev)
+{
+       struct udp_port_cfg udp_conf;
+       struct socket *sock;
+       int err;
+
+       memset(&udp_conf, 0, sizeof(udp_conf));
+       udp_conf.family = AF_INET;
+       udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
+       udp_conf.local_udp_port = htons(GTP1U_PORT);
+
+       err = udp_sock_create(dev_net(dev), &udp_conf, &sock);
+       if (err < 0) {
+               pr_debug("create gtp sock failed: %d\n", err);
+               return ERR_PTR(err);
+       }
+       err = __gtp_encap_enable_socket(sock, UDP_ENCAP_GTP1U, gtp);
+       if (err) {
+               pr_debug("enable gtp sock encap failed: %d\n", err);
+               udp_tunnel_sock_release(sock);
+               return ERR_PTR(err);
+       }
+       pr_debug("create gtp sock done\n");
+       return sock;
+}
+
+static int gtp_encap_enable(struct gtp_dev *gtp, struct net_device *dev, struct nlattr *data[])
 {
        struct sock *sk1u = NULL;
        struct sock *sk0 = NULL;
@@ -853,11 +1078,25 @@ static int gtp_encap_enable(struct gtp_dev *gtp, struct nlattr *data[])
                }
        }
 
+       if (data[IFLA_GTP_COLLECT_METADATA]) {
+               struct socket *sock;
+
+               if (!sk1u) {
+                       sock = gtp_create_gtp_socket(gtp, dev);
+                       if (IS_ERR(sock))
+                               return PTR_ERR(sock);
+
+                       gtp->collect_md_sock = sock;
+                       sk1u = sock->sk;
+               } else {
+                       gtp->collect_md_sock = NULL;
+               }
+       }
+
        if (data[IFLA_GTP_ROLE]) {
                role = nla_get_u32(data[IFLA_GTP_ROLE]);
                if (role > GTP_ROLE_SGSN) {
-                       gtp_encap_disable_sock(sk0);
-                       gtp_encap_disable_sock(sk1u);
+                       gtp_encap_disable(gtp);
                        return -EINVAL;
                }
        }
@@ -1416,7 +1655,7 @@ static int __init gtp_init(void)
        if (err < 0)
                goto unreg_genl_family;
 
-       pr_info("GTP module loaded (pdp ctx size %zd bytes)\n",
+       pr_info("GTP module loaded (pdp ctx size %zd bytes) with tnl-md support\n",
                sizeof(struct pdp_ctx));
        return 0;
 
index 2a87cfa..e1a497d 100644 (file)
@@ -105,9 +105,43 @@ struct ndis_recv_scale_param { /* NDIS_RECEIVE_SCALE_PARAMETERS */
        u32 processor_masks_entry_size;
 };
 
-/* Fwd declaration */
-struct ndis_tcp_ip_checksum_info;
-struct ndis_pkt_8021q_info;
+struct ndis_tcp_ip_checksum_info {
+       union {
+               struct {
+                       u32 is_ipv4:1;
+                       u32 is_ipv6:1;
+                       u32 tcp_checksum:1;
+                       u32 udp_checksum:1;
+                       u32 ip_header_checksum:1;
+                       u32 reserved:11;
+                       u32 tcp_header_offset:10;
+               } transmit;
+               struct {
+                       u32 tcp_checksum_failed:1;
+                       u32 udp_checksum_failed:1;
+                       u32 ip_checksum_failed:1;
+                       u32 tcp_checksum_succeeded:1;
+                       u32 udp_checksum_succeeded:1;
+                       u32 ip_checksum_succeeded:1;
+                       u32 loopback:1;
+                       u32 tcp_checksum_value_invalid:1;
+                       u32 ip_checksum_value_invalid:1;
+               } receive;
+               u32  value;
+       };
+};
+
+struct ndis_pkt_8021q_info {
+       union {
+               struct {
+                       u32 pri:3; /* User Priority */
+                       u32 cfi:1; /* Canonical Format ID */
+                       u32 vlanid:12; /* VLAN ID */
+                       u32 reserved:16;
+               };
+               u32 value;
+       };
+};
 
 /*
  * Represent netvsc packet which contains 1 RNDIS and 1 ethernet frame
@@ -194,7 +228,8 @@ int netvsc_send(struct net_device *net,
                struct sk_buff *skb,
                bool xdp_tx);
 void netvsc_linkstatus_callback(struct net_device *net,
-                               struct rndis_message *resp);
+                               struct rndis_message *resp,
+                               void *data);
 int netvsc_recv_callback(struct net_device *net,
                         struct netvsc_device *nvdev,
                         struct netvsc_channel *nvchan);
@@ -884,9 +919,10 @@ struct multi_recv_comp {
 #define NVSP_RSC_MAX 562 /* Max #RSC frags in a vmbus xfer page pkt */
 
 struct nvsc_rsc {
-       const struct ndis_pkt_8021q_info *vlan;
-       const struct ndis_tcp_ip_checksum_info *csum_info;
-       const u32 *hash_info;
+       struct ndis_pkt_8021q_info vlan;
+       struct ndis_tcp_ip_checksum_info csum_info;
+       u32 hash_info;
+       u8 ppi_flags; /* valid/present bits for the above PPIs */
        u8 is_last; /* last RNDIS msg in a vmtransfer_page */
        u32 cnt; /* #fragments in an RSC packet */
        u32 pktlen; /* Full packet length */
@@ -894,6 +930,10 @@ struct nvsc_rsc {
        u32 len[NVSP_RSC_MAX];
 };
 
+#define NVSC_RSC_VLAN          BIT(0)  /* valid/present bit for 'vlan' */
+#define NVSC_RSC_CSUM_INFO     BIT(1)  /* valid/present bit for 'csum_info' */
+#define NVSC_RSC_HASH_INFO     BIT(2)  /* valid/present bit for 'hash_info' */
+
 struct netvsc_stats {
        u64 packets;
        u64 bytes;
@@ -1002,6 +1042,7 @@ struct net_device_context {
 struct netvsc_channel {
        struct vmbus_channel *channel;
        struct netvsc_device *net_device;
+       void *recv_buf; /* buffer to copy packets out from the receive buffer */
        const struct vmpacket_descriptor *desc;
        struct napi_struct napi;
        struct multi_send_data msd;
@@ -1234,18 +1275,6 @@ struct rndis_pktinfo_id {
        u16 pkt_id;
 };
 
-struct ndis_pkt_8021q_info {
-       union {
-               struct {
-                       u32 pri:3; /* User Priority */
-                       u32 cfi:1; /* Canonical Format ID */
-                       u32 vlanid:12; /* VLAN ID */
-                       u32 reserved:16;
-               };
-               u32 value;
-       };
-};
-
 struct ndis_object_header {
        u8 type;
        u8 revision;
@@ -1436,32 +1465,6 @@ struct ndis_offload_params {
        };
 };
 
-struct ndis_tcp_ip_checksum_info {
-       union {
-               struct {
-                       u32 is_ipv4:1;
-                       u32 is_ipv6:1;
-                       u32 tcp_checksum:1;
-                       u32 udp_checksum:1;
-                       u32 ip_header_checksum:1;
-                       u32 reserved:11;
-                       u32 tcp_header_offset:10;
-               } transmit;
-               struct {
-                       u32 tcp_checksum_failed:1;
-                       u32 udp_checksum_failed:1;
-                       u32 ip_checksum_failed:1;
-                       u32 tcp_checksum_succeeded:1;
-                       u32 udp_checksum_succeeded:1;
-                       u32 ip_checksum_succeeded:1;
-                       u32 loopback:1;
-                       u32 tcp_checksum_value_invalid:1;
-                       u32 ip_checksum_value_invalid:1;
-               } receive;
-               u32  value;
-       };
-};
-
 struct ndis_tcp_lso_info {
        union {
                struct {
index 2350342..0fba825 100644 (file)
@@ -37,6 +37,10 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf)
        struct netvsc_device *nv_dev = rtnl_dereference(net_device_ctx->nvdev);
        struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt;
 
+       /* Block sending traffic to VF if it's about to be gone */
+       if (!vf)
+               net_device_ctx->data_path_is_vf = vf;
+
        memset(init_pkt, 0, sizeof(struct nvsp_message));
        init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH;
        if (vf)
@@ -50,8 +54,11 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf)
 
        vmbus_sendpacket(dev->channel, init_pkt,
                               sizeof(struct nvsp_message),
-                              VMBUS_RQST_ID_NO_RESPONSE,
-                              VM_PKT_DATA_INBAND, 0);
+                              (unsigned long)init_pkt,
+                              VM_PKT_DATA_INBAND,
+                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       wait_for_completion(&nv_dev->channel_init_wait);
+       net_device_ctx->data_path_is_vf = vf;
 }
 
 /* Worker to setup sub channels on initial setup
@@ -124,6 +131,7 @@ static void free_netvsc_device(struct rcu_head *head)
 
        for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
                xdp_rxq_info_unreg(&nvdev->chan_table[i].xdp_rxq);
+               kfree(nvdev->chan_table[i].recv_buf);
                vfree(nvdev->chan_table[i].mrc.slots);
        }
 
@@ -754,8 +762,31 @@ static void netvsc_send_completion(struct net_device *ndev,
                                   const struct vmpacket_descriptor *desc,
                                   int budget)
 {
-       const struct nvsp_message *nvsp_packet = hv_pkt_data(desc);
+       const struct nvsp_message *nvsp_packet;
        u32 msglen = hv_pkt_datalen(desc);
+       struct nvsp_message *pkt_rqst;
+       u64 cmd_rqst;
+
+       /* First check if this is a VMBUS completion without data payload */
+       if (!msglen) {
+               cmd_rqst = vmbus_request_addr(&incoming_channel->requestor,
+                                             (u64)desc->trans_id);
+               if (cmd_rqst == VMBUS_RQST_ERROR) {
+                       netdev_err(ndev, "Invalid transaction id\n");
+                       return;
+               }
+
+               pkt_rqst = (struct nvsp_message *)(uintptr_t)cmd_rqst;
+               switch (pkt_rqst->hdr.msg_type) {
+               case NVSP_MSG4_TYPE_SWITCH_DATA_PATH:
+                       complete(&net_device->channel_init_wait);
+                       break;
+
+               default:
+                       netdev_err(ndev, "Unexpected VMBUS completion!!\n");
+               }
+               return;
+       }
 
        /* Ensure packet is big enough to read header fields */
        if (msglen < sizeof(struct nvsp_message_header)) {
@@ -763,6 +794,7 @@ static void netvsc_send_completion(struct net_device *ndev,
                return;
        }
 
+       nvsp_packet = hv_pkt_data(desc);
        switch (nvsp_packet->hdr.msg_type) {
        case NVSP_MSG_TYPE_INIT_COMPLETE:
                if (msglen < sizeof(struct nvsp_message_header) +
@@ -887,6 +919,7 @@ static inline int netvsc_send_pkt(
        int ret;
        u32 ring_avail = hv_get_avail_to_write_percent(&out_channel->outbound);
 
+       memset(&nvmsg, 0, sizeof(struct nvsp_message));
        nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
        if (skb)
                rpkt->channel_type = 0;         /* 0 is RMC_DATA */
@@ -1252,6 +1285,19 @@ static int netvsc_receive(struct net_device *ndev,
                        continue;
                }
 
+               /* We're going to copy (sections of) the packet into nvchan->recv_buf;
+                * make sure that nvchan->recv_buf is large enough to hold the packet.
+                */
+               if (unlikely(buflen > net_device->recv_section_size)) {
+                       nvchan->rsc.cnt = 0;
+                       status = NVSP_STAT_FAIL;
+                       netif_err(net_device_ctx, rx_err, ndev,
+                                 "Packet too big: buflen=%u recv_section_size=%u\n",
+                                 buflen, net_device->recv_section_size);
+
+                       continue;
+               }
+
                data = recv_buf + offset;
 
                nvchan->rsc.is_last = (i == count - 1);
@@ -1306,7 +1352,7 @@ static void netvsc_send_table(struct net_device *ndev,
                         sizeof(union nvsp_6_message_uber);
 
        /* Boundary check for all versions */
-       if (offset > msglen - count * sizeof(u32)) {
+       if (msglen < count * sizeof(u32) || offset > msglen - count * sizeof(u32)) {
                netdev_err(ndev, "Received send-table offset too big:%u\n",
                           offset);
                return;
@@ -1503,6 +1549,12 @@ struct netvsc_device *netvsc_device_add(struct hv_device *device,
        for (i = 0; i < VRSS_CHANNEL_MAX; i++) {
                struct netvsc_channel *nvchan = &net_device->chan_table[i];
 
+               nvchan->recv_buf = kzalloc(device_info->recv_section_size, GFP_KERNEL);
+               if (nvchan->recv_buf == NULL) {
+                       ret = -ENOMEM;
+                       goto cleanup2;
+               }
+
                nvchan->channel = device->channel;
                nvchan->net_device = net_device;
                u64_stats_init(&nvchan->tx_stats.syncp);
index 440486d..aa877da 100644 (file)
@@ -37,6 +37,12 @@ u32 netvsc_run_xdp(struct net_device *ndev, struct netvsc_channel *nvchan,
        if (!prog)
                goto out;
 
+       /* Ensure that the below memcpy() won't overflow the page buffer. */
+       if (len > ndev->mtu + ETH_HLEN) {
+               act = XDP_DROP;
+               goto out;
+       }
+
        /* allocate page buffer for data */
        page = alloc_page(GFP_ATOMIC);
        if (!page) {
@@ -44,12 +50,8 @@ u32 netvsc_run_xdp(struct net_device *ndev, struct netvsc_channel *nvchan,
                goto out;
        }
 
-       xdp->data_hard_start = page_address(page);
-       xdp->data = xdp->data_hard_start + NETVSC_XDP_HDRM;
-       xdp_set_data_meta_invalid(xdp);
-       xdp->data_end = xdp->data + len;
-       xdp->rxq = &nvchan->xdp_rxq;
-       xdp->frame_sz = PAGE_SIZE;
+       xdp_init_buff(xdp, PAGE_SIZE, &nvchan->xdp_rxq);
+       xdp_prepare_buff(xdp, page_address(page), NETVSC_XDP_HDRM, len, false);
 
        memcpy(xdp->data, data, len);
 
index f32f283..8176fa0 100644 (file)
@@ -539,7 +539,8 @@ static int netvsc_xmit(struct sk_buff *skb, struct net_device *net, bool xdp_tx)
         */
        vf_netdev = rcu_dereference_bh(net_device_ctx->vf_netdev);
        if (vf_netdev && netif_running(vf_netdev) &&
-           netif_carrier_ok(vf_netdev) && !netpoll_tx_running(net))
+           netif_carrier_ok(vf_netdev) && !netpoll_tx_running(net) &&
+           net_device_ctx->data_path_is_vf)
                return netvsc_vf_xmit(net, vf_netdev, skb);
 
        /* We will atmost need two pages to describe the rndis
@@ -742,7 +743,8 @@ static netdev_tx_t netvsc_start_xmit(struct sk_buff *skb,
  * netvsc_linkstatus_callback - Link up/down notification
  */
 void netvsc_linkstatus_callback(struct net_device *net,
-                               struct rndis_message *resp)
+                               struct rndis_message *resp,
+                               void *data)
 {
        struct rndis_indicate_status *indicate = &resp->msg.indicate_status;
        struct net_device_context *ndev_ctx = netdev_priv(net);
@@ -756,12 +758,24 @@ void netvsc_linkstatus_callback(struct net_device *net,
                return;
        }
 
+       /* Copy the RNDIS indicate status into nvchan->recv_buf */
+       memcpy(indicate, data + RNDIS_HEADER_SIZE, sizeof(*indicate));
+
        /* Update the physical link speed when changing to another vSwitch */
        if (indicate->status == RNDIS_STATUS_LINK_SPEED_CHANGE) {
                u32 speed;
 
-               speed = *(u32 *)((void *)indicate
-                                + indicate->status_buf_offset) / 10000;
+               /* Validate status_buf_offset */
+               if (indicate->status_buflen < sizeof(speed) ||
+                   indicate->status_buf_offset < sizeof(*indicate) ||
+                   resp->msg_len - RNDIS_HEADER_SIZE < indicate->status_buf_offset ||
+                   resp->msg_len - RNDIS_HEADER_SIZE - indicate->status_buf_offset
+                               < indicate->status_buflen) {
+                       netdev_err(net, "invalid rndis_indicate_status packet\n");
+                       return;
+               }
+
+               speed = *(u32 *)(data + RNDIS_HEADER_SIZE + indicate->status_buf_offset) / 10000;
                ndev_ctx->speed = speed;
                return;
        }
@@ -816,10 +830,11 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
                                             struct xdp_buff *xdp)
 {
        struct napi_struct *napi = &nvchan->napi;
-       const struct ndis_pkt_8021q_info *vlan = nvchan->rsc.vlan;
+       const struct ndis_pkt_8021q_info *vlan = &nvchan->rsc.vlan;
        const struct ndis_tcp_ip_checksum_info *csum_info =
-                                               nvchan->rsc.csum_info;
-       const u32 *hash_info = nvchan->rsc.hash_info;
+                                               &nvchan->rsc.csum_info;
+       const u32 *hash_info = &nvchan->rsc.hash_info;
+       u8 ppi_flags = nvchan->rsc.ppi_flags;
        struct sk_buff *skb;
        void *xbuf = xdp->data_hard_start;
        int i;
@@ -863,22 +878,28 @@ static struct sk_buff *netvsc_alloc_recv_skb(struct net_device *net,
         * We compute it here if the flags are set, because on Linux, the IP
         * checksum is always checked.
         */
-       if (csum_info && csum_info->receive.ip_checksum_value_invalid &&
+       if ((ppi_flags & NVSC_RSC_CSUM_INFO) && csum_info->receive.ip_checksum_value_invalid &&
            csum_info->receive.ip_checksum_succeeded &&
-           skb->protocol == htons(ETH_P_IP))
+           skb->protocol == htons(ETH_P_IP)) {
+               /* Check that there is enough space to hold the IP header. */
+               if (skb_headlen(skb) < sizeof(struct iphdr)) {
+                       kfree_skb(skb);
+                       return NULL;
+               }
                netvsc_comp_ipcsum(skb);
+       }
 
        /* Do L4 checksum offload if enabled and present. */
-       if (csum_info && (net->features & NETIF_F_RXCSUM)) {
+       if ((ppi_flags & NVSC_RSC_CSUM_INFO) && (net->features & NETIF_F_RXCSUM)) {
                if (csum_info->receive.tcp_checksum_succeeded ||
                    csum_info->receive.udp_checksum_succeeded)
                        skb->ip_summed = CHECKSUM_UNNECESSARY;
        }
 
-       if (hash_info && (net->features & NETIF_F_RXHASH))
+       if ((ppi_flags & NVSC_RSC_HASH_INFO) && (net->features & NETIF_F_RXHASH))
                skb_set_hash(skb, *hash_info, PKT_HASH_TYPE_L4);
 
-       if (vlan) {
+       if (ppi_flags & NVSC_RSC_VLAN) {
                u16 vlan_tci = vlan->vlanid | (vlan->pri << VLAN_PRIO_SHIFT) |
                        (vlan->cfi ? VLAN_CFI_MASK : 0);
 
@@ -2381,12 +2402,15 @@ static int netvsc_register_vf(struct net_device *vf_netdev)
  * During hibernation, if a VF NIC driver (e.g. mlx5) preserves the network
  * interface, there is only the CHANGE event and no UP or DOWN event.
  */
-static int netvsc_vf_changed(struct net_device *vf_netdev)
+static int netvsc_vf_changed(struct net_device *vf_netdev, unsigned long event)
 {
        struct net_device_context *net_device_ctx;
        struct netvsc_device *netvsc_dev;
        struct net_device *ndev;
-       bool vf_is_up = netif_running(vf_netdev);
+       bool vf_is_up = false;
+
+       if (event != NETDEV_GOING_DOWN)
+               vf_is_up = netif_running(vf_netdev);
 
        ndev = get_netvsc_byref(vf_netdev);
        if (!ndev)
@@ -2399,7 +2423,6 @@ static int netvsc_vf_changed(struct net_device *vf_netdev)
 
        if (net_device_ctx->data_path_is_vf == vf_is_up)
                return NOTIFY_OK;
-       net_device_ctx->data_path_is_vf = vf_is_up;
 
        netvsc_switch_datapath(ndev, vf_is_up);
        netdev_info(ndev, "Data path switched %s VF: %s\n",
@@ -2716,7 +2739,8 @@ static int netvsc_netdev_event(struct notifier_block *this,
        case NETDEV_UP:
        case NETDEV_DOWN:
        case NETDEV_CHANGE:
-               return netvsc_vf_changed(event_dev);
+       case NETDEV_GOING_DOWN:
+               return netvsc_vf_changed(event_dev, event);
        default:
                return NOTIFY_DONE;
        }
index 598713c..6c48a4d 100644 (file)
@@ -127,70 +127,89 @@ static void put_rndis_request(struct rndis_device *dev,
 }
 
 static void dump_rndis_message(struct net_device *netdev,
-                              const struct rndis_message *rndis_msg)
+                              const struct rndis_message *rndis_msg,
+                              const void *data)
 {
        switch (rndis_msg->ndis_msg_type) {
        case RNDIS_MSG_PACKET:
-               netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
-                          "data offset %u data len %u, # oob %u, "
-                          "oob offset %u, oob len %u, pkt offset %u, "
-                          "pkt len %u\n",
-                          rndis_msg->msg_len,
-                          rndis_msg->msg.pkt.data_offset,
-                          rndis_msg->msg.pkt.data_len,
-                          rndis_msg->msg.pkt.num_oob_data_elements,
-                          rndis_msg->msg.pkt.oob_data_offset,
-                          rndis_msg->msg.pkt.oob_data_len,
-                          rndis_msg->msg.pkt.per_pkt_info_offset,
-                          rndis_msg->msg.pkt.per_pkt_info_len);
+               if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >= sizeof(struct rndis_packet)) {
+                       const struct rndis_packet *pkt = data + RNDIS_HEADER_SIZE;
+                       netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
+                                  "data offset %u data len %u, # oob %u, "
+                                  "oob offset %u, oob len %u, pkt offset %u, "
+                                  "pkt len %u\n",
+                                  rndis_msg->msg_len,
+                                  pkt->data_offset,
+                                  pkt->data_len,
+                                  pkt->num_oob_data_elements,
+                                  pkt->oob_data_offset,
+                                  pkt->oob_data_len,
+                                  pkt->per_pkt_info_offset,
+                                  pkt->per_pkt_info_len);
+               }
                break;
 
        case RNDIS_MSG_INIT_C:
-               netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
-                       "(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
-                       "device flags %d, max xfer size 0x%x, max pkts %u, "
-                       "pkt aligned %u)\n",
-                       rndis_msg->msg_len,
-                       rndis_msg->msg.init_complete.req_id,
-                       rndis_msg->msg.init_complete.status,
-                       rndis_msg->msg.init_complete.major_ver,
-                       rndis_msg->msg.init_complete.minor_ver,
-                       rndis_msg->msg.init_complete.dev_flags,
-                       rndis_msg->msg.init_complete.max_xfer_size,
-                       rndis_msg->msg.init_complete.
-                          max_pkt_per_msg,
-                       rndis_msg->msg.init_complete.
-                          pkt_alignment_factor);
+               if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
+                               sizeof(struct rndis_initialize_complete)) {
+                       const struct rndis_initialize_complete *init_complete =
+                               data + RNDIS_HEADER_SIZE;
+                       netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
+                               "(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
+                               "device flags %d, max xfer size 0x%x, max pkts %u, "
+                               "pkt aligned %u)\n",
+                               rndis_msg->msg_len,
+                               init_complete->req_id,
+                               init_complete->status,
+                               init_complete->major_ver,
+                               init_complete->minor_ver,
+                               init_complete->dev_flags,
+                               init_complete->max_xfer_size,
+                               init_complete->max_pkt_per_msg,
+                               init_complete->pkt_alignment_factor);
+               }
                break;
 
        case RNDIS_MSG_QUERY_C:
-               netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
-                       "(len %u, id 0x%x, status 0x%x, buf len %u, "
-                       "buf offset %u)\n",
-                       rndis_msg->msg_len,
-                       rndis_msg->msg.query_complete.req_id,
-                       rndis_msg->msg.query_complete.status,
-                       rndis_msg->msg.query_complete.
-                          info_buflen,
-                       rndis_msg->msg.query_complete.
-                          info_buf_offset);
+               if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
+                               sizeof(struct rndis_query_complete)) {
+                       const struct rndis_query_complete *query_complete =
+                               data + RNDIS_HEADER_SIZE;
+                       netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
+                               "(len %u, id 0x%x, status 0x%x, buf len %u, "
+                               "buf offset %u)\n",
+                               rndis_msg->msg_len,
+                               query_complete->req_id,
+                               query_complete->status,
+                               query_complete->info_buflen,
+                               query_complete->info_buf_offset);
+               }
                break;
 
        case RNDIS_MSG_SET_C:
-               netdev_dbg(netdev,
-                       "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
-                       rndis_msg->msg_len,
-                       rndis_msg->msg.set_complete.req_id,
-                       rndis_msg->msg.set_complete.status);
+               if (rndis_msg->msg_len - RNDIS_HEADER_SIZE + sizeof(struct rndis_set_complete)) {
+                       const struct rndis_set_complete *set_complete =
+                               data + RNDIS_HEADER_SIZE;
+                       netdev_dbg(netdev,
+                               "RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
+                               rndis_msg->msg_len,
+                               set_complete->req_id,
+                               set_complete->status);
+               }
                break;
 
        case RNDIS_MSG_INDICATE:
-               netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
-                       "(len %u, status 0x%x, buf len %u, buf offset %u)\n",
-                       rndis_msg->msg_len,
-                       rndis_msg->msg.indicate_status.status,
-                       rndis_msg->msg.indicate_status.status_buflen,
-                       rndis_msg->msg.indicate_status.status_buf_offset);
+               if (rndis_msg->msg_len - RNDIS_HEADER_SIZE >=
+                               sizeof(struct rndis_indicate_status)) {
+                       const struct rndis_indicate_status *indicate_status =
+                               data + RNDIS_HEADER_SIZE;
+                       netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
+                               "(len %u, status 0x%x, buf len %u, buf offset %u)\n",
+                               rndis_msg->msg_len,
+                               indicate_status->status,
+                               indicate_status->status_buflen,
+                               indicate_status->status_buf_offset);
+               }
                break;
 
        default:
@@ -246,11 +265,20 @@ static void rndis_set_link_state(struct rndis_device *rdev,
 {
        u32 link_status;
        struct rndis_query_complete *query_complete;
+       u32 msg_len = request->response_msg.msg_len;
+
+       /* Ensure the packet is big enough to access its fields */
+       if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete))
+               return;
 
        query_complete = &request->response_msg.msg.query_complete;
 
        if (query_complete->status == RNDIS_STATUS_SUCCESS &&
-           query_complete->info_buflen == sizeof(u32)) {
+           query_complete->info_buflen >= sizeof(u32) &&
+           query_complete->info_buf_offset >= sizeof(*query_complete) &&
+           msg_len - RNDIS_HEADER_SIZE >= query_complete->info_buf_offset &&
+           msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset
+                       >= query_complete->info_buflen) {
                memcpy(&link_status, (void *)((unsigned long)query_complete +
                       query_complete->info_buf_offset), sizeof(u32));
                rdev->link_state = link_status != 0;
@@ -259,8 +287,10 @@ static void rndis_set_link_state(struct rndis_device *rdev,
 
 static void rndis_filter_receive_response(struct net_device *ndev,
                                          struct netvsc_device *nvdev,
-                                         const struct rndis_message *resp)
+                                         struct rndis_message *resp,
+                                         void *data)
 {
+       u32 *req_id = &resp->msg.init_complete.req_id;
        struct rndis_device *dev = nvdev->extension;
        struct rndis_request *request = NULL;
        bool found = false;
@@ -285,14 +315,16 @@ static void rndis_filter_receive_response(struct net_device *ndev,
                return;
        }
 
+       /* Copy the request ID into nvchan->recv_buf */
+       *req_id = *(u32 *)(data + RNDIS_HEADER_SIZE);
+
        spin_lock_irqsave(&dev->request_lock, flags);
        list_for_each_entry(request, &dev->req_list, list_ent) {
                /*
                 * All request/response message contains RequestId as the 1st
                 * field
                 */
-               if (request->request_msg.msg.init_req.req_id
-                   == resp->msg.init_complete.req_id) {
+               if (request->request_msg.msg.init_req.req_id == *req_id) {
                        found = true;
                        break;
                }
@@ -302,8 +334,10 @@ static void rndis_filter_receive_response(struct net_device *ndev,
        if (found) {
                if (resp->msg_len <=
                    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
-                       memcpy(&request->response_msg, resp,
-                              resp->msg_len);
+                       memcpy(&request->response_msg, resp, RNDIS_HEADER_SIZE + sizeof(*req_id));
+                       memcpy((void *)&request->response_msg + RNDIS_HEADER_SIZE + sizeof(*req_id),
+                              data + RNDIS_HEADER_SIZE + sizeof(*req_id),
+                              resp->msg_len - RNDIS_HEADER_SIZE - sizeof(*req_id));
                        if (request->request_msg.ndis_msg_type ==
                            RNDIS_MSG_QUERY && request->request_msg.msg.
                            query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
@@ -332,7 +366,7 @@ static void rndis_filter_receive_response(struct net_device *ndev,
                netdev_err(ndev,
                        "no rndis request found for this response "
                        "(id 0x%x res type 0x%x)\n",
-                       resp->msg.init_complete.req_id,
+                       *req_id,
                        resp->ndis_msg_type);
        }
 }
@@ -343,7 +377,8 @@ static void rndis_filter_receive_response(struct net_device *ndev,
  */
 static inline void *rndis_get_ppi(struct net_device *ndev,
                                  struct rndis_packet *rpkt,
-                                 u32 rpkt_len, u32 type, u8 internal)
+                                 u32 rpkt_len, u32 type, u8 internal,
+                                 u32 ppi_size, void *data)
 {
        struct rndis_per_packet_info *ppi;
        int len;
@@ -359,7 +394,8 @@ static inline void *rndis_get_ppi(struct net_device *ndev,
                return NULL;
        }
 
-       if (rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) {
+       if (rpkt->per_pkt_info_len < sizeof(*ppi) ||
+           rpkt->per_pkt_info_len > rpkt_len - rpkt->per_pkt_info_offset) {
                netdev_err(ndev, "Invalid per_pkt_info_len: %u\n",
                           rpkt->per_pkt_info_len);
                return NULL;
@@ -367,6 +403,8 @@ static inline void *rndis_get_ppi(struct net_device *ndev,
 
        ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
                rpkt->per_pkt_info_offset);
+       /* Copy the PPIs into nvchan->recv_buf */
+       memcpy(ppi, data + RNDIS_HEADER_SIZE + rpkt->per_pkt_info_offset, rpkt->per_pkt_info_len);
        len = rpkt->per_pkt_info_len;
 
        while (len > 0) {
@@ -381,8 +419,15 @@ static inline void *rndis_get_ppi(struct net_device *ndev,
                        continue;
                }
 
-               if (ppi->type == type && ppi->internal == internal)
+               if (ppi->type == type && ppi->internal == internal) {
+                       /* ppi->size should be big enough to hold the returned object. */
+                       if (ppi->size - ppi->ppi_offset < ppi_size) {
+                               netdev_err(ndev, "Invalid ppi: size %u ppi_offset %u\n",
+                                          ppi->size, ppi->ppi_offset);
+                               continue;
+                       }
                        return (void *)((ulong)ppi + ppi->ppi_offset);
+               }
                len -= ppi->size;
                ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
        }
@@ -402,10 +447,29 @@ void rsc_add_data(struct netvsc_channel *nvchan,
        if (cnt) {
                nvchan->rsc.pktlen += len;
        } else {
-               nvchan->rsc.vlan = vlan;
-               nvchan->rsc.csum_info = csum_info;
+               /* The data/values pointed by vlan, csum_info and hash_info are shared
+                * across the different 'fragments' of the RSC packet; store them into
+                * the packet itself.
+                */
+               if (vlan != NULL) {
+                       memcpy(&nvchan->rsc.vlan, vlan, sizeof(*vlan));
+                       nvchan->rsc.ppi_flags |= NVSC_RSC_VLAN;
+               } else {
+                       nvchan->rsc.ppi_flags &= ~NVSC_RSC_VLAN;
+               }
+               if (csum_info != NULL) {
+                       memcpy(&nvchan->rsc.csum_info, csum_info, sizeof(*csum_info));
+                       nvchan->rsc.ppi_flags |= NVSC_RSC_CSUM_INFO;
+               } else {
+                       nvchan->rsc.ppi_flags &= ~NVSC_RSC_CSUM_INFO;
+               }
                nvchan->rsc.pktlen = len;
-               nvchan->rsc.hash_info = hash_info;
+               if (hash_info != NULL) {
+                       nvchan->rsc.csum_info = *csum_info;
+                       nvchan->rsc.ppi_flags |= NVSC_RSC_HASH_INFO;
+               } else {
+                       nvchan->rsc.ppi_flags &= ~NVSC_RSC_HASH_INFO;
+               }
        }
 
        nvchan->rsc.data[cnt] = data;
@@ -417,7 +481,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
                                     struct netvsc_device *nvdev,
                                     struct netvsc_channel *nvchan,
                                     struct rndis_message *msg,
-                                    u32 data_buflen)
+                                    void *data, u32 data_buflen)
 {
        struct rndis_packet *rndis_pkt = &msg->msg.pkt;
        const struct ndis_tcp_ip_checksum_info *csum_info;
@@ -425,7 +489,6 @@ static int rndis_filter_receive_data(struct net_device *ndev,
        const struct rndis_pktinfo_id *pktinfo_id;
        const u32 *hash_info;
        u32 data_offset, rpkt_len;
-       void *data;
        bool rsc_more = false;
        int ret;
 
@@ -436,6 +499,9 @@ static int rndis_filter_receive_data(struct net_device *ndev,
                return NVSP_STAT_FAIL;
        }
 
+       /* Copy the RNDIS packet into nvchan->recv_buf */
+       memcpy(rndis_pkt, data + RNDIS_HEADER_SIZE, sizeof(*rndis_pkt));
+
        /* Validate rndis_pkt offset */
        if (rndis_pkt->data_offset >= data_buflen - RNDIS_HEADER_SIZE) {
                netdev_err(ndev, "invalid rndis packet offset: %u\n",
@@ -461,15 +527,17 @@ static int rndis_filter_receive_data(struct net_device *ndev,
                return NVSP_STAT_FAIL;
        }
 
-       vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0);
-
-       csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0);
+       vlan = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, IEEE_8021Q_INFO, 0, sizeof(*vlan),
+                            data);
 
-       hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0);
+       csum_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, TCPIP_CHKSUM_PKTINFO, 0,
+                                 sizeof(*csum_info), data);
 
-       pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1);
+       hash_info = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, NBL_HASH_VALUE, 0,
+                                 sizeof(*hash_info), data);
 
-       data = (void *)msg + data_offset;
+       pktinfo_id = rndis_get_ppi(ndev, rndis_pkt, rpkt_len, RNDIS_PKTINFO_ID, 1,
+                                  sizeof(*pktinfo_id), data);
 
        /* Identify RSC frags, drop erroneous packets */
        if (pktinfo_id && (pktinfo_id->flag & RNDIS_PKTINFO_SUBALLOC)) {
@@ -498,7 +566,7 @@ static int rndis_filter_receive_data(struct net_device *ndev,
         * the data packet to the stack, without the rndis trailer padding
         */
        rsc_add_data(nvchan, vlan, csum_info, hash_info,
-                    data, rndis_pkt->data_len);
+                    data + data_offset, rndis_pkt->data_len);
 
        if (rsc_more)
                return NVSP_STAT_SUCCESS;
@@ -520,33 +588,41 @@ int rndis_filter_receive(struct net_device *ndev,
                         void *data, u32 buflen)
 {
        struct net_device_context *net_device_ctx = netdev_priv(ndev);
-       struct rndis_message *rndis_msg = data;
+       struct rndis_message *rndis_msg = nvchan->recv_buf;
 
-       if (netif_msg_rx_status(net_device_ctx))
-               dump_rndis_message(ndev, rndis_msg);
+       if (buflen < RNDIS_HEADER_SIZE) {
+               netdev_err(ndev, "Invalid rndis_msg (buflen: %u)\n", buflen);
+               return NVSP_STAT_FAIL;
+       }
+
+       /* Copy the RNDIS msg header into nvchan->recv_buf */
+       memcpy(rndis_msg, data, RNDIS_HEADER_SIZE);
 
        /* Validate incoming rndis_message packet */
-       if (buflen < RNDIS_HEADER_SIZE || rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
+       if (rndis_msg->msg_len < RNDIS_HEADER_SIZE ||
            buflen < rndis_msg->msg_len) {
                netdev_err(ndev, "Invalid rndis_msg (buflen: %u, msg_len: %u)\n",
                           buflen, rndis_msg->msg_len);
                return NVSP_STAT_FAIL;
        }
 
+       if (netif_msg_rx_status(net_device_ctx))
+               dump_rndis_message(ndev, rndis_msg, data);
+
        switch (rndis_msg->ndis_msg_type) {
        case RNDIS_MSG_PACKET:
                return rndis_filter_receive_data(ndev, net_dev, nvchan,
-                                                rndis_msg, buflen);
+                                                rndis_msg, data, buflen);
        case RNDIS_MSG_INIT_C:
        case RNDIS_MSG_QUERY_C:
        case RNDIS_MSG_SET_C:
                /* completion msgs */
-               rndis_filter_receive_response(ndev, net_dev, rndis_msg);
+               rndis_filter_receive_response(ndev, net_dev, rndis_msg, data);
                break;
 
        case RNDIS_MSG_INDICATE:
                /* notification msgs */
-               netvsc_linkstatus_callback(ndev, rndis_msg);
+               netvsc_linkstatus_callback(ndev, rndis_msg, data);
                break;
        default:
                netdev_err(ndev,
@@ -567,6 +643,7 @@ static int rndis_filter_query_device(struct rndis_device *dev,
        u32 inresult_size = *result_size;
        struct rndis_query_request *query;
        struct rndis_query_complete *query_complete;
+       u32 msg_len;
        int ret = 0;
 
        if (!result)
@@ -634,8 +711,19 @@ static int rndis_filter_query_device(struct rndis_device *dev,
 
        /* Copy the response back */
        query_complete = &request->response_msg.msg.query_complete;
+       msg_len = request->response_msg.msg_len;
+
+       /* Ensure the packet is big enough to access its fields */
+       if (msg_len - RNDIS_HEADER_SIZE < sizeof(struct rndis_query_complete)) {
+               ret = -1;
+               goto cleanup;
+       }
 
-       if (query_complete->info_buflen > inresult_size) {
+       if (query_complete->info_buflen > inresult_size ||
+           query_complete->info_buf_offset < sizeof(*query_complete) ||
+           msg_len - RNDIS_HEADER_SIZE < query_complete->info_buf_offset ||
+           msg_len - RNDIS_HEADER_SIZE - query_complete->info_buf_offset
+                       < query_complete->info_buflen) {
                ret = -1;
                goto cleanup;
        }
index 9f0d2a9..b68f128 100644 (file)
@@ -1,9 +1,10 @@
 config QCOM_IPA
        tristate "Qualcomm IPA support"
-       depends on ARCH_QCOM && 64BIT && NET
-       depends on QCOM_Q6V5_MSS
+       depends on 64BIT && NET && QCOM_SMEM
+       depends on ARCH_QCOM || COMPILE_TEST
+       depends on QCOM_RPROC_COMMON || (QCOM_RPROC_COMMON=n && COMPILE_TEST)
+       select QCOM_MDT_LOADER if ARCH_QCOM
        select QCOM_QMI_HELPERS
-       select QCOM_MDT_LOADER
        help
          Choose Y or M here to include support for the Qualcomm
          IP Accelerator (IPA), a hardware block present in some
@@ -11,7 +12,8 @@ config QCOM_IPA
          that is capable of generic hardware handling of IP packets,
          including routing, filtering, and NAT.  Currently the IPA
          driver supports only basic transport of network traffic
-         between the AP and modem, on the Qualcomm SDM845 SoC.
+         between the AP and modem, on the Qualcomm SDM845 and SC7180
+         SoCs.
 
          Note that if selected, the selection type must match that
          of QCOM_Q6V5_COMMON (Y or M).
index c479524..f79cf3c 100644 (file)
@@ -89,9 +89,9 @@
 /* Delay period for interrupt moderation (in 32KHz IPA internal timer ticks) */
 #define GSI_EVT_RING_INT_MODT          (32 * 1) /* 1ms under 32KHz clock */
 
-#define GSI_CMD_TIMEOUT                        5       /* seconds */
+#define GSI_CMD_TIMEOUT                        50      /* milliseconds */
 
-#define GSI_CHANNEL_STOP_RX_RETRIES    10
+#define GSI_CHANNEL_STOP_RETRIES       10
 #define GSI_CHANNEL_MODEM_HALT_RETRIES 10
 
 #define GSI_MHI_EVENT_ID_START         10      /* 1st reserved event id */
@@ -220,7 +220,59 @@ static void gsi_irq_teardown(struct gsi *gsi)
        /* Nothing to do */
 }
 
-static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id)
+/* Event ring commands are performed one at a time.  Their completion
+ * is signaled by the event ring control GSI interrupt type, which is
+ * only enabled when we issue an event ring command.  Only the event
+ * ring being operated on has this interrupt enabled.
+ */
+static void gsi_irq_ev_ctrl_enable(struct gsi *gsi, u32 evt_ring_id)
+{
+       u32 val = BIT(evt_ring_id);
+
+       /* There's a small chance that a previous command completed
+        * after the interrupt was disabled, so make sure we have no
+        * pending interrupts before we enable them.
+        */
+       iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_CLR_OFFSET);
+
+       iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
+       gsi_irq_type_enable(gsi, GSI_EV_CTRL);
+}
+
+/* Disable event ring control interrupts */
+static void gsi_irq_ev_ctrl_disable(struct gsi *gsi)
+{
+       gsi_irq_type_disable(gsi, GSI_EV_CTRL);
+       iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
+}
+
+/* Channel commands are performed one at a time.  Their completion is
+ * signaled by the channel control GSI interrupt type, which is only
+ * enabled when we issue a channel command.  Only the channel being
+ * operated on has this interrupt enabled.
+ */
+static void gsi_irq_ch_ctrl_enable(struct gsi *gsi, u32 channel_id)
+{
+       u32 val = BIT(channel_id);
+
+       /* There's a small chance that a previous command completed
+        * after the interrupt was disabled, so make sure we have no
+        * pending interrupts before we enable them.
+        */
+       iowrite32(~0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_CLR_OFFSET);
+
+       iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
+       gsi_irq_type_enable(gsi, GSI_CH_CTRL);
+}
+
+/* Disable channel control interrupts */
+static void gsi_irq_ch_ctrl_disable(struct gsi *gsi)
+{
+       gsi_irq_type_disable(gsi, GSI_CH_CTRL);
+       iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
+}
+
+static void gsi_irq_ieob_enable_one(struct gsi *gsi, u32 evt_ring_id)
 {
        bool enable_ieob = !gsi->ieob_enabled_bitmap;
        u32 val;
@@ -234,11 +286,11 @@ static void gsi_irq_ieob_enable(struct gsi *gsi, u32 evt_ring_id)
                gsi_irq_type_enable(gsi, GSI_IEOB);
 }
 
-static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id)
+static void gsi_irq_ieob_disable(struct gsi *gsi, u32 event_mask)
 {
        u32 val;
 
-       gsi->ieob_enabled_bitmap &= ~BIT(evt_ring_id);
+       gsi->ieob_enabled_bitmap &= ~event_mask;
 
        /* Disable the interrupt type if this was the last enabled channel */
        if (!gsi->ieob_enabled_bitmap)
@@ -248,6 +300,11 @@ static void gsi_irq_ieob_disable(struct gsi *gsi, u32 evt_ring_id)
        iowrite32(val, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_MSK_OFFSET);
 }
 
+static void gsi_irq_ieob_disable_one(struct gsi *gsi, u32 evt_ring_id)
+{
+       gsi_irq_ieob_disable(gsi, BIT(evt_ring_id));
+}
+
 /* Enable all GSI_interrupt types */
 static void gsi_irq_enable(struct gsi *gsi)
 {
@@ -307,11 +364,13 @@ static u32 gsi_ring_index(struct gsi_ring *ring, u32 offset)
 static bool
 gsi_command(struct gsi *gsi, u32 reg, u32 val, struct completion *completion)
 {
+       unsigned long timeout = msecs_to_jiffies(GSI_CMD_TIMEOUT);
+
        reinit_completion(completion);
 
        iowrite32(val, gsi->virt + reg);
 
-       return !!wait_for_completion_timeout(completion, GSI_CMD_TIMEOUT * HZ);
+       return !!wait_for_completion_timeout(completion, timeout);
 }
 
 /* Return the hardware's notion of the current state of an event ring */
@@ -326,48 +385,36 @@ gsi_evt_ring_state(struct gsi *gsi, u32 evt_ring_id)
 }
 
 /* Issue an event ring command and wait for it to complete */
-static int evt_ring_command(struct gsi *gsi, u32 evt_ring_id,
-                           enum gsi_evt_cmd_opcode opcode)
+static void gsi_evt_ring_command(struct gsi *gsi, u32 evt_ring_id,
+                                enum gsi_evt_cmd_opcode opcode)
 {
        struct gsi_evt_ring *evt_ring = &gsi->evt_ring[evt_ring_id];
        struct completion *completion = &evt_ring->completion;
        struct device *dev = gsi->dev;
-       bool success;
+       bool timeout;
        u32 val;
 
-       /* We only perform one event ring command at a time, and event
-        * control interrupts should only occur when such a command
-        * is issued here.  Only permit *this* event ring to trigger
-        * an interrupt, and only enable the event control IRQ type
-        * when we expect it to occur.
-        */
-       val = BIT(evt_ring_id);
-       iowrite32(val, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
-       gsi_irq_type_enable(gsi, GSI_EV_CTRL);
+       /* Enable the completion interrupt for the command */
+       gsi_irq_ev_ctrl_enable(gsi, evt_ring_id);
 
        val = u32_encode_bits(evt_ring_id, EV_CHID_FMASK);
        val |= u32_encode_bits(opcode, EV_OPCODE_FMASK);
 
-       success = gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion);
+       timeout = !gsi_command(gsi, GSI_EV_CH_CMD_OFFSET, val, completion);
 
-       /* Disable the interrupt again */
-       gsi_irq_type_disable(gsi, GSI_EV_CTRL);
-       iowrite32(0, gsi->virt + GSI_CNTXT_SRC_EV_CH_IRQ_MSK_OFFSET);
+       gsi_irq_ev_ctrl_disable(gsi);
 
-       if (success)
-               return 0;
+       if (!timeout)
+               return;
 
        dev_err(dev, "GSI command %u for event ring %u timed out, state %u\n",
                opcode, evt_ring_id, evt_ring->state);
-
-       return -ETIMEDOUT;
 }
 
 /* Allocate an event ring in NOT_ALLOCATED state */
 static int gsi_evt_ring_alloc_command(struct gsi *gsi, u32 evt_ring_id)
 {
        struct gsi_evt_ring *evt_ring = &gsi->evt_ring[evt_ring_id];
-       int ret;
 
        /* Get initial event ring state */
        evt_ring->state = gsi_evt_ring_state(gsi, evt_ring_id);
@@ -377,14 +424,16 @@ static int gsi_evt_ring_alloc_command(struct gsi *gsi, u32 evt_ring_id)
                return -EINVAL;
        }
 
-       ret = evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE);
-       if (!ret && evt_ring->state != GSI_EVT_RING_STATE_ALLOCATED) {
-               dev_err(gsi->dev, "event ring %u bad state %u after alloc\n",
-                       evt_ring_id, evt_ring->state);
-               ret = -EIO;
-       }
+       gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_ALLOCATE);
 
-       return ret;
+       /* If successful the event ring state will have changed */
+       if (evt_ring->state == GSI_EVT_RING_STATE_ALLOCATED)
+               return 0;
+
+       dev_err(gsi->dev, "event ring %u bad state %u after alloc\n",
+               evt_ring_id, evt_ring->state);
+
+       return -EIO;
 }
 
 /* Reset a GSI event ring in ALLOCATED or ERROR state. */
@@ -392,7 +441,6 @@ static void gsi_evt_ring_reset_command(struct gsi *gsi, u32 evt_ring_id)
 {
        struct gsi_evt_ring *evt_ring = &gsi->evt_ring[evt_ring_id];
        enum gsi_evt_ring_state state = evt_ring->state;
-       int ret;
 
        if (state != GSI_EVT_RING_STATE_ALLOCATED &&
            state != GSI_EVT_RING_STATE_ERROR) {
@@ -401,17 +449,20 @@ static void gsi_evt_ring_reset_command(struct gsi *gsi, u32 evt_ring_id)
                return;
        }
 
-       ret = evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET);
-       if (!ret && evt_ring->state != GSI_EVT_RING_STATE_ALLOCATED)
-               dev_err(gsi->dev, "event ring %u bad state %u after reset\n",
-                       evt_ring_id, evt_ring->state);
+       gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_RESET);
+
+       /* If successful the event ring state will have changed */
+       if (evt_ring->state == GSI_EVT_RING_STATE_ALLOCATED)
+               return;
+
+       dev_err(gsi->dev, "event ring %u bad state %u after reset\n",
+               evt_ring_id, evt_ring->state);
 }
 
 /* Issue a hardware de-allocation request for an allocated event ring */
 static void gsi_evt_ring_de_alloc_command(struct gsi *gsi, u32 evt_ring_id)
 {
        struct gsi_evt_ring *evt_ring = &gsi->evt_ring[evt_ring_id];
-       int ret;
 
        if (evt_ring->state != GSI_EVT_RING_STATE_ALLOCATED) {
                dev_err(gsi->dev, "event ring %u state %u before dealloc\n",
@@ -419,10 +470,14 @@ static void gsi_evt_ring_de_alloc_command(struct gsi *gsi, u32 evt_ring_id)
                return;
        }
 
-       ret = evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC);
-       if (!ret && evt_ring->state != GSI_EVT_RING_STATE_NOT_ALLOCATED)
-               dev_err(gsi->dev, "event ring %u bad state %u after dealloc\n",
-                       evt_ring_id, evt_ring->state);
+       gsi_evt_ring_command(gsi, evt_ring_id, GSI_EVT_DE_ALLOC);
+
+       /* If successful the event ring state will have changed */
+       if (evt_ring->state == GSI_EVT_RING_STATE_NOT_ALLOCATED)
+               return;
+
+       dev_err(gsi->dev, "event ring %u bad state %u after dealloc\n",
+               evt_ring_id, evt_ring->state);
 }
 
 /* Fetch the current state of a channel from hardware */
@@ -438,41 +493,30 @@ static enum gsi_channel_state gsi_channel_state(struct gsi_channel *channel)
 }
 
 /* Issue a channel command and wait for it to complete */
-static int
+static void
 gsi_channel_command(struct gsi_channel *channel, enum gsi_ch_cmd_opcode opcode)
 {
        struct completion *completion = &channel->completion;
        u32 channel_id = gsi_channel_id(channel);
        struct gsi *gsi = channel->gsi;
        struct device *dev = gsi->dev;
-       bool success;
+       bool timeout;
        u32 val;
 
-       /* We only perform one channel command at a time, and channel
-        * control interrupts should only occur when such a command is
-        * issued here.  So we only permit *this* channel to trigger
-        * an interrupt and only enable the channel control IRQ type
-        * when we expect it to occur.
-        */
-       val = BIT(channel_id);
-       iowrite32(val, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
-       gsi_irq_type_enable(gsi, GSI_CH_CTRL);
+       /* Enable the completion interrupt for the command */
+       gsi_irq_ch_ctrl_enable(gsi, channel_id);
 
        val = u32_encode_bits(channel_id, CH_CHID_FMASK);
        val |= u32_encode_bits(opcode, CH_OPCODE_FMASK);
-       success = gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion);
+       timeout = !gsi_command(gsi, GSI_CH_CMD_OFFSET, val, completion);
 
-       /* Disable the interrupt again */
-       gsi_irq_type_disable(gsi, GSI_CH_CTRL);
-       iowrite32(0, gsi->virt + GSI_CNTXT_SRC_CH_IRQ_MSK_OFFSET);
+       gsi_irq_ch_ctrl_disable(gsi);
 
-       if (success)
-               return 0;
+       if (!timeout)
+               return;
 
        dev_err(dev, "GSI command %u for channel %u timed out, state %u\n",
                opcode, channel_id, gsi_channel_state(channel));
-
-       return -ETIMEDOUT;
 }
 
 /* Allocate GSI channel in NOT_ALLOCATED state */
@@ -481,7 +525,6 @@ static int gsi_channel_alloc_command(struct gsi *gsi, u32 channel_id)
        struct gsi_channel *channel = &gsi->channel[channel_id];
        struct device *dev = gsi->dev;
        enum gsi_channel_state state;
-       int ret;
 
        /* Get initial channel state */
        state = gsi_channel_state(channel);
@@ -491,17 +534,17 @@ static int gsi_channel_alloc_command(struct gsi *gsi, u32 channel_id)
                return -EINVAL;
        }
 
-       ret = gsi_channel_command(channel, GSI_CH_ALLOCATE);
+       gsi_channel_command(channel, GSI_CH_ALLOCATE);
 
-       /* Channel state will normally have been updated */
+       /* If successful the channel state will have changed */
        state = gsi_channel_state(channel);
-       if (!ret && state != GSI_CHANNEL_STATE_ALLOCATED) {
-               dev_err(dev, "channel %u bad state %u after alloc\n",
-                       channel_id, state);
-               ret = -EIO;
-       }
+       if (state == GSI_CHANNEL_STATE_ALLOCATED)
+               return 0;
 
-       return ret;
+       dev_err(dev, "channel %u bad state %u after alloc\n",
+               channel_id, state);
+
+       return -EIO;
 }
 
 /* Start an ALLOCATED channel */
@@ -509,7 +552,6 @@ static int gsi_channel_start_command(struct gsi_channel *channel)
 {
        struct device *dev = channel->gsi->dev;
        enum gsi_channel_state state;
-       int ret;
 
        state = gsi_channel_state(channel);
        if (state != GSI_CHANNEL_STATE_ALLOCATED &&
@@ -519,17 +561,17 @@ static int gsi_channel_start_command(struct gsi_channel *channel)
                return -EINVAL;
        }
 
-       ret = gsi_channel_command(channel, GSI_CH_START);
+       gsi_channel_command(channel, GSI_CH_START);
 
-       /* Channel state will normally have been updated */
+       /* If successful the channel state will have changed */
        state = gsi_channel_state(channel);
-       if (!ret && state != GSI_CHANNEL_STATE_STARTED) {
-               dev_err(dev, "channel %u bad state %u after start\n",
-                       gsi_channel_id(channel), state);
-               ret = -EIO;
-       }
+       if (state == GSI_CHANNEL_STATE_STARTED)
+               return 0;
 
-       return ret;
+       dev_err(dev, "channel %u bad state %u after start\n",
+               gsi_channel_id(channel), state);
+
+       return -EIO;
 }
 
 /* Stop a GSI channel in STARTED state */
@@ -537,7 +579,6 @@ static int gsi_channel_stop_command(struct gsi_channel *channel)
 {
        struct device *dev = channel->gsi->dev;
        enum gsi_channel_state state;
-       int ret;
 
        state = gsi_channel_state(channel);
 
@@ -554,12 +595,12 @@ static int gsi_channel_stop_command(struct gsi_channel *channel)
                return -EINVAL;
        }
 
-       ret = gsi_channel_command(channel, GSI_CH_STOP);
+       gsi_channel_command(channel, GSI_CH_STOP);
 
-       /* Channel state will normally have been updated */
+       /* If successful the channel state will have changed */
        state = gsi_channel_state(channel);
-       if (ret || state == GSI_CHANNEL_STATE_STOPPED)
-               return ret;
+       if (state == GSI_CHANNEL_STATE_STOPPED)
+               return 0;
 
        /* We may have to try again if stop is in progress */
        if (state == GSI_CHANNEL_STATE_STOP_IN_PROC)
@@ -576,9 +617,9 @@ static void gsi_channel_reset_command(struct gsi_channel *channel)
 {
        struct device *dev = channel->gsi->dev;
        enum gsi_channel_state state;
-       int ret;
 
-       msleep(1);      /* A short delay is required before a RESET command */
+       /* A short delay is required before a RESET command */
+       usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
 
        state = gsi_channel_state(channel);
        if (state != GSI_CHANNEL_STATE_STOPPED &&
@@ -590,11 +631,11 @@ static void gsi_channel_reset_command(struct gsi_channel *channel)
                return;
        }
 
-       ret = gsi_channel_command(channel, GSI_CH_RESET);
+       gsi_channel_command(channel, GSI_CH_RESET);
 
-       /* Channel state will normally have been updated */
+       /* If successful the channel state will have changed */
        state = gsi_channel_state(channel);
-       if (!ret && state != GSI_CHANNEL_STATE_ALLOCATED)
+       if (state != GSI_CHANNEL_STATE_ALLOCATED)
                dev_err(dev, "channel %u bad state %u after reset\n",
                        gsi_channel_id(channel), state);
 }
@@ -605,7 +646,6 @@ static void gsi_channel_de_alloc_command(struct gsi *gsi, u32 channel_id)
        struct gsi_channel *channel = &gsi->channel[channel_id];
        struct device *dev = gsi->dev;
        enum gsi_channel_state state;
-       int ret;
 
        state = gsi_channel_state(channel);
        if (state != GSI_CHANNEL_STATE_ALLOCATED) {
@@ -614,11 +654,12 @@ static void gsi_channel_de_alloc_command(struct gsi *gsi, u32 channel_id)
                return;
        }
 
-       ret = gsi_channel_command(channel, GSI_CH_DE_ALLOC);
+       gsi_channel_command(channel, GSI_CH_DE_ALLOC);
 
-       /* Channel state will normally have been updated */
+       /* If successful the channel state will have changed */
        state = gsi_channel_state(channel);
-       if (!ret && state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
+
+       if (state != GSI_CHANNEL_STATE_NOT_ALLOCATED)
                dev_err(dev, "channel %u bad state %u after dealloc\n",
                        channel_id, state);
 }
@@ -730,13 +771,13 @@ static void gsi_channel_freeze(struct gsi_channel *channel)
 
        napi_disable(&channel->napi);
 
-       gsi_irq_ieob_disable(channel->gsi, channel->evt_ring_id);
+       gsi_irq_ieob_disable_one(channel->gsi, channel->evt_ring_id);
 }
 
 /* Allow transactions to be used on the channel again. */
 static void gsi_channel_thaw(struct gsi_channel *channel)
 {
-       gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id);
+       gsi_irq_ieob_enable_one(channel->gsi, channel->evt_ring_id);
 
        napi_enable(&channel->napi);
 }
@@ -853,21 +894,18 @@ int gsi_channel_start(struct gsi *gsi, u32 channel_id)
 int gsi_channel_stop(struct gsi *gsi, u32 channel_id)
 {
        struct gsi_channel *channel = &gsi->channel[channel_id];
-       u32 retries;
+       u32 retries = GSI_CHANNEL_STOP_RETRIES;
        int ret;
 
        gsi_channel_freeze(channel);
 
-       /* RX channels might require a little time to enter STOPPED state */
-       retries = channel->toward_ipa ? 0 : GSI_CHANNEL_STOP_RX_RETRIES;
-
        mutex_lock(&gsi->mutex);
 
        do {
                ret = gsi_channel_stop_command(channel);
                if (ret != -EAGAIN)
                        break;
-               msleep(1);
+               usleep_range(3 * USEC_PER_MSEC, 5 * USEC_PER_MSEC);
        } while (retries--);
 
        mutex_unlock(&gsi->mutex);
@@ -1167,6 +1205,7 @@ static void gsi_isr_ieob(struct gsi *gsi)
        u32 event_mask;
 
        event_mask = ioread32(gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_OFFSET);
+       gsi_irq_ieob_disable(gsi, event_mask);
        iowrite32(event_mask, gsi->virt + GSI_CNTXT_SRC_IEOB_IRQ_CLR_OFFSET);
 
        while (event_mask) {
@@ -1174,7 +1213,6 @@ static void gsi_isr_ieob(struct gsi *gsi)
 
                event_mask ^= BIT(evt_ring_id);
 
-               gsi_irq_ieob_disable(gsi, evt_ring_id);
                napi_schedule(&gsi->evt_ring[evt_ring_id].channel->napi);
        }
 }
@@ -1419,7 +1457,7 @@ void gsi_channel_doorbell(struct gsi_channel *channel)
 }
 
 /* Consult hardware, move any newly completed transactions to completed list */
-static void gsi_channel_update(struct gsi_channel *channel)
+static struct gsi_trans *gsi_channel_update(struct gsi_channel *channel)
 {
        u32 evt_ring_id = channel->evt_ring_id;
        struct gsi *gsi = channel->gsi;
@@ -1438,7 +1476,7 @@ static void gsi_channel_update(struct gsi_channel *channel)
        offset = GSI_EV_CH_E_CNTXT_4_OFFSET(evt_ring_id);
        index = gsi_ring_index(ring, ioread32(gsi->virt + offset));
        if (index == ring->index % ring->count)
-               return;
+               return NULL;
 
        /* Get the transaction for the latest completed event.  Take a
         * reference to keep it from completing before we give the events
@@ -1463,6 +1501,8 @@ static void gsi_channel_update(struct gsi_channel *channel)
        gsi_evt_ring_doorbell(channel->gsi, channel->evt_ring_id, index);
 
        gsi_trans_free(trans);
+
+       return gsi_channel_trans_complete(channel);
 }
 
 /**
@@ -1483,11 +1523,8 @@ static struct gsi_trans *gsi_channel_poll_one(struct gsi_channel *channel)
 
        /* Get the first transaction from the completed list */
        trans = gsi_channel_trans_complete(channel);
-       if (!trans) {
-               /* List is empty; see if there's more to do */
-               gsi_channel_update(channel);
-               trans = gsi_channel_trans_complete(channel);
-       }
+       if (!trans)     /* List is empty; see if there's more to do */
+               trans = gsi_channel_update(channel);
 
        if (trans)
                gsi_trans_move_polled(trans);
@@ -1510,23 +1547,20 @@ static struct gsi_trans *gsi_channel_poll_one(struct gsi_channel *channel)
 static int gsi_channel_poll(struct napi_struct *napi, int budget)
 {
        struct gsi_channel *channel;
-       int count = 0;
+       int count;
 
        channel = container_of(napi, struct gsi_channel, napi);
-       while (count < budget) {
+       for (count = 0; count < budget; count++) {
                struct gsi_trans *trans;
 
-               count++;
                trans = gsi_channel_poll_one(channel);
                if (!trans)
                        break;
                gsi_trans_complete(trans);
        }
 
-       if (count < budget) {
-               napi_complete(&channel->napi);
-               gsi_irq_ieob_enable(channel->gsi, channel->evt_ring_id);
-       }
+       if (count < budget && napi_complete(napi))
+               gsi_irq_ieob_enable_one(channel->gsi, channel->evt_ring_id);
 
        return count;
 }
@@ -1616,7 +1650,7 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
                               enum gsi_generic_cmd_opcode opcode)
 {
        struct completion *completion = &gsi->completion;
-       bool success;
+       bool timeout;
        u32 val;
 
        /* The error global interrupt type is always enabled (until we
@@ -1639,12 +1673,12 @@ static int gsi_generic_command(struct gsi *gsi, u32 channel_id,
        val |= u32_encode_bits(channel_id, GENERIC_CHID_FMASK);
        val |= u32_encode_bits(GSI_EE_MODEM, GENERIC_EE_FMASK);
 
-       success = gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion);
+       timeout = !gsi_command(gsi, GSI_GENERIC_CMD_OFFSET, val, completion);
 
        /* Disable the GP_INT1 IRQ type again */
        iowrite32(BIT(ERROR_INT), gsi->virt + GSI_CNTXT_GLOB_IRQ_EN_OFFSET);
 
-       if (success)
+       if (!timeout)
                return gsi->result;
 
        dev_err(gsi->dev, "GSI generic command %u to channel %u timed out\n",
index 4d4606b..3a4ab8a 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "ipa_cmd.h"
 
+struct page;
 struct scatterlist;
 struct device;
 struct sk_buff;
index 6c23710..8020776 100644 (file)
@@ -43,7 +43,7 @@ enum ipa_flag {
  * @flags:             Boolean state flags
  * @version:           IPA hardware version
  * @pdev:              Platform device
- * @modem_rproc:       Remoteproc handle for modem subsystem
+ * @completion:                Used to signal pipeline clear transfer complete
  * @smp2p:             SMP2P information
  * @clock:             IPA clocking information
  * @table_addr:                DMA address of filter/route table content
@@ -83,7 +83,7 @@ struct ipa {
        DECLARE_BITMAP(flags, IPA_FLAG_COUNT);
        enum ipa_version version;
        struct platform_device *pdev;
-       struct rproc *modem_rproc;
+       struct completion completion;
        struct notifier_block nb;
        void *notifier;
        struct ipa_smp2p *smp2p;
index 9dcf16f..354675a 100644 (file)
  * An IPA clock reference must be held for any access to IPA hardware.
  */
 
+/**
+ * struct ipa_interconnect - IPA interconnect information
+ * @path:              Interconnect path
+ * @average_bandwidth: Average interconnect bandwidth (KB/second)
+ * @peak_bandwidth:    Peak interconnect bandwidth (KB/second)
+ */
+struct ipa_interconnect {
+       struct icc_path *path;
+       u32 average_bandwidth;
+       u32 peak_bandwidth;
+};
+
 /**
  * struct ipa_clock - IPA clocking information
  * @count:             Clocking reference count
  * @mutex:             Protects clock enable/disable
  * @core:              IPA core clock
- * @memory_path:       Memory interconnect
- * @imem_path:         Internal memory interconnect
- * @config_path:       Configuration space interconnect
- * @interconnect_data: Interconnect configuration data
+ * @interconnect_count:        Number of elements in interconnect[]
+ * @interconnect:      Interconnect array
  */
 struct ipa_clock {
        refcount_t count;
        struct mutex mutex; /* protects clock enable/disable */
        struct clk *core;
-       struct icc_path *memory_path;
-       struct icc_path *imem_path;
-       struct icc_path *config_path;
-       const struct ipa_interconnect_data *interconnect_data;
+       u32 interconnect_count;
+       struct ipa_interconnect *interconnect;
 };
 
-static struct icc_path *
-ipa_interconnect_init_one(struct device *dev, const char *name)
+static int ipa_interconnect_init_one(struct device *dev,
+                                    struct ipa_interconnect *interconnect,
+                                    const struct ipa_interconnect_data *data)
 {
        struct icc_path *path;
 
-       path = of_icc_get(dev, name);
-       if (IS_ERR(path))
-               dev_err(dev, "error %ld getting %s interconnect\n",
-                       PTR_ERR(path), name);
+       path = of_icc_get(dev, data->name);
+       if (IS_ERR(path)) {
+               int ret = PTR_ERR(path);
+
+               dev_err(dev, "error %d getting %s interconnect\n", ret,
+                       data->name);
 
-       return path;
+               return ret;
+       }
+
+       interconnect->path = path;
+       interconnect->average_bandwidth = data->average_bandwidth;
+       interconnect->peak_bandwidth = data->peak_bandwidth;
+
+       return 0;
 }
 
-/* Initialize interconnects required for IPA operation */
-static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev)
+static void ipa_interconnect_exit_one(struct ipa_interconnect *interconnect)
 {
-       struct icc_path *path;
-
-       path = ipa_interconnect_init_one(dev, "memory");
-       if (IS_ERR(path))
-               goto err_return;
-       clock->memory_path = path;
+       icc_put(interconnect->path);
+       memset(interconnect, 0, sizeof(*interconnect));
+}
 
-       path = ipa_interconnect_init_one(dev, "imem");
-       if (IS_ERR(path))
-               goto err_memory_path_put;
-       clock->imem_path = path;
+/* Initialize interconnects required for IPA operation */
+static int ipa_interconnect_init(struct ipa_clock *clock, struct device *dev,
+                                const struct ipa_interconnect_data *data)
+{
+       struct ipa_interconnect *interconnect;
+       u32 count;
+       int ret;
 
-       path = ipa_interconnect_init_one(dev, "config");
-       if (IS_ERR(path))
-               goto err_imem_path_put;
-       clock->config_path = path;
+       count = clock->interconnect_count;
+       interconnect = kcalloc(count, sizeof(*interconnect), GFP_KERNEL);
+       if (!interconnect)
+               return -ENOMEM;
+       clock->interconnect = interconnect;
+
+       while (count--) {
+               ret = ipa_interconnect_init_one(dev, interconnect, data++);
+               if (ret)
+                       goto out_unwind;
+               interconnect++;
+       }
 
        return 0;
 
-err_imem_path_put:
-       icc_put(clock->imem_path);
-err_memory_path_put:
-       icc_put(clock->memory_path);
-err_return:
-       return PTR_ERR(path);
+out_unwind:
+       while (interconnect-- > clock->interconnect)
+               ipa_interconnect_exit_one(interconnect);
+       kfree(clock->interconnect);
+       clock->interconnect = NULL;
+
+       return ret;
 }
 
 /* Inverse of ipa_interconnect_init() */
 static void ipa_interconnect_exit(struct ipa_clock *clock)
 {
-       icc_put(clock->config_path);
-       icc_put(clock->imem_path);
-       icc_put(clock->memory_path);
+       struct ipa_interconnect *interconnect;
+
+       interconnect = clock->interconnect + clock->interconnect_count;
+       while (interconnect-- > clock->interconnect)
+               ipa_interconnect_exit_one(interconnect);
+       kfree(clock->interconnect);
+       clock->interconnect = NULL;
 }
 
 /* Currently we only use one bandwidth level, so just "enable" interconnects */
 static int ipa_interconnect_enable(struct ipa *ipa)
 {
-       const struct ipa_interconnect_data *data;
+       struct ipa_interconnect *interconnect;
        struct ipa_clock *clock = ipa->clock;
        int ret;
-
-       data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY];
-       ret = icc_set_bw(clock->memory_path, data->average_rate,
-                        data->peak_rate);
-       if (ret)
-               return ret;
-
-       data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM];
-       ret = icc_set_bw(clock->memory_path, data->average_rate,
-                        data->peak_rate);
-       if (ret)
-               goto err_memory_path_disable;
-
-       data = &clock->interconnect_data[IPA_INTERCONNECT_CONFIG];
-       ret = icc_set_bw(clock->memory_path, data->average_rate,
-                        data->peak_rate);
-       if (ret)
-               goto err_imem_path_disable;
+       u32 i;
+
+       interconnect = clock->interconnect;
+       for (i = 0; i < clock->interconnect_count; i++) {
+               ret = icc_set_bw(interconnect->path,
+                                interconnect->average_bandwidth,
+                                interconnect->peak_bandwidth);
+               if (ret)
+                       goto out_unwind;
+               interconnect++;
+       }
 
        return 0;
 
-err_imem_path_disable:
-       (void)icc_set_bw(clock->imem_path, 0, 0);
-err_memory_path_disable:
-       (void)icc_set_bw(clock->memory_path, 0, 0);
+out_unwind:
+       while (interconnect-- > clock->interconnect)
+               (void)icc_set_bw(interconnect->path, 0, 0);
 
        return ret;
 }
 
 /* To disable an interconnect, we just its bandwidth to 0 */
-static int ipa_interconnect_disable(struct ipa *ipa)
+static void ipa_interconnect_disable(struct ipa *ipa)
 {
-       const struct ipa_interconnect_data *data;
+       struct ipa_interconnect *interconnect;
        struct ipa_clock *clock = ipa->clock;
+       int result = 0;
+       u32 count;
        int ret;
 
-       ret = icc_set_bw(clock->memory_path, 0, 0);
-       if (ret)
-               return ret;
-
-       ret = icc_set_bw(clock->imem_path, 0, 0);
-       if (ret)
-               goto err_memory_path_reenable;
-
-       ret = icc_set_bw(clock->config_path, 0, 0);
-       if (ret)
-               goto err_imem_path_reenable;
-
-       return 0;
-
-err_imem_path_reenable:
-       data = &clock->interconnect_data[IPA_INTERCONNECT_IMEM];
-       (void)icc_set_bw(clock->imem_path, data->average_rate,
-                        data->peak_rate);
-err_memory_path_reenable:
-       data = &clock->interconnect_data[IPA_INTERCONNECT_MEMORY];
-       (void)icc_set_bw(clock->memory_path, data->average_rate,
-                        data->peak_rate);
+       count = clock->interconnect_count;
+       interconnect = clock->interconnect + count;
+       while (count--) {
+               interconnect--;
+               ret = icc_set_bw(interconnect->path, 0, 0);
+               if (ret && !result)
+                       result = ret;
+       }
 
-       return ret;
+       if (result)
+               dev_err(&ipa->pdev->dev,
+                       "error %d disabling IPA interconnects\n", ret);
 }
 
 /* Turn on IPA clocks, including interconnects */
@@ -189,7 +201,7 @@ static int ipa_clock_enable(struct ipa *ipa)
 static void ipa_clock_disable(struct ipa *ipa)
 {
        clk_disable_unprepare(ipa->clock->core);
-       (void)ipa_interconnect_disable(ipa);
+       ipa_interconnect_disable(ipa);
 }
 
 /* Get an IPA clock reference, but only if the reference count is
@@ -286,9 +298,9 @@ ipa_clock_init(struct device *dev, const struct ipa_clock_data *data)
                goto err_clk_put;
        }
        clock->core = clk;
-       clock->interconnect_data = data->interconnect;
+       clock->interconnect_count = data->interconnect_count;
 
-       ret = ipa_interconnect_init(clock, dev);
+       ret = ipa_interconnect_init(clock, dev, data->interconnect_data);
        if (ret)
                goto err_kfree;
 
index 002e514..97b50fe 100644 (file)
@@ -529,7 +529,7 @@ void ipa_cmd_dma_shared_mem_add(struct gsi_trans *trans, u32 offset, u16 size,
                          direction, opcode);
 }
 
-static void ipa_cmd_ip_tag_status_add(struct gsi_trans *trans, u64 tag)
+static void ipa_cmd_ip_tag_status_add(struct gsi_trans *trans)
 {
        struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi);
        enum ipa_cmd_opcode opcode = IPA_CMD_IP_PACKET_TAG_STATUS;
@@ -543,14 +543,14 @@ static void ipa_cmd_ip_tag_status_add(struct gsi_trans *trans, u64 tag)
        cmd_payload = ipa_cmd_payload_alloc(ipa, &payload_addr);
        payload = &cmd_payload->ip_packet_tag_status;
 
-       payload->tag = u64_encode_bits(tag, IP_PACKET_TAG_STATUS_TAG_FMASK);
+       payload->tag = le64_encode_bits(0, IP_PACKET_TAG_STATUS_TAG_FMASK);
 
        gsi_trans_cmd_add(trans, payload, sizeof(*payload), payload_addr,
                          direction, opcode);
 }
 
 /* Issue a small command TX data transfer */
-static void ipa_cmd_transfer_add(struct gsi_trans *trans, u16 size)
+static void ipa_cmd_transfer_add(struct gsi_trans *trans)
 {
        struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi);
        enum dma_data_direction direction = DMA_TO_DEVICE;
@@ -558,8 +558,6 @@ static void ipa_cmd_transfer_add(struct gsi_trans *trans, u16 size)
        union ipa_cmd_payload *payload;
        dma_addr_t payload_addr;
 
-       /* assert(size <= sizeof(*payload)); */
-
        /* Just transfer a zero-filled payload structure */
        payload = ipa_cmd_payload_alloc(ipa, &payload_addr);
 
@@ -567,34 +565,53 @@ static void ipa_cmd_transfer_add(struct gsi_trans *trans, u16 size)
                          direction, opcode);
 }
 
-void ipa_cmd_tag_process_add(struct gsi_trans *trans)
+/* Add immediate commands to a transaction to clear the hardware pipeline */
+void ipa_cmd_pipeline_clear_add(struct gsi_trans *trans)
 {
        struct ipa *ipa = container_of(trans->gsi, struct ipa, gsi);
        struct ipa_endpoint *endpoint;
 
-       endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];
+       /* This will complete when the transfer is received */
+       reinit_completion(&ipa->completion);
 
+       /* Issue a no-op register write command (mask 0 means no write) */
        ipa_cmd_register_write_add(trans, 0, 0, 0, true);
+
+       /* Send a data packet through the IPA pipeline.  The packet_init
+        * command says to send the next packet directly to the exception
+        * endpoint without any other IPA processing.  The tag_status
+        * command requests that status be generated on completion of
+        * that transfer, and that it will be tagged with a value.
+        * Finally, the transfer command sends a small packet of data
+        * (instead of a command) using the command endpoint.
+        */
+       endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX];
        ipa_cmd_ip_packet_init_add(trans, endpoint->endpoint_id);
-       ipa_cmd_ip_tag_status_add(trans, 0xcba987654321);
-       ipa_cmd_transfer_add(trans, 4);
+       ipa_cmd_ip_tag_status_add(trans);
+       ipa_cmd_transfer_add(trans);
 }
 
-/* Returns the number of commands required for the tag process */
-u32 ipa_cmd_tag_process_count(void)
+/* Returns the number of commands required to clear the pipeline */
+u32 ipa_cmd_pipeline_clear_count(void)
 {
        return 4;
 }
 
-void ipa_cmd_tag_process(struct ipa *ipa)
+void ipa_cmd_pipeline_clear_wait(struct ipa *ipa)
+{
+       wait_for_completion(&ipa->completion);
+}
+
+void ipa_cmd_pipeline_clear(struct ipa *ipa)
 {
-       u32 count = ipa_cmd_tag_process_count();
+       u32 count = ipa_cmd_pipeline_clear_count();
        struct gsi_trans *trans;
 
        trans = ipa_cmd_trans_alloc(ipa, count);
        if (trans) {
-               ipa_cmd_tag_process_add(trans);
+               ipa_cmd_pipeline_clear_add(trans);
                gsi_trans_commit_wait(trans);
+               ipa_cmd_pipeline_clear_wait(ipa);
        } else {
                dev_err(&ipa->pdev->dev,
                        "error allocating %u entry tag transaction\n", count);
index 4ed09c4..6dd3d35 100644 (file)
@@ -157,26 +157,30 @@ void ipa_cmd_dma_shared_mem_add(struct gsi_trans *trans, u32 offset,
                                u16 size, dma_addr_t addr, bool toward_ipa);
 
 /**
- * ipa_cmd_tag_process_add() - Add IPA tag process commands to a transaction
+ * ipa_cmd_pipeline_clear_add() - Add pipeline clear commands to a transaction
  * @trans:     GSI transaction
  */
-void ipa_cmd_tag_process_add(struct gsi_trans *trans);
+void ipa_cmd_pipeline_clear_add(struct gsi_trans *trans);
 
 /**
- * ipa_cmd_tag_process_add_count() - Number of commands in a tag process
+ * ipa_cmd_pipeline_clear_count() - # commands required to clear pipeline
  *
  * Return:     The number of elements to allocate in a transaction
- *             to hold tag process commands
+ *             to hold commands to clear the pipeline
  */
-u32 ipa_cmd_tag_process_count(void);
+u32 ipa_cmd_pipeline_clear_count(void);
 
 /**
- * ipa_cmd_tag_process() - Perform a tag process
- *
- * @Return:    The number of elements to allocate in a transaction
- *             to hold tag process commands
+ * ipa_cmd_pipeline_clear_wait() - Wait pipeline clear to complete
+ * @ipa:       - IPA pointer
+ */
+void ipa_cmd_pipeline_clear_wait(struct ipa *ipa);
+
+/**
+ * ipa_cmd_pipeline_clear() - Clear the hardware pipeline
+ * @ipa:       - IPA pointer
  */
-void ipa_cmd_tag_process(struct ipa *ipa);
+void ipa_cmd_pipeline_clear(struct ipa *ipa);
 
 /**
  * ipa_cmd_trans_alloc() - Allocate a transaction for the command TX endpoint
index 5cc0ed7..997b51c 100644 (file)
@@ -309,24 +309,30 @@ static struct ipa_mem_data ipa_mem_data = {
        .smem_size      = 0x00002000,
 };
 
+/* Interconnect bandwidths are in 1000 byte/second units */
+static struct ipa_interconnect_data ipa_interconnect_data[] = {
+       {
+               .name                   = "memory",
+               .peak_bandwidth         = 465000,       /* 465 MBps */
+               .average_bandwidth      = 80000,        /* 80 MBps */
+       },
+       /* Average bandwidth is unused for the next two interconnects */
+       {
+               .name                   = "imem",
+               .peak_bandwidth         = 68570,        /* 68.570 MBps */
+               .average_bandwidth      = 0,            /* unused */
+       },
+       {
+               .name                   = "config",
+               .peak_bandwidth         = 30000,        /* 30 MBps */
+               .average_bandwidth      = 0,            /* unused */
+       },
+};
+
 static struct ipa_clock_data ipa_clock_data = {
        .core_clock_rate        = 100 * 1000 * 1000,    /* Hz */
-       /* Interconnect rates are in 1000 byte/second units */
-       .interconnect = {
-               [IPA_INTERCONNECT_MEMORY] = {
-                       .peak_rate      = 465000,       /* 465 MBps */
-                       .average_rate   = 80000,        /* 80 MBps */
-               },
-               /* Average rate is unused for the next two interconnects */
-               [IPA_INTERCONNECT_IMEM] = {
-                       .peak_rate      = 68570,        /* 68.570 MBps */
-                       .average_rate   = 0,            /* unused */
-               },
-               [IPA_INTERCONNECT_CONFIG] = {
-                       .peak_rate      = 30000,        /* 30 MBps */
-                       .average_rate   = 0,            /* unused */
-               },
-       },
+       .interconnect_count     = ARRAY_SIZE(ipa_interconnect_data),
+       .interconnect_data      = ipa_interconnect_data,
 };
 
 /* Configuration data for the SC7180 SoC. */
index f8fee8d..88c9c35 100644 (file)
@@ -329,24 +329,30 @@ static struct ipa_mem_data ipa_mem_data = {
        .smem_size      = 0x00002000,
 };
 
+/* Interconnect bandwidths are in 1000 byte/second units */
+static struct ipa_interconnect_data ipa_interconnect_data[] = {
+       {
+               .name                   = "memory",
+               .peak_bandwidth         = 600000,       /* 600 MBps */
+               .average_bandwidth      = 80000,        /* 80 MBps */
+       },
+       /* Average bandwidth is unused for the next two interconnects */
+       {
+               .name                   = "imem",
+               .peak_bandwidth         = 350000,       /* 350 MBps */
+               .average_bandwidth      = 0,            /* unused */
+       },
+       {
+               .name                   = "config",
+               .peak_bandwidth         = 40000,        /* 40 MBps */
+               .average_bandwidth      = 0,            /* unused */
+       },
+};
+
 static struct ipa_clock_data ipa_clock_data = {
        .core_clock_rate        = 75 * 1000 * 1000,     /* Hz */
-       /* Interconnect rates are in 1000 byte/second units */
-       .interconnect = {
-               [IPA_INTERCONNECT_MEMORY] = {
-                       .peak_rate      = 600000,       /* 600 MBps */
-                       .average_rate   = 80000,        /* 80 MBps */
-               },
-               /* Average rate is unused for the next two interconnects */
-               [IPA_INTERCONNECT_IMEM] = {
-                       .peak_rate      = 350000,       /* 350 MBps */
-                       .average_rate   = 0,            /* unused */
-               },
-               [IPA_INTERCONNECT_CONFIG] = {
-                       .peak_rate      = 40000,        /* 40 MBps */
-                       .average_rate   = 0,            /* unused */
-               },
-       },
+       .interconnect_count     = ARRAY_SIZE(ipa_interconnect_data),
+       .interconnect_data      = ipa_interconnect_data,
 };
 
 /* Configuration data for the SDM845 SoC. */
index 0ed5ffe..b476fc3 100644 (file)
@@ -258,32 +258,28 @@ struct ipa_mem_data {
        u32 smem_size;
 };
 
-/** enum ipa_interconnect_id - IPA interconnect identifier */
-enum ipa_interconnect_id {
-       IPA_INTERCONNECT_MEMORY,
-       IPA_INTERCONNECT_IMEM,
-       IPA_INTERCONNECT_CONFIG,
-       IPA_INTERCONNECT_COUNT,         /* Last; not an interconnect */
-};
-
 /**
- * struct ipa_interconnect_data - description of IPA interconnect rates
- * @peak_rate:         Peak interconnect bandwidth (in 1000 byte/sec units)
- * @average_rate:      Average interconnect bandwidth (in 1000 byte/sec units)
+ * struct ipa_interconnect_data - description of IPA interconnect bandwidths
+ * @name:              Interconnect name (matches interconnect-name in DT)
+ * @peak_bandwidth:    Peak interconnect bandwidth (in 1000 byte/sec units)
+ * @average_bandwidth: Average interconnect bandwidth (in 1000 byte/sec units)
  */
 struct ipa_interconnect_data {
-       u32 peak_rate;
-       u32 average_rate;
+       const char *name;
+       u32 peak_bandwidth;
+       u32 average_bandwidth;
 };
 
 /**
  * struct ipa_clock_data - description of IPA clock and interconnect rates
  * @core_clock_rate:   Core clock rate (Hz)
- * @interconnect:      Array of interconnect bandwidth parameters
+ * @interconnect_count:        Number of entries in the interconnect_data array
+ * @interconnect_data: IPA interconnect configuration data
  */
 struct ipa_clock_data {
        u32 core_clock_rate;
-       struct ipa_interconnect_data interconnect[IPA_INTERCONNECT_COUNT];
+       u32 interconnect_count;         /* # entries in interconnect_data[] */
+       const struct ipa_interconnect_data *interconnect_data;
 };
 
 /**
index 9f4be98..8313220 100644 (file)
@@ -69,8 +69,11 @@ struct ipa_status {
 };
 
 /* Field masks for struct ipa_status structure fields */
+#define IPA_STATUS_MASK_TAG_VALID_FMASK                GENMASK(4, 4)
+#define IPA_STATUS_SRC_IDX_FMASK               GENMASK(4, 0)
 #define IPA_STATUS_DST_IDX_FMASK               GENMASK(4, 0)
 #define IPA_STATUS_FLAGS1_RT_RULE_ID_FMASK     GENMASK(31, 22)
+#define IPA_STATUS_FLAGS2_TAG_FMASK            GENMASK_ULL(63, 16)
 
 #ifdef IPA_VALIDATE
 
@@ -399,7 +402,7 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa)
         * That won't happen, and we could be more precise, but this is fine
         * for now.  We need to end the transaction with a "tag process."
         */
-       count = hweight32(initialized) + ipa_cmd_tag_process_count();
+       count = hweight32(initialized) + ipa_cmd_pipeline_clear_count();
        trans = ipa_cmd_trans_alloc(ipa, count);
        if (!trans) {
                dev_err(&ipa->pdev->dev,
@@ -428,11 +431,13 @@ int ipa_endpoint_modem_exception_reset_all(struct ipa *ipa)
                ipa_cmd_register_write_add(trans, offset, 0, ~0, false);
        }
 
-       ipa_cmd_tag_process_add(trans);
+       ipa_cmd_pipeline_clear_add(trans);
 
        /* XXX This should have a 1 second timeout */
        gsi_trans_commit_wait(trans);
 
+       ipa_cmd_pipeline_clear_wait(ipa);
+
        return 0;
 }
 
@@ -1172,11 +1177,45 @@ static bool ipa_endpoint_status_skip(struct ipa_endpoint *endpoint,
        return false;   /* Don't skip this packet, process it */
 }
 
+static bool ipa_endpoint_status_tag(struct ipa_endpoint *endpoint,
+                                   const struct ipa_status *status)
+{
+       struct ipa_endpoint *command_endpoint;
+       struct ipa *ipa = endpoint->ipa;
+       u32 endpoint_id;
+
+       if (!le16_get_bits(status->mask, IPA_STATUS_MASK_TAG_VALID_FMASK))
+               return false;   /* No valid tag */
+
+       /* The status contains a valid tag.  We know the packet was sent to
+        * this endpoint (already verified by ipa_endpoint_status_skip()).
+        * If the packet came from the AP->command TX endpoint we know
+        * this packet was sent as part of the pipeline clear process.
+        */
+       endpoint_id = u8_get_bits(status->endp_src_idx,
+                                 IPA_STATUS_SRC_IDX_FMASK);
+       command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX];
+       if (endpoint_id == command_endpoint->endpoint_id) {
+               complete(&ipa->completion);
+       } else {
+               dev_err(&ipa->pdev->dev,
+                       "unexpected tagged packet from endpoint %u\n",
+                       endpoint_id);
+       }
+
+       return true;
+}
+
 /* Return whether the status indicates the packet should be dropped */
-static bool ipa_status_drop_packet(const struct ipa_status *status)
+static bool ipa_endpoint_status_drop(struct ipa_endpoint *endpoint,
+                                    const struct ipa_status *status)
 {
        u32 val;
 
+       /* If the status indicates a tagged transfer, we'll drop the packet */
+       if (ipa_endpoint_status_tag(endpoint, status))
+               return true;
+
        /* Deaggregation exceptions we drop; all other types we consume */
        if (status->exception)
                return status->exception == IPA_STATUS_EXCEPTION_DEAGGR;
@@ -1213,12 +1252,11 @@ static void ipa_endpoint_status_parse(struct ipa_endpoint *endpoint,
                        continue;
                }
 
-               /* Compute the amount of buffer space consumed by the
-                * packet, including the status element.  If the hardware
-                * is configured to pad packet data to an aligned boundary,
-                * account for that.  And if checksum offload is is enabled
-                * a trailer containing computed checksum information will
-                * be appended.
+               /* Compute the amount of buffer space consumed by the packet,
+                * including the status element.  If the hardware is configured
+                * to pad packet data to an aligned boundary, account for that.
+                * And if checksum offload is enabled a trailer containing
+                * computed checksum information will be appended.
                 */
                align = endpoint->data->rx.pad_align ? : 1;
                len = le16_to_cpu(status->pkt_len);
@@ -1226,16 +1264,21 @@ static void ipa_endpoint_status_parse(struct ipa_endpoint *endpoint,
                if (endpoint->data->checksum)
                        len += sizeof(struct rmnet_map_dl_csum_trailer);
 
-               /* Charge the new packet with a proportional fraction of
-                * the unused space in the original receive buffer.
-                * XXX Charge a proportion of the *whole* receive buffer?
-                */
-               if (!ipa_status_drop_packet(status)) {
-                       u32 extra = unused * len / total_len;
-                       void *data2 = data + sizeof(*status);
-                       u32 len2 = le16_to_cpu(status->pkt_len);
+               if (!ipa_endpoint_status_drop(endpoint, status)) {
+                       void *data2;
+                       u32 extra;
+                       u32 len2;
 
                        /* Client receives only packet data (no status) */
+                       data2 = data + sizeof(*status);
+                       len2 = le16_to_cpu(status->pkt_len);
+
+                       /* Have the true size reflect the extra unused space in
+                        * the original receive buffer.  Distribute the "cost"
+                        * proportionately across all aggregated packets in the
+                        * buffer.
+                        */
+                       extra = DIV_ROUND_CLOSEST(unused * len, total_len);
                        ipa_endpoint_skb_copy(endpoint, data2, len2, extra);
                }
 
@@ -1378,7 +1421,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
        do {
                if (!ipa_endpoint_aggr_active(endpoint))
                        break;
-               msleep(1);
+               usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
        } while (retries--);
 
        /* Check one last time */
@@ -1399,7 +1442,7 @@ static int ipa_endpoint_reset_rx_aggr(struct ipa_endpoint *endpoint)
         */
        gsi_channel_reset(gsi, endpoint->channel_id, true);
 
-       msleep(1);
+       usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC);
 
        goto out_suspend_again;
 
@@ -1564,7 +1607,7 @@ void ipa_endpoint_suspend(struct ipa *ipa)
        if (ipa->modem_netdev)
                ipa_modem_suspend(ipa->modem_netdev);
 
-       ipa_cmd_tag_process(ipa);
+       ipa_cmd_pipeline_clear(ipa);
 
        ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_LAN_RX]);
        ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX]);
index 84bb8ae..c10e734 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_address.h>
-#include <linux/remoteproc.h>
 #include <linux/qcom_scm.h>
 #include <linux/soc/qcom/mdt_loader.h>
 
@@ -729,19 +728,6 @@ static const struct of_device_id ipa_match[] = {
 };
 MODULE_DEVICE_TABLE(of, ipa_match);
 
-static phandle of_property_read_phandle(const struct device_node *np,
-                                       const char *name)
-{
-        struct property *prop;
-        int len = 0;
-
-        prop = of_find_property(np, name, &len);
-        if (!prop || len != sizeof(__be32))
-                return 0;
-
-        return be32_to_cpup(prop->value);
-}
-
 /* Check things that can be validated at build time.  This just
  * groups these things BUILD_BUG_ON() calls don't clutter the rest
  * of the code.
@@ -807,10 +793,8 @@ static int ipa_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        const struct ipa_data *data;
        struct ipa_clock *clock;
-       struct rproc *rproc;
        bool modem_init;
        struct ipa *ipa;
-       phandle ph;
        int ret;
 
        ipa_validate_build();
@@ -829,25 +813,12 @@ static int ipa_probe(struct platform_device *pdev)
                if (!qcom_scm_is_available())
                        return -EPROBE_DEFER;
 
-       /* We rely on remoteproc to tell us about modem state changes */
-       ph = of_property_read_phandle(dev->of_node, "modem-remoteproc");
-       if (!ph) {
-               dev_err(dev, "DT missing \"modem-remoteproc\" property\n");
-               return -EINVAL;
-       }
-
-       rproc = rproc_get_by_phandle(ph);
-       if (!rproc)
-               return -EPROBE_DEFER;
-
        /* The clock and interconnects might not be ready when we're
         * probed, so might return -EPROBE_DEFER.
         */
        clock = ipa_clock_init(dev, data->clock_data);
-       if (IS_ERR(clock)) {
-               ret = PTR_ERR(clock);
-               goto err_rproc_put;
-       }
+       if (IS_ERR(clock))
+               return PTR_ERR(clock);
 
        /* No more EPROBE_DEFER.  Allocate and initialize the IPA structure */
        ipa = kzalloc(sizeof(*ipa), GFP_KERNEL);
@@ -858,9 +829,9 @@ static int ipa_probe(struct platform_device *pdev)
 
        ipa->pdev = pdev;
        dev_set_drvdata(dev, ipa);
-       ipa->modem_rproc = rproc;
        ipa->clock = clock;
        ipa->version = data->version;
+       init_completion(&ipa->completion);
 
        ret = ipa_reg_init(ipa);
        if (ret)
@@ -935,8 +906,6 @@ err_kfree_ipa:
        kfree(ipa);
 err_clock_exit:
        ipa_clock_exit(clock);
-err_rproc_put:
-       rproc_put(rproc);
 
        return ret;
 }
@@ -944,7 +913,6 @@ err_rproc_put:
 static int ipa_remove(struct platform_device *pdev)
 {
        struct ipa *ipa = dev_get_drvdata(&pdev->dev);
-       struct rproc *rproc = ipa->modem_rproc;
        struct ipa_clock *clock = ipa->clock;
        int ret;
 
@@ -970,7 +938,6 @@ static int ipa_remove(struct platform_device *pdev)
        ipa_reg_exit(ipa);
        kfree(ipa);
        ipa_clock_exit(clock);
-       rproc_put(rproc);
 
        return 0;
 }
index e34fe2d..9b08eb8 100644 (file)
@@ -216,6 +216,7 @@ int ipa_modem_start(struct ipa *ipa)
        ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = netdev;
        ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = netdev;
 
+       SET_NETDEV_DEV(netdev, &ipa->pdev->dev);
        priv = netdev_priv(netdev);
        priv->ipa = ipa;
 
index 8801d09..6cd5010 100644 (file)
@@ -651,8 +651,7 @@ int ipvlan_queue_xmit(struct sk_buff *skb, struct net_device *dev)
        }
 
        /* Should not reach here */
-       WARN_ONCE(true, "ipvlan_queue_xmit() called for mode = [%hx]\n",
-                         port->mode);
+       WARN_ONCE(true, "%s called for mode = [%x]\n", __func__, port->mode);
 out:
        kfree_skb(skb);
        return NET_XMIT_DROP;
@@ -749,8 +748,7 @@ rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb)
        }
 
        /* Should not reach here */
-       WARN_ONCE(true, "ipvlan_handle_frame() called for mode = [%hx]\n",
-                         port->mode);
+       WARN_ONCE(true, "%s called for mode = [%x]\n", __func__, port->mode);
        kfree_skb(skb);
        return RX_HANDLER_CONSUMED;
 }
index fb51329..9a9a5cf 100644 (file)
@@ -1385,7 +1385,7 @@ static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode,
                                return ret;
                }
 
-               if (!data || !data[IFLA_MACVLAN_MACADDR_DATA])
+               if (!data[IFLA_MACVLAN_MACADDR_DATA])
                        return 0;
 
                head = nla_data(data[IFLA_MACVLAN_MACADDR_DATA]);
index 5136275..d3915f8 100644 (file)
@@ -149,7 +149,7 @@ static int mdiobb_cmd_addr(struct mdiobb_ctrl *ctrl, int phy, u32 addr)
        return dev_addr;
 }
 
-static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
+int mdiobb_read(struct mii_bus *bus, int phy, int reg)
 {
        struct mdiobb_ctrl *ctrl = bus->priv;
        int ret, i;
@@ -180,8 +180,9 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg)
        mdiobb_get_bit(ctrl);
        return ret;
 }
+EXPORT_SYMBOL(mdiobb_read);
 
-static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
+int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
 {
        struct mdiobb_ctrl *ctrl = bus->priv;
 
@@ -201,6 +202,7 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val)
        mdiobb_get_bit(ctrl);
        return 0;
 }
+EXPORT_SYMBOL(mdiobb_write);
 
 struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl)
 {
index fa41d8c..a5a214d 100644 (file)
@@ -122,7 +122,7 @@ static const struct net_device_ops mhi_netdev_ops = {
 static void mhi_net_setup(struct net_device *ndev)
 {
        ndev->header_ops = NULL;  /* No header */
-       ndev->type = ARPHRD_NONE; /* QMAP... */
+       ndev->type = ARPHRD_RAWIP;
        ndev->hard_header_len = 0;
        ndev->addr_len = 0;
        ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
@@ -158,7 +158,18 @@ static void mhi_net_dl_callback(struct mhi_device *mhi_dev,
                u64_stats_add(&mhi_netdev->stats.rx_bytes, mhi_res->bytes_xferd);
                u64_stats_update_end(&mhi_netdev->stats.rx_syncp);
 
-               skb->protocol = htons(ETH_P_MAP);
+               switch (skb->data[0] & 0xf0) {
+               case 0x40:
+                       skb->protocol = htons(ETH_P_IP);
+                       break;
+               case 0x60:
+                       skb->protocol = htons(ETH_P_IPV6);
+                       break;
+               default:
+                       skb->protocol = htons(ETH_P_MAP);
+                       break;
+               }
+
                skb_put(skb, mhi_res->bytes_xferd);
                netif_rx(skb);
        }
@@ -237,6 +248,10 @@ static void mhi_net_rx_refill_work(struct work_struct *work)
                schedule_delayed_work(&mhi_netdev->rx_refill, HZ / 2);
 }
 
+static struct device_type wwan_type = {
+       .name = "wwan",
+};
+
 static int mhi_net_probe(struct mhi_device *mhi_dev,
                         const struct mhi_device_id *id)
 {
@@ -256,6 +271,7 @@ static int mhi_net_probe(struct mhi_device *mhi_dev,
        mhi_netdev->ndev = ndev;
        mhi_netdev->mdev = mhi_dev;
        SET_NETDEV_DEV(ndev, &mhi_dev->dev);
+       SET_NETDEV_DEVTYPE(ndev, &wwan_type);
 
        /* All MHI net channels have 128 ring elements (at least for now) */
        mhi_netdev->rx_queue_sz = 128;
index 45d8a77..f140bbc 100644 (file)
@@ -860,7 +860,7 @@ static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
 
        nexthop = kzalloc(sizeof(*nexthop), GFP_KERNEL);
        if (!nexthop)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        nexthop->id = info->id;
 
@@ -868,15 +868,20 @@ static struct nsim_nexthop *nsim_nexthop_create(struct nsim_fib_data *data,
         * occupy.
         */
 
-       if (!info->is_grp) {
+       switch (info->type) {
+       case NH_NOTIFIER_INFO_TYPE_SINGLE:
                occ = 1;
-               goto out;
+               break;
+       case NH_NOTIFIER_INFO_TYPE_GRP:
+               for (i = 0; i < info->nh_grp->num_nh; i++)
+                       occ += info->nh_grp->nh_entries[i].weight;
+               break;
+       default:
+               NL_SET_ERR_MSG_MOD(info->extack, "Unsupported nexthop type");
+               kfree(nexthop);
+               return ERR_PTR(-EOPNOTSUPP);
        }
 
-       for (i = 0; i < info->nh_grp->num_nh; i++)
-               occ += info->nh_grp->nh_entries[i].weight;
-
-out:
        nexthop->occ = occ;
        return nexthop;
 }
@@ -972,8 +977,8 @@ static int nsim_nexthop_insert(struct nsim_fib_data *data,
        int err;
 
        nexthop = nsim_nexthop_create(data, info);
-       if (!nexthop)
-               return -ENOMEM;
+       if (IS_ERR(nexthop))
+               return PTR_ERR(nexthop);
 
        nexthop_old = rhashtable_lookup_fast(&data->nexthop_ht, &info->id,
                                             nsim_nexthop_ht_params);
index 7178468..aec9244 100644 (file)
@@ -258,8 +258,6 @@ static const struct net_device_ops nsim_netdev_ops = {
        .ndo_setup_tc           = nsim_setup_tc,
        .ndo_set_features       = nsim_set_features,
        .ndo_bpf                = nsim_bpf,
-       .ndo_udp_tunnel_add     = udp_tunnel_nic_add_port,
-       .ndo_udp_tunnel_del     = udp_tunnel_nic_del_port,
        .ndo_get_devlink_port   = nsim_get_devlink_port,
 };
 
index d0b36fd..d67bddc 100644 (file)
 #define AT803X_MIN_DOWNSHIFT 2
 #define AT803X_MAX_DOWNSHIFT 9
 
+#define AT803X_MMD3_SMARTEEE_CTL1              0x805b
+#define AT803X_MMD3_SMARTEEE_CTL2              0x805c
+#define AT803X_MMD3_SMARTEEE_CTL3              0x805d
+#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN       BIT(8)
+
 #define ATH9331_PHY_ID 0x004dd041
 #define ATH8030_PHY_ID 0x004dd076
 #define ATH8031_PHY_ID 0x004dd074
@@ -146,8 +151,11 @@ MODULE_LICENSE("GPL");
 struct at803x_priv {
        int flags;
 #define AT803X_KEEP_PLL_ENABLED        BIT(0)  /* don't turn off internal PLL */
+#define AT803X_DISABLE_SMARTEEE        BIT(1)
        u16 clk_25m_reg;
        u16 clk_25m_mask;
+       u8 smarteee_lpi_tw_1g;
+       u8 smarteee_lpi_tw_100m;
        struct regulator_dev *vddio_rdev;
        struct regulator_dev *vddh_rdev;
        struct regulator *vddio;
@@ -411,13 +419,32 @@ static int at803x_parse_dt(struct phy_device *phydev)
 {
        struct device_node *node = phydev->mdio.dev.of_node;
        struct at803x_priv *priv = phydev->priv;
-       u32 freq, strength;
+       u32 freq, strength, tw;
        unsigned int sel;
        int ret;
 
        if (!IS_ENABLED(CONFIG_OF_MDIO))
                return 0;
 
+       if (of_property_read_bool(node, "qca,disable-smarteee"))
+               priv->flags |= AT803X_DISABLE_SMARTEEE;
+
+       if (!of_property_read_u32(node, "qca,smarteee-tw-us-1g", &tw)) {
+               if (!tw || tw > 255) {
+                       phydev_err(phydev, "invalid qca,smarteee-tw-us-1g\n");
+                       return -EINVAL;
+               }
+               priv->smarteee_lpi_tw_1g = tw;
+       }
+
+       if (!of_property_read_u32(node, "qca,smarteee-tw-us-100m", &tw)) {
+               if (!tw || tw > 255) {
+                       phydev_err(phydev, "invalid qca,smarteee-tw-us-100m\n");
+                       return -EINVAL;
+               }
+               priv->smarteee_lpi_tw_100m = tw;
+       }
+
        ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
        if (!ret) {
                switch (freq) {
@@ -526,22 +553,47 @@ static void at803x_remove(struct phy_device *phydev)
                regulator_disable(priv->vddio);
 }
 
-static int at803x_clk_out_config(struct phy_device *phydev)
+static int at803x_smarteee_config(struct phy_device *phydev)
 {
        struct at803x_priv *priv = phydev->priv;
-       int val;
+       u16 mask = 0, val = 0;
+       int ret;
 
-       if (!priv->clk_25m_mask)
+       if (priv->flags & AT803X_DISABLE_SMARTEEE)
+               return phy_modify_mmd(phydev, MDIO_MMD_PCS,
+                                     AT803X_MMD3_SMARTEEE_CTL3,
+                                     AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
+
+       if (priv->smarteee_lpi_tw_1g) {
+               mask |= 0xff00;
+               val |= priv->smarteee_lpi_tw_1g << 8;
+       }
+       if (priv->smarteee_lpi_tw_100m) {
+               mask |= 0x00ff;
+               val |= priv->smarteee_lpi_tw_100m;
+       }
+       if (!mask)
                return 0;
 
-       val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M);
-       if (val < 0)
-               return val;
+       ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL1,
+                            mask, val);
+       if (ret)
+               return ret;
 
-       val &= ~priv->clk_25m_mask;
-       val |= priv->clk_25m_reg;
+       return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
+                             AT803X_MMD3_SMARTEEE_CTL3_LPI_EN,
+                             AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+}
+
+static int at803x_clk_out_config(struct phy_device *phydev)
+{
+       struct at803x_priv *priv = phydev->priv;
 
-       return phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val);
+       if (!priv->clk_25m_mask)
+               return 0;
+
+       return phy_modify_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M,
+                             priv->clk_25m_mask, priv->clk_25m_reg);
 }
 
 static int at8031_pll_config(struct phy_device *phydev)
@@ -584,6 +636,10 @@ static int at803x_config_init(struct phy_device *phydev)
        if (ret < 0)
                return ret;
 
+       ret = at803x_smarteee_config(phydev);
+       if (ret < 0)
+               return ret;
+
        ret = at803x_clk_out_config(phydev);
        if (ret < 0)
                return ret;
@@ -594,7 +650,13 @@ static int at803x_config_init(struct phy_device *phydev)
                        return ret;
        }
 
-       return 0;
+       /* Ar803x extended next page bit is enabled by default. Cisco
+        * multigig switches read this bit and attempt to negotiate 10Gbps
+        * rates even if the next page bit is disabled. This is incorrect
+        * behaviour but we still need to accommodate it. XNP is only needed
+        * for 10Gbps support, so disable XNP.
+        */
+       return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0);
 }
 
 static int at803x_ack_interrupt(struct phy_device *phydev)
index 1581200..e79297a 100644 (file)
@@ -612,6 +612,7 @@ static void bcm7xxx_28nm_remove(struct phy_device *phydev)
 
 static struct phy_driver bcm7xxx_driver[] = {
        BCM7XXX_28NM_EPHY(PHY_ID_BCM72113, "Broadcom BCM72113"),
+       BCM7XXX_28NM_EPHY(PHY_ID_BCM72116, "Broadcom BCM72116"),
        BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
        BCM7XXX_28NM_EPHY(PHY_ID_BCM7255, "Broadcom BCM7255"),
        BCM7XXX_28NM_EPHY(PHY_ID_BCM7260, "Broadcom BCM7260"),
@@ -633,6 +634,7 @@ static struct phy_driver bcm7xxx_driver[] = {
 
 static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
        { PHY_ID_BCM72113, 0xfffffff0 },
+       { PHY_ID_BCM72116, 0xfffffff0, },
        { PHY_ID_BCM7250, 0xfffffff0, },
        { PHY_ID_BCM7255, 0xfffffff0, },
        { PHY_ID_BCM7260, 0xfffffff0, },
index 2b42e46..040509b 100644 (file)
@@ -740,7 +740,7 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
 {
        int retval;
 
-       WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
+       lockdep_assert_held_once(&bus->mdio_lock);
 
        retval = bus->read(bus, addr, regnum);
 
@@ -766,7 +766,7 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
 {
        int err;
 
-       WARN_ON_ONCE(!mutex_is_locked(&bus->mdio_lock));
+       lockdep_assert_held_once(&bus->mdio_lock);
 
        err = bus->write(bus, addr, regnum, val);
 
index 54e0d75..39c7c78 100644 (file)
@@ -1389,7 +1389,7 @@ static struct phy_driver ksphy_driver[] = {
 }, {
        .phy_id         = PHY_ID_KSZ886X,
        .phy_id_mask    = MICREL_PHY_ID_MASK,
-       .name           = "Micrel KSZ886X Switch",
+       .name           = "Micrel KSZ8851 Ethernet MAC or KSZ886X Switch",
        /* PHY_BASIC_FEATURES */
        .config_init    = kszphy_config_init,
        .suspend        = genphy_suspend,
index 5a8c8eb..46160ba 100644 (file)
@@ -19,8 +19,6 @@
 #include <linux/phy.h>
 #include <linux/netdevice.h>
 
-#define DEBUG
-
 /* DP83865 phy identifier values */
 #define DP83865_PHY_ID 0x20005c7a
 
index 45f7553..9cb7e4d 100644 (file)
@@ -724,7 +724,7 @@ static int phy_check_link_status(struct phy_device *phydev)
 {
        int err;
 
-       WARN_ON(!mutex_is_locked(&phydev->lock));
+       lockdep_assert_held(&phydev->lock);
 
        /* Keep previous state if loopback is enabled because some PHYs
         * report that Link is Down when loopback is enabled.
index 80c2e64..8447e56 100644 (file)
@@ -1740,7 +1740,7 @@ int __phy_resume(struct phy_device *phydev)
        struct phy_driver *phydrv = phydev->drv;
        int ret;
 
-       WARN_ON(!mutex_is_locked(&phydev->lock));
+       lockdep_assert_held(&phydev->lock);
 
        if (!phydrv || !phydrv->resume)
                return 0;
index 99ecd6c..821e85a 100644 (file)
@@ -60,6 +60,9 @@
 #define RTL_LPADV_5000FULL                     BIT(6)
 #define RTL_LPADV_2500FULL                     BIT(5)
 
+#define RTL9000A_GINMR                         0x14
+#define RTL9000A_GINMR_LINK_STATUS             BIT(4)
+
 #define RTLGEN_SPEED_MASK                      0x0630
 
 #define RTL_GENERIC_PHYID                      0x001cc800
@@ -655,6 +658,122 @@ static int rtlgen_resume(struct phy_device *phydev)
        return ret;
 }
 
+static int rtl9000a_config_init(struct phy_device *phydev)
+{
+       phydev->autoneg = AUTONEG_DISABLE;
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+
+       return 0;
+}
+
+static int rtl9000a_config_aneg(struct phy_device *phydev)
+{
+       int ret;
+       u16 ctl = 0;
+
+       switch (phydev->master_slave_set) {
+       case MASTER_SLAVE_CFG_MASTER_FORCE:
+               ctl |= CTL1000_AS_MASTER;
+               break;
+       case MASTER_SLAVE_CFG_SLAVE_FORCE:
+               break;
+       case MASTER_SLAVE_CFG_UNKNOWN:
+       case MASTER_SLAVE_CFG_UNSUPPORTED:
+               return 0;
+       default:
+               phydev_warn(phydev, "Unsupported Master/Slave mode\n");
+               return -EOPNOTSUPP;
+       }
+
+       ret = phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl);
+       if (ret == 1)
+               ret = genphy_soft_reset(phydev);
+
+       return ret;
+}
+
+static int rtl9000a_read_status(struct phy_device *phydev)
+{
+       int ret;
+
+       phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
+       phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;
+
+       ret = genphy_update_link(phydev);
+       if (ret)
+               return ret;
+
+       ret = phy_read(phydev, MII_CTRL1000);
+       if (ret < 0)
+               return ret;
+       if (ret & CTL1000_AS_MASTER)
+               phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
+       else
+               phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;
+
+       ret = phy_read(phydev, MII_STAT1000);
+       if (ret < 0)
+               return ret;
+       if (ret & LPA_1000MSRES)
+               phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER;
+       else
+               phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE;
+
+       return 0;
+}
+
+static int rtl9000a_ack_interrupt(struct phy_device *phydev)
+{
+       int err;
+
+       err = phy_read(phydev, RTL8211F_INSR);
+
+       return (err < 0) ? err : 0;
+}
+
+static int rtl9000a_config_intr(struct phy_device *phydev)
+{
+       u16 val;
+       int err;
+
+       if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+               err = rtl9000a_ack_interrupt(phydev);
+               if (err)
+                       return err;
+
+               val = (u16)~RTL9000A_GINMR_LINK_STATUS;
+               err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val);
+       } else {
+               val = ~0;
+               err = phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val);
+               if (err)
+                       return err;
+
+               err = rtl9000a_ack_interrupt(phydev);
+       }
+
+       return phy_write_paged(phydev, 0xa42, RTL9000A_GINMR, val);
+}
+
+static irqreturn_t rtl9000a_handle_interrupt(struct phy_device *phydev)
+{
+       int irq_status;
+
+       irq_status = phy_read(phydev, RTL8211F_INSR);
+       if (irq_status < 0) {
+               phy_error(phydev);
+               return IRQ_NONE;
+       }
+
+       if (!(irq_status & RTL8211F_INER_LINK_STATUS))
+               return IRQ_NONE;
+
+       phy_trigger_machine(phydev);
+
+       return IRQ_HANDLED;
+}
+
 static struct phy_driver realtek_drvs[] = {
        {
                PHY_ID_MATCH_EXACT(0x00008201),
@@ -823,6 +942,19 @@ static struct phy_driver realtek_drvs[] = {
                .handle_interrupt = genphy_handle_interrupt_no_ack,
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
+       }, {
+               PHY_ID_MATCH_EXACT(0x001ccb00),
+               .name           = "RTL9000AA_RTL9000AN Ethernet",
+               .features       = PHY_BASIC_T1_FEATURES,
+               .config_init    = rtl9000a_config_init,
+               .config_aneg    = rtl9000a_config_aneg,
+               .read_status    = rtl9000a_read_status,
+               .config_intr    = rtl9000a_config_intr,
+               .handle_interrupt = rtl9000a_handle_interrupt,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .read_page      = rtl821x_read_page,
+               .write_page     = rtl821x_write_page,
        },
 };
 
index 20b91f5..3cfd773 100644 (file)
@@ -44,6 +44,17 @@ static void sfp_quirk_2500basex(const struct sfp_eeprom_id *id,
        phylink_set(modes, 2500baseX_Full);
 }
 
+static void sfp_quirk_ubnt_uf_instant(const struct sfp_eeprom_id *id,
+                                     unsigned long *modes)
+{
+       /* Ubiquiti U-Fiber Instant module claims that support all transceiver
+        * types including 10G Ethernet which is not truth. So clear all claimed
+        * modes and set only one mode which module supports: 1000baseX_Full.
+        */
+       phylink_zero(modes);
+       phylink_set(modes, 1000baseX_Full);
+}
+
 static const struct sfp_quirk sfp_quirks[] = {
        {
                // Alcatel Lucent G-010S-P can operate at 2500base-X, but
@@ -63,6 +74,10 @@ static const struct sfp_quirk sfp_quirks[] = {
                .vendor = "HUAWEI",
                .part = "MA5671A",
                .modes = sfp_quirk_2500basex,
+       }, {
+               .vendor = "UBNT",
+               .part = "UF-INSTANT",
+               .modes = sfp_quirk_ubnt_uf_instant,
        },
 };
 
@@ -265,6 +280,12 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
            br_min <= 1300 && br_max >= 1200)
                phylink_set(modes, 1000baseX_Full);
 
+       /* 100Base-FX, 100Base-LX, 100Base-PX, 100Base-BX10 */
+       if (id->base.e100_base_fx || id->base.e100_base_lx)
+               phylink_set(modes, 100baseFX_Full);
+       if ((id->base.e_base_px || id->base.e_base_bx10) && br_nom == 100)
+               phylink_set(modes, 100baseFX_Full);
+
        /* For active or passive cables, select the link modes
         * based on the bit rates and the cable compliance bytes.
         */
@@ -337,11 +358,16 @@ void sfp_parse_support(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
         * the bitrate to determine supported modes. Some BiDi modules (eg,
         * 1310nm/1550nm) are not 1000BASE-BX compliant due to the differing
         * wavelengths, so do not set any transceiver bits.
+        *
+        * Do the same for modules supporting 2500BASE-X. Note that some
+        * modules use 2500Mbaud rather than 3100 or 3200Mbaud for
+        * 2500BASE-X, so we allow some slack here.
         */
-       if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS)) {
-               /* If the bit rate allows 1000baseX */
-               if (br_nom && br_min <= 1300 && br_max >= 1200)
+       if (bitmap_empty(modes, __ETHTOOL_LINK_MODE_MASK_NBITS) && br_nom) {
+               if (br_min <= 1300 && br_max >= 1200)
                        phylink_set(modes, 1000baseX_Full);
+               if (br_min <= 3200 && br_max >= 2500)
+                       phylink_set(modes, 2500baseX_Full);
        }
 
        if (bus->sfp_quirk)
@@ -384,6 +410,9 @@ phy_interface_t sfp_select_interface(struct sfp_bus *bus,
        if (phylink_test(link_modes, 1000baseX_Full))
                return PHY_INTERFACE_MODE_1000BASEX;
 
+       if (phylink_test(link_modes, 100baseFX_Full))
+               return PHY_INTERFACE_MODE_100BASEX;
+
        dev_warn(bus->sfp_dev, "Unable to ascertain link mode\n");
 
        return PHY_INTERFACE_MODE_NA;
index 91d74c1..7998acc 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/acpi.h>
 #include <linux/ctype.h>
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/hwmon.h>
@@ -258,6 +259,9 @@ struct sfp {
        char *hwmon_name;
 #endif
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+       struct dentry *debugfs_dir;
+#endif
 };
 
 static bool sff_module_supported(const struct sfp_eeprom_id *id)
@@ -273,8 +277,21 @@ static const struct sff_data sff_data = {
 
 static bool sfp_module_supported(const struct sfp_eeprom_id *id)
 {
-       return id->base.phys_id == SFF8024_ID_SFP &&
-              id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP;
+       if (id->base.phys_id == SFF8024_ID_SFP &&
+           id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP)
+               return true;
+
+       /* SFP GPON module Ubiquiti U-Fiber Instant has in its EEPROM stored
+        * phys id SFF instead of SFP. Therefore mark this module explicitly
+        * as supported based on vendor name and pn match.
+        */
+       if (id->base.phys_id == SFF8024_ID_SFF_8472 &&
+           id->base.phys_ext_id == SFP_PHYS_EXT_ID_SFP &&
+           !memcmp(id->base.vendor_name, "UBNT            ", 16) &&
+           !memcmp(id->base.vendor_pn, "UF-INSTANT      ", 16))
+               return true;
+
+       return false;
 }
 
 static const struct sff_data sfp_data = {
@@ -336,19 +353,11 @@ static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
                        size_t len)
 {
        struct i2c_msg msgs[2];
-       size_t block_size;
+       u8 bus_addr = a2 ? 0x51 : 0x50;
+       size_t block_size = sfp->i2c_block_size;
        size_t this_len;
-       u8 bus_addr;
        int ret;
 
-       if (a2) {
-               block_size = 16;
-               bus_addr = 0x51;
-       } else {
-               block_size = sfp->i2c_block_size;
-               bus_addr = 0x50;
-       }
-
        msgs[0].addr = bus_addr;
        msgs[0].flags = 0;
        msgs[0].len = 1;
@@ -1282,6 +1291,20 @@ static void sfp_hwmon_probe(struct work_struct *work)
        struct sfp *sfp = container_of(work, struct sfp, hwmon_probe.work);
        int err, i;
 
+       /* hwmon interface needs to access 16bit registers in atomic way to
+        * guarantee coherency of the diagnostic monitoring data. If it is not
+        * possible to guarantee coherency because EEPROM is broken in such way
+        * that does not support atomic 16bit read operation then we have to
+        * skip registration of hwmon device.
+        */
+       if (sfp->i2c_block_size < 2) {
+               dev_info(sfp->dev,
+                        "skipping hwmon device registration due to broken EEPROM\n");
+               dev_info(sfp->dev,
+                        "diagnostic EEPROM area cannot be read atomically to guarantee data coherency\n");
+               return;
+       }
+
        err = sfp_read(sfp, true, 0, &sfp->diag, sizeof(sfp->diag));
        if (err < 0) {
                if (sfp->hwmon_tries--) {
@@ -1390,6 +1413,54 @@ static void sfp_module_tx_enable(struct sfp *sfp)
        sfp_set_state(sfp, sfp->state);
 }
 
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static int sfp_debug_state_show(struct seq_file *s, void *data)
+{
+       struct sfp *sfp = s->private;
+
+       seq_printf(s, "Module state: %s\n",
+                  mod_state_to_str(sfp->sm_mod_state));
+       seq_printf(s, "Module probe attempts: %d %d\n",
+                  R_PROBE_RETRY_INIT - sfp->sm_mod_tries_init,
+                  R_PROBE_RETRY_SLOW - sfp->sm_mod_tries);
+       seq_printf(s, "Device state: %s\n",
+                  dev_state_to_str(sfp->sm_dev_state));
+       seq_printf(s, "Main state: %s\n",
+                  sm_state_to_str(sfp->sm_state));
+       seq_printf(s, "Fault recovery remaining retries: %d\n",
+                  sfp->sm_fault_retries);
+       seq_printf(s, "PHY probe remaining retries: %d\n",
+                  sfp->sm_phy_retries);
+       seq_printf(s, "moddef0: %d\n", !!(sfp->state & SFP_F_PRESENT));
+       seq_printf(s, "rx_los: %d\n", !!(sfp->state & SFP_F_LOS));
+       seq_printf(s, "tx_fault: %d\n", !!(sfp->state & SFP_F_TX_FAULT));
+       seq_printf(s, "tx_disable: %d\n", !!(sfp->state & SFP_F_TX_DISABLE));
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(sfp_debug_state);
+
+static void sfp_debugfs_init(struct sfp *sfp)
+{
+       sfp->debugfs_dir = debugfs_create_dir(dev_name(sfp->dev), NULL);
+
+       debugfs_create_file("state", 0600, sfp->debugfs_dir, sfp,
+                           &sfp_debug_state_fops);
+}
+
+static void sfp_debugfs_exit(struct sfp *sfp)
+{
+       debugfs_remove_recursive(sfp->debugfs_dir);
+}
+#else
+static void sfp_debugfs_init(struct sfp *sfp)
+{
+}
+
+static void sfp_debugfs_exit(struct sfp *sfp)
+{
+}
+#endif
+
 static void sfp_module_tx_fault_reset(struct sfp *sfp)
 {
        unsigned int state = sfp->state;
@@ -1482,15 +1553,19 @@ static void sfp_sm_link_down(struct sfp *sfp)
 
 static void sfp_sm_link_check_los(struct sfp *sfp)
 {
-       unsigned int los = sfp->state & SFP_F_LOS;
+       const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+       const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+       __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal);
+       bool los = false;
 
        /* If neither SFP_OPTIONS_LOS_INVERTED nor SFP_OPTIONS_LOS_NORMAL
-        * are set, we assume that no LOS signal is available.
+        * are set, we assume that no LOS signal is available. If both are
+        * set, we assume LOS is not implemented (and is meaningless.)
         */
-       if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED))
-               los ^= SFP_F_LOS;
-       else if (!(sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL)))
-               los = 0;
+       if (los_options == los_inverted)
+               los = !(sfp->state & SFP_F_LOS);
+       else if (los_options == los_normal)
+               los = !!(sfp->state & SFP_F_LOS);
 
        if (los)
                sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
@@ -1500,18 +1575,22 @@ static void sfp_sm_link_check_los(struct sfp *sfp)
 
 static bool sfp_los_event_active(struct sfp *sfp, unsigned int event)
 {
-       return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) &&
-               event == SFP_E_LOS_LOW) ||
-              (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) &&
-               event == SFP_E_LOS_HIGH);
+       const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+       const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+       __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal);
+
+       return (los_options == los_inverted && event == SFP_E_LOS_LOW) ||
+              (los_options == los_normal && event == SFP_E_LOS_HIGH);
 }
 
 static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event)
 {
-       return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) &&
-               event == SFP_E_LOS_HIGH) ||
-              (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) &&
-               event == SFP_E_LOS_LOW);
+       const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+       const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+       __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal);
+
+       return (los_options == los_inverted && event == SFP_E_LOS_HIGH) ||
+              (los_options == los_normal && event == SFP_E_LOS_LOW);
 }
 
 static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
@@ -1642,26 +1721,30 @@ static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable)
        return 0;
 }
 
-/* Some modules (Nokia 3FE46541AA) lock up if byte 0x51 is read as a
- * single read. Switch back to reading 16 byte blocks unless we have
- * a CarlitoxxPro module (rebranded VSOL V2801F). Even more annoyingly,
- * some VSOL V2801F have the vendor name changed to OEM.
+/* GPON modules based on Realtek RTL8672 and RTL9601C chips (e.g. V-SOL
+ * V2801F, CarlitoxxPro CPGOS03-0490, Ubiquiti U-Fiber Instant, ...) do
+ * not support multibyte reads from the EEPROM. Each multi-byte read
+ * operation returns just one byte of EEPROM followed by zeros. There is
+ * no way to identify which modules are using Realtek RTL8672 and RTL9601C
+ * chips. Moreover every OEM of V-SOL V2801F module puts its own vendor
+ * name and vendor id into EEPROM, so there is even no way to detect if
+ * module is V-SOL V2801F. Therefore check for those zeros in the read
+ * data and then based on check switch to reading EEPROM to one byte
+ * at a time.
  */
-static int sfp_quirk_i2c_block_size(const struct sfp_eeprom_base *base)
+static bool sfp_id_needs_byte_io(struct sfp *sfp, void *buf, size_t len)
 {
-       if (!memcmp(base->vendor_name, "VSOL            ", 16))
-               return 1;
-       if (!memcmp(base->vendor_name, "OEM             ", 16) &&
-           !memcmp(base->vendor_pn,   "V2801F          ", 16))
-               return 1;
+       size_t i, block_size = sfp->i2c_block_size;
 
-       /* Some modules can't cope with long reads */
-       return 16;
-}
+       /* Already using byte IO */
+       if (block_size == 1)
+               return false;
 
-static void sfp_quirks_base(struct sfp *sfp, const struct sfp_eeprom_base *base)
-{
-       sfp->i2c_block_size = sfp_quirk_i2c_block_size(base);
+       for (i = 1; i < len; i += block_size) {
+               if (memchr_inv(buf + i, '\0', min(block_size - 1, len - i)))
+                       return false;
+       }
+       return true;
 }
 
 static int sfp_cotsworks_fixup_check(struct sfp *sfp, struct sfp_eeprom_id *id)
@@ -1705,11 +1788,11 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
        u8 check;
        int ret;
 
-       /* Some modules (CarlitoxxPro CPGOS03-0490) do not support multibyte
-        * reads from the EEPROM, so start by reading the base identifying
-        * information one byte at a time.
+       /* Some SFP modules and also some Linux I2C drivers do not like reads
+        * longer than 16 bytes, so read the EEPROM in chunks of 16 bytes at
+        * a time.
         */
-       sfp->i2c_block_size = 1;
+       sfp->i2c_block_size = 16;
 
        ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base));
        if (ret < 0) {
@@ -1723,6 +1806,33 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
                return -EAGAIN;
        }
 
+       /* Some SFP modules (e.g. Nokia 3FE46541AA) lock up if read from
+        * address 0x51 is just one byte at a time. Also SFF-8472 requires
+        * that EEPROM supports atomic 16bit read operation for diagnostic
+        * fields, so do not switch to one byte reading at a time unless it
+        * is really required and we have no other option.
+        */
+       if (sfp_id_needs_byte_io(sfp, &id.base, sizeof(id.base))) {
+               dev_info(sfp->dev,
+                        "Detected broken RTL8672/RTL9601C emulated EEPROM\n");
+               dev_info(sfp->dev,
+                        "Switching to reading EEPROM to one byte at a time\n");
+               sfp->i2c_block_size = 1;
+
+               ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base));
+               if (ret < 0) {
+                       if (report)
+                               dev_err(sfp->dev, "failed to read EEPROM: %d\n",
+                                       ret);
+                       return -EAGAIN;
+               }
+
+               if (ret != sizeof(id.base)) {
+                       dev_err(sfp->dev, "EEPROM short read: %d\n", ret);
+                       return -EAGAIN;
+               }
+       }
+
        /* Cotsworks do not seem to update the checksums when they
         * do the final programming with the final module part number,
         * serial number and date code.
@@ -1757,9 +1867,6 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
                }
        }
 
-       /* Apply any early module-specific quirks */
-       sfp_quirks_base(sfp, &id.base);
-
        ret = sfp_read(sfp, false, SFP_CC_BASE + 1, &id.ext, sizeof(id.ext));
        if (ret < 0) {
                if (report)
@@ -2483,6 +2590,8 @@ static int sfp_probe(struct platform_device *pdev)
        if (!sfp->sfp_bus)
                return -ENOMEM;
 
+       sfp_debugfs_init(sfp);
+
        return 0;
 }
 
@@ -2490,6 +2599,7 @@ static int sfp_remove(struct platform_device *pdev)
 {
        struct sfp *sfp = platform_get_drvdata(pdev);
 
+       sfp_debugfs_exit(sfp);
        sfp_unregister_socket(sfp->sfp_bus);
 
        rtnl_lock();
index 3337275..ddb78fb 100644 (file)
@@ -317,7 +317,8 @@ static int smsc_phy_probe(struct phy_device *phydev)
        /* Make clk optional to keep DTB backward compatibility. */
        priv->refclk = clk_get_optional(dev, NULL);
        if (IS_ERR(priv->refclk))
-               dev_err_probe(dev, PTR_ERR(priv->refclk), "Failed to request clock\n");
+               return dev_err_probe(dev, PTR_ERR(priv->refclk),
+                                    "Failed to request clock\n");
 
        ret = clk_prepare_enable(priv->refclk);
        if (ret)
index 09c27f7..d445ecb 100644 (file)
@@ -623,6 +623,7 @@ static int ppp_bridge_channels(struct channel *pch, struct channel *pchb)
                write_unlock_bh(&pch->upl);
                return -EALREADY;
        }
+       refcount_inc(&pchb->file.refcnt);
        rcu_assign_pointer(pch->bridge, pchb);
        write_unlock_bh(&pch->upl);
 
@@ -632,19 +633,24 @@ static int ppp_bridge_channels(struct channel *pch, struct channel *pchb)
                write_unlock_bh(&pchb->upl);
                goto err_unset;
        }
+       refcount_inc(&pch->file.refcnt);
        rcu_assign_pointer(pchb->bridge, pch);
        write_unlock_bh(&pchb->upl);
 
-       refcount_inc(&pch->file.refcnt);
-       refcount_inc(&pchb->file.refcnt);
-
        return 0;
 
 err_unset:
        write_lock_bh(&pch->upl);
+       /* Re-read pch->bridge with upl held in case it was modified concurrently */
+       pchb = rcu_dereference_protected(pch->bridge, lockdep_is_held(&pch->upl));
        RCU_INIT_POINTER(pch->bridge, NULL);
        write_unlock_bh(&pch->upl);
        synchronize_rcu();
+
+       if (pchb)
+               if (refcount_dec_and_test(&pchb->file.refcnt))
+                       ppp_destroy_channel(pchb);
+
        return -EALREADY;
 }
 
index ee50584..0fe7882 100644 (file)
@@ -278,10 +278,8 @@ static int pptp_rcv_core(struct sock *sk, struct sk_buff *skb)
                header = (struct pptp_gre_header *)(skb->data);
 
                /* ack in different place if S = 0 */
-               ack = GRE_IS_SEQ(header->gre_hd.flags) ? header->ack : header->seq;
-
-               ack = ntohl(ack);
-
+               ack = GRE_IS_SEQ(header->gre_hd.flags) ? ntohl(header->ack) :
+                                                        ntohl(header->seq);
                if (ack > opt->ack_recv)
                        opt->ack_recv = ack;
                /* also handle sequence number wrap-around  */
@@ -355,7 +353,7 @@ static int pptp_rcv(struct sk_buff *skb)
                /* if invalid, discard this packet */
                goto drop;
 
-       po = lookup_chan(htons(header->call_id), iph->saddr);
+       po = lookup_chan(ntohs(header->call_id), iph->saddr);
        if (po) {
                skb_dst_drop(skb);
                nf_reset_ct(skb);
index 1f4bdd9..ff4aa35 100644 (file)
@@ -713,8 +713,7 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
        skb_probe_transport_header(skb);
 
        /* Move network header to the right position for VLAN tagged packets */
-       if ((skb->protocol == htons(ETH_P_8021Q) ||
-            skb->protocol == htons(ETH_P_8021AD)) &&
+       if (eth_type_vlan(skb->protocol) &&
            __vlan_get_protocol(skb, skb->protocol, &depth) != 0)
                skb_set_network_header(skb, depth);
 
@@ -722,12 +721,10 @@ static ssize_t tap_get_user(struct tap_queue *q, void *msg_control,
        tap = rcu_dereference(q->tap);
        /* copy skb_ubuf_info for callback when skb has no error */
        if (zerocopy) {
-               skb_shinfo(skb)->destructor_arg = msg_control;
-               skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
-               skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
+               skb_zcopy_init(skb, msg_control);
        } else if (msg_control) {
                struct ubuf_info *uarg = msg_control;
-               uarg->callback(uarg, false);
+               uarg->callback(NULL, uarg, false);
        }
 
        if (tap) {
@@ -1166,8 +1163,7 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
        }
 
        /* Move network header to the right position for VLAN tagged packets */
-       if ((skb->protocol == htons(ETH_P_8021Q) ||
-            skb->protocol == htons(ETH_P_8021AD)) &&
+       if (eth_type_vlan(skb->protocol) &&
            __vlan_get_protocol(skb, skb->protocol, &depth) != 0)
                skb_set_network_header(skb, depth);
 
index c19dac2..dd7917c 100644 (file)
@@ -992,7 +992,8 @@ static void __team_compute_features(struct team *team)
        unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
                                        IFF_XMIT_DST_RELEASE_PERM;
 
-       list_for_each_entry(port, &team->port_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(port, &team->port_list, list) {
                vlan_features = netdev_increment_features(vlan_features,
                                        port->dev->vlan_features,
                                        TEAM_VLAN_FEATURES);
@@ -1006,6 +1007,7 @@ static void __team_compute_features(struct team *team)
                if (port->dev->hard_header_len > max_hard_header_len)
                        max_hard_header_len = port->dev->hard_header_len;
        }
+       rcu_read_unlock();
 
        team->dev->vlan_features = vlan_features;
        team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
@@ -1020,9 +1022,7 @@ static void __team_compute_features(struct team *team)
 
 static void team_compute_features(struct team *team)
 {
-       mutex_lock(&team->lock);
        __team_compute_features(team);
-       mutex_unlock(&team->lock);
        netdev_change_features(team->dev);
 }
 
index fbed05a..62690ba 100644 (file)
@@ -1365,7 +1365,7 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
        int i;
 
        if (it->nr_segs > MAX_SKB_FRAGS + 1)
-               return ERR_PTR(-ENOMEM);
+               return ERR_PTR(-EMSGSIZE);
 
        local_bh_disable();
        skb = napi_get_frags(&tfile->napi);
@@ -1599,12 +1599,8 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun,
                struct xdp_buff xdp;
                u32 act;
 
-               xdp.data_hard_start = buf;
-               xdp.data = buf + pad;
-               xdp_set_data_meta_invalid(&xdp);
-               xdp.data_end = xdp.data + len;
-               xdp.rxq = &tfile->xdp_rxq;
-               xdp.frame_sz = buflen;
+               xdp_init_buff(&xdp, buflen, &tfile->xdp_rxq);
+               xdp_prepare_buff(&xdp, buf, pad, len, false);
 
                act = bpf_prog_run_xdp(xdp_prog, &xdp);
                if (act == XDP_REDIRECT || act == XDP_TX) {
@@ -1814,12 +1810,10 @@ drop:
 
        /* copy skb_ubuf_info for callback when skb has no error */
        if (zerocopy) {
-               skb_shinfo(skb)->destructor_arg = msg_control;
-               skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
-               skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
+               skb_zcopy_init(skb, msg_control);
        } else if (msg_control) {
                struct ubuf_info *uarg = msg_control;
-               uarg->callback(uarg, false);
+               uarg->callback(NULL, uarg, false);
        }
 
        skb_reset_network_header(skb);
@@ -2344,9 +2338,9 @@ static int tun_xdp_one(struct tun_struct *tun,
                        skb_xdp = true;
                        goto build;
                }
+
+               xdp_init_buff(xdp, buflen, &tfile->xdp_rxq);
                xdp_set_data_meta_invalid(xdp);
-               xdp->rxq = &tfile->xdp_rxq;
-               xdp->frame_sz = buflen;
 
                act = bpf_prog_run_xdp(xdp_prog, xdp);
                err = tun_xdp_act(tun, xdp_prog, xdp, act);
@@ -2741,7 +2735,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
                err = register_netdevice(tun->dev);
                if (err < 0)
                        goto err_detach;
-               /* free_netdev() won't check refcnt, to aovid race
+               /* free_netdev() won't check refcnt, to avoid race
                 * with dev_put() we need publish tun after registration.
                 */
                rcu_assign_pointer(tfile->tun, tun);
index 1e37190..fbbe786 100644 (file)
@@ -631,7 +631,6 @@ config USB_NET_AQC111
 config USB_RTL8153_ECM
        tristate "RTL8153 ECM support"
        depends on USB_NET_CDCETHER && (USB_RTL8152 || USB_RTL8152=n)
-       default y
        help
          This option supports ECM mode for RTL8153 ethernet adapter, when
          CONFIG_USB_RTL8152 is not set, or the RTL8153 device is not
index 8c1d61c..a9b5510 100644 (file)
@@ -793,6 +793,13 @@ static const struct usb_device_id  products[] = {
        .driver_info = 0,
 },
 
+/* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0x721e, USB_CLASS_COMM,
+                       USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+       .driver_info = 0,
+},
+
 /* ThinkPad USB-C Dock Gen 2 (based on Realtek RTL8153) */
 {
        USB_DEVICE_AND_INTERFACE_INFO(LENOVO_VENDOR_ID, 0xa387, USB_CLASS_COMM,
@@ -961,6 +968,12 @@ static const struct usb_device_id  products[] = {
                                      USB_CDC_SUBCLASS_ETHERNET,
                                      USB_CDC_PROTO_NONE),
        .driver_info = (unsigned long)&wwan_info,
+}, {
+       /* Cinterion PLS83/PLS63 modem by GEMALTO/THALES */
+       USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0069, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET,
+                                     USB_CDC_PROTO_NONE),
+       .driver_info = (unsigned long)&wwan_info,
 }, {
        USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
                        USB_CDC_PROTO_NONE),
index 2bac57d..291e76d 100644 (file)
@@ -1199,7 +1199,10 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
         * accordingly. Otherwise, we should check here.
         */
        if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
-               delayed_ndp_size = ALIGN(ctx->max_ndp_size, ctx->tx_ndp_modulus);
+               delayed_ndp_size = ctx->max_ndp_size +
+                       max_t(u32,
+                             ctx->tx_ndp_modulus,
+                             ctx->tx_modulus + ctx->tx_remainder) - 1;
        else
                delayed_ndp_size = 0;
 
@@ -1410,7 +1413,8 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
        if (!(dev->driver_info->flags & FLAG_SEND_ZLP) &&
            skb_out->len > ctx->min_tx_pkt) {
                padding_count = ctx->tx_curr_size - skb_out->len;
-               skb_put_zero(skb_out, padding_count);
+               if (!WARN_ON(padding_count > ctx->tx_curr_size))
+                       skb_put_zero(skb_out, padding_count);
        } else if (skb_out->len < ctx->tx_curr_size &&
                   (skb_out->len % dev->maxpacket) == 0) {
                skb_put_u8(skb_out, 0); /* force short packet */
@@ -1823,6 +1827,15 @@ cdc_ncm_speed_change(struct usbnet *dev,
        uint32_t rx_speed = le32_to_cpu(data->DLBitRRate);
        uint32_t tx_speed = le32_to_cpu(data->ULBitRate);
 
+       /* if the speed hasn't changed, don't report it.
+        * RTL8156 shipped before 2021 sends notification about every 32ms.
+        */
+       if (dev->rx_speed == rx_speed && dev->tx_speed == tx_speed)
+               return;
+
+       dev->rx_speed = rx_speed;
+       dev->tx_speed = tx_speed;
+
        /*
         * Currently the USB-NET API does not support reporting the actual
         * device speed. Do print it instead.
@@ -1863,10 +1876,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb)
                 * USB_CDC_NOTIFY_NETWORK_CONNECTION notification shall be
                 * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE.
                 */
-               netif_info(dev, link, dev->net,
-                          "network connection: %sconnected\n",
-                          !!event->wValue ? "" : "dis");
-               usbnet_link_change(dev, !!event->wValue, 0);
+               if (netif_carrier_ok(dev->net) != !!event->wValue)
+                       usbnet_link_change(dev, !!event->wValue, 0);
                break;
 
        case USB_CDC_NOTIFY_SPEED_CHANGE:
index 2bb28db..ef6dd01 100644 (file)
@@ -370,7 +370,7 @@ static struct usb_driver hso_driver;
 static struct tty_driver *tty_drv;
 static struct hso_device *serial_table[HSO_SERIAL_TTY_MINORS];
 static struct hso_device *network_table[HSO_MAX_NET_DEVICES];
-static spinlock_t serial_table_lock;
+static DEFINE_SPINLOCK(serial_table_lock);
 
 static const s32 default_port_spec[] = {
        HSO_INTF_MUX | HSO_PORT_NETWORK,
@@ -3236,7 +3236,6 @@ static int __init hso_init(void)
        pr_info("%s\n", version);
 
        /* Initialise the serial table semaphore and table */
-       spin_lock_init(&serial_table_lock);
        for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++)
                serial_table[i] = NULL;
 
index d166c32..c8b2b60 100644 (file)
@@ -57,6 +57,7 @@ struct qmi_wwan_state {
 enum qmi_wwan_flags {
        QMI_WWAN_FLAG_RAWIP = 1 << 0,
        QMI_WWAN_FLAG_MUX = 1 << 1,
+       QMI_WWAN_FLAG_PASS_THROUGH = 1 << 2,
 };
 
 enum qmi_wwan_quirks {
@@ -186,7 +187,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                net = qmimux_find_dev(dev, hdr->mux_id);
                if (!net)
                        goto skip;
-               skbn = netdev_alloc_skb(net, pkt_len);
+               skbn = netdev_alloc_skb(net, pkt_len + LL_MAX_HEADER);
                if (!skbn)
                        return 0;
                skbn->dev = net;
@@ -203,6 +204,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        goto skip;
                }
 
+               skb_reserve(skbn, LL_MAX_HEADER);
                skb_put_data(skbn, skb->data + offset + qmimux_hdr_sz, pkt_len);
                if (netif_rx(skbn) != NET_RX_SUCCESS) {
                        net->stats.rx_errors++;
@@ -217,6 +219,28 @@ skip:
        return 1;
 }
 
+static ssize_t mux_id_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct net_device *dev = to_net_dev(d);
+       struct qmimux_priv *priv;
+
+       priv = netdev_priv(dev);
+
+       return sysfs_emit(buf, "0x%02x\n", priv->mux_id);
+}
+
+static DEVICE_ATTR_RO(mux_id);
+
+static struct attribute *qmi_wwan_sysfs_qmimux_attrs[] = {
+       &dev_attr_mux_id.attr,
+       NULL,
+};
+
+static struct attribute_group qmi_wwan_sysfs_qmimux_attr_group = {
+       .name = "qmap",
+       .attrs = qmi_wwan_sysfs_qmimux_attrs,
+};
+
 static int qmimux_register_device(struct net_device *real_dev, u8 mux_id)
 {
        struct net_device *new_dev;
@@ -239,6 +263,8 @@ static int qmimux_register_device(struct net_device *real_dev, u8 mux_id)
                goto out_free_newdev;
        }
 
+       new_dev->sysfs_groups[0] = &qmi_wwan_sysfs_qmimux_attr_group;
+
        err = register_netdevice(new_dev);
        if (err < 0)
                goto out_free_newdev;
@@ -325,6 +351,13 @@ static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, co
        if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP))
                return len;
 
+       /* ip mode cannot be cleared when pass through mode is set */
+       if (!enable && (info->flags & QMI_WWAN_FLAG_PASS_THROUGH)) {
+               netdev_err(dev->net,
+                          "Cannot clear ip mode on pass through device\n");
+               return -EINVAL;
+       }
+
        if (!rtnl_trylock())
                return restart_syscall();
 
@@ -455,14 +488,59 @@ err:
        return ret;
 }
 
+static ssize_t pass_through_show(struct device *d,
+                                struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct qmi_wwan_state *info;
+
+       info = (void *)&dev->data;
+       return sprintf(buf, "%c\n",
+                      info->flags & QMI_WWAN_FLAG_PASS_THROUGH ? 'Y' : 'N');
+}
+
+static ssize_t pass_through_store(struct device *d,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct qmi_wwan_state *info;
+       bool enable;
+
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       info = (void *)&dev->data;
+
+       /* no change? */
+       if (enable == (info->flags & QMI_WWAN_FLAG_PASS_THROUGH))
+               return len;
+
+       /* pass through mode can be set for raw ip devices only */
+       if (!(info->flags & QMI_WWAN_FLAG_RAWIP)) {
+               netdev_err(dev->net,
+                          "Cannot set pass through mode on non ip device\n");
+               return -EINVAL;
+       }
+
+       if (enable)
+               info->flags |= QMI_WWAN_FLAG_PASS_THROUGH;
+       else
+               info->flags &= ~QMI_WWAN_FLAG_PASS_THROUGH;
+
+       return len;
+}
+
 static DEVICE_ATTR_RW(raw_ip);
 static DEVICE_ATTR_RW(add_mux);
 static DEVICE_ATTR_RW(del_mux);
+static DEVICE_ATTR_RW(pass_through);
 
 static struct attribute *qmi_wwan_sysfs_attrs[] = {
        &dev_attr_raw_ip.attr,
        &dev_attr_add_mux.attr,
        &dev_attr_del_mux.attr,
+       &dev_attr_pass_through.attr,
        NULL,
 };
 
@@ -509,6 +587,11 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
        if (info->flags & QMI_WWAN_FLAG_MUX)
                return qmimux_rx_fixup(dev, skb);
 
+       if (info->flags & QMI_WWAN_FLAG_PASS_THROUGH) {
+               skb->protocol = htons(ETH_P_MAP);
+               return (netif_rx(skb) == NET_RX_SUCCESS);
+       }
+
        switch (skb->data[0] & 0xf0) {
        case 0x40:
                proto = htons(ETH_P_IP);
@@ -1013,6 +1096,7 @@ static const struct usb_device_id products[] = {
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)},   /* Quectel EC25, EC20 R2.0  Mini PCIe */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)},   /* Quectel EP06/EG06/EM06 */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)},   /* Quectel EG12/EM12 */
+       {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)},   /* Quectel EM160R-GL */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)},   /* Quectel RM500Q-GL */
 
        /* 3. Combined interface devices matching on interface number */
@@ -1301,6 +1385,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x0b3c, 0xc00a, 6)},    /* Olivetti Olicard 160 */
        {QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)},    /* Olivetti Olicard 500 */
        {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},    /* Cinterion PLxx */
+       {QMI_QUIRK_SET_DTR(0x1e2d, 0x006f, 8)}, /* Cinterion PLS83/PLS63 */
        {QMI_FIXED_INTF(0x1e2d, 0x0053, 4)},    /* Cinterion PHxx,PXxx */
        {QMI_FIXED_INTF(0x1e2d, 0x0063, 10)},   /* Cinterion ALASxx (1 RmNet) */
        {QMI_FIXED_INTF(0x1e2d, 0x0082, 4)},    /* Cinterion PHxx,PXxx (2 RmNet) */
index c448d60..67cd698 100644 (file)
@@ -6877,6 +6877,7 @@ static const struct usb_device_id rtl8152_table[] = {
        {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7205)},
        {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x720c)},
        {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x7214)},
+       {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0x721e)},
        {REALTEK_USB_DEVICE(VENDOR_ID_LENOVO,  0xa387)},
        {REALTEK_USB_DEVICE(VENDOR_ID_LINKSYS, 0x0041)},
        {REALTEK_USB_DEVICE(VENDOR_ID_NVIDIA,  0x09ff)},
index 2c3fabd..20b2df8 100644 (file)
@@ -122,12 +122,20 @@ static const struct driver_info r8153_info = {
 };
 
 static const struct usb_device_id products[] = {
+/* Realtek RTL8153 Based USB 3.0 Ethernet Adapters */
 {
        USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_REALTEK, 0x8153, USB_CLASS_COMM,
                                      USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
        .driver_info = (unsigned long)&r8153_info,
 },
 
+/* Lenovo Powered USB-C Travel Hub (4X90S92381, based on Realtek RTL8153) */
+{
+       USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID_LENOVO, 0x721e, USB_CLASS_COMM,
+                                     USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),
+       .driver_info = (unsigned long)&r8153_info,
+},
+
        { },            /* END */
 };
 MODULE_DEVICE_TABLE(usb, products);
index 6609d21..f813ca9 100644 (file)
@@ -387,7 +387,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)
        reply_len = sizeof *phym;
        retval = rndis_query(dev, intf, u.buf,
                             RNDIS_OID_GEN_PHYSICAL_MEDIUM,
-                            0, (void **) &phym, &reply_len);
+                            reply_len, (void **)&phym, &reply_len);
        if (retval != 0 || !phym) {
                /* OID is optional so don't fail here. */
                phym_unspec = cpu_to_le32(RNDIS_PHYSICAL_MEDIUM_UNSPECIFIED);
index 1447da1..b4c8080 100644 (file)
@@ -1539,11 +1539,11 @@ static void usbnet_bh (struct timer_list *t)
        }
 }
 
-static void usbnet_bh_tasklet(unsigned long data)
+static void usbnet_bh_tasklet(struct tasklet_struct *t)
 {
-       struct timer_list *t = (struct timer_list *)data;
+       struct usbnet *dev = from_tasklet(dev, t, bh);
 
-       usbnet_bh(t);
+       usbnet_bh(&dev->delay);
 }
 
 
@@ -1673,8 +1673,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
        skb_queue_head_init (&dev->txq);
        skb_queue_head_init (&dev->done);
        skb_queue_head_init(&dev->rxq_pause);
-       dev->bh.func = usbnet_bh_tasklet;
-       dev->bh.data = (unsigned long)&dev->delay;
+       tasklet_setup(&dev->bh, usbnet_bh_tasklet);
        INIT_WORK (&dev->kevent, usbnet_deferred_kevent);
        init_usb_anchor(&dev->deferred);
        timer_setup(&dev->delay, usbnet_bh, 0);
@@ -1964,12 +1963,12 @@ static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
                              cmd, reqtype, value, index, buf, size,
                              USB_CTRL_GET_TIMEOUT);
        if (err > 0 && err <= size) {
-        if (data)
-            memcpy(data, buf, err);
-        else
-            netdev_dbg(dev->net,
-                "Huh? Data requested but thrown away.\n");
-    }
+               if (data)
+                       memcpy(data, buf, err);
+               else
+                       netdev_dbg(dev->net,
+                                  "Huh? Data requested but thrown away.\n");
+       }
        kfree(buf);
 out:
        return err;
index 02bfcdf..99caae7 100644 (file)
@@ -654,7 +654,7 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
                                        struct veth_xdp_tx_bq *bq,
                                        struct veth_stats *stats)
 {
-       u32 pktlen, headroom, act, metalen;
+       u32 pktlen, headroom, act, metalen, frame_sz;
        void *orig_data, *orig_data_end;
        struct bpf_prog *xdp_prog;
        int mac_len, delta, off;
@@ -710,15 +710,11 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
                skb = nskb;
        }
 
-       xdp.data_hard_start = skb->head;
-       xdp.data = skb_mac_header(skb);
-       xdp.data_end = xdp.data + pktlen;
-       xdp.data_meta = xdp.data;
-       xdp.rxq = &rq->xdp_rxq;
-
        /* SKB "head" area always have tailroom for skb_shared_info */
-       xdp.frame_sz = (void *)skb_end_pointer(skb) - xdp.data_hard_start;
-       xdp.frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       frame_sz = skb_end_pointer(skb) - skb->head;
+       frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       xdp_init_buff(&xdp, frame_sz, &rq->xdp_rxq);
+       xdp_prepare_buff(&xdp, skb->head, skb->mac_header, pktlen, true);
 
        orig_data = xdp.data;
        orig_data_end = xdp.data_end;
index 4c41df6..ba8e637 100644 (file)
@@ -689,12 +689,9 @@ static struct sk_buff *receive_small(struct net_device *dev,
                        page = xdp_page;
                }
 
-               xdp.data_hard_start = buf + VIRTNET_RX_PAD + vi->hdr_len;
-               xdp.data = xdp.data_hard_start + xdp_headroom;
-               xdp.data_end = xdp.data + len;
-               xdp.data_meta = xdp.data;
-               xdp.rxq = &rq->xdp_rxq;
-               xdp.frame_sz = buflen;
+               xdp_init_buff(&xdp, buflen, &rq->xdp_rxq);
+               xdp_prepare_buff(&xdp, buf + VIRTNET_RX_PAD + vi->hdr_len,
+                                xdp_headroom, len, true);
                orig_data = xdp.data;
                act = bpf_prog_run_xdp(xdp_prog, &xdp);
                stats->xdp_packets++;
@@ -859,12 +856,9 @@ static struct sk_buff *receive_mergeable(struct net_device *dev,
                 * the descriptor on if we get an XDP_TX return code.
                 */
                data = page_address(xdp_page) + offset;
-               xdp.data_hard_start = data - VIRTIO_XDP_HEADROOM + vi->hdr_len;
-               xdp.data = data + vi->hdr_len;
-               xdp.data_end = xdp.data + (len - vi->hdr_len);
-               xdp.data_meta = xdp.data;
-               xdp.rxq = &rq->xdp_rxq;
-               xdp.frame_sz = frame_sz - vi->hdr_len;
+               xdp_init_buff(&xdp, frame_sz - vi->hdr_len, &rq->xdp_rxq);
+               xdp_prepare_buff(&xdp, data - VIRTIO_XDP_HEADROOM + vi->hdr_len,
+                                VIRTIO_XDP_HEADROOM, len - vi->hdr_len, true);
 
                act = bpf_prog_run_xdp(xdp_prog, &xdp);
                stats->xdp_packets++;
@@ -2093,14 +2087,16 @@ static int virtnet_set_channels(struct net_device *dev,
 
        get_online_cpus();
        err = _virtnet_set_queues(vi, queue_pairs);
-       if (!err) {
-               netif_set_real_num_tx_queues(dev, queue_pairs);
-               netif_set_real_num_rx_queues(dev, queue_pairs);
-
-               virtnet_set_affinity(vi);
+       if (err) {
+               put_online_cpus();
+               goto err;
        }
+       virtnet_set_affinity(vi);
        put_online_cpus();
 
+       netif_set_real_num_tx_queues(dev, queue_pairs);
+       netif_set_real_num_rx_queues(dev, queue_pairs);
+ err:
        return err;
 }
 
index a8ad710..3929e43 100644 (file)
@@ -3283,12 +3283,13 @@ static void vxlan_setup(struct net_device *dev)
        SET_NETDEV_DEVTYPE(dev, &vxlan_type);
 
        dev->features   |= NETIF_F_LLTX;
-       dev->features   |= NETIF_F_SG | NETIF_F_HW_CSUM;
+       dev->features   |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
        dev->features   |= NETIF_F_RXCSUM;
        dev->features   |= NETIF_F_GSO_SOFTWARE;
 
        dev->vlan_features = dev->features;
-       dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;
+       dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_FRAGLIST;
+       dev->hw_features |= NETIF_F_RXCSUM;
        dev->hw_features |= NETIF_F_GSO_SOFTWARE;
        netif_keep_dst(dev);
        dev->priv_flags |= IFF_NO_QUEUE;
@@ -4521,17 +4522,12 @@ static int vxlan_netdevice_event(struct notifier_block *unused,
        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
        struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);
 
-       if (event == NETDEV_UNREGISTER) {
-               if (!dev->udp_tunnel_nic_info)
-                       vxlan_offload_rx_ports(dev, false);
+       if (event == NETDEV_UNREGISTER)
                vxlan_handle_lowerdev_unregister(vn, dev);
-       } else if (event == NETDEV_REGISTER) {
-               if (!dev->udp_tunnel_nic_info)
-                       vxlan_offload_rx_ports(dev, true);
-       } else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO ||
-                  event == NETDEV_UDP_TUNNEL_DROP_INFO) {
-               vxlan_offload_rx_ports(dev, event == NETDEV_UDP_TUNNEL_PUSH_INFO);
-       }
+       else if (event == NETDEV_UDP_TUNNEL_PUSH_INFO)
+               vxlan_offload_rx_ports(dev, true);
+       else if (event == NETDEV_UDP_TUNNEL_DROP_INFO)
+               vxlan_offload_rx_ports(dev, false);
 
        return NOTIFY_DONE;
 }
index 4029fde..83c9481 100644 (file)
@@ -282,6 +282,7 @@ config SLIC_DS26522
        tristate "Slic Maxim ds26522 card support"
        depends on SPI
        depends on FSL_SOC || ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
+       select BITREVERSE
        help
          This module initializes and configures the slic maxim card
          in T1 or E1 mode.
index 64f8556..261b53f 100644 (file)
@@ -569,6 +569,13 @@ static void ppp_timer(struct timer_list *t)
        unsigned long flags;
 
        spin_lock_irqsave(&ppp->lock, flags);
+       /* mod_timer could be called after we entered this function but
+        * before we got the lock.
+        */
+       if (timer_pending(&proto->timer)) {
+               spin_unlock_irqrestore(&ppp->lock, flags);
+               return;
+       }
        switch (proto->state) {
        case STOPPING:
        case REQ_SENT:
index 7c5cf77..ecea09f 100644 (file)
@@ -323,7 +323,7 @@ struct desc {
 
 static int ports_open;
 static struct dma_pool *dma_pool;
-static spinlock_t npe_lock;
+static DEFINE_SPINLOCK(npe_lock);
 
 static const struct {
        int tx, txdone, rx, rxfree;
@@ -1402,8 +1402,6 @@ static int __init hss_init_module(void)
            (IXP4XX_FEATURE_HDLC | IXP4XX_FEATURE_HSS))
                return -ENODEV;
 
-       spin_lock_init(&npe_lock);
-
        return platform_driver_register(&ixp4xx_hss_driver);
 }
 
index 2fde439..3092a09 100644 (file)
@@ -1535,7 +1535,7 @@ sbni_setup( char  *p )
                goto  bad_param;
 
        for( n = 0, parm = 0;  *p  &&  n < 8; ) {
-               (*dest[ parm ])[ n ] = simple_strtol( p, &p, 0 );
+               (*dest[ parm ])[ n ] = simple_strtoul( p, &p, 0 );
                if( !*p  ||  *p == ')' )
                        return 1;
                if( *p == ';' ) {
index b97c38b..350b791 100644 (file)
@@ -185,7 +185,7 @@ int ath11k_core_suspend(struct ath11k_base *ab)
        ath11k_hif_ce_irq_disable(ab);
 
        ret = ath11k_hif_suspend(ab);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to suspend hif: %d\n", ret);
                return ret;
        }
index 205c0f1..920e502 100644 (file)
@@ -2294,6 +2294,7 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
 {
        u8 channel_num;
        u32 center_freq;
+       struct ieee80211_channel *channel;
 
        rx_status->freq = 0;
        rx_status->rate_idx = 0;
@@ -2314,9 +2315,12 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
                rx_status->band = NL80211_BAND_5GHZ;
        } else {
                spin_lock_bh(&ar->data_lock);
-               rx_status->band = ar->rx_channel->band;
-               channel_num =
-                       ieee80211_frequency_to_channel(ar->rx_channel->center_freq);
+               channel = ar->rx_channel;
+               if (channel) {
+                       rx_status->band = channel->band;
+                       channel_num =
+                               ieee80211_frequency_to_channel(channel->center_freq);
+               }
                spin_unlock_bh(&ar->data_lock);
                ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "rx_desc: ",
                                rx_desc, sizeof(struct hal_rx_desc));
index 5c175e3..c1608f6 100644 (file)
@@ -3021,6 +3021,7 @@ static int ath11k_mac_station_add(struct ath11k *ar,
        }
 
        if (ab->hw_params.vdev_start_delay &&
+           !arvif->is_started &&
            arvif->vdev_type != WMI_VDEV_TYPE_AP) {
                ret = ath11k_start_vdev_delay(ar->hw, vif);
                if (ret) {
@@ -5284,7 +5285,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
        /* for QCA6390 bss peer must be created before vdev_start */
        if (ab->hw_params.vdev_start_delay &&
            arvif->vdev_type != WMI_VDEV_TYPE_AP &&
-           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR) {
+           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
+           !ath11k_peer_find_by_vdev_id(ab, arvif->vdev_id)) {
                memcpy(&arvif->chanctx, ctx, sizeof(*ctx));
                ret = 0;
                goto out;
@@ -5295,7 +5297,9 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
                goto out;
        }
 
-       if (ab->hw_params.vdev_start_delay) {
+       if (ab->hw_params.vdev_start_delay &&
+           (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
+           arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)) {
                param.vdev_id = arvif->vdev_id;
                param.peer_type = WMI_PEER_TYPE_DEFAULT;
                param.peer_addr = ar->mac_addr;
index 857647a..20b415c 100644 (file)
@@ -274,7 +274,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG,
                                      PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL,
                                      PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set sysclk: %d\n", ret);
                return ret;
        }
@@ -283,7 +283,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_REG,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_VAL,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set dtct config1 error: %d\n", ret);
                return ret;
        }
@@ -292,7 +292,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_REG,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_VAL,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set dtct config2: %d\n", ret);
                return ret;
        }
@@ -301,7 +301,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_REG,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_VAL,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set dtct config4: %d\n", ret);
                return ret;
        }
@@ -886,6 +886,32 @@ static void ath11k_pci_free_region(struct ath11k_pci *ab_pci)
                pci_disable_device(pci_dev);
 }
 
+static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)
+{
+       struct ath11k_base *ab = ab_pci->ab;
+
+       pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL,
+                                 &ab_pci->link_ctl);
+
+       ath11k_dbg(ab, ATH11K_DBG_PCI, "pci link_ctl 0x%04x L0s %d L1 %d\n",
+                  ab_pci->link_ctl,
+                  u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S),
+                  u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1));
+
+       /* disable L0s and L1 */
+       pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL,
+                                  ab_pci->link_ctl & ~PCI_EXP_LNKCTL_ASPMC);
+
+       set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags);
+}
+
+static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci)
+{
+       if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags))
+               pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL,
+                                          ab_pci->link_ctl);
+}
+
 static int ath11k_pci_power_up(struct ath11k_base *ab)
 {
        struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
@@ -895,6 +921,11 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
        clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
        ath11k_pci_sw_reset(ab_pci->ab, true);
 
+       /* Disable ASPM during firmware download due to problems switching
+        * to AMSS state.
+        */
+       ath11k_pci_aspm_disable(ab_pci);
+
        ret = ath11k_mhi_start(ab_pci);
        if (ret) {
                ath11k_err(ab, "failed to start mhi: %d\n", ret);
@@ -908,6 +939,9 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
 {
        struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
 
+       /* restore aspm in case firmware bootup fails */
+       ath11k_pci_aspm_restore(ab_pci);
+
        ath11k_pci_force_wake(ab_pci->ab);
        ath11k_mhi_stop(ab_pci);
        clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
@@ -965,6 +999,8 @@ static int ath11k_pci_start(struct ath11k_base *ab)
 
        set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
 
+       ath11k_pci_aspm_restore(ab_pci);
+
        ath11k_pci_ce_irqs_enable(ab);
        ath11k_ce_rx_post_buf(ab);
 
index 0432a70..fe44d0d 100644 (file)
@@ -63,6 +63,7 @@ struct ath11k_msi_config {
 enum ath11k_pci_flags {
        ATH11K_PCI_FLAG_INIT_DONE,
        ATH11K_PCI_FLAG_IS_MSI_64,
+       ATH11K_PCI_ASPM_RESTORE,
 };
 
 struct ath11k_pci {
@@ -80,6 +81,7 @@ struct ath11k_pci {
 
        /* enum ath11k_pci_flags */
        unsigned long flags;
+       u16 link_ctl;
 };
 
 static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
index 1866d82..b69e7eb 100644 (file)
@@ -76,6 +76,23 @@ struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,
        return NULL;
 }
 
+struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
+                                               int vdev_id)
+{
+       struct ath11k_peer *peer;
+
+       spin_lock_bh(&ab->base_lock);
+
+       list_for_each_entry(peer, &ab->peers, list) {
+               if (vdev_id == peer->vdev_id) {
+                       spin_unlock_bh(&ab->base_lock);
+                       return peer;
+               }
+       }
+       spin_unlock_bh(&ab->base_lock);
+       return NULL;
+}
+
 void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)
 {
        struct ath11k_peer *peer;
index bba2e00..8553ed0 100644 (file)
@@ -43,5 +43,7 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
                       struct ieee80211_sta *sta, struct peer_create_params *param);
 int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
                                     const u8 *addr);
+struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
+                                               int vdev_id);
 
 #endif /* _PEER_H_ */
index f0b5c50..0db623f 100644 (file)
@@ -1660,6 +1660,7 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
        struct qmi_wlanfw_respond_mem_resp_msg_v01 resp;
        struct qmi_txn txn = {};
        int ret = 0, i;
+       bool delayed;
 
        req = kzalloc(sizeof(*req), GFP_KERNEL);
        if (!req)
@@ -1672,11 +1673,13 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
         * failure to FW and FW will then request mulitple blocks of small
         * chunk size memory.
         */
-       if (!ab->bus_params.fixed_mem_region && ab->qmi.mem_seg_count <= 2) {
+       if (!ab->bus_params.fixed_mem_region && ab->qmi.target_mem_delayed) {
+               delayed = true;
                ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi delays mem_request %d\n",
                           ab->qmi.mem_seg_count);
                memset(req, 0, sizeof(*req));
        } else {
+               delayed = false;
                req->mem_seg_len = ab->qmi.mem_seg_count;
 
                for (i = 0; i < req->mem_seg_len ; i++) {
@@ -1708,6 +1711,12 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
        }
 
        if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+               /* the error response is expected when
+                * target_mem_delayed is true.
+                */
+               if (delayed && resp.resp.error == 0)
+                       goto out;
+
                ath11k_warn(ab, "Respond mem req failed, result: %d, err: %d\n",
                            resp.resp.result, resp.resp.error);
                ret = -EINVAL;
@@ -1742,6 +1751,8 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
        int i;
        struct target_mem_chunk *chunk;
 
+       ab->qmi.target_mem_delayed = false;
+
        for (i = 0; i < ab->qmi.mem_seg_count; i++) {
                chunk = &ab->qmi.target_mem[i];
                chunk->vaddr = dma_alloc_coherent(ab->dev,
@@ -1749,6 +1760,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
                                                  &chunk->paddr,
                                                  GFP_KERNEL);
                if (!chunk->vaddr) {
+                       if (ab->qmi.mem_seg_count <= 2) {
+                               ath11k_dbg(ab, ATH11K_DBG_QMI,
+                                          "qmi dma allocation failed (%d B type %u), will try later with small size\n",
+                                           chunk->size,
+                                           chunk->type);
+                               ath11k_qmi_free_target_mem_chunk(ab);
+                               ab->qmi.target_mem_delayed = true;
+                               return 0;
+                       }
                        ath11k_err(ab, "failed to alloc memory, size: 0x%x, type: %u\n",
                                   chunk->size,
                                   chunk->type);
@@ -2517,7 +2537,7 @@ static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl,
                                    ret);
                        return;
                }
-       } else if (msg->mem_seg_len > 2) {
+       } else {
                ret = ath11k_qmi_alloc_target_mem_chunk(ab);
                if (ret) {
                        ath11k_warn(ab, "qmi failed to alloc target memory: %d\n",
index 92925c9..7bad374 100644 (file)
@@ -125,6 +125,7 @@ struct ath11k_qmi {
        struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
        u32 mem_seg_count;
        u32 target_mem_mode;
+       bool target_mem_delayed;
        u8 cal_done;
        struct target_info target;
        struct m3_mem_region m3_mem;
index b876fec..e1a1df1 100644 (file)
@@ -247,7 +247,9 @@ int ath11k_regd_update(struct ath11k *ar, bool init)
        }
 
        rtnl_lock();
-       ret = regulatory_set_wiphy_regd_sync_rtnl(ar->hw->wiphy, regd_copy);
+       wiphy_lock(ar->hw->wiphy);
+       ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy);
+       wiphy_unlock(ar->hw->wiphy);
        rtnl_unlock();
 
        kfree(regd_copy);
index da4b546..73869d4 100644 (file)
@@ -3460,6 +3460,9 @@ int ath11k_wmi_set_hw_mode(struct ath11k_base *ab,
        len = sizeof(*cmd);
 
        skb = ath11k_wmi_alloc_skb(wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
        cmd = (struct wmi_pdev_set_hw_mode_cmd_param *)skb->data;
 
        cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_HW_MODE_CMD) |
index 9c83e9a..29527e8 100644 (file)
@@ -3648,7 +3648,7 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif)
                kfree(mc_filter);
        }
 
-       unregister_netdevice(vif->ndev);
+       cfg80211_unregister_netdevice(vif->ndev);
 
        ar->num_vif--;
 }
@@ -3821,7 +3821,7 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name,
 
        netdev_set_default_ethtool_ops(ndev, &ath6kl_ethtool_ops);
 
-       if (register_netdevice(ndev))
+       if (cfg80211_register_netdevice(ndev))
                goto err;
 
        ar->avail_idx_map &= ~BIT(fw_vif_idx);
index ebb9f16..4f0a7a1 100644 (file)
@@ -212,11 +212,13 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type)
                ar->avail_idx_map |= BIT(i);
 
        rtnl_lock();
+       wiphy_lock(ar->wiphy);
 
        /* Add an initial station interface */
        wdev = ath6kl_interface_add(ar, "wlan%d", NET_NAME_ENUM,
                                    NL80211_IFTYPE_STATION, 0, INFRA_NETWORK);
 
+       wiphy_unlock(ar->wiphy);
        rtnl_unlock();
 
        if (!wdev) {
index 39bf196..9b5c7d8 100644 (file)
@@ -1904,7 +1904,9 @@ void ath6kl_stop_txrx(struct ath6kl *ar)
                spin_unlock_bh(&ar->list_lock);
                ath6kl_cfg80211_vif_stop(vif, test_bit(WMI_READY, &ar->flag));
                rtnl_lock();
+               wiphy_lock(ar->wiphy);
                ath6kl_cfg80211_vif_cleanup(vif);
+               wiphy_unlock(ar->wiphy);
                rtnl_unlock();
                spin_lock_bh(&ar->list_lock);
        }
index 6a95b19..f074e9c 100644 (file)
@@ -2,6 +2,7 @@
 config WIL6210
        tristate "Wilocity 60g WiFi card wil6210 support"
        select WANT_DEV_COREDUMP
+       select CRC32
        depends on CFG80211
        depends on PCI
        default n
index 1c42410..60bba5b 100644 (file)
@@ -2820,7 +2820,9 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil)
        wil->radio_wdev = wil->main_ndev->ieee80211_ptr;
        mutex_unlock(&wil->vif_mutex);
        if (p2p_wdev) {
+               wiphy_lock(wil->wiphy);
                cfg80211_unregister_wdev(p2p_wdev);
+               wiphy_unlock(wil->wiphy);
                kfree(p2p_wdev);
        }
 }
index 07b4a25..0913f0b 100644 (file)
@@ -424,7 +424,7 @@ int wil_vif_add(struct wil6210_priv *wil, struct wil6210_vif *vif)
                if (rc)
                        return rc;
        }
-       rc = register_netdevice(ndev);
+       rc = cfg80211_register_netdevice(ndev);
        if (rc < 0) {
                dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
                if (any_active && vif->mid != 0)
@@ -473,7 +473,9 @@ int wil_if_add(struct wil6210_priv *wil)
        wil_update_net_queues_bh(wil, vif, NULL, true);
 
        rtnl_lock();
+       wiphy_lock(wiphy);
        rc = wil_vif_add(wil, vif);
+       wiphy_unlock(wiphy);
        rtnl_unlock();
        if (rc < 0)
                goto out_wiphy;
@@ -511,7 +513,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid)
        /* during unregister_netdevice cfg80211_leave may perform operations
         * such as stop AP, disconnect, so we only clear the VIF afterwards
         */
-       unregister_netdevice(ndev);
+       cfg80211_unregister_netdevice(ndev);
 
        if (any_active && vif->mid != 0)
                wmi_port_delete(wil, vif->mid);
@@ -543,15 +545,18 @@ void wil_if_remove(struct wil6210_priv *wil)
 {
        struct net_device *ndev = wil->main_ndev;
        struct wireless_dev *wdev = ndev->ieee80211_ptr;
+       struct wiphy *wiphy = wdev->wiphy;
 
        wil_dbg_misc(wil, "if_remove\n");
 
        rtnl_lock();
+       wiphy_lock(wiphy);
        wil_vif_remove(wil, 0);
+       wiphy_unlock(wiphy);
        rtnl_unlock();
 
        netif_napi_del(&wil->napi_tx);
        netif_napi_del(&wil->napi_rx);
 
-       wiphy_unregister(wdev->wiphy);
+       wiphy_unregister(wiphy);
 }
index c174323..ce40d94 100644 (file)
@@ -473,8 +473,10 @@ static void wil_pcie_remove(struct pci_dev *pdev)
 
        wil6210_debugfs_remove(wil);
        rtnl_lock();
+       wiphy_lock(wil->wiphy);
        wil_p2p_wdev_free(wil);
        wil_remove_all_additional_vifs(wil);
+       wiphy_unlock(wil->wiphy);
        rtnl_unlock();
        wil_if_remove(wil);
        wil_if_pcie_disable(wil);
index 3dd28f5..ea78fe5 100644 (file)
@@ -633,7 +633,7 @@ static const struct net_device_ops brcmf_netdev_ops_pri = {
        .ndo_set_rx_mode = brcmf_netdev_set_multicast_list
 };
 
-int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
+int brcmf_net_attach(struct brcmf_if *ifp, bool locked)
 {
        struct brcmf_pub *drvr = ifp->drvr;
        struct net_device *ndev;
@@ -656,8 +656,8 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
        INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);
        INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
 
-       if (rtnl_locked)
-               err = register_netdevice(ndev);
+       if (locked)
+               err = cfg80211_register_netdevice(ndev);
        else
                err = register_netdev(ndev);
        if (err != 0) {
@@ -677,11 +677,11 @@ fail:
        return -EBADE;
 }
 
-void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked)
+void brcmf_net_detach(struct net_device *ndev, bool locked)
 {
        if (ndev->reg_state == NETREG_REGISTERED) {
-               if (rtnl_locked)
-                       unregister_netdevice(ndev);
+               if (locked)
+                       cfg80211_unregister_netdevice(ndev);
                else
                        unregister_netdev(ndev);
        } else {
@@ -758,7 +758,7 @@ int brcmf_net_mon_attach(struct brcmf_if *ifp)
        ndev = ifp->ndev;
        ndev->netdev_ops = &brcmf_netdev_ops_mon;
 
-       err = register_netdevice(ndev);
+       err = cfg80211_register_netdevice(ndev);
        if (err)
                bphy_err(drvr, "Failed to register %s device\n", ndev->name);
 
@@ -909,7 +909,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
 }
 
 static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
-                        bool rtnl_locked)
+                        bool locked)
 {
        struct brcmf_if *ifp;
        int ifidx;
@@ -938,7 +938,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
                        cancel_work_sync(&ifp->multicast_work);
                        cancel_work_sync(&ifp->ndoffload_work);
                }
-               brcmf_net_detach(ifp->ndev, rtnl_locked);
+               brcmf_net_detach(ifp->ndev, locked);
        } else {
                /* Only p2p device interfaces which get dynamically created
                 * end up here. In this case the p2p module should be informed
@@ -947,7 +947,7 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
                 * serious troublesome side effects. The p2p module will clean
                 * up the ifp if needed.
                 */
-               brcmf_p2p_ifp_removed(ifp, rtnl_locked);
+               brcmf_p2p_ifp_removed(ifp, locked);
                kfree(ifp);
        }
 
@@ -956,14 +956,14 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bsscfgidx,
                drvr->if2bss[ifidx] = BRCMF_BSSIDX_INVALID;
 }
 
-void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked)
+void brcmf_remove_interface(struct brcmf_if *ifp, bool locked)
 {
        if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bsscfgidx] != ifp))
                return;
        brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, ifidx=%d\n", ifp->bsscfgidx,
                  ifp->ifidx);
        brcmf_proto_del_if(ifp->drvr, ifp);
-       brcmf_del_if(ifp->drvr, ifp->bsscfgidx, rtnl_locked);
+       brcmf_del_if(ifp->drvr, ifp->bsscfgidx, locked);
 }
 
 static int brcmf_psm_watchdog_notify(struct brcmf_if *ifp,
index 5767d66..8212c9d 100644 (file)
@@ -201,16 +201,16 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp);
 char *brcmf_ifname(struct brcmf_if *ifp);
 struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx);
 void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable);
-int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked);
+int brcmf_net_attach(struct brcmf_if *ifp, bool locked);
 struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx,
                              bool is_p2pdev, const char *name, u8 *mac_addr);
-void brcmf_remove_interface(struct brcmf_if *ifp, bool rtnl_locked);
+void brcmf_remove_interface(struct brcmf_if *ifp, bool locked);
 void brcmf_txflowblock_if(struct brcmf_if *ifp,
                          enum brcmf_netif_stop_reason reason, bool state);
 void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
 void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq);
 void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb);
-void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked);
+void brcmf_net_detach(struct net_device *ndev, bool locked);
 int brcmf_net_mon_attach(struct brcmf_if *ifp);
 void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
 int __init brcmf_core_init(void);
index ec6fc7a..6d30a0f 100644 (file)
@@ -2430,7 +2430,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
        return err;
 }
 
-void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool rtnl_locked)
+void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool locked)
 {
        struct brcmf_cfg80211_info *cfg;
        struct brcmf_cfg80211_vif *vif;
@@ -2439,11 +2439,15 @@ void brcmf_p2p_ifp_removed(struct brcmf_if *ifp, bool rtnl_locked)
        vif = ifp->vif;
        cfg = wdev_to_cfg(&vif->wdev);
        cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-       if (!rtnl_locked)
+       if (locked) {
                rtnl_lock();
-       cfg80211_unregister_wdev(&vif->wdev);
-       if (!rtnl_locked)
+               wiphy_lock(cfg->wiphy);
+               cfg80211_unregister_wdev(&vif->wdev);
+               wiphy_unlock(cfg->wiphy);
                rtnl_unlock();
+       } else {
+               cfg80211_unregister_wdev(&vif->wdev);
+       }
        brcmf_free_vif(vif);
 }
 
index 7220fc8..8280092 100644 (file)
@@ -314,6 +314,7 @@ const struct iwl_cfg_trans_params iwl_ma_trans_cfg = {
 const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101";
 const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz";
 const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz";
+const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203";
 const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6 AX211 160MHz";
 const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6 AX411 160MHz";
 const char iwl_ma_name[] = "Intel(R) Wi-Fi 6";
@@ -340,6 +341,18 @@ const struct iwl_cfg iwl_qu_b0_hr1_b0 = {
        .num_rbds = IWL_NUM_RBDS_22000_HE,
 };
 
+const struct iwl_cfg iwl_qu_b0_hr_b0 = {
+       .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
+       IWL_DEVICE_22500,
+       /*
+        * This device doesn't support receiving BlockAck with a large bitmap
+        * so we need to restrict the size of transmitted aggregation to the
+        * HT size; mac80211 would otherwise pick the HE max (256) by default.
+        */
+       .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
+       .num_rbds = IWL_NUM_RBDS_22000_HE,
+};
+
 const struct iwl_cfg iwl_ax201_cfg_qu_hr = {
        .name = "Intel(R) Wi-Fi 6 AX201 160MHz",
        .fw_name_pre = IWL_QU_B_HR_B_FW_PRE,
@@ -366,6 +379,18 @@ const struct iwl_cfg iwl_qu_c0_hr1_b0 = {
        .num_rbds = IWL_NUM_RBDS_22000_HE,
 };
 
+const struct iwl_cfg iwl_qu_c0_hr_b0 = {
+       .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
+       IWL_DEVICE_22500,
+       /*
+        * This device doesn't support receiving BlockAck with a large bitmap
+        * so we need to restrict the size of transmitted aggregation to the
+        * HT size; mac80211 would otherwise pick the HE max (256) by default.
+        */
+       .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT,
+       .num_rbds = IWL_NUM_RBDS_22000_HE,
+};
+
 const struct iwl_cfg iwl_ax201_cfg_qu_c0_hr_b0 = {
        .name = "Intel(R) Wi-Fi 6 AX201 160MHz",
        .fw_name_pre = IWL_QU_C_HR_B_FW_PRE,
index 15248b0..d8b7776 100644 (file)
@@ -80,19 +80,45 @@ static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
 }
 
 /*
- * Evaluate a DSM with no arguments and a single u8 return value (inside a
- * buffer object), verify and return that value.
+ * Generic function to evaluate a DSM with no arguments
+ * and an integer return value,
+ * (as an integer object or inside a buffer object),
+ * verify and assign the value in the "value" parameter.
+ * return 0 in success and the appropriate errno otherwise.
  */
-int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func)
+static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func,
+                                   u64 *value, size_t expected_size)
 {
        union acpi_object *obj;
-       int ret;
+       int ret = 0;
 
        obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL);
-       if (IS_ERR(obj))
+       if (IS_ERR(obj)) {
+               IWL_DEBUG_DEV_RADIO(dev,
+                                   "Failed to get  DSM object. func= %d\n",
+                                   func);
                return -ENOENT;
+       }
+
+       if (obj->type == ACPI_TYPE_INTEGER) {
+               *value = obj->integer.value;
+       } else if (obj->type == ACPI_TYPE_BUFFER) {
+               __le64 le_value = 0;
 
-       if (obj->type != ACPI_TYPE_BUFFER) {
+               if (WARN_ON_ONCE(expected_size > sizeof(le_value)))
+                       return -EINVAL;
+
+               /* if the buffer size doesn't match the expected size */
+               if (obj->buffer.length != expected_size)
+                       IWL_DEBUG_DEV_RADIO(dev,
+                                           "ACPI: DSM invalid buffer size, padding or truncating (%d)\n",
+                                           obj->buffer.length);
+
+                /* assuming LE from Intel BIOS spec */
+               memcpy(&le_value, obj->buffer.pointer,
+                      min_t(size_t, expected_size, (size_t)obj->buffer.length));
+               *value = le64_to_cpu(le_value);
+       } else {
                IWL_DEBUG_DEV_RADIO(dev,
                                    "ACPI: DSM method did not return a valid object, type=%d\n",
                                    obj->type);
@@ -100,15 +126,6 @@ int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func)
                goto out;
        }
 
-       if (obj->buffer.length != sizeof(u8)) {
-               IWL_DEBUG_DEV_RADIO(dev,
-                                   "ACPI: DSM method returned invalid buffer, length=%d\n",
-                                   obj->buffer.length);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       ret = obj->buffer.pointer[0];
        IWL_DEBUG_DEV_RADIO(dev,
                            "ACPI: DSM method evaluated: func=%d, ret=%d\n",
                            func, ret);
@@ -116,6 +133,24 @@ out:
        ACPI_FREE(obj);
        return ret;
 }
+
+/*
+ * Evaluate a DSM with no arguments and a u8 return value,
+ */
+int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, u8 *value)
+{
+       int ret;
+       u64 val;
+
+       ret = iwl_acpi_get_dsm_integer(dev, rev, func, &val, sizeof(u8));
+
+       if (ret < 0)
+               return ret;
+
+       /* cast val (u64) to be u8 */
+       *value = (u8)val;
+       return 0;
+}
 IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8);
 
 union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
index 042dd24..1cce30d 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
  * Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2020 Intel Corporation
+ * Copyright (C) 2018-2021 Intel Corporation
  */
 #ifndef __iwl_fw_acpi__
 #define __iwl_fw_acpi__
@@ -99,7 +99,7 @@ struct iwl_fw_runtime;
 
 void *iwl_acpi_get_object(struct device *dev, acpi_string method);
 
-int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func);
+int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, u8 *value);
 
 union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
                                         union acpi_object *data,
@@ -159,7 +159,8 @@ static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev,
        return ERR_PTR(-ENOENT);
 }
 
-static inline int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func)
+static inline
+int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, u8 *value)
 {
        return -ENOENT;
 }
index 6d8f7bf..895a907 100644 (file)
@@ -224,40 +224,46 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data,
 int iwl_pnvm_load(struct iwl_trans *trans,
                  struct iwl_notif_wait_data *notif_wait)
 {
-       const struct firmware *pnvm;
        struct iwl_notification_wait pnvm_wait;
        static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
                                                PNVM_INIT_COMPLETE_NTFY) };
-       char pnvm_name[64];
-       int ret;
 
        /* if the SKU_ID is empty, there's nothing to do */
        if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2])
                return 0;
 
-       /* if we already have it, nothing to do either */
-       if (trans->pnvm_loaded)
-               return 0;
+       /* load from disk only if we haven't done it (or tried) before */
+       if (!trans->pnvm_loaded) {
+               const struct firmware *pnvm;
+               char pnvm_name[64];
+               int ret;
+
+               /*
+                * The prefix unfortunately includes a hyphen at the end, so
+                * don't add the dot here...
+                */
+               snprintf(pnvm_name, sizeof(pnvm_name), "%spnvm",
+                        trans->cfg->fw_name_pre);
+
+               /* ...but replace the hyphen with the dot here. */
+               if (strlen(trans->cfg->fw_name_pre) < sizeof(pnvm_name))
+                       pnvm_name[strlen(trans->cfg->fw_name_pre) - 1] = '.';
+
+               ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
+               if (ret) {
+                       IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
+                                    pnvm_name, ret);
+                       /*
+                        * Pretend we've loaded it - at least we've tried and
+                        * couldn't load it at all, so there's no point in
+                        * trying again over and over.
+                        */
+                       trans->pnvm_loaded = true;
+               } else {
+                       iwl_pnvm_parse(trans, pnvm->data, pnvm->size);
 
-       /*
-        * The prefix unfortunately includes a hyphen at the end, so
-        * don't add the dot here...
-        */
-       snprintf(pnvm_name, sizeof(pnvm_name), "%spnvm",
-                trans->cfg->fw_name_pre);
-
-       /* ...but replace the hyphen with the dot here. */
-       if (strlen(trans->cfg->fw_name_pre) < sizeof(pnvm_name))
-               pnvm_name[strlen(trans->cfg->fw_name_pre) - 1] = '.';
-
-       ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev);
-       if (ret) {
-               IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n",
-                            pnvm_name, ret);
-       } else {
-               iwl_pnvm_parse(trans, pnvm->data, pnvm->size);
-
-               release_firmware(pnvm);
+                       release_firmware(pnvm);
+               }
        }
 
        iwl_init_notification_wait(notif_wait, &pnvm_wait,
index 27cb040..86e1d57 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2005-2014, 2018-2020 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2021 Intel Corporation
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
 #ifndef __IWL_CONFIG_H__
@@ -445,7 +445,7 @@ struct iwl_cfg {
 #define IWL_CFG_CORES_BT_GNSS          0x5
 
 #define IWL_SUBDEVICE_RF_ID(subdevice) ((u16)((subdevice) & 0x00F0) >> 4)
-#define IWL_SUBDEVICE_NO_160(subdevice)        ((u16)((subdevice) & 0x0100) >> 9)
+#define IWL_SUBDEVICE_NO_160(subdevice)        ((u16)((subdevice) & 0x0200) >> 9)
 #define IWL_SUBDEVICE_CORES(subdevice) ((u16)((subdevice) & 0x1C00) >> 10)
 
 struct iwl_dev_info {
@@ -491,6 +491,7 @@ extern const char iwl9260_killer_1550_name[];
 extern const char iwl9560_killer_1550i_name[];
 extern const char iwl9560_killer_1550s_name[];
 extern const char iwl_ax200_name[];
+extern const char iwl_ax203_name[];
 extern const char iwl_ax201_name[];
 extern const char iwl_ax101_name[];
 extern const char iwl_ax200_killer_1650w_name[];
@@ -574,6 +575,8 @@ extern const struct iwl_cfg iwl9560_2ac_cfg_soc;
 extern const struct iwl_cfg iwl_qu_b0_hr1_b0;
 extern const struct iwl_cfg iwl_qu_c0_hr1_b0;
 extern const struct iwl_cfg iwl_quz_a0_hr1_b0;
+extern const struct iwl_cfg iwl_qu_b0_hr_b0;
+extern const struct iwl_cfg iwl_qu_c0_hr_b0;
 extern const struct iwl_cfg iwl_ax200_cfg_cc;
 extern const struct iwl_cfg iwl_ax201_cfg_qu_hr;
 extern const struct iwl_cfg iwl_ax201_cfg_qu_hr;
index a654147..a80a35a 100644 (file)
@@ -180,13 +180,6 @@ static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans,
        if (le32_to_cpu(tlv->length) < sizeof(*reg))
                return -EINVAL;
 
-       /* For safe using a string from FW make sure we have a
-        * null terminator
-        */
-       reg->name[IWL_FW_INI_MAX_NAME - 1] = 0;
-
-       IWL_DEBUG_FW(trans, "WRT: parsing region: %s\n", reg->name);
-
        if (id >= IWL_FW_INI_MAX_REGION_ID) {
                IWL_ERR(trans, "WRT: Invalid region id %u\n", id);
                return -EINVAL;
index 2ac20d0..2b7ef15 100644 (file)
@@ -150,16 +150,17 @@ u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs)
 }
 IWL_EXPORT_SYMBOL(iwl_read_prph);
 
-void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
+void iwl_write_prph_delay(struct iwl_trans *trans, u32 ofs, u32 val, u32 delay_ms)
 {
        unsigned long flags;
 
        if (iwl_trans_grab_nic_access(trans, &flags)) {
+               mdelay(delay_ms);
                iwl_write_prph_no_grab(trans, ofs, val);
                iwl_trans_release_nic_access(trans, &flags);
        }
 }
-IWL_EXPORT_SYMBOL(iwl_write_prph);
+IWL_EXPORT_SYMBOL(iwl_write_prph_delay);
 
 int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
                      u32 bits, u32 mask, int timeout)
@@ -219,8 +220,8 @@ IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
 void iwl_force_nmi(struct iwl_trans *trans)
 {
        if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000)
-               iwl_write_prph(trans, DEVICE_SET_NMI_REG,
-                              DEVICE_SET_NMI_VAL_DRV);
+               iwl_write_prph_delay(trans, DEVICE_SET_NMI_REG,
+                                    DEVICE_SET_NMI_VAL_DRV, 1);
        else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
                iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER,
                                UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER);
index 39bceee..3c21c0e 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2018-2019 Intel Corporation
+ * Copyright (C) 2018-2020 Intel Corporation
  */
 #ifndef __iwl_io_h__
 #define __iwl_io_h__
@@ -37,7 +37,13 @@ u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs);
 u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs);
 void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val);
 void iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val);
-void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
+void iwl_write_prph_delay(struct iwl_trans *trans, u32 ofs,
+                         u32 val, u32 delay_ms);
+static inline void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
+{
+       iwl_write_prph_delay(trans, ofs, val, 0);
+}
+
 int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
                      u32 bits, u32 mask, int timeout);
 void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
index 0b03fde..1158e25 100644 (file)
 #define RADIO_RSP_ADDR_POS             (6)
 #define RADIO_RSP_RD_CMD               (3)
 
+/* LTR control (Qu only) */
+#define HPM_MAC_LTR_CSR                        0xa0348c
+#define HPM_MAC_LRT_ENABLE_ALL         0xf
+/* also uses CSR_LTR_* for values */
+#define HPM_UMAC_LTR                   0xa03480
+
 /* FW monitor */
 #define MON_BUFF_SAMPLE_CTL            (0xa03c00)
 #define MON_BUFF_BASE_ADDR             (0xa03c1c)
index c025188..64c10ca 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2012-2014, 2018-2020 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2021 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
@@ -2032,8 +2032,6 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
 
        mutex_lock(&mvm->mutex);
 
-       clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
-
        /* get the BSS vif pointer again */
        vif = iwl_mvm_get_bss_vif(mvm);
        if (IS_ERR_OR_NULL(vif))
@@ -2143,11 +2141,13 @@ err:
 
 out_iterate:
        if (!test)
-               ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
+               ieee80211_iterate_active_interfaces_mtx(mvm->hw,
                        IEEE80211_IFACE_ITER_NORMAL,
                        iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
 
 out:
+       clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
+
        /* no need to reset the device in unified images, if successful */
        if (unified_image && !ret) {
                /* nothing else to do if we already sent D0I3_END_CMD */
index 573e469..38d0bfb 100644 (file)
@@ -459,7 +459,10 @@ static ssize_t iwl_dbgfs_os_device_timediff_read(struct file *file,
        const size_t bufsz = sizeof(buf);
        int pos = 0;
 
+       mutex_lock(&mvm->mutex);
        iwl_mvm_get_sync_time(mvm, &curr_gp2, &curr_os);
+       mutex_unlock(&mvm->mutex);
+
        do_div(curr_os, NSEC_PER_USEC);
        diff = curr_os - curr_gp2;
        pos += scnprintf(buf + pos, bufsz - pos, "diff=%lld\n", diff);
index 0637eb1..313e9f1 100644 (file)
@@ -1090,20 +1090,22 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm)
 
 static u8 iwl_mvm_eval_dsm_indonesia_5g2(struct iwl_mvm *mvm)
 {
+       u8 value;
+
        int ret = iwl_acpi_get_dsm_u8((&mvm->fwrt)->dev, 0,
-                                     DSM_FUNC_ENABLE_INDONESIA_5G2);
+                                     DSM_FUNC_ENABLE_INDONESIA_5G2, &value);
 
        if (ret < 0)
                IWL_DEBUG_RADIO(mvm,
                                "Failed to evaluate DSM function ENABLE_INDONESIA_5G2, ret=%d\n",
                                ret);
 
-       else if (ret >= DSM_VALUE_INDONESIA_MAX)
+       else if (value >= DSM_VALUE_INDONESIA_MAX)
                IWL_DEBUG_RADIO(mvm,
-                               "DSM function ENABLE_INDONESIA_5G2 return invalid value, ret=%d\n",
-                               ret);
+                               "DSM function ENABLE_INDONESIA_5G2 return invalid value, value=%d\n",
+                               value);
 
-       else if (ret == DSM_VALUE_INDONESIA_ENABLE) {
+       else if (value == DSM_VALUE_INDONESIA_ENABLE) {
                IWL_DEBUG_RADIO(mvm,
                                "Evaluated DSM function ENABLE_INDONESIA_5G2: Enabling 5g2\n");
                return DSM_VALUE_INDONESIA_ENABLE;
@@ -1114,25 +1116,26 @@ static u8 iwl_mvm_eval_dsm_indonesia_5g2(struct iwl_mvm *mvm)
 
 static u8 iwl_mvm_eval_dsm_disable_srd(struct iwl_mvm *mvm)
 {
+       u8 value;
        int ret = iwl_acpi_get_dsm_u8((&mvm->fwrt)->dev, 0,
-                                     DSM_FUNC_DISABLE_SRD);
+                                     DSM_FUNC_DISABLE_SRD, &value);
 
        if (ret < 0)
                IWL_DEBUG_RADIO(mvm,
                                "Failed to evaluate DSM function DISABLE_SRD, ret=%d\n",
                                ret);
 
-       else if (ret >= DSM_VALUE_SRD_MAX)
+       else if (value >= DSM_VALUE_SRD_MAX)
                IWL_DEBUG_RADIO(mvm,
-                               "DSM function DISABLE_SRD return invalid value, ret=%d\n",
-                               ret);
+                               "DSM function DISABLE_SRD return invalid value, value=%d\n",
+                               value);
 
-       else if (ret == DSM_VALUE_SRD_PASSIVE) {
+       else if (value == DSM_VALUE_SRD_PASSIVE) {
                IWL_DEBUG_RADIO(mvm,
                                "Evaluated DSM function DISABLE_SRD: setting SRD to passive\n");
                return DSM_VALUE_SRD_PASSIVE;
 
-       } else if (ret == DSM_VALUE_SRD_DISABLE) {
+       } else if (value == DSM_VALUE_SRD_DISABLE) {
                IWL_DEBUG_RADIO(mvm,
                                "Evaluated DSM function DISABLE_SRD: disabling SRD\n");
                return DSM_VALUE_SRD_DISABLE;
index da32937..bcbd77e 100644 (file)
@@ -260,7 +260,7 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
        int ret;
        bool changed;
        const struct ieee80211_regdomain *r =
-                       rtnl_dereference(mvm->hw->wiphy->regd);
+                       wiphy_dereference(mvm->hw->wiphy, mvm->hw->wiphy->regd);
 
        if (!r)
                return -ENOENT;
@@ -282,7 +282,7 @@ int iwl_mvm_init_fw_regd(struct iwl_mvm *mvm)
 
        /* update cfg80211 if the regdomain was changed */
        if (changed)
-               ret = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+               ret = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd);
        else
                ret = 0;
 
@@ -4194,6 +4194,9 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
        iwl_mvm_binding_remove_vif(mvm, vif);
 
 out:
+       if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD) &&
+           switching_chanctx)
+               return;
        mvmvif->phy_ctxt = NULL;
        iwl_mvm_power_update_mac(mvm);
 }
index abb8c10..7fb4e61 100644 (file)
@@ -545,7 +545,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
                        return -EIO;
        }
 
-       retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+       retval = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd);
        kfree(regd);
        return retval;
 }
index 98f62d7..61618f6 100644 (file)
@@ -791,6 +791,10 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        if (!mvm->scan_cmd)
                goto out_free;
 
+       /* invalidate ids to prevent accidental removal of sta_id 0 */
+       mvm->aux_sta.sta_id = IWL_MVM_INVALID_STA;
+       mvm->snif_sta.sta_id = IWL_MVM_INVALID_STA;
+
        /* Set EBS as successful as long as not stated otherwise by the FW. */
        mvm->last_ebs_successful = true;
 
@@ -1205,6 +1209,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk)
        reprobe = container_of(wk, struct iwl_mvm_reprobe, work);
        if (device_reprobe(reprobe->dev))
                dev_err(reprobe->dev, "reprobe failed!\n");
+       put_device(reprobe->dev);
        kfree(reprobe);
        module_put(THIS_MODULE);
 }
@@ -1255,7 +1260,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
                        module_put(THIS_MODULE);
                        return;
                }
-               reprobe->dev = mvm->trans->dev;
+               reprobe->dev = get_device(mvm->trans->dev);
                INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
                schedule_work(&reprobe->work);
        } else if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED,
index dc17441..578c353 100644 (file)
@@ -2057,6 +2057,9 @@ int iwl_mvm_rm_snif_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (WARN_ON_ONCE(mvm->snif_sta.sta_id == IWL_MVM_INVALID_STA))
+               return -EINVAL;
+
        iwl_mvm_disable_txq(mvm, NULL, mvm->snif_queue, IWL_MAX_TID_COUNT, 0);
        ret = iwl_mvm_rm_sta_common(mvm, mvm->snif_sta.sta_id);
        if (ret)
@@ -2071,6 +2074,9 @@ int iwl_mvm_rm_aux_sta(struct iwl_mvm *mvm)
 
        lockdep_assert_held(&mvm->mutex);
 
+       if (WARN_ON_ONCE(mvm->aux_sta.sta_id == IWL_MVM_INVALID_STA))
+               return -EINVAL;
+
        iwl_mvm_disable_txq(mvm, NULL, mvm->aux_queue, IWL_MAX_TID_COUNT, 0);
        ret = iwl_mvm_rm_sta_common(mvm, mvm->aux_sta.sta_id);
        if (ret)
index a983c21..3712adc 100644 (file)
@@ -773,6 +773,7 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
 
        next = skb_gso_segment(skb, netdev_flags);
        skb_shinfo(skb)->gso_size = mss;
+       skb_shinfo(skb)->gso_type = ipv4 ? SKB_GSO_TCPV4 : SKB_GSO_TCPV6;
        if (WARN_ON_ONCE(IS_ERR(next)))
                return -EINVAL;
        else if (next)
@@ -795,6 +796,8 @@ iwl_mvm_tx_tso_segment(struct sk_buff *skb, unsigned int num_subframes,
 
                if (tcp_payload_len > mss) {
                        skb_shinfo(tmp)->gso_size = mss;
+                       skb_shinfo(tmp)->gso_type = ipv4 ? SKB_GSO_TCPV4 :
+                                                          SKB_GSO_TCPV6;
                } else {
                        if (qos) {
                                u8 *qc;
index 36bf414..5b5134d 100644 (file)
@@ -75,6 +75,15 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
                                 const struct fw_img *fw)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 ltr_val = CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ |
+                     u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC,
+                                     CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE) |
+                     u32_encode_bits(250,
+                                     CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL) |
+                     CSR_LTR_LONG_VAL_AD_SNOOP_REQ |
+                     u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC,
+                                     CSR_LTR_LONG_VAL_AD_SNOOP_SCALE) |
+                     u32_encode_bits(250, CSR_LTR_LONG_VAL_AD_SNOOP_VAL);
        struct iwl_context_info_gen3 *ctxt_info_gen3;
        struct iwl_prph_scratch *prph_scratch;
        struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl;
@@ -189,8 +198,10 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
        /* Allocate IML */
        iml_img = dma_alloc_coherent(trans->dev, trans->iml_len,
                                     &trans_pcie->iml_dma_addr, GFP_KERNEL);
-       if (!iml_img)
-               return -ENOMEM;
+       if (!iml_img) {
+               ret = -ENOMEM;
+               goto err_free_ctxt_info;
+       }
 
        memcpy(iml_img, trans->iml, trans->iml_len);
 
@@ -206,23 +217,19 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
        iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,
                    CSR_AUTO_FUNC_BOOT_ENA);
 
-       if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) {
-               /*
-                * The firmware initializes this again later (to a smaller
-                * value), but for the boot process initialize the LTR to
-                * ~250 usec.
-                */
-               u32 val = CSR_LTR_LONG_VAL_AD_NO_SNOOP_REQ |
-                         u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC,
-                                         CSR_LTR_LONG_VAL_AD_NO_SNOOP_SCALE) |
-                         u32_encode_bits(250,
-                                         CSR_LTR_LONG_VAL_AD_NO_SNOOP_VAL) |
-                         CSR_LTR_LONG_VAL_AD_SNOOP_REQ |
-                         u32_encode_bits(CSR_LTR_LONG_VAL_AD_SCALE_USEC,
-                                         CSR_LTR_LONG_VAL_AD_SNOOP_SCALE) |
-                         u32_encode_bits(250, CSR_LTR_LONG_VAL_AD_SNOOP_VAL);
-
-               iwl_write32(trans, CSR_LTR_LONG_VAL_AD, val);
+       /*
+        * To workaround hardware latency issues during the boot process,
+        * initialize the LTR to ~250 usec (see ltr_val above).
+        * The firmware initializes this again later (to a smaller value).
+        */
+       if ((trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210 ||
+            trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) &&
+           !trans->trans_cfg->integrated) {
+               iwl_write32(trans, CSR_LTR_LONG_VAL_AD, ltr_val);
+       } else if (trans->trans_cfg->integrated &&
+                  trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) {
+               iwl_write_prph(trans, HPM_MAC_LTR_CSR, HPM_MAC_LRT_ENABLE_ALL);
+               iwl_write_prph(trans, HPM_UMAC_LTR, ltr_val);
        }
 
        if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
@@ -232,6 +239,11 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
 
        return 0;
 
+err_free_ctxt_info:
+       dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3),
+                         trans_pcie->ctxt_info_gen3,
+                         trans_pcie->ctxt_info_dma_addr);
+       trans_pcie->ctxt_info_gen3 = NULL;
 err_free_prph_info:
        dma_free_coherent(trans->dev,
                          sizeof(*prph_info),
@@ -294,6 +306,9 @@ int iwl_trans_pcie_ctx_info_gen3_set_pnvm(struct iwl_trans *trans,
                return ret;
        }
 
+       if (WARN_ON(prph_sc_ctrl->pnvm_cfg.pnvm_size))
+               return -EBUSY;
+
        prph_sc_ctrl->pnvm_cfg.pnvm_base_addr =
                cpu_to_le64(trans_pcie->pnvm_dram.physical);
        prph_sc_ctrl->pnvm_cfg.pnvm_size =
index 9659826..ed3f5b7 100644 (file)
@@ -910,6 +910,11 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {
                      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY,
                      IWL_CFG_ANY, IWL_CFG_ANY,
                      iwl_qu_b0_hr1_b0, iwl_ax101_name),
+       _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
+                     IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
+                     IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY,
+                     IWL_CFG_ANY, IWL_CFG_ANY,
+                     iwl_qu_b0_hr_b0, iwl_ax203_name),
 
        /* Qu C step */
        _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
@@ -917,6 +922,11 @@ static const struct iwl_dev_info iwl_dev_info_table[] = {
                      IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY,
                      IWL_CFG_ANY, IWL_CFG_ANY,
                      iwl_qu_c0_hr1_b0, iwl_ax101_name),
+       _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
+                     IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP,
+                     IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY,
+                     IWL_CFG_ANY, IWL_CFG_ANY,
+                     iwl_qu_c0_hr_b0, iwl_ax203_name),
 
        /* QuZ */
        _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY,
index 285e0d5..ab93a84 100644 (file)
@@ -2107,7 +2107,8 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
 
        while (offs < dwords) {
                /* limit the time we spin here under lock to 1/2s */
-               ktime_t timeout = ktime_add_us(ktime_get(), 500 * USEC_PER_MSEC);
+               unsigned long end = jiffies + HZ / 2;
+               bool resched = false;
 
                if (iwl_trans_grab_nic_access(trans, &flags)) {
                        iwl_write32(trans, HBUS_TARG_MEM_RADDR,
@@ -2118,14 +2119,15 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr,
                                                        HBUS_TARG_MEM_RDAT);
                                offs++;
 
-                               /* calling ktime_get is expensive so
-                                * do it once in 128 reads
-                                */
-                               if (offs % 128 == 0 && ktime_after(ktime_get(),
-                                                                  timeout))
+                               if (time_after(jiffies, end)) {
+                                       resched = true;
                                        break;
+                               }
                        }
                        iwl_trans_release_nic_access(trans, &flags);
+
+                       if (resched)
+                               cond_resched();
                } else {
                        return -EBUSY;
                }
index 5dda001..83f4964 100644 (file)
@@ -201,6 +201,11 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq = trans->txqs.txq[txq_id];
 
+       if (!txq) {
+               IWL_ERR(trans, "Trying to free a queue that wasn't allocated?\n");
+               return;
+       }
+
        spin_lock_bh(&txq->lock);
        while (txq->write_ptr != txq->read_ptr) {
                IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n",
index 27eea90..7ff1bb0 100644 (file)
@@ -142,26 +142,25 @@ void iwl_txq_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
         * idx is bounded by n_window
         */
        int idx = iwl_txq_get_cmd_index(txq, txq->read_ptr);
+       struct sk_buff *skb;
 
        lockdep_assert_held(&txq->lock);
 
+       if (!txq->entries)
+               return;
+
        iwl_txq_gen2_tfd_unmap(trans, &txq->entries[idx].meta,
                               iwl_txq_get_tfd(trans, txq, idx));
 
-       /* free SKB */
-       if (txq->entries) {
-               struct sk_buff *skb;
-
-               skb = txq->entries[idx].skb;
+       skb = txq->entries[idx].skb;
 
-               /* Can be called from irqs-disabled context
-                * If skb is not NULL, it means that the whole queue is being
-                * freed and that the queue is not empty - free the skb
-                */
-               if (skb) {
-                       iwl_op_mode_free_skb(trans->op_mode, skb);
-                       txq->entries[idx].skb = NULL;
-               }
+       /* Can be called from irqs-disabled context
+        * If skb is not NULL, it means that the whole queue is being
+        * freed and that the queue is not empty - free the skb
+        */
+       if (skb) {
+               iwl_op_mode_free_skb(trans->op_mode, skb);
+               txq->entries[idx].skb = NULL;
        }
 }
 
@@ -841,10 +840,8 @@ void iwl_txq_gen2_unmap(struct iwl_trans *trans, int txq_id)
                        int idx = iwl_txq_get_cmd_index(txq, txq->read_ptr);
                        struct sk_buff *skb = txq->entries[idx].skb;
 
-                       if (WARN_ON_ONCE(!skb))
-                               continue;
-
-                       iwl_txq_free_tso_page(trans, skb);
+                       if (!WARN_ON_ONCE(!skb))
+                               iwl_txq_free_tso_page(trans, skb);
                }
                iwl_txq_gen2_free_tfd(trans, txq);
                txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr);
@@ -1494,28 +1491,28 @@ void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
         */
        int rd_ptr = txq->read_ptr;
        int idx = iwl_txq_get_cmd_index(txq, rd_ptr);
+       struct sk_buff *skb;
 
        lockdep_assert_held(&txq->lock);
 
+       if (!txq->entries)
+               return;
+
        /* We have only q->n_window txq->entries, but we use
         * TFD_QUEUE_SIZE_MAX tfds
         */
        iwl_txq_gen1_tfd_unmap(trans, &txq->entries[idx].meta, txq, rd_ptr);
 
        /* free SKB */
-       if (txq->entries) {
-               struct sk_buff *skb;
-
-               skb = txq->entries[idx].skb;
+       skb = txq->entries[idx].skb;
 
-               /* Can be called from irqs-disabled context
-                * If skb is not NULL, it means that the whole queue is being
-                * freed and that the queue is not empty - free the skb
-                */
-               if (skb) {
-                       iwl_op_mode_free_skb(trans->op_mode, skb);
-                       txq->entries[idx].skb = NULL;
-               }
+       /* Can be called from irqs-disabled context
+        * If skb is not NULL, it means that the whole queue is being
+        * freed and that the queue is not empty - free the skb
+        */
+       if (skb) {
+               iwl_op_mode_free_skb(trans->op_mode, skb);
+               txq->entries[idx].skb = NULL;
        }
 }
 
index 3b3fc7c..fa7d4c2 100644 (file)
@@ -311,6 +311,12 @@ static struct net_device *hwsim_mon; /* global monitor netdev */
        .hw_value = (_freq), \
 }
 
+#define CHAN6G(_freq) { \
+       .band = NL80211_BAND_6GHZ, \
+       .center_freq = (_freq), \
+       .hw_value = (_freq), \
+}
+
 static const struct ieee80211_channel hwsim_channels_2ghz[] = {
        CHAN2G(2412), /* Channel 1 */
        CHAN2G(2417), /* Channel 2 */
@@ -377,6 +383,68 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = {
        CHAN5G(5925), /* Channel 185 */
 };
 
+static const struct ieee80211_channel hwsim_channels_6ghz[] = {
+       CHAN6G(5955), /* Channel 1 */
+       CHAN6G(5975), /* Channel 5 */
+       CHAN6G(5995), /* Channel 9 */
+       CHAN6G(6015), /* Channel 13 */
+       CHAN6G(6035), /* Channel 17 */
+       CHAN6G(6055), /* Channel 21 */
+       CHAN6G(6075), /* Channel 25 */
+       CHAN6G(6095), /* Channel 29 */
+       CHAN6G(6115), /* Channel 33 */
+       CHAN6G(6135), /* Channel 37 */
+       CHAN6G(6155), /* Channel 41 */
+       CHAN6G(6175), /* Channel 45 */
+       CHAN6G(6195), /* Channel 49 */
+       CHAN6G(6215), /* Channel 53 */
+       CHAN6G(6235), /* Channel 57 */
+       CHAN6G(6255), /* Channel 61 */
+       CHAN6G(6275), /* Channel 65 */
+       CHAN6G(6295), /* Channel 69 */
+       CHAN6G(6315), /* Channel 73 */
+       CHAN6G(6335), /* Channel 77 */
+       CHAN6G(6355), /* Channel 81 */
+       CHAN6G(6375), /* Channel 85 */
+       CHAN6G(6395), /* Channel 89 */
+       CHAN6G(6415), /* Channel 93 */
+       CHAN6G(6435), /* Channel 97 */
+       CHAN6G(6455), /* Channel 181 */
+       CHAN6G(6475), /* Channel 105 */
+       CHAN6G(6495), /* Channel 109 */
+       CHAN6G(6515), /* Channel 113 */
+       CHAN6G(6535), /* Channel 117 */
+       CHAN6G(6555), /* Channel 121 */
+       CHAN6G(6575), /* Channel 125 */
+       CHAN6G(6595), /* Channel 129 */
+       CHAN6G(6615), /* Channel 133 */
+       CHAN6G(6635), /* Channel 137 */
+       CHAN6G(6655), /* Channel 141 */
+       CHAN6G(6675), /* Channel 145 */
+       CHAN6G(6695), /* Channel 149 */
+       CHAN6G(6715), /* Channel 153 */
+       CHAN6G(6735), /* Channel 157 */
+       CHAN6G(6755), /* Channel 161 */
+       CHAN6G(6775), /* Channel 165 */
+       CHAN6G(6795), /* Channel 169 */
+       CHAN6G(6815), /* Channel 173 */
+       CHAN6G(6835), /* Channel 177 */
+       CHAN6G(6855), /* Channel 181 */
+       CHAN6G(6875), /* Channel 185 */
+       CHAN6G(6895), /* Channel 189 */
+       CHAN6G(6915), /* Channel 193 */
+       CHAN6G(6935), /* Channel 197 */
+       CHAN6G(6955), /* Channel 201 */
+       CHAN6G(6975), /* Channel 205 */
+       CHAN6G(6995), /* Channel 209 */
+       CHAN6G(7015), /* Channel 213 */
+       CHAN6G(7035), /* Channel 217 */
+       CHAN6G(7055), /* Channel 221 */
+       CHAN6G(7075), /* Channel 225 */
+       CHAN6G(7095), /* Channel 229 */
+       CHAN6G(7115), /* Channel 233 */
+};
+
 #define NUM_S1G_CHANS_US 51
 static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US];
 
@@ -548,6 +616,7 @@ struct mac80211_hwsim_data {
        struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
        struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
        struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
+       struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)];
        struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)];
        struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
        struct ieee80211_iface_combination if_combination;
@@ -578,7 +647,8 @@ struct mac80211_hwsim_data {
                struct ieee80211_channel *channel;
                unsigned long next_start, start, end;
        } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) +
-                     ARRAY_SIZE(hwsim_channels_5ghz)];
+                     ARRAY_SIZE(hwsim_channels_5ghz) +
+                     ARRAY_SIZE(hwsim_channels_6ghz)];
 
        struct ieee80211_channel *channel;
        u64 beacon_int  /* beacon interval in us */;
@@ -3149,6 +3219,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
                sizeof(hwsim_channels_2ghz));
        memcpy(data->channels_5ghz, hwsim_channels_5ghz,
                sizeof(hwsim_channels_5ghz));
+       memcpy(data->channels_6ghz, hwsim_channels_6ghz,
+               sizeof(hwsim_channels_6ghz));
        memcpy(data->channels_s1g, hwsim_channels_s1g,
               sizeof(hwsim_channels_s1g));
        memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates));
index a6b9dc6..5553df9 100644 (file)
@@ -2097,7 +2097,7 @@ mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
        if (!mwifiex_stop_bg_scan(priv))
-               cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0);
+               cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);
 
        if (mwifiex_deauthenticate(priv, NULL))
                return -EFAULT;
@@ -2366,7 +2366,7 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
                    (int)sme->ssid_len, (char *)sme->ssid, sme->bssid);
 
        if (!mwifiex_stop_bg_scan(priv))
-               cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0);
+               cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);
 
        ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid,
                                     priv->bss_mode, sme->channel, sme, 0);
@@ -2576,7 +2576,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy,
                priv->scan_block = false;
 
        if (!mwifiex_stop_bg_scan(priv))
-               cfg80211_sched_scan_stopped_rtnl(priv->wdev.wiphy, 0);
+               cfg80211_sched_scan_stopped_locked(priv->wdev.wiphy, 0);
 
        user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL);
        if (!user_scan_cfg)
@@ -3081,7 +3081,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        mutex_init(&priv->async_mutex);
 
        /* Register network device */
-       if (register_netdevice(dev)) {
+       if (cfg80211_register_netdevice(dev)) {
                mwifiex_dbg(adapter, ERROR, "cannot register network device\n");
                ret = -EFAULT;
                goto err_reg_netdev;
@@ -3160,7 +3160,7 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
                netif_carrier_off(priv->netdev);
 
        if (wdev->netdev->reg_state == NETREG_REGISTERED)
-               unregister_netdevice(wdev->netdev);
+               cfg80211_unregister_netdevice(wdev->netdev);
 
        if (priv->dfs_cac_workqueue) {
                flush_workqueue(priv->dfs_cac_workqueue);
index ee52fb8..529dfd8 100644 (file)
@@ -598,12 +598,14 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
        }
 
        rtnl_lock();
+       wiphy_lock(adapter->wiphy);
        /* Create station interface by default */
        wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM,
                                        NL80211_IFTYPE_STATION, NULL);
        if (IS_ERR(wdev)) {
                mwifiex_dbg(adapter, ERROR,
                            "cannot create default STA interface\n");
+               wiphy_unlock(adapter->wiphy);
                rtnl_unlock();
                goto err_add_intf;
        }
@@ -614,6 +616,7 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
                if (IS_ERR(wdev)) {
                        mwifiex_dbg(adapter, ERROR,
                                    "cannot create AP interface\n");
+                       wiphy_unlock(adapter->wiphy);
                        rtnl_unlock();
                        goto err_add_intf;
                }
@@ -625,10 +628,12 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
                if (IS_ERR(wdev)) {
                        mwifiex_dbg(adapter, ERROR,
                                    "cannot create p2p client interface\n");
+                       wiphy_unlock(adapter->wiphy);
                        rtnl_unlock();
                        goto err_add_intf;
                }
        }
+       wiphy_unlock(adapter->wiphy);
        rtnl_unlock();
 
        mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
@@ -1440,9 +1445,11 @@ static void mwifiex_uninit_sw(struct mwifiex_adapter *adapter)
                if (!priv)
                        continue;
                rtnl_lock();
+               wiphy_lock(adapter->wiphy);
                if (priv->netdev &&
                    priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED)
                        mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev);
+               wiphy_unlock(adapter->wiphy);
                rtnl_unlock();
        }
 
index a44b776..c135478 100644 (file)
@@ -231,7 +231,7 @@ mt7615_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
                        int cmd, int *seq)
 {
        struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76);
-       enum mt76_txq_id qid;
+       enum mt76_mcuq_id qid;
 
        mt7615_mcu_fill_msg(dev, skb, cmd, seq);
        if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
index 13d77f8..9fb506f 100644 (file)
@@ -83,7 +83,7 @@ static int mt7663s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid,
 {
        struct mt76_queue *q = &dev->q_rx[qid];
        struct mt76_sdio *sdio = &dev->sdio;
-       int len = 0, err, i, order;
+       int len = 0, err, i;
        struct page *page;
        u8 *buf;
 
@@ -96,8 +96,7 @@ static int mt7663s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid,
        if (len > sdio->func->cur_blksize)
                len = roundup(len, sdio->func->cur_blksize);
 
-       order = get_order(len);
-       page = __dev_alloc_pages(GFP_KERNEL, order);
+       page = __dev_alloc_pages(GFP_KERNEL, get_order(len));
        if (!page)
                return -ENOMEM;
 
@@ -106,7 +105,7 @@ static int mt7663s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid,
        err = sdio_readsb(sdio->func, buf, MCR_WRDR(qid), len);
        if (err < 0) {
                dev_err(dev->dev, "sdio read data failed:%d\n", err);
-               __free_pages(page, order);
+               put_page(page);
                return err;
        }
 
@@ -123,7 +122,7 @@ static int mt7663s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid,
                if (q->queued + i + 1 == q->ndesc)
                        break;
        }
-       __free_pages(page, order);
+       put_page(page);
 
        spin_lock_bh(&q->lock);
        q->head = (q->head + i) % q->ndesc;
index ed4635b..102a8f1 100644 (file)
@@ -40,9 +40,9 @@ static const struct ieee80211_iface_limit if_limits[] = {
                .types = BIT(NL80211_IFTYPE_ADHOC)
        }, {
                .max = 16,
-               .types = BIT(NL80211_IFTYPE_AP) |
+               .types = BIT(NL80211_IFTYPE_AP)
 #ifdef CONFIG_MAC80211_MESH
-                        BIT(NL80211_IFTYPE_MESH_POINT)
+                        BIT(NL80211_IFTYPE_MESH_POINT)
 #endif
        }, {
                .max = MT7915_MAX_INTERFACES,
index 5fdd1a6..e211a2b 100644 (file)
@@ -256,7 +256,7 @@ mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
        struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
        struct mt7915_mcu_txd *mcu_txd;
        u8 seq, pkt_fmt, qidx;
-       enum mt76_txq_id txq;
+       enum mt76_mcuq_id qid;
        __le32 *txd;
        u32 val;
 
@@ -268,18 +268,18 @@ mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
                seq = ++dev->mt76.mcu.msg_seq & 0xf;
 
        if (cmd == -MCU_CMD_FW_SCATTER) {
-               txq = MT_MCUQ_FWDL;
+               qid = MT_MCUQ_FWDL;
                goto exit;
        }
 
        mcu_txd = (struct mt7915_mcu_txd *)skb_push(skb, sizeof(*mcu_txd));
 
        if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) {
-               txq = MT_MCUQ_WA;
+               qid = MT_MCUQ_WA;
                qidx = MT_TX_MCU_PORT_RX_Q0;
                pkt_fmt = MT_TX_TYPE_CMD;
        } else {
-               txq = MT_MCUQ_WM;
+               qid = MT_MCUQ_WM;
                qidx = MT_TX_MCU_PORT_RX_Q0;
                pkt_fmt = MT_TX_TYPE_CMD;
        }
@@ -326,7 +326,7 @@ exit:
        if (wait_seq)
                *wait_seq = seq;
 
-       return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0);
+       return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0);
 }
 
 static void
index 62b5b91..0b6facb 100644 (file)
@@ -157,10 +157,14 @@ static void mt76s_net_worker(struct mt76_worker *w)
 
 static int mt76s_process_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
 {
-       bool wake, mcu = q == dev->q_mcu[MT_MCUQ_WM];
        struct mt76_queue_entry entry;
        int nframes = 0;
+       bool mcu;
 
+       if (!q)
+               return 0;
+
+       mcu = q == dev->q_mcu[MT_MCUQ_WM];
        while (q->queued > 0) {
                if (!q->entry[q->tail].done)
                        break;
@@ -177,21 +181,12 @@ static int mt76s_process_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
                nframes++;
        }
 
-       wake = q->stopped && q->queued < q->ndesc - 8;
-       if (wake)
-               q->stopped = false;
-
        if (!q->queued)
                wake_up(&dev->tx_wait);
 
-       if (mcu)
-               goto out;
-
-       mt76_txq_schedule(&dev->phy, q->qid);
+       if (!mcu)
+               mt76_txq_schedule(&dev->phy, q->qid);
 
-       if (wake)
-               ieee80211_wake_queue(dev->hw, q->qid);
-out:
        return nframes;
 }
 
index dc85010..b95d093 100644 (file)
@@ -811,11 +811,12 @@ static void mt76u_status_worker(struct mt76_worker *w)
        struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
        struct mt76_queue_entry entry;
        struct mt76_queue *q;
-       bool wake;
        int i;
 
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                q = dev->phy.q_tx[i];
+               if (!q)
+                       continue;
 
                while (q->queued > 0) {
                        if (!q->entry[q->tail].done)
@@ -827,10 +828,6 @@ static void mt76u_status_worker(struct mt76_worker *w)
                        mt76_queue_tx_complete(dev, q, &entry);
                }
 
-               wake = q->stopped && q->queued < q->ndesc - 8;
-               if (wake)
-                       q->stopped = false;
-
                if (!q->queued)
                        wake_up(&dev->tx_wait);
 
@@ -839,8 +836,6 @@ static void mt76u_status_worker(struct mt76_worker *w)
                if (dev->drv->tx_status_data &&
                    !test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
                        queue_work(dev->wq, &dev->usb.stat_work);
-               if (wake)
-                       ieee80211_wake_queue(dev->hw, i);
        }
 }
 
index 5f99054..af7d1ec 100644 (file)
@@ -152,8 +152,7 @@ mt7601u_rx_process_entry(struct mt7601u_dev *dev, struct mt7601u_dma_buf_rx *e)
 
        if (new_p) {
                /* we have one extra ref from the allocator */
-               __free_pages(e->p, MT_RX_ORDER);
-
+               put_page(e->p);
                e->p = new_p;
        }
 }
@@ -310,7 +309,6 @@ static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev,
        }
 
        e = &q->e[q->end];
-       e->skb = skb;
        usb_fill_bulk_urb(e->urb, usb_dev, snd_pipe, skb->data, skb->len,
                          mt7601u_complete_tx, q);
        ret = usb_submit_urb(e->urb, GFP_ATOMIC);
@@ -328,6 +326,7 @@ static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev,
 
        q->end = (q->end + 1) % q->entries;
        q->used++;
+       e->skb = skb;
 
        if (q->used >= q->entries)
                ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb));
index e3dd205..96973ec 100644 (file)
@@ -1538,7 +1538,7 @@ static int del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
                wilc_wfi_deinit_mon_interface(wl, true);
        vif = netdev_priv(wdev->netdev);
        cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL);
-       unregister_netdevice(vif->ndev);
+       cfg80211_unregister_netdevice(vif->ndev);
        vif->monitor_flag = 0;
 
        wilc_set_operation_mode(vif, 0, 0, 0);
index b5a1b65..6bd6393 100644 (file)
@@ -233,7 +233,7 @@ struct net_device *wilc_wfi_init_mon_interface(struct wilc *wl,
        wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops;
        wl->monitor_dev->needs_free_netdev = true;
 
-       if (register_netdevice(wl->monitor_dev)) {
+       if (cfg80211_register_netdevice(wl->monitor_dev)) {
                netdev_err(real_dev, "register_netdevice failed\n");
                free_netdev(wl->monitor_dev);
                return NULL;
@@ -251,7 +251,7 @@ void wilc_wfi_deinit_mon_interface(struct wilc *wl, bool rtnl_locked)
                return;
 
        if (rtnl_locked)
-               unregister_netdevice(wl->monitor_dev);
+               cfg80211_unregister_netdevice(wl->monitor_dev);
        else
                unregister_netdev(wl->monitor_dev);
        wl->monitor_dev = NULL;
index 2a1fbbd..643cbb1 100644 (file)
@@ -950,7 +950,7 @@ struct wilc_vif *wilc_netdev_ifc_init(struct wilc *wl, const char *name,
        vif->priv.dev = ndev;
 
        if (rtnl_locked)
-               ret = register_netdevice(ndev);
+               ret = cfg80211_register_netdevice(ndev);
        else
                ret = register_netdev(ndev);
 
index 54cdf3a..504b4d0 100644 (file)
@@ -180,7 +180,7 @@ int qtnf_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
        cancel_work_sync(&vif->high_pri_tx_work);
 
        if (netdev->reg_state == NETREG_REGISTERED)
-               unregister_netdevice(netdev);
+               cfg80211_unregister_netdevice(netdev);
 
        if (qtnf_cmd_send_del_intf(vif))
                pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid,
@@ -267,7 +267,7 @@ static struct wireless_dev *qtnf_add_virtual_intf(struct wiphy *wiphy,
        if (qtnf_hwcap_is_set(&mac->bus->hw_info, QLINK_HW_CAPAB_HW_BRIDGE)) {
                ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex);
                if (ret) {
-                       unregister_netdevice(vif->netdev);
+                       cfg80211_unregister_netdevice(vif->netdev);
                        vif->netdev = NULL;
                        goto error_del_vif;
                }
index ad726bd..b4dd60b 100644 (file)
@@ -492,7 +492,7 @@ int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif,
 
        SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
 
-       ret = register_netdevice(dev);
+       ret = cfg80211_register_netdevice(dev);
        if (ret) {
                free_netdev(dev);
                vif->netdev = NULL;
@@ -611,8 +611,9 @@ static int qtnf_core_mac_attach(struct qtnf_bus *bus, unsigned int macid)
        mac->wiphy_registered = 1;
 
        rtnl_lock();
-
+       wiphy_lock(priv_to_wiphy(mac));
        ret = qtnf_core_net_attach(mac, vif, "wlan%d", NET_NAME_ENUM);
+       wiphy_unlock(priv_to_wiphy(mac));
        rtnl_unlock();
 
        if (ret) {
index a7259db..965bd95 100644 (file)
@@ -78,7 +78,6 @@ static void rtl_fw_do_work(const struct firmware *firmware, void *context,
 
        rtl_dbg(rtlpriv, COMP_ERR, DBG_LOUD,
                "Firmware callback routine entered!\n");
-       complete(&rtlpriv->firmware_loading_complete);
        if (!firmware) {
                if (rtlpriv->cfg->alt_fw_name) {
                        err = request_firmware(&firmware,
@@ -91,13 +90,13 @@ static void rtl_fw_do_work(const struct firmware *firmware, void *context,
                }
                pr_err("Selected firmware is not available\n");
                rtlpriv->max_fw_size = 0;
-               return;
+               goto exit;
        }
 found_alt:
        if (firmware->size > rtlpriv->max_fw_size) {
                pr_err("Firmware is too big!\n");
                release_firmware(firmware);
-               return;
+               goto exit;
        }
        if (!is_wow) {
                memcpy(rtlpriv->rtlhal.pfirmware, firmware->data,
@@ -109,6 +108,9 @@ found_alt:
                rtlpriv->rtlhal.wowlan_fwsize = firmware->size;
        }
        release_firmware(firmware);
+
+exit:
+       complete(&rtlpriv->firmware_loading_complete);
 }
 
 void rtl_fw_cb(const struct firmware *firmware, void *context)
index 8ee24e3..4a16d6e 100644 (file)
@@ -399,7 +399,8 @@ void xenvif_rx_queue_tail(struct xenvif_queue *queue, struct sk_buff *skb);
 void xenvif_carrier_on(struct xenvif *vif);
 
 /* Callback from stack when TX packet can be released */
-void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success);
+void xenvif_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *ubuf,
+                             bool zerocopy_success);
 
 /* Unmap a pending page and release it back to the guest */
 void xenvif_idx_unmap(struct xenvif_queue *queue, u16 pending_idx);
index acb786d..08b0e3d 100644 (file)
@@ -47,7 +47,7 @@
 /* Number of bytes allowed on the internal guest Rx queue. */
 #define XENVIF_RX_QUEUE_BYTES (XEN_NETIF_RX_RING_SIZE/2 * PAGE_SIZE)
 
-/* This function is used to set SKBTX_DEV_ZEROCOPY as well as
+/* This function is used to set SKBFL_ZEROCOPY_ENABLE as well as
  * increasing the inflight counter. We need to increase the inflight
  * counter because core driver calls into xenvif_zerocopy_callback
  * which calls xenvif_skb_zerocopy_complete.
@@ -55,7 +55,7 @@
 void xenvif_skb_zerocopy_prepare(struct xenvif_queue *queue,
                                 struct sk_buff *skb)
 {
-       skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;
+       skb_shinfo(skb)->flags |= SKBFL_ZEROCOPY_ENABLE;
        atomic_inc(&queue->inflight_packets);
 }
 
index bc3421d..19a27dc 100644 (file)
@@ -1091,7 +1091,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s
        uarg = skb_shinfo(skb)->destructor_arg;
        /* increase inflight counter to offset decrement in callback */
        atomic_inc(&queue->inflight_packets);
-       uarg->callback(uarg, true);
+       uarg->callback(NULL, uarg, true);
        skb_shinfo(skb)->destructor_arg = NULL;
 
        /* Fill the skb with the new (local) frags. */
@@ -1228,7 +1228,8 @@ static int xenvif_tx_submit(struct xenvif_queue *queue)
        return work_done;
 }
 
-void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success)
+void xenvif_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *ubuf,
+                             bool zerocopy_success)
 {
        unsigned long flags;
        pending_ring_idx_t index;
index 6f10e09..a5439c1 100644 (file)
@@ -411,7 +411,7 @@ static void read_xenbus_frontend_xdp(struct backend_info *be,
        vif->xdp_headroom = headroom;
 }
 
-/**
+/*
  * Callback received when the frontend's state changes.
  */
 static void frontend_changed(struct xenbus_device *dev,
@@ -996,7 +996,7 @@ static int netback_remove(struct xenbus_device *dev)
        return 0;
 }
 
-/**
+/*
  * Entry point to this code when a new device is created.  Allocate the basic
  * structures and switch to InitWait.
  */
index b01848e..6ef2adb 100644 (file)
@@ -864,12 +864,10 @@ static u32 xennet_run_xdp(struct netfront_queue *queue, struct page *pdata,
        u32 act;
        int err;
 
-       xdp->data_hard_start = page_address(pdata);
-       xdp->data = xdp->data_hard_start + XDP_PACKET_HEADROOM;
-       xdp_set_data_meta_invalid(xdp);
-       xdp->data_end = xdp->data + len;
-       xdp->rxq = &queue->xdp_rxq;
-       xdp->frame_sz = XEN_PAGE_SIZE - XDP_PACKET_HEADROOM;
+       xdp_init_buff(xdp, XEN_PAGE_SIZE - XDP_PACKET_HEADROOM,
+                     &queue->xdp_rxq);
+       xdp_prepare_buff(xdp, page_address(pdata), XDP_PACKET_HEADROOM,
+                        len, false);
 
        act = bpf_prog_run_xdp(prog, xdp);
        switch (act) {
@@ -1582,7 +1580,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev)
        return ERR_PTR(err);
 }
 
-/**
+/*
  * Entry point to this code when a new device is created.  Allocate the basic
  * structures and the ring buffers for communication with the backend, and
  * inform the backend of the appropriate details for those.
@@ -1659,7 +1657,7 @@ static void xennet_disconnect_backend(struct netfront_info *info)
        }
 }
 
-/**
+/*
  * We are reconnecting to the backend, due to a suspend/resume, or a backend
  * driver restart.  We tear down our netif structure and recreate it, but
  * leave the device-layer structures intact so that this is transparent to the
@@ -2305,7 +2303,7 @@ static int xennet_connect(struct net_device *dev)
        return 0;
 }
 
-/**
+/*
  * Callback received when the backend's state changes.
  */
 static void netback_changed(struct xenbus_device *dev,
index 75c65d3..288c6f1 100644 (file)
@@ -49,6 +49,17 @@ config NFC_PORT100
 
          If unsure, say N.
 
+config NFC_VIRTUAL_NCI
+       tristate "NCI device simulator driver"
+       depends on NFC_NCI
+       help
+         NCI virtual device simulates a NCI device to the user.
+         It can be used to validate the NCI module and applications.
+         This driver supports communication between the virtual NCI device and
+         module.
+
+         If unsure, say N.
+
 source "drivers/nfc/fdp/Kconfig"
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/pn533/Kconfig"
index 5393ba5..7b1bfde 100644 (file)
@@ -17,3 +17,4 @@ obj-$(CONFIG_NFC_ST_NCI)      += st-nci/
 obj-$(CONFIG_NFC_NXP_NCI)      += nxp-nci/
 obj-$(CONFIG_NFC_S3FWRN5)      += s3fwrn5/
 obj-$(CONFIG_NFC_ST95HF)       += st95hf/
+obj-$(CONFIG_NFC_VIRTUAL_NCI)  += virtual_ncidev.o
index ad0abb1..adaa1a7 100644 (file)
@@ -155,7 +155,7 @@ static int fdp_nci_i2c_read(struct fdp_i2c_phy *phy, struct sk_buff **skb)
 
                /*
                 * LRC check failed. This may due to transmission error or
-                * desynchronization between driver and FDP. Drop the paquet
+                * desynchronization between driver and FDP. Drop the packet
                 * and force resynchronization
                 */
                if (lrc) {
index c70f62f..3397802 100644 (file)
 
 /* Bits determining whether its a direct command or register R/W,
  * whether to use a continuous SPI transaction or not, and the actual
- * direct cmd opcode or regster address.
+ * direct cmd opcode or register address.
  */
 #define TRF7970A_CMD_BIT_CTRL                  BIT(7)
 #define TRF7970A_CMD_BIT_RW                    BIT(6)
diff --git a/drivers/nfc/virtual_ncidev.c b/drivers/nfc/virtual_ncidev.c
new file mode 100644 (file)
index 0000000..f73ee0b
--- /dev/null
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Virtual NCI device simulation driver
+ *
+ * Copyright (C) 2020 Samsung Electrnoics
+ * Bongsu Jeon <bongsu.jeon@samsung.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <net/nfc/nci_core.h>
+
+enum virtual_ncidev_mode {
+       virtual_ncidev_enabled,
+       virtual_ncidev_disabled,
+       virtual_ncidev_disabling,
+};
+
+#define IOCTL_GET_NCIDEV_IDX    0
+#define VIRTUAL_NFC_PROTOCOLS  (NFC_PROTO_JEWEL_MASK | \
+                                NFC_PROTO_MIFARE_MASK | \
+                                NFC_PROTO_FELICA_MASK | \
+                                NFC_PROTO_ISO14443_MASK | \
+                                NFC_PROTO_ISO14443_B_MASK | \
+                                NFC_PROTO_ISO15693_MASK)
+
+static enum virtual_ncidev_mode state;
+static struct miscdevice miscdev;
+static struct sk_buff *send_buff;
+static struct nci_dev *ndev;
+static DEFINE_MUTEX(nci_mutex);
+
+static int virtual_nci_open(struct nci_dev *ndev)
+{
+       return 0;
+}
+
+static int virtual_nci_close(struct nci_dev *ndev)
+{
+       mutex_lock(&nci_mutex);
+       kfree_skb(send_buff);
+       send_buff = NULL;
+       mutex_unlock(&nci_mutex);
+
+       return 0;
+}
+
+static int virtual_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+       mutex_lock(&nci_mutex);
+       if (state != virtual_ncidev_enabled) {
+               mutex_unlock(&nci_mutex);
+               return 0;
+       }
+
+       if (send_buff) {
+               mutex_unlock(&nci_mutex);
+               return -1;
+       }
+       send_buff = skb_copy(skb, GFP_KERNEL);
+       mutex_unlock(&nci_mutex);
+
+       return 0;
+}
+
+static struct nci_ops virtual_nci_ops = {
+       .open = virtual_nci_open,
+       .close = virtual_nci_close,
+       .send = virtual_nci_send
+};
+
+static ssize_t virtual_ncidev_read(struct file *file, char __user *buf,
+                                  size_t count, loff_t *ppos)
+{
+       size_t actual_len;
+
+       mutex_lock(&nci_mutex);
+       if (!send_buff) {
+               mutex_unlock(&nci_mutex);
+               return 0;
+       }
+
+       actual_len = min_t(size_t, count, send_buff->len);
+
+       if (copy_to_user(buf, send_buff->data, actual_len)) {
+               mutex_unlock(&nci_mutex);
+               return -EFAULT;
+       }
+
+       skb_pull(send_buff, actual_len);
+       if (send_buff->len == 0) {
+               consume_skb(send_buff);
+               send_buff = NULL;
+       }
+       mutex_unlock(&nci_mutex);
+
+       return actual_len;
+}
+
+static ssize_t virtual_ncidev_write(struct file *file,
+                                   const char __user *buf,
+                                   size_t count, loff_t *ppos)
+{
+       struct sk_buff *skb;
+
+       skb = alloc_skb(count, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       if (copy_from_user(skb_put(skb, count), buf, count)) {
+               kfree_skb(skb);
+               return -EFAULT;
+       }
+
+       nci_recv_frame(ndev, skb);
+       return count;
+}
+
+static int virtual_ncidev_open(struct inode *inode, struct file *file)
+{
+       int ret = 0;
+
+       mutex_lock(&nci_mutex);
+       if (state != virtual_ncidev_disabled) {
+               mutex_unlock(&nci_mutex);
+               return -EBUSY;
+       }
+
+       ndev = nci_allocate_device(&virtual_nci_ops, VIRTUAL_NFC_PROTOCOLS,
+                                  0, 0);
+       if (!ndev) {
+               mutex_unlock(&nci_mutex);
+               return -ENOMEM;
+       }
+
+       ret = nci_register_device(ndev);
+       if (ret < 0) {
+               nci_free_device(ndev);
+               mutex_unlock(&nci_mutex);
+               return ret;
+       }
+       state = virtual_ncidev_enabled;
+       mutex_unlock(&nci_mutex);
+
+       return 0;
+}
+
+static int virtual_ncidev_close(struct inode *inode, struct file *file)
+{
+       mutex_lock(&nci_mutex);
+
+       if (state == virtual_ncidev_enabled) {
+               state = virtual_ncidev_disabling;
+               mutex_unlock(&nci_mutex);
+
+               nci_unregister_device(ndev);
+               nci_free_device(ndev);
+
+               mutex_lock(&nci_mutex);
+       }
+
+       state = virtual_ncidev_disabled;
+       mutex_unlock(&nci_mutex);
+
+       return 0;
+}
+
+static long virtual_ncidev_ioctl(struct file *flip, unsigned int cmd,
+                                unsigned long arg)
+{
+       struct nfc_dev *nfc_dev = ndev->nfc_dev;
+       void __user *p = (void __user *)arg;
+
+       if (cmd != IOCTL_GET_NCIDEV_IDX)
+               return -ENOTTY;
+
+       if (copy_to_user(p, &nfc_dev->idx, sizeof(nfc_dev->idx)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static const struct file_operations virtual_ncidev_fops = {
+       .owner = THIS_MODULE,
+       .read = virtual_ncidev_read,
+       .write = virtual_ncidev_write,
+       .open = virtual_ncidev_open,
+       .release = virtual_ncidev_close,
+       .unlocked_ioctl = virtual_ncidev_ioctl
+};
+
+static int __init virtual_ncidev_init(void)
+{
+       state = virtual_ncidev_disabled;
+       miscdev.minor = MISC_DYNAMIC_MINOR;
+       miscdev.name = "virtual_nci";
+       miscdev.fops = &virtual_ncidev_fops;
+       miscdev.mode = S_IALLUGO;
+
+       return misc_register(&miscdev);
+}
+
+static void __exit virtual_ncidev_exit(void)
+{
+       misc_deregister(&miscdev);
+}
+
+module_init(virtual_ncidev_init);
+module_exit(virtual_ncidev_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Virtual NCI device simulation driver");
+MODULE_AUTHOR("Bongsu Jeon <bongsu.jeon@samsung.com>");
index ce1b615..8caf9b3 100644 (file)
@@ -179,7 +179,7 @@ int nvme_reset_ctrl(struct nvme_ctrl *ctrl)
 }
 EXPORT_SYMBOL_GPL(nvme_reset_ctrl);
 
-int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl)
+static int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl)
 {
        int ret;
 
@@ -192,7 +192,6 @@ int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl)
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(nvme_reset_ctrl_sync);
 
 static void nvme_do_delete_ctrl(struct nvme_ctrl *ctrl)
 {
@@ -331,7 +330,7 @@ static inline void nvme_end_req(struct request *req)
                req->__sector = nvme_lba_to_sect(req->q->queuedata,
                        le64_to_cpu(nvme_req(req)->result.u64));
 
-       nvme_trace_bio_complete(req, status);
+       nvme_trace_bio_complete(req);
        blk_mq_end_request(req, status);
 }
 
@@ -578,7 +577,7 @@ struct request *nvme_alloc_request(struct request_queue *q,
 }
 EXPORT_SYMBOL_GPL(nvme_alloc_request);
 
-struct request *nvme_alloc_request_qid(struct request_queue *q,
+static struct request *nvme_alloc_request_qid(struct request_queue *q,
                struct nvme_command *cmd, blk_mq_req_flags_t flags, int qid)
 {
        struct request *req;
@@ -589,7 +588,6 @@ struct request *nvme_alloc_request_qid(struct request_queue *q,
                nvme_init_request(req, cmd);
        return req;
 }
-EXPORT_SYMBOL_GPL(nvme_alloc_request_qid);
 
 static int nvme_toggle_streams(struct nvme_ctrl *ctrl, bool enable)
 {
@@ -1545,8 +1543,21 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
        }
 
        length = (io.nblocks + 1) << ns->lba_shift;
-       meta_len = (io.nblocks + 1) * ns->ms;
-       metadata = nvme_to_user_ptr(io.metadata);
+
+       if ((io.control & NVME_RW_PRINFO_PRACT) &&
+           ns->ms == sizeof(struct t10_pi_tuple)) {
+               /*
+                * Protection information is stripped/inserted by the
+                * controller.
+                */
+               if (nvme_to_user_ptr(io.metadata))
+                       return -EINVAL;
+               meta_len = 0;
+               metadata = NULL;
+       } else {
+               meta_len = (io.nblocks + 1) * ns->ms;
+               metadata = nvme_to_user_ptr(io.metadata);
+       }
 
        if (ns->features & NVME_NS_EXT_LBAS) {
                length += meta_len;
@@ -2858,6 +2869,11 @@ static const struct attribute_group *nvme_subsys_attrs_groups[] = {
        NULL,
 };
 
+static inline bool nvme_discovery_ctrl(struct nvme_ctrl *ctrl)
+{
+       return ctrl->opts && ctrl->opts->discovery_nqn;
+}
+
 static bool nvme_validate_cntlid(struct nvme_subsystem *subsys,
                struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
 {
@@ -2877,7 +2893,7 @@ static bool nvme_validate_cntlid(struct nvme_subsystem *subsys,
                }
 
                if ((id->cmic & NVME_CTRL_CMIC_MULTI_CTRL) ||
-                   (ctrl->opts && ctrl->opts->discovery_nqn))
+                   nvme_discovery_ctrl(ctrl))
                        continue;
 
                dev_err(ctrl->device,
@@ -3146,7 +3162,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
                        goto out_free;
                }
 
-               if (!ctrl->opts->discovery_nqn && !ctrl->kas) {
+               if (!nvme_discovery_ctrl(ctrl) && !ctrl->kas) {
                        dev_err(ctrl->device,
                                "keep-alive support is mandatory for fabrics\n");
                        ret = -EINVAL;
@@ -3186,7 +3202,7 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
        if (ret < 0)
                return ret;
 
-       if (!ctrl->identified) {
+       if (!ctrl->identified && !nvme_discovery_ctrl(ctrl)) {
                ret = nvme_hwmon_init(ctrl);
                if (ret < 0)
                        return ret;
index 38373a0..5f36cfa 100644 (file)
@@ -166,6 +166,7 @@ struct nvme_fc_ctrl {
        struct blk_mq_tag_set   admin_tag_set;
        struct blk_mq_tag_set   tag_set;
 
+       struct work_struct      ioerr_work;
        struct delayed_work     connect_work;
 
        struct kref             ref;
@@ -1888,6 +1889,15 @@ __nvme_fc_fcpop_chk_teardowns(struct nvme_fc_ctrl *ctrl,
        }
 }
 
+static void
+nvme_fc_ctrl_ioerr_work(struct work_struct *work)
+{
+       struct nvme_fc_ctrl *ctrl =
+                       container_of(work, struct nvme_fc_ctrl, ioerr_work);
+
+       nvme_fc_error_recovery(ctrl, "transport detected io error");
+}
+
 static void
 nvme_fc_fcpio_done(struct nvmefc_fcp_req *req)
 {
@@ -2046,7 +2056,7 @@ done:
 
 check_error:
        if (terminate_assoc)
-               nvme_fc_error_recovery(ctrl, "transport detected io error");
+               queue_work(nvme_reset_wq, &ctrl->ioerr_work);
 }
 
 static int
@@ -3233,6 +3243,7 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
 {
        struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
 
+       cancel_work_sync(&ctrl->ioerr_work);
        cancel_delayed_work_sync(&ctrl->connect_work);
        /*
         * kill the association on the link side.  this will block
@@ -3449,6 +3460,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
 
        INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work);
        INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work);
+       INIT_WORK(&ctrl->ioerr_work, nvme_fc_ctrl_ioerr_work);
        spin_lock_init(&ctrl->lock);
 
        /* io queue count */
@@ -3540,6 +3552,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
 
 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);
 
index 7e49f61..88a6b97 100644 (file)
@@ -610,8 +610,6 @@ void nvme_start_freeze(struct nvme_ctrl *ctrl);
 #define NVME_QID_ANY -1
 struct request *nvme_alloc_request(struct request_queue *q,
                struct nvme_command *cmd, blk_mq_req_flags_t flags);
-struct request *nvme_alloc_request_qid(struct request_queue *q,
-               struct nvme_command *cmd, blk_mq_req_flags_t flags, int qid);
 void nvme_cleanup_cmd(struct request *req);
 blk_status_t nvme_setup_cmd(struct nvme_ns *ns, struct request *req,
                struct nvme_command *cmd);
@@ -630,7 +628,6 @@ int nvme_get_features(struct nvme_ctrl *dev, unsigned int fid,
 int nvme_set_queue_count(struct nvme_ctrl *ctrl, int *count);
 void nvme_stop_keep_alive(struct nvme_ctrl *ctrl);
 int nvme_reset_ctrl(struct nvme_ctrl *ctrl);
-int nvme_reset_ctrl_sync(struct nvme_ctrl *ctrl);
 int nvme_try_sched_reset(struct nvme_ctrl *ctrl);
 int nvme_delete_ctrl(struct nvme_ctrl *ctrl);
 
@@ -675,8 +672,7 @@ static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
                kblockd_schedule_work(&head->requeue_work);
 }
 
-static inline void nvme_trace_bio_complete(struct request *req,
-        blk_status_t status)
+static inline void nvme_trace_bio_complete(struct request *req)
 {
        struct nvme_ns *ns = req->q->queuedata;
 
@@ -731,8 +727,7 @@ static inline void nvme_mpath_clear_ctrl_paths(struct nvme_ctrl *ctrl)
 static inline void nvme_mpath_check_last_path(struct nvme_ns *ns)
 {
 }
-static inline void nvme_trace_bio_complete(struct request *req,
-        blk_status_t status)
+static inline void nvme_trace_bio_complete(struct request *req)
 {
 }
 static inline int nvme_mpath_init(struct nvme_ctrl *ctrl,
index b4385cb..856aa31 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/t10-pi.h>
 #include <linux/types.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/io-64-nonatomic-hi-lo.h>
 #include <linux/sed-opal.h>
 #include <linux/pci-p2pdma.h>
 
@@ -542,50 +543,71 @@ static inline bool nvme_pci_use_sgls(struct nvme_dev *dev, struct request *req)
        return true;
 }
 
-static void nvme_unmap_data(struct nvme_dev *dev, struct request *req)
+static void nvme_free_prps(struct nvme_dev *dev, struct request *req)
 {
-       struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
        const int last_prp = NVME_CTRL_PAGE_SIZE / sizeof(__le64) - 1;
-       dma_addr_t dma_addr = iod->first_dma, next_dma_addr;
+       struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+       dma_addr_t dma_addr = iod->first_dma;
        int i;
 
-       if (iod->dma_len) {
-               dma_unmap_page(dev->dev, dma_addr, iod->dma_len,
-                              rq_dma_dir(req));
-               return;
+       for (i = 0; i < iod->npages; i++) {
+               __le64 *prp_list = nvme_pci_iod_list(req)[i];
+               dma_addr_t next_dma_addr = le64_to_cpu(prp_list[last_prp]);
+
+               dma_pool_free(dev->prp_page_pool, prp_list, dma_addr);
+               dma_addr = next_dma_addr;
        }
 
-       WARN_ON_ONCE(!iod->nents);
+}
 
-       if (is_pci_p2pdma_page(sg_page(iod->sg)))
-               pci_p2pdma_unmap_sg(dev->dev, iod->sg, iod->nents,
-                                   rq_dma_dir(req));
-       else
-               dma_unmap_sg(dev->dev, iod->sg, iod->nents, rq_dma_dir(req));
+static void nvme_free_sgls(struct nvme_dev *dev, struct request *req)
+{
+       const int last_sg = SGES_PER_PAGE - 1;
+       struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
+       dma_addr_t dma_addr = iod->first_dma;
+       int i;
 
+       for (i = 0; i < iod->npages; i++) {
+               struct nvme_sgl_desc *sg_list = nvme_pci_iod_list(req)[i];
+               dma_addr_t next_dma_addr = le64_to_cpu((sg_list[last_sg]).addr);
 
-       if (iod->npages == 0)
-               dma_pool_free(dev->prp_small_pool, nvme_pci_iod_list(req)[0],
-                       dma_addr);
+               dma_pool_free(dev->prp_page_pool, sg_list, dma_addr);
+               dma_addr = next_dma_addr;
+       }
 
-       for (i = 0; i < iod->npages; i++) {
-               void *addr = nvme_pci_iod_list(req)[i];
+}
 
-               if (iod->use_sgl) {
-                       struct nvme_sgl_desc *sg_list = addr;
+static void nvme_unmap_sg(struct nvme_dev *dev, struct request *req)
+{
+       struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
 
-                       next_dma_addr =
-                           le64_to_cpu((sg_list[SGES_PER_PAGE - 1]).addr);
-               } else {
-                       __le64 *prp_list = addr;
+       if (is_pci_p2pdma_page(sg_page(iod->sg)))
+               pci_p2pdma_unmap_sg(dev->dev, iod->sg, iod->nents,
+                                   rq_dma_dir(req));
+       else
+               dma_unmap_sg(dev->dev, iod->sg, iod->nents, rq_dma_dir(req));
+}
 
-                       next_dma_addr = le64_to_cpu(prp_list[last_prp]);
-               }
+static void nvme_unmap_data(struct nvme_dev *dev, struct request *req)
+{
+       struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
 
-               dma_pool_free(dev->prp_page_pool, addr, dma_addr);
-               dma_addr = next_dma_addr;
+       if (iod->dma_len) {
+               dma_unmap_page(dev->dev, iod->first_dma, iod->dma_len,
+                              rq_dma_dir(req));
+               return;
        }
 
+       WARN_ON_ONCE(!iod->nents);
+
+       nvme_unmap_sg(dev, req);
+       if (iod->npages == 0)
+               dma_pool_free(dev->prp_small_pool, nvme_pci_iod_list(req)[0],
+                             iod->first_dma);
+       else if (iod->use_sgl)
+               nvme_free_sgls(dev, req);
+       else
+               nvme_free_prps(dev, req);
        mempool_free(iod->sg, dev->iod_mempool);
 }
 
@@ -661,7 +683,7 @@ static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev,
                        __le64 *old_prp_list = prp_list;
                        prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma);
                        if (!prp_list)
-                               return BLK_STS_RESOURCE;
+                               goto free_prps;
                        list[iod->npages++] = prp_list;
                        prp_list[0] = old_prp_list[i - 1];
                        old_prp_list[i - 1] = cpu_to_le64(prp_dma);
@@ -681,14 +703,14 @@ static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev,
                dma_addr = sg_dma_address(sg);
                dma_len = sg_dma_len(sg);
        }
-
 done:
        cmnd->dptr.prp1 = cpu_to_le64(sg_dma_address(iod->sg));
        cmnd->dptr.prp2 = cpu_to_le64(iod->first_dma);
-
        return BLK_STS_OK;
-
- bad_sgl:
+free_prps:
+       nvme_free_prps(dev, req);
+       return BLK_STS_RESOURCE;
+bad_sgl:
        WARN(DO_ONCE(nvme_print_sgl, iod->sg, iod->nents),
                        "Invalid SGL for payload:%d nents:%d\n",
                        blk_rq_payload_bytes(req), iod->nents);
@@ -760,7 +782,7 @@ static blk_status_t nvme_pci_setup_sgls(struct nvme_dev *dev,
 
                        sg_list = dma_pool_alloc(pool, GFP_ATOMIC, &sgl_dma);
                        if (!sg_list)
-                               return BLK_STS_RESOURCE;
+                               goto free_sgls;
 
                        i = 0;
                        nvme_pci_iod_list(req)[iod->npages++] = sg_list;
@@ -773,6 +795,9 @@ static blk_status_t nvme_pci_setup_sgls(struct nvme_dev *dev,
        } while (--entries > 0);
 
        return BLK_STS_OK;
+free_sgls:
+       nvme_free_sgls(dev, req);
+       return BLK_STS_RESOURCE;
 }
 
 static blk_status_t nvme_setup_prp_simple(struct nvme_dev *dev,
@@ -841,7 +866,7 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
        sg_init_table(iod->sg, blk_rq_nr_phys_segments(req));
        iod->nents = blk_rq_map_sg(req->q, req, iod->sg);
        if (!iod->nents)
-               goto out;
+               goto out_free_sg;
 
        if (is_pci_p2pdma_page(sg_page(iod->sg)))
                nr_mapped = pci_p2pdma_map_sg_attrs(dev->dev, iod->sg,
@@ -850,16 +875,21 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req,
                nr_mapped = dma_map_sg_attrs(dev->dev, iod->sg, iod->nents,
                                             rq_dma_dir(req), DMA_ATTR_NO_WARN);
        if (!nr_mapped)
-               goto out;
+               goto out_free_sg;
 
        iod->use_sgl = nvme_pci_use_sgls(dev, req);
        if (iod->use_sgl)
                ret = nvme_pci_setup_sgls(dev, req, &cmnd->rw, nr_mapped);
        else
                ret = nvme_pci_setup_prps(dev, req, &cmnd->rw);
-out:
        if (ret != BLK_STS_OK)
-               nvme_unmap_data(dev, req);
+               goto out_unmap_sg;
+       return BLK_STS_OK;
+
+out_unmap_sg:
+       nvme_unmap_sg(dev, req);
+out_free_sg:
+       mempool_free(iod->sg, dev->iod_mempool);
        return ret;
 }
 
@@ -967,6 +997,7 @@ static inline struct blk_mq_tags *nvme_queue_tagset(struct nvme_queue *nvmeq)
 static inline void nvme_handle_cqe(struct nvme_queue *nvmeq, u16 idx)
 {
        struct nvme_completion *cqe = &nvmeq->cqes[idx];
+       __u16 command_id = READ_ONCE(cqe->command_id);
        struct request *req;
 
        /*
@@ -975,17 +1006,17 @@ static inline void nvme_handle_cqe(struct nvme_queue *nvmeq, u16 idx)
         * aborts.  We don't even bother to allocate a struct request
         * for them but rather special case them here.
         */
-       if (unlikely(nvme_is_aen_req(nvmeq->qid, cqe->command_id))) {
+       if (unlikely(nvme_is_aen_req(nvmeq->qid, command_id))) {
                nvme_complete_async_event(&nvmeq->dev->ctrl,
                                cqe->status, &cqe->result);
                return;
        }
 
-       req = blk_mq_tag_to_rq(nvme_queue_tagset(nvmeq), cqe->command_id);
+       req = blk_mq_tag_to_rq(nvme_queue_tagset(nvmeq), command_id);
        if (unlikely(!req)) {
                dev_warn(nvmeq->dev->ctrl.device,
                        "invalid id %d completed on queue %d\n",
-                       cqe->command_id, le16_to_cpu(cqe->sq_id));
+                       command_id, le16_to_cpu(cqe->sq_id));
                return;
        }
 
@@ -1794,6 +1825,9 @@ static void nvme_map_cmb(struct nvme_dev *dev)
        if (dev->cmb_size)
                return;
 
+       if (NVME_CAP_CMBS(dev->ctrl.cap))
+               writel(NVME_CMBMSC_CRE, dev->bar + NVME_REG_CMBMSC);
+
        dev->cmbsz = readl(dev->bar + NVME_REG_CMBSZ);
        if (!dev->cmbsz)
                return;
@@ -1807,6 +1841,16 @@ static void nvme_map_cmb(struct nvme_dev *dev)
        if (offset > bar_size)
                return;
 
+       /*
+        * Tell the controller about the host side address mapping the CMB,
+        * and enable CMB decoding for the NVMe 1.4+ scheme:
+        */
+       if (NVME_CAP_CMBS(dev->ctrl.cap)) {
+               hi_lo_writeq(NVME_CMBMSC_CRE | NVME_CMBMSC_CMSE |
+                            (pci_bus_address(pdev, bar) + offset),
+                            dev->bar + NVME_REG_CMBMSC);
+       }
+
        /*
         * Controllers may support a CMB size larger than their BAR,
         * for example, due to being behind a bridge. Reduce the CMB to
@@ -3196,7 +3240,8 @@ static const struct pci_device_id nvme_id_table[] = {
        { PCI_DEVICE(0x144d, 0xa821),   /* Samsung PM1725 */
                .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, },
        { PCI_DEVICE(0x144d, 0xa822),   /* Samsung PM1725a */
-               .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY, },
+               .driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY |
+                               NVME_QUIRK_IGNORE_DEV_SUBNQN, },
        { PCI_DEVICE(0x1d1d, 0x1f1f),   /* LighNVM qemu device */
                .driver_data = NVME_QUIRK_LIGHTNVM, },
        { PCI_DEVICE(0x1d1d, 0x2807),   /* CNEX WL */
index cf6c49d..b7ce4f2 100644 (file)
@@ -97,6 +97,7 @@ struct nvme_rdma_queue {
        struct completion       cm_done;
        bool                    pi_support;
        int                     cq_size;
+       struct mutex            queue_lock;
 };
 
 struct nvme_rdma_ctrl {
@@ -579,6 +580,7 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl,
        int ret;
 
        queue = &ctrl->queues[idx];
+       mutex_init(&queue->queue_lock);
        queue->ctrl = ctrl;
        if (idx && ctrl->ctrl.max_integrity_segments)
                queue->pi_support = true;
@@ -598,7 +600,8 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl,
        if (IS_ERR(queue->cm_id)) {
                dev_info(ctrl->ctrl.device,
                        "failed to create CM ID: %ld\n", PTR_ERR(queue->cm_id));
-               return PTR_ERR(queue->cm_id);
+               ret = PTR_ERR(queue->cm_id);
+               goto out_destroy_mutex;
        }
 
        if (ctrl->ctrl.opts->mask & NVMF_OPT_HOST_TRADDR)
@@ -628,6 +631,8 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl,
 out_destroy_cm_id:
        rdma_destroy_id(queue->cm_id);
        nvme_rdma_destroy_queue_ib(queue);
+out_destroy_mutex:
+       mutex_destroy(&queue->queue_lock);
        return ret;
 }
 
@@ -639,9 +644,10 @@ static void __nvme_rdma_stop_queue(struct nvme_rdma_queue *queue)
 
 static void nvme_rdma_stop_queue(struct nvme_rdma_queue *queue)
 {
-       if (!test_and_clear_bit(NVME_RDMA_Q_LIVE, &queue->flags))
-               return;
-       __nvme_rdma_stop_queue(queue);
+       mutex_lock(&queue->queue_lock);
+       if (test_and_clear_bit(NVME_RDMA_Q_LIVE, &queue->flags))
+               __nvme_rdma_stop_queue(queue);
+       mutex_unlock(&queue->queue_lock);
 }
 
 static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue)
@@ -651,6 +657,7 @@ static void nvme_rdma_free_queue(struct nvme_rdma_queue *queue)
 
        nvme_rdma_destroy_queue_ib(queue);
        rdma_destroy_id(queue->cm_id);
+       mutex_destroy(&queue->queue_lock);
 }
 
 static void nvme_rdma_free_io_queues(struct nvme_rdma_ctrl *ctrl)
index 1ba6599..881d28e 100644 (file)
@@ -76,6 +76,7 @@ struct nvme_tcp_queue {
        struct work_struct      io_work;
        int                     io_cpu;
 
+       struct mutex            queue_lock;
        struct mutex            send_mutex;
        struct llist_head       req_list;
        struct list_head        send_list;
@@ -201,7 +202,7 @@ static inline size_t nvme_tcp_req_cur_offset(struct nvme_tcp_request *req)
 
 static inline size_t nvme_tcp_req_cur_length(struct nvme_tcp_request *req)
 {
-       return min_t(size_t, req->iter.bvec->bv_len - req->iter.iov_offset,
+       return min_t(size_t, iov_iter_single_seg_count(&req->iter),
                        req->pdu_len - req->pdu_sent);
 }
 
@@ -262,6 +263,16 @@ static inline void nvme_tcp_advance_req(struct nvme_tcp_request *req,
        }
 }
 
+static inline void nvme_tcp_send_all(struct nvme_tcp_queue *queue)
+{
+       int ret;
+
+       /* drain the send queue as much as we can... */
+       do {
+               ret = nvme_tcp_try_send(queue);
+       } while (ret > 0);
+}
+
 static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,
                bool sync, bool last)
 {
@@ -276,10 +287,10 @@ static inline void nvme_tcp_queue_request(struct nvme_tcp_request *req,
         * directly, otherwise queue io_work. Also, only do that if we
         * are on the same cpu, so we don't introduce contention.
         */
-       if (queue->io_cpu == smp_processor_id() &&
+       if (queue->io_cpu == __smp_processor_id() &&
            sync && empty && mutex_trylock(&queue->send_mutex)) {
                queue->more_requests = !last;
-               nvme_tcp_try_send(queue);
+               nvme_tcp_send_all(queue);
                queue->more_requests = false;
                mutex_unlock(&queue->send_mutex);
        } else if (last) {
@@ -1209,6 +1220,7 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, int qid)
 
        sock_release(queue->sock);
        kfree(queue->pdu);
+       mutex_destroy(&queue->queue_lock);
 }
 
 static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue)
@@ -1370,6 +1382,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl,
        struct nvme_tcp_queue *queue = &ctrl->queues[qid];
        int ret, rcv_pdu_size;
 
+       mutex_init(&queue->queue_lock);
        queue->ctrl = ctrl;
        init_llist_head(&queue->req_list);
        INIT_LIST_HEAD(&queue->send_list);
@@ -1388,7 +1401,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl,
        if (ret) {
                dev_err(nctrl->device,
                        "failed to create socket: %d\n", ret);
-               return ret;
+               goto err_destroy_mutex;
        }
 
        /* Single syn retry */
@@ -1497,6 +1510,8 @@ err_crypto:
 err_sock:
        sock_release(queue->sock);
        queue->sock = NULL;
+err_destroy_mutex:
+       mutex_destroy(&queue->queue_lock);
        return ret;
 }
 
@@ -1524,9 +1539,10 @@ static void nvme_tcp_stop_queue(struct nvme_ctrl *nctrl, int qid)
        struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
        struct nvme_tcp_queue *queue = &ctrl->queues[qid];
 
-       if (!test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags))
-               return;
-       __nvme_tcp_stop_queue(queue);
+       mutex_lock(&queue->queue_lock);
+       if (test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags))
+               __nvme_tcp_stop_queue(queue);
+       mutex_unlock(&queue->queue_lock);
 }
 
 static int nvme_tcp_start_queue(struct nvme_ctrl *nctrl, int idx)
index 8d90235..dc1ea46 100644 (file)
@@ -487,8 +487,10 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
 
        /* return an all zeroed buffer if we can't find an active namespace */
        ns = nvmet_find_namespace(ctrl, req->cmd->identify.nsid);
-       if (!ns)
+       if (!ns) {
+               status = NVME_SC_INVALID_NS;
                goto done;
+       }
 
        nvmet_ns_revalidate(ns);
 
@@ -541,7 +543,9 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
                id->nsattr |= (1 << 0);
        nvmet_put_namespace(ns);
 done:
-       status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id));
+       if (!status)
+               status = nvmet_copy_to_sgl(req, 0, id, sizeof(*id));
+
        kfree(id);
 out:
        nvmet_req_complete(req, status);
index 733d936..68213f0 100644 (file)
@@ -1501,7 +1501,8 @@ static ssize_t
 fcloop_set_cmd_drop(struct device *dev, struct device_attribute *attr,
                const char *buf, size_t count)
 {
-       int opcode, starting, amount;
+       unsigned int opcode;
+       int starting, amount;
 
        if (sscanf(buf, "%x:%d:%d", &opcode, &starting, &amount) != 3)
                return -EBADRQC;
@@ -1588,8 +1589,8 @@ out_destroy_class:
 
 static void __exit fcloop_exit(void)
 {
-       struct fcloop_lport *lport;
-       struct fcloop_nport *nport;
+       struct fcloop_lport *lport = NULL;
+       struct fcloop_nport *nport = NULL;
        struct fcloop_tport *tport;
        struct fcloop_rport *rport;
        unsigned long flags;
index 5c1e7cb..06b6b74 100644 (file)
@@ -1220,6 +1220,14 @@ nvmet_rdma_find_get_device(struct rdma_cm_id *cm_id)
        }
        ndev->inline_data_size = nport->inline_data_size;
        ndev->inline_page_count = inline_page_count;
+
+       if (nport->pi_enable && !(cm_id->device->attrs.device_cap_flags &
+                                 IB_DEVICE_INTEGRITY_HANDOVER)) {
+               pr_warn("T10-PI is not supported by device %s. Disabling it\n",
+                       cm_id->device->name);
+               nport->pi_enable = false;
+       }
+
        ndev->device = cm_id->device;
        kref_init(&ndev->ref);
 
@@ -1641,6 +1649,16 @@ static void __nvmet_rdma_queue_disconnect(struct nvmet_rdma_queue *queue)
        spin_lock_irqsave(&queue->state_lock, flags);
        switch (queue->state) {
        case NVMET_RDMA_Q_CONNECTING:
+               while (!list_empty(&queue->rsp_wait_list)) {
+                       struct nvmet_rdma_rsp *rsp;
+
+                       rsp = list_first_entry(&queue->rsp_wait_list,
+                                              struct nvmet_rdma_rsp,
+                                              wait_list);
+                       list_del(&rsp->wait_list);
+                       nvmet_rdma_put_rsp(rsp);
+               }
+               fallthrough;
        case NVMET_RDMA_Q_LIVE:
                queue->state = NVMET_RDMA_Q_DISCONNECTING;
                disconnect = true;
@@ -1845,14 +1863,6 @@ static int nvmet_rdma_enable_port(struct nvmet_rdma_port *port)
                goto out_destroy_id;
        }
 
-       if (port->nport->pi_enable &&
-           !(cm_id->device->attrs.device_cap_flags &
-             IB_DEVICE_INTEGRITY_HANDOVER)) {
-               pr_err("T10-PI is not supported for %pISpcs\n", addr);
-               ret = -EINVAL;
-               goto out_destroy_id;
-       }
-
        port->cm_id = cm_id;
        return 0;
 
index 4268eb3..8c905aa 100644 (file)
@@ -1092,7 +1092,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
        if (IS_ERR(opp_table->clk)) {
                ret = PTR_ERR(opp_table->clk);
                if (ret == -EPROBE_DEFER)
-                       goto err;
+                       goto remove_opp_dev;
 
                dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret);
        }
@@ -1101,7 +1101,7 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
        ret = dev_pm_opp_of_find_icc_paths(dev, opp_table);
        if (ret) {
                if (ret == -EPROBE_DEFER)
-                       goto err;
+                       goto put_clk;
 
                dev_warn(dev, "%s: Error finding interconnect paths: %d\n",
                         __func__, ret);
@@ -1113,6 +1113,11 @@ static struct opp_table *_allocate_opp_table(struct device *dev, int index)
 
        return opp_table;
 
+put_clk:
+       if (!IS_ERR(opp_table->clk))
+               clk_put(opp_table->clk);
+remove_opp_dev:
+       _remove_opp_dev(opp_dev, opp_table);
 err:
        kfree(opp_table);
        return ERR_PTR(ret);
index 794a37d..cb2f55f 100644 (file)
@@ -726,11 +726,6 @@ static int armpmu_get_cpu_irq(struct arm_pmu *pmu, int cpu)
        return per_cpu(hw_events->irq, cpu);
 }
 
-bool arm_pmu_irq_is_nmi(void)
-{
-       return has_nmi;
-}
-
 /*
  * PMU hardware loses all context when a CPU goes offline.
  * When a CPU is hotplugged back in, since some hardware registers are
index 65d5ea0..1cb158d 100644 (file)
@@ -1,2 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-y          += phy-ingenic-usb.o
+obj-$(CONFIG_PHY_INGENIC_USB)          += phy-ingenic-usb.o
index d38def4..55f8e6c 100644 (file)
@@ -49,7 +49,9 @@ config PHY_MTK_HDMI
 
 config PHY_MTK_MIPI_DSI
        tristate "MediaTek MIPI-DSI Driver"
-       depends on ARCH_MEDIATEK && OF
+       depends on ARCH_MEDIATEK || COMPILE_TEST
+       depends on COMMON_CLK
+       depends on OF
        select GENERIC_PHY
        help
          Support MIPI DSI for Mediatek SoCs.
index 442522b..4728e2b 100644 (file)
@@ -662,35 +662,42 @@ static int cpcap_usb_phy_probe(struct platform_device *pdev)
        generic_phy = devm_phy_create(ddata->dev, NULL, &ops);
        if (IS_ERR(generic_phy)) {
                error = PTR_ERR(generic_phy);
-               return PTR_ERR(generic_phy);
+               goto out_reg_disable;
        }
 
        phy_set_drvdata(generic_phy, ddata);
 
        phy_provider = devm_of_phy_provider_register(ddata->dev,
                                                     of_phy_simple_xlate);
-       if (IS_ERR(phy_provider))
-               return PTR_ERR(phy_provider);
+       if (IS_ERR(phy_provider)) {
+               error = PTR_ERR(phy_provider);
+               goto out_reg_disable;
+       }
 
        error = cpcap_usb_init_optional_pins(ddata);
        if (error)
-               return error;
+               goto out_reg_disable;
 
        cpcap_usb_init_optional_gpios(ddata);
 
        error = cpcap_usb_init_iio(ddata);
        if (error)
-               return error;
+               goto out_reg_disable;
 
        error = cpcap_usb_init_interrupts(pdev, ddata);
        if (error)
-               return error;
+               goto out_reg_disable;
 
        usb_add_phy_dev(&ddata->phy);
        atomic_set(&ddata->active, 1);
        schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1));
 
        return 0;
+
+out_reg_disable:
+       regulator_disable(ddata->vusb);
+
+       return error;
 }
 
 static int cpcap_usb_phy_remove(struct platform_device *pdev)
index 34803a6..5c1a109 100644 (file)
@@ -347,7 +347,7 @@ FUNC_GROUP_DECL(RMII4, F24, E23, E24, E25, C25, C24, B26, B25, B24);
 
 #define D22 40
 SIG_EXPR_LIST_DECL_SESG(D22, SD1CLK, SD1, SIG_DESC_SET(SCU414, 8));
-SIG_EXPR_LIST_DECL_SEMG(D22, PWM8, PWM8G0, PWM8, SIG_DESC_SET(SCU414, 8));
+SIG_EXPR_LIST_DECL_SEMG(D22, PWM8, PWM8G0, PWM8, SIG_DESC_SET(SCU4B4, 8));
 PIN_DECL_2(D22, GPIOF0, SD1CLK, PWM8);
 GROUP_DECL(PWM8G0, D22);
 
index 7aeb552..72f17f2 100644 (file)
@@ -920,6 +920,10 @@ int mtk_pinconf_adv_pull_set(struct mtk_pinctrl *hw,
                        err = hw->soc->bias_set(hw, desc, pullup);
                        if (err)
                                return err;
+               } else if (hw->soc->bias_set_combo) {
+                       err = hw->soc->bias_set_combo(hw, desc, pullup, arg);
+                       if (err)
+                               return err;
                } else {
                        return -ENOTSUPP;
                }
index d4ea108..abfe11c 100644 (file)
@@ -949,7 +949,6 @@ static void nmk_gpio_dbg_show_one(struct seq_file *s,
        } else {
                int irq = chip->to_irq(chip, offset);
                const int pullidx = pull ? 1 : 0;
-               bool wake;
                int val;
                static const char * const pulls[] = {
                        "none        ",
index 53a6a24..3ea1634 100644 (file)
 #define JZ4740_GPIO_TRIG       0x70
 #define JZ4740_GPIO_FLAG       0x80
 
-#define JZ4760_GPIO_INT                0x10
-#define JZ4760_GPIO_PAT1       0x30
-#define JZ4760_GPIO_PAT0       0x40
-#define JZ4760_GPIO_FLAG       0x50
-#define JZ4760_GPIO_PEN                0x70
+#define JZ4770_GPIO_INT                0x10
+#define JZ4770_GPIO_PAT1       0x30
+#define JZ4770_GPIO_PAT0       0x40
+#define JZ4770_GPIO_FLAG       0x50
+#define JZ4770_GPIO_PEN                0x70
 
 #define X1830_GPIO_PEL                 0x110
 #define X1830_GPIO_PEH                 0x120
@@ -1688,8 +1688,8 @@ static inline bool ingenic_gpio_get_value(struct ingenic_gpio_chip *jzgc,
 static void ingenic_gpio_set_value(struct ingenic_gpio_chip *jzgc,
                                   u8 offset, int value)
 {
-       if (jzgc->jzpc->info->version >= ID_JZ4760)
-               ingenic_gpio_set_bit(jzgc, JZ4760_GPIO_PAT0, offset, !!value);
+       if (jzgc->jzpc->info->version >= ID_JZ4770)
+               ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_PAT0, offset, !!value);
        else
                ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, offset, !!value);
 }
@@ -1718,9 +1718,9 @@ static void irq_set_type(struct ingenic_gpio_chip *jzgc,
                break;
        }
 
-       if (jzgc->jzpc->info->version >= ID_JZ4760) {
-               reg1 = JZ4760_GPIO_PAT1;
-               reg2 = JZ4760_GPIO_PAT0;
+       if (jzgc->jzpc->info->version >= ID_JZ4770) {
+               reg1 = JZ4770_GPIO_PAT1;
+               reg2 = JZ4770_GPIO_PAT0;
        } else {
                reg1 = JZ4740_GPIO_TRIG;
                reg2 = JZ4740_GPIO_DIR;
@@ -1758,8 +1758,8 @@ static void ingenic_gpio_irq_enable(struct irq_data *irqd)
        struct ingenic_gpio_chip *jzgc = gpiochip_get_data(gc);
        int irq = irqd->hwirq;
 
-       if (jzgc->jzpc->info->version >= ID_JZ4760)
-               ingenic_gpio_set_bit(jzgc, JZ4760_GPIO_INT, irq, true);
+       if (jzgc->jzpc->info->version >= ID_JZ4770)
+               ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, true);
        else
                ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, true);
 
@@ -1774,8 +1774,8 @@ static void ingenic_gpio_irq_disable(struct irq_data *irqd)
 
        ingenic_gpio_irq_mask(irqd);
 
-       if (jzgc->jzpc->info->version >= ID_JZ4760)
-               ingenic_gpio_set_bit(jzgc, JZ4760_GPIO_INT, irq, false);
+       if (jzgc->jzpc->info->version >= ID_JZ4770)
+               ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_INT, irq, false);
        else
                ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_SELECT, irq, false);
 }
@@ -1799,8 +1799,8 @@ static void ingenic_gpio_irq_ack(struct irq_data *irqd)
                        irq_set_type(jzgc, irq, IRQ_TYPE_LEVEL_HIGH);
        }
 
-       if (jzgc->jzpc->info->version >= ID_JZ4760)
-               ingenic_gpio_set_bit(jzgc, JZ4760_GPIO_FLAG, irq, false);
+       if (jzgc->jzpc->info->version >= ID_JZ4770)
+               ingenic_gpio_set_bit(jzgc, JZ4770_GPIO_FLAG, irq, false);
        else
                ingenic_gpio_set_bit(jzgc, JZ4740_GPIO_DATA, irq, true);
 }
@@ -1856,8 +1856,8 @@ static void ingenic_gpio_irq_handler(struct irq_desc *desc)
 
        chained_irq_enter(irq_chip, desc);
 
-       if (jzgc->jzpc->info->version >= ID_JZ4760)
-               flag = ingenic_gpio_read_reg(jzgc, JZ4760_GPIO_FLAG);
+       if (jzgc->jzpc->info->version >= ID_JZ4770)
+               flag = ingenic_gpio_read_reg(jzgc, JZ4770_GPIO_FLAG);
        else
                flag = ingenic_gpio_read_reg(jzgc, JZ4740_GPIO_FLAG);
 
@@ -1938,9 +1938,9 @@ static int ingenic_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
        struct ingenic_pinctrl *jzpc = jzgc->jzpc;
        unsigned int pin = gc->base + offset;
 
-       if (jzpc->info->version >= ID_JZ4760) {
-               if (ingenic_get_pin_config(jzpc, pin, JZ4760_GPIO_INT) ||
-                   ingenic_get_pin_config(jzpc, pin, JZ4760_GPIO_PAT1))
+       if (jzpc->info->version >= ID_JZ4770) {
+               if (ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_INT) ||
+                   ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PAT1))
                        return GPIO_LINE_DIRECTION_IN;
                return GPIO_LINE_DIRECTION_OUT;
        }
@@ -1991,20 +1991,20 @@ static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
                        'A' + offt, idx, func);
 
        if (jzpc->info->version >= ID_X1000) {
-               ingenic_shadow_config_pin(jzpc, pin, JZ4760_GPIO_INT, false);
+               ingenic_shadow_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
                ingenic_shadow_config_pin(jzpc, pin, GPIO_MSK, false);
-               ingenic_shadow_config_pin(jzpc, pin, JZ4760_GPIO_PAT1, func & 0x2);
-               ingenic_shadow_config_pin(jzpc, pin, JZ4760_GPIO_PAT0, func & 0x1);
+               ingenic_shadow_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2);
+               ingenic_shadow_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1);
                ingenic_shadow_config_pin_load(jzpc, pin);
-       } else if (jzpc->info->version >= ID_JZ4760) {
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_INT, false);
+       } else if (jzpc->info->version >= ID_JZ4770) {
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
                ingenic_config_pin(jzpc, pin, GPIO_MSK, false);
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_PAT1, func & 0x2);
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_PAT0, func & 0x1);
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2);
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1);
        } else {
                ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, true);
                ingenic_config_pin(jzpc, pin, JZ4740_GPIO_TRIG, func & 0x2);
-               ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, func > 0);
+               ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, func & 0x1);
        }
 
        return 0;
@@ -2057,14 +2057,14 @@ static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
                        'A' + offt, idx, input ? "in" : "out");
 
        if (jzpc->info->version >= ID_X1000) {
-               ingenic_shadow_config_pin(jzpc, pin, JZ4760_GPIO_INT, false);
+               ingenic_shadow_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
                ingenic_shadow_config_pin(jzpc, pin, GPIO_MSK, true);
-               ingenic_shadow_config_pin(jzpc, pin, JZ4760_GPIO_PAT1, input);
+               ingenic_shadow_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input);
                ingenic_shadow_config_pin_load(jzpc, pin);
-       } else if (jzpc->info->version >= ID_JZ4760) {
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_INT, false);
+       } else if (jzpc->info->version >= ID_JZ4770) {
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
                ingenic_config_pin(jzpc, pin, GPIO_MSK, true);
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_PAT1, input);
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input);
        } else {
                ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false);
                ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DIR, !input);
@@ -2091,8 +2091,8 @@ static int ingenic_pinconf_get(struct pinctrl_dev *pctldev,
        unsigned int offt = pin / PINS_PER_GPIO_CHIP;
        bool pull;
 
-       if (jzpc->info->version >= ID_JZ4760)
-               pull = !ingenic_get_pin_config(jzpc, pin, JZ4760_GPIO_PEN);
+       if (jzpc->info->version >= ID_JZ4770)
+               pull = !ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PEN);
        else
                pull = !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_PULL_DIS);
 
@@ -2141,8 +2141,8 @@ static void ingenic_set_bias(struct ingenic_pinctrl *jzpc,
                                        REG_SET(X1830_GPIO_PEH), bias << idxh);
                }
 
-       } else if (jzpc->info->version >= ID_JZ4760) {
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_PEN, !bias);
+       } else if (jzpc->info->version >= ID_JZ4770) {
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PEN, !bias);
        } else {
                ingenic_config_pin(jzpc, pin, JZ4740_GPIO_PULL_DIS, !bias);
        }
@@ -2151,8 +2151,8 @@ static void ingenic_set_bias(struct ingenic_pinctrl *jzpc,
 static void ingenic_set_output_level(struct ingenic_pinctrl *jzpc,
                                     unsigned int pin, bool high)
 {
-       if (jzpc->info->version >= ID_JZ4760)
-               ingenic_config_pin(jzpc, pin, JZ4760_GPIO_PAT0, high);
+       if (jzpc->info->version >= ID_JZ4770)
+               ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, high);
        else
                ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DATA, high);
 }
index e051aec..d70caec 100644 (file)
@@ -51,6 +51,7 @@
  * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge
  *                  detection.
  * @skip_wake_irqs: Skip IRQs that are handled by wakeup interrupt controller
+ * @disabled_for_mux: These IRQs were disabled because we muxed away.
  * @soc:            Reference to soc_data of platform specific data.
  * @regs:           Base addresses for the TLMM tiles.
  * @phys_base:      Physical base address
@@ -72,6 +73,7 @@ struct msm_pinctrl {
        DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO);
        DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO);
        DECLARE_BITMAP(skip_wake_irqs, MAX_NR_GPIO);
+       DECLARE_BITMAP(disabled_for_mux, MAX_NR_GPIO);
 
        const struct msm_pinctrl_soc_data *soc;
        void __iomem *regs[MAX_NR_TILES];
@@ -96,6 +98,14 @@ MSM_ACCESSOR(intr_cfg)
 MSM_ACCESSOR(intr_status)
 MSM_ACCESSOR(intr_target)
 
+static void msm_ack_intr_status(struct msm_pinctrl *pctrl,
+                               const struct msm_pingroup *g)
+{
+       u32 val = g->intr_ack_high ? BIT(g->intr_status_bit) : 0;
+
+       msm_writel_intr_status(val, pctrl, g);
+}
+
 static int msm_get_groups_count(struct pinctrl_dev *pctldev)
 {
        struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
@@ -171,6 +181,10 @@ static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
                              unsigned group)
 {
        struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
+       struct gpio_chip *gc = &pctrl->chip;
+       unsigned int irq = irq_find_mapping(gc->irq.domain, group);
+       struct irq_data *d = irq_get_irq_data(irq);
+       unsigned int gpio_func = pctrl->soc->gpio_func;
        const struct msm_pingroup *g;
        unsigned long flags;
        u32 val, mask;
@@ -187,6 +201,20 @@ static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
        if (WARN_ON(i == g->nfuncs))
                return -EINVAL;
 
+       /*
+        * If an GPIO interrupt is setup on this pin then we need special
+        * handling.  Specifically interrupt detection logic will still see
+        * the pin twiddle even when we're muxed away.
+        *
+        * When we see a pin with an interrupt setup on it then we'll disable
+        * (mask) interrupts on it when we mux away until we mux back.  Note
+        * that disable_irq() refcounts and interrupts are disabled as long as
+        * at least one disable_irq() has been called.
+        */
+       if (d && i != gpio_func &&
+           !test_and_set_bit(d->hwirq, pctrl->disabled_for_mux))
+               disable_irq(irq);
+
        raw_spin_lock_irqsave(&pctrl->lock, flags);
 
        val = msm_readl_ctl(pctrl, g);
@@ -196,6 +224,20 @@ static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
 
        raw_spin_unlock_irqrestore(&pctrl->lock, flags);
 
+       if (d && i == gpio_func &&
+           test_and_clear_bit(d->hwirq, pctrl->disabled_for_mux)) {
+               /*
+                * Clear interrupts detected while not GPIO since we only
+                * masked things.
+                */
+               if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
+                       irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, false);
+               else
+                       msm_ack_intr_status(pctrl, g);
+
+               enable_irq(irq);
+       }
+
        return 0;
 }
 
@@ -210,8 +252,7 @@ static int msm_pinmux_request_gpio(struct pinctrl_dev *pctldev,
        if (!g->nfuncs)
                return 0;
 
-       /* For now assume function 0 is GPIO because it always is */
-       return msm_pinmux_set_mux(pctldev, g->funcs[0], offset);
+       return msm_pinmux_set_mux(pctldev, g->funcs[pctrl->soc->gpio_func], offset);
 }
 
 static const struct pinmux_ops msm_pinmux_ops = {
@@ -774,7 +815,7 @@ static void msm_gpio_irq_mask(struct irq_data *d)
        raw_spin_unlock_irqrestore(&pctrl->lock, flags);
 }
 
-static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
+static void msm_gpio_irq_unmask(struct irq_data *d)
 {
        struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
        struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
@@ -792,17 +833,6 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear)
 
        raw_spin_lock_irqsave(&pctrl->lock, flags);
 
-       if (status_clear) {
-               /*
-                * clear the interrupt status bit before unmask to avoid
-                * any erroneous interrupts that would have got latched
-                * when the interrupt is not in use.
-                */
-               val = msm_readl_intr_status(pctrl, g);
-               val &= ~BIT(g->intr_status_bit);
-               msm_writel_intr_status(val, pctrl, g);
-       }
-
        val = msm_readl_intr_cfg(pctrl, g);
        val |= BIT(g->intr_raw_status_bit);
        val |= BIT(g->intr_enable_bit);
@@ -822,7 +852,7 @@ static void msm_gpio_irq_enable(struct irq_data *d)
                irq_chip_enable_parent(d);
 
        if (!test_bit(d->hwirq, pctrl->skip_wake_irqs))
-               msm_gpio_irq_clear_unmask(d, true);
+               msm_gpio_irq_unmask(d);
 }
 
 static void msm_gpio_irq_disable(struct irq_data *d)
@@ -837,11 +867,6 @@ static void msm_gpio_irq_disable(struct irq_data *d)
                msm_gpio_irq_mask(d);
 }
 
-static void msm_gpio_irq_unmask(struct irq_data *d)
-{
-       msm_gpio_irq_clear_unmask(d, false);
-}
-
 /**
  * msm_gpio_update_dual_edge_parent() - Prime next edge for IRQs handled by parent.
  * @d: The irq dta.
@@ -894,7 +919,6 @@ static void msm_gpio_irq_ack(struct irq_data *d)
        struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
        const struct msm_pingroup *g;
        unsigned long flags;
-       u32 val;
 
        if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
                if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
@@ -906,12 +930,7 @@ static void msm_gpio_irq_ack(struct irq_data *d)
 
        raw_spin_lock_irqsave(&pctrl->lock, flags);
 
-       val = msm_readl_intr_status(pctrl, g);
-       if (g->intr_ack_high)
-               val |= BIT(g->intr_status_bit);
-       else
-               val &= ~BIT(g->intr_status_bit);
-       msm_writel_intr_status(val, pctrl, g);
+       msm_ack_intr_status(pctrl, g);
 
        if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
                msm_gpio_update_dual_edge_pos(pctrl, g, d);
@@ -936,6 +955,7 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
        struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
        const struct msm_pingroup *g;
        unsigned long flags;
+       bool was_enabled;
        u32 val;
 
        if (msm_gpio_needs_dual_edge_parent_workaround(d, type)) {
@@ -997,6 +1017,7 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
         * could cause the INTR_STATUS to be set for EDGE interrupts.
         */
        val = msm_readl_intr_cfg(pctrl, g);
+       was_enabled = val & BIT(g->intr_raw_status_bit);
        val |= BIT(g->intr_raw_status_bit);
        if (g->intr_detection_width == 2) {
                val &= ~(3 << g->intr_detection_bit);
@@ -1046,6 +1067,14 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
        }
        msm_writel_intr_cfg(val, pctrl, g);
 
+       /*
+        * The first time we set RAW_STATUS_EN it could trigger an interrupt.
+        * Clear the interrupt.  This is safe because we have
+        * IRQCHIP_SET_TYPE_MASKED.
+        */
+       if (!was_enabled)
+               msm_ack_intr_status(pctrl, g);
+
        if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
                msm_gpio_update_dual_edge_pos(pctrl, g, d);
 
@@ -1099,16 +1128,11 @@ static int msm_gpio_irq_reqres(struct irq_data *d)
        }
 
        /*
-        * Clear the interrupt that may be pending before we enable
-        * the line.
-        * This is especially a problem with the GPIOs routed to the
-        * PDC. These GPIOs are direct-connect interrupts to the GIC.
-        * Disabling the interrupt line at the PDC does not prevent
-        * the interrupt from being latched at the GIC. The state at
-        * GIC needs to be cleared before enabling.
+        * The disable / clear-enable workaround we do in msm_pinmux_set_mux()
+        * only works if disable is not lazy since we only clear any bogus
+        * interrupt in hardware. Explicitly mark the interrupt as UNLAZY.
         */
-       if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs))
-               irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0);
+       irq_set_status_flags(d->irq, IRQ_DISABLE_UNLAZY);
 
        return 0;
 out:
index 333f992..e31a516 100644 (file)
@@ -118,6 +118,7 @@ struct msm_gpio_wakeirq_map {
  * @wakeirq_dual_edge_errata: If true then GPIOs using the wakeirq_map need
  *                            to be aware that their parent can't handle dual
  *                            edge interrupts.
+ * @gpio_func: Which function number is GPIO (usually 0).
  */
 struct msm_pinctrl_soc_data {
        const struct pinctrl_pin_desc *pins;
@@ -134,6 +135,7 @@ struct msm_pinctrl_soc_data {
        const struct msm_gpio_wakeirq_map *wakeirq_map;
        unsigned int nwakeirq_map;
        bool wakeirq_dual_edge_errata;
+       unsigned int gpio_func;
 };
 
 extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
index 33040b0..2c941cd 100644 (file)
@@ -5,6 +5,7 @@
 
 menuconfig SURFACE_PLATFORMS
        bool "Microsoft Surface Platform-Specific Device Drivers"
+       depends on ACPI
        default y
        help
          Say Y here to get to see options for platform-specific device drivers
@@ -29,20 +30,19 @@ config SURFACE3_WMI
 
 config SURFACE_3_BUTTON
        tristate "Power/home/volume buttons driver for Microsoft Surface 3 tablet"
-       depends on ACPI && KEYBOARD_GPIO && I2C
+       depends on KEYBOARD_GPIO && I2C
        help
          This driver handles the power/home/volume buttons on the Microsoft Surface 3 tablet.
 
 config SURFACE_3_POWER_OPREGION
        tristate "Surface 3 battery platform operation region support"
-       depends on ACPI && I2C
+       depends on I2C
        help
          This driver provides support for ACPI operation
          region of the Surface 3 battery platform driver.
 
 config SURFACE_GPE
        tristate "Surface GPE/Lid Support Driver"
-       depends on ACPI
        depends on DMI
        help
          This driver marks the GPEs related to the ACPI lid device found on
@@ -52,7 +52,7 @@ config SURFACE_GPE
 
 config SURFACE_PRO3_BUTTON
        tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet"
-       depends on ACPI && INPUT
+       depends on INPUT
        help
          This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet.
 
index e49e5d6..86f6991 100644 (file)
@@ -181,12 +181,12 @@ static int surface_lid_enable_wakeup(struct device *dev, bool enable)
        return 0;
 }
 
-static int surface_gpe_suspend(struct device *dev)
+static int __maybe_unused surface_gpe_suspend(struct device *dev)
 {
        return surface_lid_enable_wakeup(dev, true);
 }
 
-static int surface_gpe_resume(struct device *dev)
+static int __maybe_unused surface_gpe_resume(struct device *dev)
 {
        return surface_lid_enable_wakeup(dev, false);
 }
index 0102bf1..ef83425 100644 (file)
@@ -85,7 +85,7 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3
        iowrite32(val, dev->regbase + reg_offset);
 }
 
-#if CONFIG_DEBUG_FS
+#ifdef CONFIG_DEBUG_FS
 static int smu_fw_info_show(struct seq_file *s, void *unused)
 {
        struct amd_pmc_dev *dev = s->private;
index ecd4779..18bf8ae 100644 (file)
@@ -247,7 +247,8 @@ static int hp_wmi_perform_query(int query, enum hp_wmi_command command,
        ret = bios_return->return_code;
 
        if (ret) {
-               if (ret != HPWMI_RET_UNKNOWN_CMDTYPE)
+               if (ret != HPWMI_RET_UNKNOWN_COMMAND &&
+                   ret != HPWMI_RET_UNKNOWN_CMDTYPE)
                        pr_warn("query 0x%x returned error 0x%x\n", query, ret);
                goto out_free;
        }
index b457b0b..2cce825 100644 (file)
@@ -164,13 +164,29 @@ static const struct i2c_inst_data bsg2150_data[]  = {
        {}
 };
 
-static const struct i2c_inst_data int3515_data[]  = {
-       { "tps6598x", IRQ_RESOURCE_APIC, 0 },
-       { "tps6598x", IRQ_RESOURCE_APIC, 1 },
-       { "tps6598x", IRQ_RESOURCE_APIC, 2 },
-       { "tps6598x", IRQ_RESOURCE_APIC, 3 },
-       {}
-};
+/*
+ * Device with _HID INT3515 (TI PD controllers) has some unresolved interrupt
+ * issues. The most common problem seen is interrupt flood.
+ *
+ * There are at least two known causes. Firstly, on some boards, the
+ * I2CSerialBus resource index does not match the Interrupt resource, i.e. they
+ * are not one-to-one mapped like in the array below. Secondly, on some boards
+ * the IRQ line from the PD controller is not actually connected at all. But the
+ * interrupt flood is also seen on some boards where those are not a problem, so
+ * there are some other problems as well.
+ *
+ * Because of the issues with the interrupt, the device is disabled for now. If
+ * you wish to debug the issues, uncomment the below, and add an entry for the
+ * INT3515 device to the i2c_multi_instance_ids table.
+ *
+ * static const struct i2c_inst_data int3515_data[]  = {
+ *     { "tps6598x", IRQ_RESOURCE_APIC, 0 },
+ *     { "tps6598x", IRQ_RESOURCE_APIC, 1 },
+ *     { "tps6598x", IRQ_RESOURCE_APIC, 2 },
+ *     { "tps6598x", IRQ_RESOURCE_APIC, 3 },
+ *     { }
+ * };
+ */
 
 /*
  * Note new device-ids must also be added to i2c_multi_instantiate_ids in
@@ -179,7 +195,6 @@ static const struct i2c_inst_data int3515_data[]  = {
 static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = {
        { "BSG1160", (unsigned long)bsg1160_data },
        { "BSG2150", (unsigned long)bsg2150_data },
-       { "INT3515", (unsigned long)int3515_data },
        { }
 };
 MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids);
index 7598cd4..5b81baf 100644 (file)
@@ -92,6 +92,7 @@ struct ideapad_private {
        struct dentry *debug;
        unsigned long cfg;
        bool has_hw_rfkill_switch;
+       bool has_touchpad_switch;
        const char *fnesc_guid;
 };
 
@@ -535,7 +536,9 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
        } else if (attr == &dev_attr_fn_lock.attr) {
                supported = acpi_has_method(priv->adev->handle, "HALS") &&
                        acpi_has_method(priv->adev->handle, "SALS");
-       } else
+       } else if (attr == &dev_attr_touchpad.attr)
+               supported = priv->has_touchpad_switch;
+       else
                supported = true;
 
        return supported ? attr->mode : 0;
@@ -867,6 +870,9 @@ static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
 {
        unsigned long value;
 
+       if (!priv->has_touchpad_switch)
+               return;
+
        /* Without reading from EC touchpad LED doesn't switch state */
        if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
                /* Some IdeaPads don't really turn off touchpad - they only
@@ -989,6 +995,9 @@ static int ideapad_acpi_add(struct platform_device *pdev)
        priv->platform_device = pdev;
        priv->has_hw_rfkill_switch = dmi_check_system(hw_rfkill_list);
 
+       /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */
+       priv->has_touchpad_switch = !acpi_dev_present("ELAN0634", NULL, -1);
+
        ret = ideapad_sysfs_init(priv);
        if (ret)
                return ret;
@@ -1006,6 +1015,10 @@ static int ideapad_acpi_add(struct platform_device *pdev)
        if (!priv->has_hw_rfkill_switch)
                write_ec_cmd(priv->adev->handle, VPCCMD_W_RF, 1);
 
+       /* The same for Touchpad */
+       if (!priv->has_touchpad_switch)
+               write_ec_cmd(priv->adev->handle, VPCCMD_W_TOUCHPAD, 1);
+
        for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
                if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
                        ideapad_register_rfkill(priv, i);
index 3b49a1f..30a9062 100644 (file)
@@ -207,19 +207,19 @@ static const struct dmi_system_id dmi_switches_allow_list[] = {
        {
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Stream x360 Convertible PC 11"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion 13 x360 PC"),
                },
        },
        {
                .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion 13 x360 PC"),
+                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Switch SA5-271"),
                },
        },
        {
                .matches = {
-                       DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "Switch SA5-271"),
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7352"),
                },
        },
        {} /* Array terminator */
index e03df28..f3e8eca 100644 (file)
@@ -8783,6 +8783,7 @@ static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
        TPACPI_Q_LNV3('N', '1', 'T', TPACPI_FAN_2CTL),  /* P71 */
        TPACPI_Q_LNV3('N', '1', 'U', TPACPI_FAN_2CTL),  /* P51 */
        TPACPI_Q_LNV3('N', '2', 'C', TPACPI_FAN_2CTL),  /* P52 / P72 */
+       TPACPI_Q_LNV3('N', '2', 'N', TPACPI_FAN_2CTL),  /* P53 / P73 */
        TPACPI_Q_LNV3('N', '2', 'E', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (1st gen) */
        TPACPI_Q_LNV3('N', '2', 'O', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (2nd gen) */
        TPACPI_Q_LNV3('N', '2', 'V', TPACPI_FAN_2CTL),  /* P1 / X1 Extreme (3nd gen) */
@@ -9951,9 +9952,9 @@ static int tpacpi_proxsensor_init(struct ibm_init_struct *iibm)
        if ((palm_err == -ENODEV) && (lap_err == -ENODEV))
                return 0;
        /* Otherwise, if there was an error return it */
-       if (palm_err && (palm_err != ENODEV))
+       if (palm_err && (palm_err != -ENODEV))
                return palm_err;
-       if (lap_err && (lap_err != ENODEV))
+       if (lap_err && (lap_err != -ENODEV))
                return lap_err;
 
        if (has_palmsensor) {
index 5783139..c4de932 100644 (file)
@@ -263,6 +263,16 @@ static const struct ts_dmi_data digma_citi_e200_data = {
        .properties     = digma_citi_e200_props,
 };
 
+static const struct property_entry estar_beauty_hd_props[] = {
+       PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+       { }
+};
+
+static const struct ts_dmi_data estar_beauty_hd_data = {
+       .acpi_name      = "GDIX1001:00",
+       .properties     = estar_beauty_hd_props,
+};
+
 static const struct property_entry gp_electronic_t701_props[] = {
        PROPERTY_ENTRY_U32("touchscreen-size-x", 960),
        PROPERTY_ENTRY_U32("touchscreen-size-y", 640),
@@ -942,6 +952,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
                        DMI_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"),
                },
        },
+       {
+               /* Estar Beauty HD (MID 7316R) */
+               .driver_data = (void *)&estar_beauty_hd_data,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Estar"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "eSTAR BEAUTY HD Intel Quad core"),
+               },
+       },
        {
                /* GP-electronic T701 */
                .driver_data = (void *)&gp_electronic_t701_data,
index 476d7c7..f2edef0 100644 (file)
@@ -64,6 +64,7 @@ config DP83640_PHY
        depends on NETWORK_PHY_TIMESTAMPING
        depends on PHYLIB
        depends on PTP_1588_CLOCK
+       select CRC32
        help
          Supports the DP83640 PHYTER with IEEE 1588 features.
 
@@ -78,6 +79,7 @@ config DP83640_PHY
 config PTP_1588_CLOCK_INES
        tristate "ZHAW InES PTP time stamping IP core"
        depends on NETWORK_PHY_TIMESTAMPING
+       depends on HAS_IOMEM
        depends on PHYLIB
        depends on PTP_1588_CLOCK
        help
index 53fa84f..5abdd29 100644 (file)
@@ -881,6 +881,7 @@ config REGULATOR_QCOM_RPM
 config REGULATOR_QCOM_RPMH
        tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
        depends on QCOM_RPMH || (QCOM_RPMH=n && COMPILE_TEST)
+       depends on QCOM_COMMAND_DB || (QCOM_COMMAND_DB=n && COMPILE_TEST)
        help
          This driver supports control of PMIC regulators via the RPMh hardware
          block found on Qualcomm Technologies Inc. SoCs.  RPMh regulator
index e6d5d98..9309765 100644 (file)
 #include <linux/regulator/of_regulator.h>
 #include <linux/slab.h>
 
+/* Typical regulator startup times as per data sheet in uS */
+#define BD71847_BUCK1_STARTUP_TIME 144
+#define BD71847_BUCK2_STARTUP_TIME 162
+#define BD71847_BUCK3_STARTUP_TIME 162
+#define BD71847_BUCK4_STARTUP_TIME 240
+#define BD71847_BUCK5_STARTUP_TIME 270
+#define BD71847_BUCK6_STARTUP_TIME 200
+#define BD71847_LDO1_STARTUP_TIME  440
+#define BD71847_LDO2_STARTUP_TIME  370
+#define BD71847_LDO3_STARTUP_TIME  310
+#define BD71847_LDO4_STARTUP_TIME  400
+#define BD71847_LDO5_STARTUP_TIME  530
+#define BD71847_LDO6_STARTUP_TIME  400
+
+#define BD71837_BUCK1_STARTUP_TIME 160
+#define BD71837_BUCK2_STARTUP_TIME 180
+#define BD71837_BUCK3_STARTUP_TIME 180
+#define BD71837_BUCK4_STARTUP_TIME 180
+#define BD71837_BUCK5_STARTUP_TIME 160
+#define BD71837_BUCK6_STARTUP_TIME 240
+#define BD71837_BUCK7_STARTUP_TIME 220
+#define BD71837_BUCK8_STARTUP_TIME 200
+#define BD71837_LDO1_STARTUP_TIME  440
+#define BD71837_LDO2_STARTUP_TIME  370
+#define BD71837_LDO3_STARTUP_TIME  310
+#define BD71837_LDO4_STARTUP_TIME  400
+#define BD71837_LDO5_STARTUP_TIME  310
+#define BD71837_LDO6_STARTUP_TIME  400
+#define BD71837_LDO7_STARTUP_TIME  530
+
 /*
  * BD718(37/47/50) have two "enable control modes". ON/OFF can either be
  * controlled by software - or by PMIC internal HW state machine. Whether
@@ -613,6 +643,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = DVS_BUCK_RUN_MASK,
                        .enable_reg = BD718XX_REG_BUCK1_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71847_BUCK1_STARTUP_TIME,
                        .owner = THIS_MODULE,
                        .of_parse_cb = buck_set_hw_dvs_levels,
                },
@@ -646,6 +677,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = DVS_BUCK_RUN_MASK,
                        .enable_reg = BD718XX_REG_BUCK2_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71847_BUCK2_STARTUP_TIME,
                        .owner = THIS_MODULE,
                        .of_parse_cb = buck_set_hw_dvs_levels,
                },
@@ -680,6 +712,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .linear_range_selectors = bd71847_buck3_volt_range_sel,
                        .enable_reg = BD718XX_REG_1ST_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71847_BUCK3_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -706,6 +739,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_range_mask = BD71847_BUCK4_RANGE_MASK,
                        .linear_range_selectors = bd71847_buck4_volt_range_sel,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71847_BUCK4_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -727,6 +761,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = BD718XX_3RD_NODVS_BUCK_MASK,
                        .enable_reg = BD718XX_REG_3RD_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71847_BUCK5_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -750,6 +785,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = BD718XX_4TH_NODVS_BUCK_MASK,
                        .enable_reg = BD718XX_REG_4TH_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71847_BUCK6_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -775,6 +811,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .linear_range_selectors = bd718xx_ldo1_volt_range_sel,
                        .enable_reg = BD718XX_REG_LDO1_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71847_LDO1_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -796,6 +833,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .n_voltages = ARRAY_SIZE(ldo_2_volts),
                        .enable_reg = BD718XX_REG_LDO2_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71847_LDO2_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -818,6 +856,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = BD718XX_LDO3_MASK,
                        .enable_reg = BD718XX_REG_LDO3_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71847_LDO3_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -840,6 +879,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = BD718XX_LDO4_MASK,
                        .enable_reg = BD718XX_REG_LDO4_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71847_LDO4_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -865,6 +905,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .linear_range_selectors = bd71847_ldo5_volt_range_sel,
                        .enable_reg = BD718XX_REG_LDO5_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71847_LDO5_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -889,6 +930,7 @@ static struct bd718xx_regulator_data bd71847_regulators[] = {
                        .vsel_mask = BD718XX_LDO6_MASK,
                        .enable_reg = BD718XX_REG_LDO6_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71847_LDO6_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -942,6 +984,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = DVS_BUCK_RUN_MASK,
                        .enable_reg = BD718XX_REG_BUCK1_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK1_STARTUP_TIME,
                        .owner = THIS_MODULE,
                        .of_parse_cb = buck_set_hw_dvs_levels,
                },
@@ -975,6 +1018,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = DVS_BUCK_RUN_MASK,
                        .enable_reg = BD718XX_REG_BUCK2_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK2_STARTUP_TIME,
                        .owner = THIS_MODULE,
                        .of_parse_cb = buck_set_hw_dvs_levels,
                },
@@ -1005,6 +1049,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = DVS_BUCK_RUN_MASK,
                        .enable_reg = BD71837_REG_BUCK3_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK3_STARTUP_TIME,
                        .owner = THIS_MODULE,
                        .of_parse_cb = buck_set_hw_dvs_levels,
                },
@@ -1033,6 +1078,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = DVS_BUCK_RUN_MASK,
                        .enable_reg = BD71837_REG_BUCK4_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK4_STARTUP_TIME,
                        .owner = THIS_MODULE,
                        .of_parse_cb = buck_set_hw_dvs_levels,
                },
@@ -1065,6 +1111,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .linear_range_selectors = bd71837_buck5_volt_range_sel,
                        .enable_reg = BD718XX_REG_1ST_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK5_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1088,6 +1135,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD71837_BUCK6_MASK,
                        .enable_reg = BD718XX_REG_2ND_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK6_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1109,6 +1157,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD718XX_3RD_NODVS_BUCK_MASK,
                        .enable_reg = BD718XX_REG_3RD_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK7_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1132,6 +1181,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD718XX_4TH_NODVS_BUCK_MASK,
                        .enable_reg = BD718XX_REG_4TH_NODVS_BUCK_CTRL,
                        .enable_mask = BD718XX_BUCK_EN,
+                       .enable_time = BD71837_BUCK8_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1157,6 +1207,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .linear_range_selectors = bd718xx_ldo1_volt_range_sel,
                        .enable_reg = BD718XX_REG_LDO1_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO1_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1178,6 +1229,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .n_voltages = ARRAY_SIZE(ldo_2_volts),
                        .enable_reg = BD718XX_REG_LDO2_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO2_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1200,6 +1252,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD718XX_LDO3_MASK,
                        .enable_reg = BD718XX_REG_LDO3_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO3_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1222,6 +1275,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD718XX_LDO4_MASK,
                        .enable_reg = BD718XX_REG_LDO4_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO4_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1246,6 +1300,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD71837_LDO5_MASK,
                        .enable_reg = BD718XX_REG_LDO5_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO5_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1272,6 +1327,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD718XX_LDO6_MASK,
                        .enable_reg = BD718XX_REG_LDO6_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO6_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
@@ -1296,6 +1352,7 @@ static struct bd718xx_regulator_data bd71837_regulators[] = {
                        .vsel_mask = BD71837_LDO7_MASK,
                        .enable_reg = BD71837_REG_LDO7_VOLT,
                        .enable_mask = BD718XX_LDO_EN,
+                       .enable_time = BD71837_LDO7_STARTUP_TIME,
                        .owner = THIS_MODULE,
                },
                .init = {
index ca03d8e..67a768f 100644 (file)
@@ -1813,13 +1813,13 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
 {
        struct regulator_dev *r;
        struct device *dev = rdev->dev.parent;
-       int ret;
+       int ret = 0;
 
        /* No supply to resolve? */
        if (!rdev->supply_name)
                return 0;
 
-       /* Supply already resolved? */
+       /* Supply already resolved? (fast-path without locking contention) */
        if (rdev->supply)
                return 0;
 
@@ -1829,7 +1829,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
 
                /* Did the lookup explicitly defer for us? */
                if (ret == -EPROBE_DEFER)
-                       return ret;
+                       goto out;
 
                if (have_full_constraints()) {
                        r = dummy_regulator_rdev;
@@ -1837,15 +1837,18 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
                } else {
                        dev_err(dev, "Failed to resolve %s-supply for %s\n",
                                rdev->supply_name, rdev->desc->name);
-                       return -EPROBE_DEFER;
+                       ret = -EPROBE_DEFER;
+                       goto out;
                }
        }
 
        if (r == rdev) {
                dev_err(dev, "Supply for %s (%s) resolved to itself\n",
                        rdev->desc->name, rdev->supply_name);
-               if (!have_full_constraints())
-                       return -EINVAL;
+               if (!have_full_constraints()) {
+                       ret = -EINVAL;
+                       goto out;
+               }
                r = dummy_regulator_rdev;
                get_device(&r->dev);
        }
@@ -1859,7 +1862,8 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
        if (r->dev.parent && r->dev.parent != rdev->dev.parent) {
                if (!device_is_bound(r->dev.parent)) {
                        put_device(&r->dev);
-                       return -EPROBE_DEFER;
+                       ret = -EPROBE_DEFER;
+                       goto out;
                }
        }
 
@@ -1867,15 +1871,32 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
        ret = regulator_resolve_supply(r);
        if (ret < 0) {
                put_device(&r->dev);
-               return ret;
+               goto out;
+       }
+
+       /*
+        * Recheck rdev->supply with rdev->mutex lock held to avoid a race
+        * between rdev->supply null check and setting rdev->supply in
+        * set_supply() from concurrent tasks.
+        */
+       regulator_lock(rdev);
+
+       /* Supply just resolved by a concurrent task? */
+       if (rdev->supply) {
+               regulator_unlock(rdev);
+               put_device(&r->dev);
+               goto out;
        }
 
        ret = set_supply(rdev, r);
        if (ret < 0) {
+               regulator_unlock(rdev);
                put_device(&r->dev);
-               return ret;
+               goto out;
        }
 
+       regulator_unlock(rdev);
+
        /*
         * In set_machine_constraints() we may have turned this regulator on
         * but we couldn't propagate to the supply if it hadn't been resolved
@@ -1886,11 +1907,12 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
                if (ret < 0) {
                        _regulator_put(rdev->supply);
                        rdev->supply = NULL;
-                       return ret;
+                       goto out;
                }
        }
 
-       return 0;
+out:
+       return ret;
 }
 
 /* Internal regulator request function */
index 308c27f..af9918c 100644 (file)
@@ -469,13 +469,17 @@ static int pf8x00_i2c_probe(struct i2c_client *client)
 }
 
 static const struct of_device_id pf8x00_dt_ids[] = {
-       { .compatible = "nxp,pf8x00",},
+       { .compatible = "nxp,pf8100",},
+       { .compatible = "nxp,pf8121a",},
+       { .compatible = "nxp,pf8200",},
        { }
 };
 MODULE_DEVICE_TABLE(of, pf8x00_dt_ids);
 
 static const struct i2c_device_id pf8x00_i2c_id[] = {
-       { "pf8x00", 0 },
+       { "pf8100", 0 },
+       { "pf8121a", 0 },
+       { "pf8200", 0 },
        {},
 };
 MODULE_DEVICE_TABLE(i2c, pf8x00_i2c_id);
index fe030ec..c395a8d 100644 (file)
@@ -726,7 +726,7 @@ static const struct rpmh_vreg_hw_data pmic5_ftsmps510 = {
 static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = {
        .regulator_type = VRM,
        .ops = &rpmh_regulator_vrm_ops,
-       .voltage_range = REGULATOR_LINEAR_RANGE(2800000, 0, 4, 1600),
+       .voltage_range = REGULATOR_LINEAR_RANGE(2800000, 0, 4, 16000),
        .n_voltages = 5,
        .pmic_mode_map = pmic_mode_map_pmic5_smps,
        .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode,
index 6f5ddc3..a1da83b 100644 (file)
@@ -956,24 +956,6 @@ static inline int qeth_get_elements_for_range(addr_t start, addr_t end)
        return PFN_UP(end) - PFN_DOWN(start);
 }
 
-static inline int qeth_get_ip_version(struct sk_buff *skb)
-{
-       struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
-       __be16 prot = veth->h_vlan_proto;
-
-       if (prot == htons(ETH_P_8021Q))
-               prot = veth->h_vlan_encapsulated_proto;
-
-       switch (prot) {
-       case htons(ETH_P_IPV6):
-               return 6;
-       case htons(ETH_P_IP):
-               return 4;
-       default:
-               return 0;
-       }
-}
-
 static inline int qeth_get_ether_cast_type(struct sk_buff *skb)
 {
        u8 *addr = eth_hdr(skb)->h_dest;
@@ -984,14 +966,20 @@ static inline int qeth_get_ether_cast_type(struct sk_buff *skb)
        return RTN_UNICAST;
 }
 
-static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb, int ipv)
+static inline struct dst_entry *qeth_dst_check_rcu(struct sk_buff *skb,
+                                                  __be16 proto)
 {
        struct dst_entry *dst = skb_dst(skb);
        struct rt6_info *rt;
 
        rt = (struct rt6_info *) dst;
-       if (dst)
-               dst = dst_check(dst, (ipv == 6) ? rt6_get_cookie(rt) : 0);
+       if (dst) {
+               if (proto == htons(ETH_P_IPV6))
+                       dst = dst_check(dst, rt6_get_cookie(rt));
+               else
+                       dst = dst_check(dst, 0);
+       }
+
        return dst;
 }
 
@@ -1014,11 +1002,11 @@ static inline struct in6_addr *qeth_next_hop_v6_rcu(struct sk_buff *skb,
                return &ipv6_hdr(skb)->daddr;
 }
 
-static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, int ipv)
+static inline void qeth_tx_csum(struct sk_buff *skb, u8 *flags, __be16 proto)
 {
        *flags |= QETH_HDR_EXT_CSUM_TRANSP_REQ;
-       if ((ipv == 4 && ip_hdr(skb)->protocol == IPPROTO_UDP) ||
-           (ipv == 6 && ipv6_hdr(skb)->nexthdr == IPPROTO_UDP))
+       if ((proto == htons(ETH_P_IP) && ip_hdr(skb)->protocol == IPPROTO_UDP) ||
+           (proto == htons(ETH_P_IPV6) && ipv6_hdr(skb)->nexthdr == IPPROTO_UDP))
                *flags |= QETH_HDR_EXT_UDP;
 }
 
@@ -1067,8 +1055,8 @@ extern const struct device_type qeth_generic_devtype;
 
 const char *qeth_get_cardname_short(struct qeth_card *);
 int qeth_resize_buffer_pool(struct qeth_card *card, unsigned int count);
-int qeth_core_load_discipline(struct qeth_card *, enum qeth_discipline_id);
-void qeth_core_free_discipline(struct qeth_card *);
+int qeth_setup_discipline(struct qeth_card *card, enum qeth_discipline_id disc);
+void qeth_remove_discipline(struct qeth_card *card);
 
 /* exports for qeth discipline device drivers */
 extern struct kmem_cache *qeth_core_header_cache;
@@ -1079,7 +1067,8 @@ struct qeth_card *qeth_get_card_by_busid(char *bus_id);
 void qeth_set_allowed_threads(struct qeth_card *card, unsigned long threads,
                              int clear_start_mask);
 int qeth_threads_running(struct qeth_card *, unsigned long);
-int qeth_set_offline(struct qeth_card *card, bool resetting);
+int qeth_set_offline(struct qeth_card *card, const struct qeth_discipline *disc,
+                    bool resetting);
 
 int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
                  int (*reply_cb)
@@ -1144,10 +1133,10 @@ int qeth_stop(struct net_device *dev);
 
 int qeth_vm_request_mac(struct qeth_card *card);
 int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
-             struct qeth_qdio_out_q *queue, int ipv,
+             struct qeth_qdio_out_q *queue, __be16 proto,
              void (*fill_header)(struct qeth_qdio_out_q *queue,
                                  struct qeth_hdr *hdr, struct sk_buff *skb,
-                                 int ipv, unsigned int data_len));
+                                 __be16 proto, unsigned int data_len));
 
 /* exports for OSN */
 int qeth_osn_assist(struct net_device *, void *, int);
index f4b6029..89b2238 100644 (file)
@@ -825,7 +825,8 @@ static bool qeth_next_hop_is_local_v4(struct qeth_card *card,
                return false;
 
        rcu_read_lock();
-       next_hop = qeth_next_hop_v4_rcu(skb, qeth_dst_check_rcu(skb, 4));
+       next_hop = qeth_next_hop_v4_rcu(skb,
+                                       qeth_dst_check_rcu(skb, htons(ETH_P_IP)));
        key = ipv4_addr_hash(next_hop);
 
        hash_for_each_possible_rcu(card->local_addrs4, tmp, hnode, key) {
@@ -851,7 +852,8 @@ static bool qeth_next_hop_is_local_v6(struct qeth_card *card,
                return false;
 
        rcu_read_lock();
-       next_hop = qeth_next_hop_v6_rcu(skb, qeth_dst_check_rcu(skb, 6));
+       next_hop = qeth_next_hop_v6_rcu(skb,
+                                       qeth_dst_check_rcu(skb, htons(ETH_P_IPV6)));
        key = ipv6_addr_hash(next_hop);
 
        hash_for_each_possible_rcu(card->local_addrs6, tmp, hnode, key) {
@@ -1407,10 +1409,12 @@ static void qeth_notify_skbs(struct qeth_qdio_out_q *q,
        struct sk_buff *skb;
 
        skb_queue_walk(&buf->skb_list, skb) {
+               struct sock *sk = skb->sk;
+
                QETH_CARD_TEXT_(q->card, 5, "skbn%d", notification);
                QETH_CARD_TEXT_(q->card, 5, "%lx", (long) skb);
-               if (skb->sk && skb->sk->sk_family == PF_IUCV)
-                       iucv_sk(skb->sk)->sk_txnotify(skb, notification);
+               if (sk && sk->sk_family == PF_IUCV)
+                       iucv_sk(sk)->sk_txnotify(sk, notification);
        }
 }
 
@@ -3690,24 +3694,27 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
        rc = do_QDIO(CARD_DDEV(queue->card), qdio_flags,
                     queue->queue_no, index, count);
 
-       /* Fake the TX completion interrupt: */
-       if (IS_IQD(card)) {
-               unsigned int frames = READ_ONCE(queue->max_coalesced_frames);
-               unsigned int usecs = READ_ONCE(queue->coalesce_usecs);
+       switch (rc) {
+       case 0:
+       case -ENOBUFS:
+               /* ignore temporary SIGA errors without busy condition */
 
-               if (frames && queue->coalesced_frames >= frames) {
-                       napi_schedule(&queue->napi);
-                       queue->coalesced_frames = 0;
-                       QETH_TXQ_STAT_INC(queue, coal_frames);
-               } else if (usecs) {
-                       qeth_tx_arm_timer(queue, usecs);
+               /* Fake the TX completion interrupt: */
+               if (IS_IQD(card)) {
+                       unsigned int frames = READ_ONCE(queue->max_coalesced_frames);
+                       unsigned int usecs = READ_ONCE(queue->coalesce_usecs);
+
+                       if (frames && queue->coalesced_frames >= frames) {
+                               napi_schedule(&queue->napi);
+                               queue->coalesced_frames = 0;
+                               QETH_TXQ_STAT_INC(queue, coal_frames);
+                       } else if (usecs) {
+                               qeth_tx_arm_timer(queue, usecs);
+                       }
                }
-       }
 
-       if (rc) {
-               /* ignore temporary SIGA errors without busy condition */
-               if (rc == -ENOBUFS)
-                       return;
+               break;
+       default:
                QETH_CARD_TEXT(queue->card, 2, "flushbuf");
                QETH_CARD_TEXT_(queue->card, 2, " q%d", queue->queue_no);
                QETH_CARD_TEXT_(queue->card, 2, " idx%d", index);
@@ -3717,7 +3724,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index,
                /* this must not happen under normal circumstances. if it
                 * happens something is really wrong -> recover */
                qeth_schedule_recovery(queue->card);
-               return;
        }
 }
 
@@ -3896,11 +3902,11 @@ int qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb)
        switch (card->qdio.do_prio_queueing) {
        case QETH_PRIO_Q_ING_TOS:
        case QETH_PRIO_Q_ING_PREC:
-               switch (qeth_get_ip_version(skb)) {
-               case 4:
+               switch (vlan_get_protocol(skb)) {
+               case htons(ETH_P_IP):
                        tos = ipv4_get_dsfield(ip_hdr(skb));
                        break;
-               case 6:
+               case htons(ETH_P_IPV6):
                        tos = ipv6_get_dsfield(ipv6_hdr(skb));
                        break;
                default:
@@ -4365,10 +4371,10 @@ static void qeth_fill_tso_ext(struct qeth_hdr_tso *hdr,
 }
 
 int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
-             struct qeth_qdio_out_q *queue, int ipv,
+             struct qeth_qdio_out_q *queue, __be16 proto,
              void (*fill_header)(struct qeth_qdio_out_q *queue,
                                  struct qeth_hdr *hdr, struct sk_buff *skb,
-                                 int ipv, unsigned int data_len))
+                                 __be16 proto, unsigned int data_len))
 {
        unsigned int proto_len, hw_hdr_len;
        unsigned int frame_len = skb->len;
@@ -4401,7 +4407,7 @@ int qeth_xmit(struct qeth_card *card, struct sk_buff *skb,
                data_offset = push_len + proto_len;
        }
        memset(hdr, 0, hw_hdr_len);
-       fill_header(queue, hdr, skb, ipv, frame_len);
+       fill_header(queue, hdr, skb, proto, frame_len);
        if (is_tso)
                qeth_fill_tso_ext((struct qeth_hdr_tso *) hdr,
                                  frame_len - proto_len, skb, proto_len);
@@ -5507,12 +5513,12 @@ out:
        return rc;
 }
 
-static int qeth_set_online(struct qeth_card *card)
+static int qeth_set_online(struct qeth_card *card,
+                          const struct qeth_discipline *disc)
 {
        bool carrier_ok;
        int rc;
 
-       mutex_lock(&card->discipline_mutex);
        mutex_lock(&card->conf_mutex);
        QETH_CARD_TEXT(card, 2, "setonlin");
 
@@ -5529,7 +5535,7 @@ static int qeth_set_online(struct qeth_card *card)
                /* no need for locking / error handling at this early stage: */
                qeth_set_real_num_tx_queues(card, qeth_tx_actual_queues(card));
 
-       rc = card->discipline->set_online(card, carrier_ok);
+       rc = disc->set_online(card, carrier_ok);
        if (rc)
                goto err_online;
 
@@ -5537,7 +5543,6 @@ static int qeth_set_online(struct qeth_card *card)
        kobject_uevent(&card->gdev->dev.kobj, KOBJ_CHANGE);
 
        mutex_unlock(&card->conf_mutex);
-       mutex_unlock(&card->discipline_mutex);
        return 0;
 
 err_online:
@@ -5552,15 +5557,14 @@ err_hardsetup:
        qdio_free(CARD_DDEV(card));
 
        mutex_unlock(&card->conf_mutex);
-       mutex_unlock(&card->discipline_mutex);
        return rc;
 }
 
-int qeth_set_offline(struct qeth_card *card, bool resetting)
+int qeth_set_offline(struct qeth_card *card, const struct qeth_discipline *disc,
+                    bool resetting)
 {
        int rc, rc2, rc3;
 
-       mutex_lock(&card->discipline_mutex);
        mutex_lock(&card->conf_mutex);
        QETH_CARD_TEXT(card, 3, "setoffl");
 
@@ -5581,7 +5585,7 @@ int qeth_set_offline(struct qeth_card *card, bool resetting)
 
        cancel_work_sync(&card->rx_mode_work);
 
-       card->discipline->set_offline(card);
+       disc->set_offline(card);
 
        qeth_qdio_clear_card(card, 0);
        qeth_drain_output_queues(card);
@@ -5602,16 +5606,19 @@ int qeth_set_offline(struct qeth_card *card, bool resetting)
        kobject_uevent(&card->gdev->dev.kobj, KOBJ_CHANGE);
 
        mutex_unlock(&card->conf_mutex);
-       mutex_unlock(&card->discipline_mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(qeth_set_offline);
 
 static int qeth_do_reset(void *data)
 {
+       const struct qeth_discipline *disc;
        struct qeth_card *card = data;
        int rc;
 
+       /* Lock-free, other users will block until we are done. */
+       disc = card->discipline;
+
        QETH_CARD_TEXT(card, 2, "recover1");
        if (!qeth_do_run_thread(card, QETH_RECOVER_THREAD))
                return 0;
@@ -5619,8 +5626,8 @@ static int qeth_do_reset(void *data)
        dev_warn(&card->gdev->dev,
                 "A recovery process has been started for the device\n");
 
-       qeth_set_offline(card, true);
-       rc = qeth_set_online(card);
+       qeth_set_offline(card, disc, true);
+       rc = qeth_set_online(card, disc);
        if (!rc) {
                dev_info(&card->gdev->dev,
                         "Device successfully recovered!\n");
@@ -6348,9 +6355,11 @@ static int qeth_register_dbf_views(void)
 
 static DEFINE_MUTEX(qeth_mod_mutex);   /* for synchronized module loading */
 
-int qeth_core_load_discipline(struct qeth_card *card,
-               enum qeth_discipline_id discipline)
+int qeth_setup_discipline(struct qeth_card *card,
+                         enum qeth_discipline_id discipline)
 {
+       int rc;
+
        mutex_lock(&qeth_mod_mutex);
        switch (discipline) {
        case QETH_DISCIPLINE_LAYER3:
@@ -6372,12 +6381,25 @@ int qeth_core_load_discipline(struct qeth_card *card,
                return -EINVAL;
        }
 
+       rc = card->discipline->setup(card->gdev);
+       if (rc) {
+               if (discipline == QETH_DISCIPLINE_LAYER2)
+                       symbol_put(qeth_l2_discipline);
+               else
+                       symbol_put(qeth_l3_discipline);
+               card->discipline = NULL;
+
+               return rc;
+       }
+
        card->options.layer = discipline;
        return 0;
 }
 
-void qeth_core_free_discipline(struct qeth_card *card)
+void qeth_remove_discipline(struct qeth_card *card)
 {
+       card->discipline->remove(card->gdev);
+
        if (IS_LAYER2(card))
                symbol_put(qeth_l2_discipline);
        else
@@ -6584,23 +6606,19 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
                break;
        default:
                card->info.layer_enforced = true;
-               rc = qeth_core_load_discipline(card, enforced_disc);
+               /* It's so early that we don't need the discipline_mutex yet. */
+               rc = qeth_setup_discipline(card, enforced_disc);
                if (rc)
-                       goto err_load;
+                       goto err_setup_disc;
 
                gdev->dev.type = IS_OSN(card) ? &qeth_osn_devtype :
                                                card->discipline->devtype;
-               rc = card->discipline->setup(card->gdev);
-               if (rc)
-                       goto err_disc;
                break;
        }
 
        return 0;
 
-err_disc:
-       qeth_core_free_discipline(card);
-err_load:
+err_setup_disc:
 err_chp_desc:
        free_netdev(card->dev);
 err_card:
@@ -6616,10 +6634,10 @@ static void qeth_core_remove_device(struct ccwgroup_device *gdev)
 
        QETH_CARD_TEXT(card, 2, "removedv");
 
-       if (card->discipline) {
-               card->discipline->remove(gdev);
-               qeth_core_free_discipline(card);
-       }
+       mutex_lock(&card->discipline_mutex);
+       if (card->discipline)
+               qeth_remove_discipline(card);
+       mutex_unlock(&card->discipline_mutex);
 
        qeth_free_qdio_queues(card);
 
@@ -6634,29 +6652,32 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev)
        int rc = 0;
        enum qeth_discipline_id def_discipline;
 
+       mutex_lock(&card->discipline_mutex);
        if (!card->discipline) {
                def_discipline = IS_IQD(card) ? QETH_DISCIPLINE_LAYER3 :
                                                QETH_DISCIPLINE_LAYER2;
-               rc = qeth_core_load_discipline(card, def_discipline);
+               rc = qeth_setup_discipline(card, def_discipline);
                if (rc)
                        goto err;
-               rc = card->discipline->setup(card->gdev);
-               if (rc) {
-                       qeth_core_free_discipline(card);
-                       goto err;
-               }
        }
 
-       rc = qeth_set_online(card);
+       rc = qeth_set_online(card, card->discipline);
+
 err:
+       mutex_unlock(&card->discipline_mutex);
        return rc;
 }
 
 static int qeth_core_set_offline(struct ccwgroup_device *gdev)
 {
        struct qeth_card *card = dev_get_drvdata(&gdev->dev);
+       int rc;
+
+       mutex_lock(&card->discipline_mutex);
+       rc = qeth_set_offline(card, card->discipline, false);
+       mutex_unlock(&card->discipline_mutex);
 
-       return qeth_set_offline(card, false);
+       return rc;
 }
 
 static void qeth_core_shutdown(struct ccwgroup_device *gdev)
index a0f777f..5815114 100644 (file)
@@ -384,19 +384,13 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,
                        goto out;
                }
 
-               card->discipline->remove(card->gdev);
-               qeth_core_free_discipline(card);
+               qeth_remove_discipline(card);
                free_netdev(card->dev);
                card->dev = ndev;
        }
 
-       rc = qeth_core_load_discipline(card, newdis);
-       if (rc)
-               goto out;
+       rc = qeth_setup_discipline(card, newdis);
 
-       rc = card->discipline->setup(card->gdev);
-       if (rc)
-               qeth_core_free_discipline(card);
 out:
        mutex_unlock(&card->discipline_mutex);
        return rc ? rc : count;
index 4ed0fb0..ca44421 100644 (file)
@@ -157,7 +157,7 @@ static void qeth_l2_drain_rx_mode_cache(struct qeth_card *card)
 
 static void qeth_l2_fill_header(struct qeth_qdio_out_q *queue,
                                struct qeth_hdr *hdr, struct sk_buff *skb,
-                               int ipv, unsigned int data_len)
+                               __be16 proto, unsigned int data_len)
 {
        int cast_type = qeth_get_ether_cast_type(skb);
        struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
@@ -169,7 +169,7 @@ static void qeth_l2_fill_header(struct qeth_qdio_out_q *queue,
        } else {
                hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
                if (skb->ip_summed == CHECKSUM_PARTIAL)
-                       qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], ipv);
+                       qeth_tx_csum(skb, &hdr->hdr.l2.flags[1], proto);
        }
 
        /* set byte byte 3 to casting flags */
@@ -551,7 +551,7 @@ static netdev_tx_t qeth_l2_hard_start_xmit(struct sk_buff *skb,
        if (IS_OSN(card))
                rc = qeth_l2_xmit_osn(card, skb, queue);
        else
-               rc = qeth_xmit(card, skb, queue, qeth_get_ip_version(skb),
+               rc = qeth_xmit(card, skb, queue, vlan_get_protocol(skb),
                               qeth_l2_fill_header);
 
        if (!rc)
@@ -2208,7 +2208,7 @@ static void qeth_l2_remove_device(struct ccwgroup_device *gdev)
        wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
 
        if (gdev->state == CCWGROUP_ONLINE)
-               qeth_set_offline(card, false);
+               qeth_set_offline(card, card->discipline, false);
 
        cancel_work_sync(&card->close_dev_work);
        if (card->dev->reg_state == NETREG_REGISTERED)
index d138ac4..dd441ea 100644 (file)
@@ -1576,7 +1576,7 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 }
 
 static int qeth_l3_get_cast_type_rcu(struct sk_buff *skb, struct dst_entry *dst,
-                                    int ipv)
+                                    __be16 proto)
 {
        struct neighbour *n = NULL;
 
@@ -1595,30 +1595,31 @@ static int qeth_l3_get_cast_type_rcu(struct sk_buff *skb, struct dst_entry *dst,
        }
 
        /* no neighbour (eg AF_PACKET), fall back to target's IP address ... */
-       switch (ipv) {
-       case 4:
+       switch (proto) {
+       case htons(ETH_P_IP):
                if (ipv4_is_lbcast(ip_hdr(skb)->daddr))
                        return RTN_BROADCAST;
                return ipv4_is_multicast(ip_hdr(skb)->daddr) ?
                                RTN_MULTICAST : RTN_UNICAST;
-       case 6:
+       case htons(ETH_P_IPV6):
                return ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr) ?
                                RTN_MULTICAST : RTN_UNICAST;
+       case htons(ETH_P_AF_IUCV):
+               return RTN_UNICAST;
        default:
-               /* ... and MAC address */
+               /* OSA only: ... and MAC address */
                return qeth_get_ether_cast_type(skb);
        }
 }
 
-static int qeth_l3_get_cast_type(struct sk_buff *skb)
+static int qeth_l3_get_cast_type(struct sk_buff *skb, __be16 proto)
 {
-       int ipv = qeth_get_ip_version(skb);
        struct dst_entry *dst;
        int cast_type;
 
        rcu_read_lock();
-       dst = qeth_dst_check_rcu(skb, ipv);
-       cast_type = qeth_l3_get_cast_type_rcu(skb, dst, ipv);
+       dst = qeth_dst_check_rcu(skb, proto);
+       cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto);
        rcu_read_unlock();
 
        return cast_type;
@@ -1637,7 +1638,7 @@ static u8 qeth_l3_cast_type_to_flag(int cast_type)
 
 static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
                                struct qeth_hdr *hdr, struct sk_buff *skb,
-                               int ipv, unsigned int data_len)
+                               __be16 proto, unsigned int data_len)
 {
        struct qeth_hdr_layer3 *l3_hdr = &hdr->hdr.l3;
        struct vlan_ethhdr *veth = vlan_eth_hdr(skb);
@@ -1652,23 +1653,15 @@ static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
        } else {
                hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
 
-               if (skb->protocol == htons(ETH_P_AF_IUCV)) {
-                       l3_hdr->flags = QETH_HDR_IPV6 | QETH_CAST_UNICAST;
-                       l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80);
-                       memcpy(&l3_hdr->next_hop.addr.s6_addr32[2],
-                              iucv_trans_hdr(skb)->destUserID, 8);
-                       return;
-               }
-
                if (skb->ip_summed == CHECKSUM_PARTIAL) {
-                       qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, ipv);
+                       qeth_tx_csum(skb, &hdr->hdr.l3.ext_flags, proto);
                        /* some HW requires combined L3+L4 csum offload: */
-                       if (ipv == 4)
+                       if (proto == htons(ETH_P_IP))
                                hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_CSUM_HDR_REQ;
                }
        }
 
-       if (ipv == 4 || IS_IQD(card)) {
+       if (proto == htons(ETH_P_IP) || IS_IQD(card)) {
                /* NETIF_F_HW_VLAN_CTAG_TX */
                if (skb_vlan_tag_present(skb)) {
                        hdr->hdr.l3.ext_flags |= QETH_HDR_EXT_VLAN_FRAME;
@@ -1680,24 +1673,33 @@ static void qeth_l3_fill_header(struct qeth_qdio_out_q *queue,
        }
 
        rcu_read_lock();
-       dst = qeth_dst_check_rcu(skb, ipv);
+       dst = qeth_dst_check_rcu(skb, proto);
 
        if (IS_IQD(card) && skb_get_queue_mapping(skb) != QETH_IQD_MCAST_TXQ)
                cast_type = RTN_UNICAST;
        else
-               cast_type = qeth_l3_get_cast_type_rcu(skb, dst, ipv);
+               cast_type = qeth_l3_get_cast_type_rcu(skb, dst, proto);
        l3_hdr->flags |= qeth_l3_cast_type_to_flag(cast_type);
 
-       if (ipv == 4) {
+       switch (proto) {
+       case htons(ETH_P_IP):
                l3_hdr->next_hop.addr.s6_addr32[3] =
                                        qeth_next_hop_v4_rcu(skb, dst);
-       } else if (ipv == 6) {
+               break;
+       case htons(ETH_P_IPV6):
                l3_hdr->next_hop.addr = *qeth_next_hop_v6_rcu(skb, dst);
 
                hdr->hdr.l3.flags |= QETH_HDR_IPV6;
                if (!IS_IQD(card))
                        hdr->hdr.l3.flags |= QETH_HDR_PASSTHRU;
-       } else {
+               break;
+       case htons(ETH_P_AF_IUCV):
+               l3_hdr->next_hop.addr.s6_addr16[0] = htons(0xfe80);
+               memcpy(&l3_hdr->next_hop.addr.s6_addr32[2],
+                      iucv_trans_hdr(skb)->destUserID, 8);
+               l3_hdr->flags |= QETH_HDR_IPV6;
+               break;
+       default:
                /* OSA only: */
                l3_hdr->flags |= QETH_HDR_PASSTHRU;
        }
@@ -1719,7 +1721,7 @@ static void qeth_l3_fixup_headers(struct sk_buff *skb)
 }
 
 static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
-                       struct qeth_qdio_out_q *queue, int ipv)
+                       struct qeth_qdio_out_q *queue, __be16 proto)
 {
        unsigned int hw_hdr_len;
        int rc;
@@ -1733,15 +1735,15 @@ static int qeth_l3_xmit(struct qeth_card *card, struct sk_buff *skb,
        skb_pull(skb, ETH_HLEN);
 
        qeth_l3_fixup_headers(skb);
-       return qeth_xmit(card, skb, queue, ipv, qeth_l3_fill_header);
+       return qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header);
 }
 
 static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
                                           struct net_device *dev)
 {
        struct qeth_card *card = dev->ml_priv;
+       __be16 proto = vlan_get_protocol(skb);
        u16 txq = skb_get_queue_mapping(skb);
-       int ipv = qeth_get_ip_version(skb);
        struct qeth_qdio_out_q *queue;
        int rc;
 
@@ -1752,22 +1754,32 @@ static netdev_tx_t qeth_l3_hard_start_xmit(struct sk_buff *skb,
 
                if (card->options.sniffer)
                        goto tx_drop;
-               if ((card->options.cq != QETH_CQ_ENABLED && !ipv) ||
-                   (card->options.cq == QETH_CQ_ENABLED &&
-                    skb->protocol != htons(ETH_P_AF_IUCV)))
+
+               switch (proto) {
+               case htons(ETH_P_AF_IUCV):
+                       if (card->options.cq != QETH_CQ_ENABLED)
+                               goto tx_drop;
+                       break;
+               case htons(ETH_P_IP):
+               case htons(ETH_P_IPV6):
+                       if (card->options.cq == QETH_CQ_ENABLED)
+                               goto tx_drop;
+                       break;
+               default:
                        goto tx_drop;
+               }
        } else {
                queue = card->qdio.out_qs[txq];
        }
 
        if (!(dev->flags & IFF_BROADCAST) &&
-           qeth_l3_get_cast_type(skb) == RTN_BROADCAST)
+           qeth_l3_get_cast_type(skb, proto) == RTN_BROADCAST)
                goto tx_drop;
 
-       if (ipv == 4 || IS_IQD(card))
-               rc = qeth_l3_xmit(card, skb, queue, ipv);
+       if (proto == htons(ETH_P_IP) || IS_IQD(card))
+               rc = qeth_l3_xmit(card, skb, queue, proto);
        else
-               rc = qeth_xmit(card, skb, queue, ipv, qeth_l3_fill_header);
+               rc = qeth_xmit(card, skb, queue, proto, qeth_l3_fill_header);
 
        if (!rc)
                return NETDEV_TX_OK;
@@ -1813,7 +1825,7 @@ static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
                                                    struct net_device *dev,
                                                    netdev_features_t features)
 {
-       if (qeth_get_ip_version(skb) != 4)
+       if (vlan_get_protocol(skb) != htons(ETH_P_IP))
                features &= ~NETIF_F_HW_VLAN_CTAG_TX;
        return qeth_features_check(skb, dev, features);
 }
@@ -1821,8 +1833,10 @@ static netdev_features_t qeth_l3_osa_features_check(struct sk_buff *skb,
 static u16 qeth_l3_iqd_select_queue(struct net_device *dev, struct sk_buff *skb,
                                    struct net_device *sb_dev)
 {
-       return qeth_iqd_select_queue(dev, skb, qeth_l3_get_cast_type(skb),
-                                    sb_dev);
+       __be16 proto = vlan_get_protocol(skb);
+
+       return qeth_iqd_select_queue(dev, skb,
+                                    qeth_l3_get_cast_type(skb, proto), sb_dev);
 }
 
 static u16 qeth_l3_osa_select_queue(struct net_device *dev, struct sk_buff *skb,
@@ -1971,7 +1985,7 @@ static void qeth_l3_remove_device(struct ccwgroup_device *cgdev)
        wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0);
 
        if (cgdev->state == CCWGROUP_ONLINE)
-               qeth_set_offline(card, false);
+               qeth_set_offline(card, card->discipline, false);
 
        cancel_work_sync(&card->close_dev_work);
        if (card->dev->reg_state == NETREG_REGISTERED)
index b206e26..8b0deec 100644 (file)
@@ -4,6 +4,7 @@ config SCSI_CXGB4_ISCSI
        depends on PCI && INET && (IPV6 || IPV6=n)
        depends on THERMAL || !THERMAL
        depends on ETHERNET
+       depends on TLS || TLS=n
        select NET_VENDOR_CHELSIO
        select CHELSIO_T4
        select CHELSIO_LIB
index a2beee6..5988c30 100644 (file)
@@ -444,7 +444,8 @@ static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
        fetch_index = ioread32(&vdev->devcmd2->wq.ctrl->fetch_index);
        if (fetch_index == 0xFFFFFFFF) { /* check for hardware gone  */
                pr_err("error in devcmd2 init");
-               return -ENODEV;
+               err = -ENODEV;
+               goto err_free_wq;
        }
 
        /*
@@ -460,7 +461,7 @@ static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
        err = vnic_dev_alloc_desc_ring(vdev, &vdev->devcmd2->results_ring,
                        DEVCMD2_RING_SIZE, DEVCMD2_DESC_SIZE);
        if (err)
-               goto err_free_wq;
+               goto err_disable_wq;
 
        vdev->devcmd2->result =
                (struct devcmd2_result *) vdev->devcmd2->results_ring.descs;
@@ -481,8 +482,9 @@ static int vnic_dev_init_devcmd2(struct vnic_dev *vdev)
 
 err_free_desc_ring:
        vnic_dev_free_desc_ring(vdev, &vdev->devcmd2->results_ring);
-err_free_wq:
+err_disable_wq:
        vnic_wq_disable(&vdev->devcmd2->wq);
+err_free_wq:
        vnic_wq_free(&vdev->devcmd2->wq);
 err_free_devcmd2:
        kfree(vdev->devcmd2);
index 2b28dd4..e821dd3 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/debugfs.h>
 #include <linux/dmapool.h>
 #include <linux/iopoll.h>
+#include <linux/irq.h>
 #include <linux/lcm.h>
 #include <linux/libata.h>
 #include <linux/mfd/syscon.h>
@@ -294,6 +295,7 @@ enum {
 
 struct hisi_sas_hw {
        int (*hw_init)(struct hisi_hba *hisi_hba);
+       int (*interrupt_preinit)(struct hisi_hba *hisi_hba);
        void (*setup_itct)(struct hisi_hba *hisi_hba,
                           struct hisi_sas_device *device);
        int (*slot_index_alloc)(struct hisi_hba *hisi_hba,
@@ -393,6 +395,8 @@ struct hisi_hba {
        u32 refclk_frequency_mhz;
        u8 sas_addr[SAS_ADDR_SIZE];
 
+       int *irq_map; /* v2 hw */
+
        int n_phy;
        spinlock_t lock;
        struct semaphore sem;
index b6d4419..cf0bfac 100644 (file)
@@ -2614,6 +2614,13 @@ err_out:
        return NULL;
 }
 
+static int hisi_sas_interrupt_preinit(struct hisi_hba *hisi_hba)
+{
+       if (hisi_hba->hw->interrupt_preinit)
+               return hisi_hba->hw->interrupt_preinit(hisi_hba);
+       return 0;
+}
+
 int hisi_sas_probe(struct platform_device *pdev,
                   const struct hisi_sas_hw *hw)
 {
@@ -2671,6 +2678,10 @@ int hisi_sas_probe(struct platform_device *pdev,
                sha->sas_port[i] = &hisi_hba->port[i].sas_port;
        }
 
+       rc = hisi_sas_interrupt_preinit(hisi_hba);
+       if (rc)
+               goto err_out_ha;
+
        rc = scsi_add_host(shost, &pdev->dev);
        if (rc)
                goto err_out_ha;
index b57177b..9adfdef 100644 (file)
@@ -3302,6 +3302,28 @@ static irq_handler_t fatal_interrupts[HISI_SAS_FATAL_INT_NR] = {
        fatal_axi_int_v2_hw
 };
 
+#define CQ0_IRQ_INDEX (96)
+
+static int hisi_sas_v2_interrupt_preinit(struct hisi_hba *hisi_hba)
+{
+       struct platform_device *pdev = hisi_hba->platform_dev;
+       struct Scsi_Host *shost = hisi_hba->shost;
+       struct irq_affinity desc = {
+               .pre_vectors = CQ0_IRQ_INDEX,
+               .post_vectors = 16,
+       };
+       int resv = desc.pre_vectors + desc.post_vectors, minvec = resv + 1, nvec;
+
+       nvec = devm_platform_get_irqs_affinity(pdev, &desc, minvec, 128,
+                                              &hisi_hba->irq_map);
+       if (nvec < 0)
+               return nvec;
+
+       shost->nr_hw_queues = hisi_hba->cq_nvecs = nvec - resv;
+
+       return 0;
+}
+
 /*
  * There is a limitation in the hip06 chipset that we need
  * to map in all mbigen interrupts, even if they are not used.
@@ -3310,14 +3332,11 @@ static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba)
 {
        struct platform_device *pdev = hisi_hba->platform_dev;
        struct device *dev = &pdev->dev;
-       int irq, rc = 0, irq_map[128];
+       int irq, rc = 0;
        int i, phy_no, fatal_no, queue_no;
 
-       for (i = 0; i < 128; i++)
-               irq_map[i] = platform_get_irq(pdev, i);
-
        for (i = 0; i < HISI_SAS_PHY_INT_NR; i++) {
-               irq = irq_map[i + 1]; /* Phy up/down is irq1 */
+               irq = hisi_hba->irq_map[i + 1]; /* Phy up/down is irq1 */
                rc = devm_request_irq(dev, irq, phy_interrupts[i], 0,
                                      DRV_NAME " phy", hisi_hba);
                if (rc) {
@@ -3331,7 +3350,7 @@ static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba)
        for (phy_no = 0; phy_no < hisi_hba->n_phy; phy_no++) {
                struct hisi_sas_phy *phy = &hisi_hba->phy[phy_no];
 
-               irq = irq_map[phy_no + 72];
+               irq = hisi_hba->irq_map[phy_no + 72];
                rc = devm_request_irq(dev, irq, sata_int_v2_hw, 0,
                                      DRV_NAME " sata", phy);
                if (rc) {
@@ -3343,7 +3362,7 @@ static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba)
        }
 
        for (fatal_no = 0; fatal_no < HISI_SAS_FATAL_INT_NR; fatal_no++) {
-               irq = irq_map[fatal_no + 81];
+               irq = hisi_hba->irq_map[fatal_no + 81];
                rc = devm_request_irq(dev, irq, fatal_interrupts[fatal_no], 0,
                                      DRV_NAME " fatal", hisi_hba);
                if (rc) {
@@ -3354,24 +3373,22 @@ static int interrupt_init_v2_hw(struct hisi_hba *hisi_hba)
                }
        }
 
-       for (queue_no = 0; queue_no < hisi_hba->queue_count; queue_no++) {
+       for (queue_no = 0; queue_no < hisi_hba->cq_nvecs; queue_no++) {
                struct hisi_sas_cq *cq = &hisi_hba->cq[queue_no];
 
-               cq->irq_no = irq_map[queue_no + 96];
+               cq->irq_no = hisi_hba->irq_map[queue_no + 96];
                rc = devm_request_threaded_irq(dev, cq->irq_no,
                                               cq_interrupt_v2_hw,
                                               cq_thread_v2_hw, IRQF_ONESHOT,
                                               DRV_NAME " cq", cq);
                if (rc) {
                        dev_err(dev, "irq init: could not request cq interrupt %d, rc=%d\n",
-                               irq, rc);
+                                       cq->irq_no, rc);
                        rc = -ENOENT;
                        goto err_out;
                }
+               cq->irq_mask = irq_get_affinity_mask(cq->irq_no);
        }
-
-       hisi_hba->cq_nvecs = hisi_hba->queue_count;
-
 err_out:
        return rc;
 }
@@ -3529,6 +3546,26 @@ static struct device_attribute *host_attrs_v2_hw[] = {
        NULL
 };
 
+static int map_queues_v2_hw(struct Scsi_Host *shost)
+{
+       struct hisi_hba *hisi_hba = shost_priv(shost);
+       struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT];
+       const struct cpumask *mask;
+       unsigned int queue, cpu;
+
+       for (queue = 0; queue < qmap->nr_queues; queue++) {
+               mask = irq_get_affinity_mask(hisi_hba->irq_map[96 + queue]);
+               if (!mask)
+                       continue;
+
+               for_each_cpu(cpu, mask)
+                       qmap->mq_map[cpu] = qmap->queue_offset + queue;
+       }
+
+       return 0;
+
+}
+
 static struct scsi_host_template sht_v2_hw = {
        .name                   = DRV_NAME,
        .proc_name              = DRV_NAME,
@@ -3553,10 +3590,13 @@ static struct scsi_host_template sht_v2_hw = {
 #endif
        .shost_attrs            = host_attrs_v2_hw,
        .host_reset             = hisi_sas_host_reset,
+       .map_queues             = map_queues_v2_hw,
+       .host_tagset            = 1,
 };
 
 static const struct hisi_sas_hw hisi_sas_v2_hw = {
        .hw_init = hisi_sas_v2_init,
+       .interrupt_preinit = hisi_sas_v2_interrupt_preinit,
        .setup_itct = setup_itct_v2_hw,
        .slot_index_alloc = slot_index_alloc_quirk_v2_hw,
        .alloc_dev = alloc_dev_quirk_v2_hw,
index 42e4d35..65f168c 100644 (file)
@@ -1744,7 +1744,7 @@ static int ibmvfc_queuecommand_lck(struct scsi_cmnd *cmnd,
                iu->pri_task_attr = IBMVFC_SIMPLE_TASK;
        }
 
-       vfc_cmd->correlation = cpu_to_be64(evt);
+       vfc_cmd->correlation = cpu_to_be64((u64)evt);
 
        if (likely(!(rc = ibmvfc_map_sg_data(cmnd, evt, vfc_cmd, vhost->dev))))
                return ibmvfc_send_event(evt, vhost, 0);
@@ -2418,7 +2418,7 @@ static int ibmvfc_abort_task_set(struct scsi_device *sdev)
                tmf->flags = cpu_to_be16((IBMVFC_NO_MEM_DESC | IBMVFC_TMF));
                evt->sync_iu = &rsp_iu;
 
-               tmf->correlation = cpu_to_be64(evt);
+               tmf->correlation = cpu_to_be64((u64)evt);
 
                init_completion(&evt->comp);
                rsp_rc = ibmvfc_send_event(evt, vhost, default_timeout);
@@ -3007,8 +3007,10 @@ static int ibmvfc_slave_configure(struct scsi_device *sdev)
        unsigned long flags = 0;
 
        spin_lock_irqsave(shost->host_lock, flags);
-       if (sdev->type == TYPE_DISK)
+       if (sdev->type == TYPE_DISK) {
                sdev->allow_restart = 1;
+               blk_queue_rq_timeout(sdev->request_queue, 120 * HZ);
+       }
        spin_unlock_irqrestore(shost->host_lock, flags);
        return 0;
 }
index d71afae..8410004 100644 (file)
@@ -1623,8 +1623,13 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
                rc = fc_exch_done_locked(ep);
                WARN_ON(fc_seq_exch(sp) != ep);
                spin_unlock_bh(&ep->ex_lock);
-               if (!rc)
+               if (!rc) {
                        fc_exch_delete(ep);
+               } else {
+                       FC_EXCH_DBG(ep, "ep is completed already,"
+                                       "hence skip calling the resp\n");
+                       goto skip_resp;
+               }
        }
 
        /*
@@ -1643,6 +1648,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
        if (!fc_invoke_resp(ep, sp, fp))
                fc_frame_free(fp);
 
+skip_resp:
        fc_exch_release(ep);
        return;
 rel:
@@ -1899,10 +1905,16 @@ static void fc_exch_reset(struct fc_exch *ep)
 
        fc_exch_hold(ep);
 
-       if (!rc)
+       if (!rc) {
                fc_exch_delete(ep);
+       } else {
+               FC_EXCH_DBG(ep, "ep is completed already,"
+                               "hence skip calling the resp\n");
+               goto skip_resp;
+       }
 
        fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED));
+skip_resp:
        fc_seq_set_resp(sp, NULL, ep->arg);
        fc_exch_release(ep);
 }
index 6e4bf05..63a4f48 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/poll.h>
 #include <linux/vmalloc.h>
 #include <linux/irq_poll.h>
+#include <linux/blk-mq-pci.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -113,6 +114,10 @@ unsigned int enable_sdev_max_qd;
 module_param(enable_sdev_max_qd, int, 0444);
 MODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue. Default: 0");
 
+int host_tagset_enable = 1;
+module_param(host_tagset_enable, int, 0444);
+MODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: enable(1)");
+
 MODULE_LICENSE("GPL");
 MODULE_VERSION(MEGASAS_VERSION);
 MODULE_AUTHOR("megaraidlinux.pdl@broadcom.com");
@@ -3119,6 +3124,19 @@ megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev,
        return 0;
 }
 
+static int megasas_map_queues(struct Scsi_Host *shost)
+{
+       struct megasas_instance *instance;
+
+       instance = (struct megasas_instance *)shost->hostdata;
+
+       if (shost->nr_hw_queues == 1)
+               return 0;
+
+       return blk_mq_pci_map_queues(&shost->tag_set.map[HCTX_TYPE_DEFAULT],
+                       instance->pdev, instance->low_latency_index_start);
+}
+
 static void megasas_aen_polling(struct work_struct *work);
 
 /**
@@ -3427,6 +3445,7 @@ static struct scsi_host_template megasas_template = {
        .eh_timed_out = megasas_reset_timer,
        .shost_attrs = megaraid_host_attrs,
        .bios_param = megasas_bios_param,
+       .map_queues = megasas_map_queues,
        .change_queue_depth = scsi_change_queue_depth,
        .max_segment_size = 0xffffffff,
 };
@@ -6808,6 +6827,26 @@ static int megasas_io_attach(struct megasas_instance *instance)
        host->max_lun = MEGASAS_MAX_LUN;
        host->max_cmd_len = 16;
 
+       /* Use shared host tagset only for fusion adaptors
+        * if there are managed interrupts (smp affinity enabled case).
+        * Single msix_vectors in kdump, so shared host tag is also disabled.
+        */
+
+       host->host_tagset = 0;
+       host->nr_hw_queues = 1;
+
+       if ((instance->adapter_type != MFI_SERIES) &&
+               (instance->msix_vectors > instance->low_latency_index_start) &&
+               host_tagset_enable &&
+               instance->smp_affinity_enable) {
+               host->host_tagset = 1;
+               host->nr_hw_queues = instance->msix_vectors -
+                       instance->low_latency_index_start;
+       }
+
+       dev_info(&instance->pdev->dev,
+               "Max firmware commands: %d shared with nr_hw_queues = %d\n",
+               instance->max_fw_cmds, host->nr_hw_queues);
        /*
         * Notify the mid-layer about the new controller
         */
@@ -8205,11 +8244,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance,
                        goto out;
                }
 
+               /* always store 64 bits regardless of addressing */
                sense_ptr = (void *)cmd->frame + ioc->sense_off;
-               if (instance->consistent_mask_64bit)
-                       put_unaligned_le64(sense_handle, sense_ptr);
-               else
-                       put_unaligned_le32(sense_handle, sense_ptr);
+               put_unaligned_le64(sense_handle, sense_ptr);
        }
 
        /*
index b0c01cf..fd60728 100644 (file)
@@ -359,24 +359,29 @@ megasas_get_msix_index(struct megasas_instance *instance,
 {
        int sdev_busy;
 
-       /* nr_hw_queue = 1 for MegaRAID */
-       struct blk_mq_hw_ctx *hctx =
-               scmd->device->request_queue->queue_hw_ctx[0];
-
-       sdev_busy = atomic_read(&hctx->nr_active);
+       /* TBD - if sml remove device_busy in future, driver
+        * should track counter in internal structure.
+        */
+       sdev_busy = atomic_read(&scmd->device->device_busy);
 
        if (instance->perf_mode == MR_BALANCED_PERF_MODE &&
-           sdev_busy > (data_arms * MR_DEVICE_HIGH_IOPS_DEPTH))
+           sdev_busy > (data_arms * MR_DEVICE_HIGH_IOPS_DEPTH)) {
                cmd->request_desc->SCSIIO.MSIxIndex =
                        mega_mod64((atomic64_add_return(1, &instance->high_iops_outstanding) /
                                        MR_HIGH_IOPS_BATCH_COUNT), instance->low_latency_index_start);
-       else if (instance->msix_load_balance)
+       } else if (instance->msix_load_balance) {
                cmd->request_desc->SCSIIO.MSIxIndex =
                        (mega_mod64(atomic64_add_return(1, &instance->total_io_count),
                                instance->msix_vectors));
-       else
+       } else if (instance->host->nr_hw_queues > 1) {
+               u32 tag = blk_mq_unique_tag(scmd->request);
+
+               cmd->request_desc->SCSIIO.MSIxIndex = blk_mq_unique_tag_to_hwq(tag) +
+                       instance->low_latency_index_start;
+       } else {
                cmd->request_desc->SCSIIO.MSIxIndex =
                        instance->reply_map[raw_smp_processor_id()];
+       }
 }
 
 /**
@@ -956,9 +961,6 @@ megasas_alloc_cmds_fusion(struct megasas_instance *instance)
        if (megasas_alloc_cmdlist_fusion(instance))
                goto fail_exit;
 
-       dev_info(&instance->pdev->dev, "Configured max firmware commands: %d\n",
-                instance->max_fw_cmds);
-
        /* The first 256 bytes (SMID 0) is not used. Don't add to the cmd list */
        io_req_base = fusion->io_request_frames + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
        io_req_base_phys = fusion->io_request_frames_phys + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE;
@@ -1102,8 +1104,9 @@ megasas_ioc_init_fusion(struct megasas_instance *instance)
                MR_HIGH_IOPS_QUEUE_COUNT) && cur_intr_coalescing)
                instance->perf_mode = MR_BALANCED_PERF_MODE;
 
-       dev_info(&instance->pdev->dev, "Performance mode :%s\n",
-               MEGASAS_PERF_MODE_2STR(instance->perf_mode));
+       dev_info(&instance->pdev->dev, "Performance mode :%s (latency index = %d)\n",
+               MEGASAS_PERF_MODE_2STR(instance->perf_mode),
+               instance->low_latency_index_start);
 
        instance->fw_sync_cache_support = (scratch_pad_1 &
                MR_CAN_HANDLE_SYNC_CACHE_OFFSET) ? 1 : 0;
index 8620945..c299f7e 100644 (file)
@@ -79,5 +79,5 @@ config SCSI_MPT2SAS
        select SCSI_MPT3SAS
        depends on PCI && SCSI
        help
-       Dummy config option for backwards compatiblity: configure the MPT3SAS
+       Dummy config option for backwards compatibility: configure the MPT3SAS
        driver instead.
index 969baf4..6e23dc3 100644 (file)
@@ -5034,7 +5034,7 @@ _base_check_for_trigger_pages_support(struct MPT3SAS_ADAPTER *ioc)
 static void
 _base_get_diag_triggers(struct MPT3SAS_ADAPTER *ioc)
 {
-       u16 trigger_flags;
+       int trigger_flags;
 
        /*
         * Default setting of master trigger.
index f5fc7f5..47ad64b 100644 (file)
@@ -2245,7 +2245,7 @@ qedi_show_boot_tgt_info(struct qedi_ctx *qedi, int type,
                             chap_name);
                break;
        case ISCSI_BOOT_TGT_CHAP_SECRET:
-               rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN,
+               rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN,
                             chap_secret);
                break;
        case ISCSI_BOOT_TGT_REV_CHAP_NAME:
@@ -2253,7 +2253,7 @@ qedi_show_boot_tgt_info(struct qedi_ctx *qedi, int type,
                             mchap_name);
                break;
        case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
-               rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN,
+               rc = sprintf(buf, "%.*s\n", NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN,
                             mchap_secret);
                break;
        case ISCSI_BOOT_TGT_FLAGS:
index 24c0f7e..4a08c45 100644 (file)
@@ -6740,7 +6740,7 @@ static int __init scsi_debug_init(void)
                k = sdeb_zbc_model_str(sdeb_zbc_model_s);
                if (k < 0) {
                        ret = k;
-                       goto free_vm;
+                       goto free_q_arr;
                }
                sdeb_zbc_model = k;
                switch (sdeb_zbc_model) {
@@ -6753,7 +6753,8 @@ static int __init scsi_debug_init(void)
                        break;
                default:
                        pr_err("Invalid ZBC model\n");
-                       return -EINVAL;
+                       ret = -EINVAL;
+                       goto free_q_arr;
                }
        }
        if (sdeb_zbc_model != BLK_ZONED_NONE) {
index 4848ae3..b3f14f0 100644 (file)
@@ -249,7 +249,8 @@ int __scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
 
        req = blk_get_request(sdev->request_queue,
                        data_direction == DMA_TO_DEVICE ?
-                       REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, BLK_MQ_REQ_PREEMPT);
+                       REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN,
+                       rq_flags & RQF_PM ? BLK_MQ_REQ_PM : 0);
        if (IS_ERR(req))
                return ret;
        rq = scsi_req(req);
@@ -1206,6 +1207,8 @@ static blk_status_t
 scsi_device_state_check(struct scsi_device *sdev, struct request *req)
 {
        switch (sdev->sdev_state) {
+       case SDEV_CREATED:
+               return BLK_STS_OK;
        case SDEV_OFFLINE:
        case SDEV_TRANSPORT_OFFLINE:
                /*
@@ -1232,18 +1235,18 @@ scsi_device_state_check(struct scsi_device *sdev, struct request *req)
                return BLK_STS_RESOURCE;
        case SDEV_QUIESCE:
                /*
-                * If the devices is blocked we defer normal commands.
+                * If the device is blocked we only accept power management
+                * commands.
                 */
-               if (req && !(req->rq_flags & RQF_PREEMPT))
+               if (req && WARN_ON_ONCE(!(req->rq_flags & RQF_PM)))
                        return BLK_STS_RESOURCE;
                return BLK_STS_OK;
        default:
                /*
                 * For any other not fully online state we only allow
-                * special commands.  In particular any user initiated
-                * command is not allowed.
+                * power management commands.
                 */
-               if (req && !(req->rq_flags & RQF_PREEMPT))
+               if (req && !(req->rq_flags & RQF_PM))
                        return BLK_STS_IOERR;
                return BLK_STS_OK;
        }
@@ -2516,15 +2519,13 @@ void sdev_evt_send_simple(struct scsi_device *sdev,
 EXPORT_SYMBOL_GPL(sdev_evt_send_simple);
 
 /**
- *     scsi_device_quiesce - Block user issued commands.
+ *     scsi_device_quiesce - Block all commands except power management.
  *     @sdev:  scsi device to quiesce.
  *
  *     This works by trying to transition to the SDEV_QUIESCE state
  *     (which must be a legal transition).  When the device is in this
- *     state, only special requests will be accepted, all others will
- *     be deferred.  Since special requests may also be requeued requests,
- *     a successful return doesn't guarantee the device will be
- *     totally quiescent.
+ *     state, only power management requests will be accepted, all others will
+ *     be deferred.
  *
  *     Must be called with user context, may sleep.
  *
@@ -2586,12 +2587,12 @@ void scsi_device_resume(struct scsi_device *sdev)
         * device deleted during suspend)
         */
        mutex_lock(&sdev->state_mutex);
+       if (sdev->sdev_state == SDEV_QUIESCE)
+               scsi_device_set_state(sdev, SDEV_RUNNING);
        if (sdev->quiesced_by) {
                sdev->quiesced_by = NULL;
                blk_clear_pm_only(sdev->request_queue);
        }
-       if (sdev->sdev_state == SDEV_QUIESCE)
-               scsi_device_set_state(sdev, SDEV_RUNNING);
        mutex_unlock(&sdev->state_mutex);
 }
 EXPORT_SYMBOL(scsi_device_resume);
index f3d5b1b..c37dd15 100644 (file)
@@ -117,12 +117,16 @@ static int spi_execute(struct scsi_device *sdev, const void *cmd,
                sshdr = &sshdr_tmp;
 
        for(i = 0; i < DV_RETRIES; i++) {
+               /*
+                * The purpose of the RQF_PM flag below is to bypass the
+                * SDEV_QUIESCE state.
+                */
                result = scsi_execute(sdev, cmd, dir, buffer, bufflen, sense,
                                      sshdr, DV_TIMEOUT, /* retries */ 1,
                                      REQ_FAILFAST_DEV |
                                      REQ_FAILFAST_TRANSPORT |
                                      REQ_FAILFAST_DRIVER,
-                                     0, NULL);
+                                     RQF_PM, NULL);
                if (driver_byte(result) != DRIVER_SENSE ||
                    sshdr->sense_key != UNIT_ATTENTION)
                        break;
@@ -1005,23 +1009,26 @@ spi_dv_device(struct scsi_device *sdev)
         */
        lock_system_sleep();
 
+       if (scsi_autopm_get_device(sdev))
+               goto unlock_system_sleep;
+
        if (unlikely(spi_dv_in_progress(starget)))
-               goto unlock;
+               goto put_autopm;
 
        if (unlikely(scsi_device_get(sdev)))
-               goto unlock;
+               goto put_autopm;
 
        spi_dv_in_progress(starget) = 1;
 
        buffer = kzalloc(len, GFP_KERNEL);
 
        if (unlikely(!buffer))
-               goto out_put;
+               goto put_sdev;
 
        /* We need to verify that the actual device will quiesce; the
         * later target quiesce is just a nice to have */
        if (unlikely(scsi_device_quiesce(sdev)))
-               goto out_free;
+               goto free_buffer;
 
        scsi_target_quiesce(starget);
 
@@ -1041,12 +1048,16 @@ spi_dv_device(struct scsi_device *sdev)
 
        spi_initial_dv(starget) = 1;
 
- out_free:
+free_buffer:
        kfree(buffer);
- out_put:
+
+put_sdev:
        spi_dv_in_progress(starget) = 0;
        scsi_device_put(sdev);
-unlock:
+put_autopm:
+       scsi_autopm_put_device(sdev);
+
+unlock_system_sleep:
        unlock_system_sleep();
 }
 EXPORT_SYMBOL(spi_dv_device);
index cba1cf6..1e939a2 100644 (file)
@@ -541,7 +541,14 @@ int srp_reconnect_rport(struct srp_rport *rport)
        res = mutex_lock_interruptible(&rport->mutex);
        if (res)
                goto out;
-       scsi_target_block(&shost->shost_gendev);
+       if (rport->state != SRP_RPORT_FAIL_FAST)
+               /*
+                * sdev state must be SDEV_TRANSPORT_OFFLINE, transition
+                * to SDEV_BLOCK is illegal. Calling scsi_target_unblock()
+                * later is ok though, scsi_internal_device_unblock_nowait()
+                * treats SDEV_TRANSPORT_OFFLINE like SDEV_BLOCK.
+                */
+               scsi_target_block(&shost->shost_gendev);
        res = rport->state != SRP_RPORT_LOST ? i->f->reconnect(rport) : -ENODEV;
        pr_debug("%s (state %d): transport.reconnect() returned %d\n",
                 dev_name(&shost->shost_gendev), rport->state, res);
index 679c2c0..a3d2d4b 100644 (file)
@@ -984,8 +984,10 @@ static blk_status_t sd_setup_write_zeroes_cmnd(struct scsi_cmnd *cmd)
                }
        }
 
-       if (sdp->no_write_same)
+       if (sdp->no_write_same) {
+               rq->rq_flags |= RQF_QUIET;
                return BLK_STS_TARGET;
+       }
 
        if (sdkp->ws16 || lba > 0xffffffff || nr_blocks > 0xffff)
                return sd_setup_write_same16_cmnd(cmd, false);
@@ -3510,10 +3512,8 @@ static int sd_probe(struct device *dev)
 static int sd_remove(struct device *dev)
 {
        struct scsi_disk *sdkp;
-       dev_t devt;
 
        sdkp = dev_get_drvdata(dev);
-       devt = disk_devt(sdkp->disk);
        scsi_autopm_get_device(sdkp->device);
 
        async_synchronize_full_domain(&scsi_sd_pm_domain);
index 3f6dfed..b915b38 100644 (file)
@@ -72,6 +72,7 @@ config SCSI_UFS_DWC_TC_PCI
 config SCSI_UFSHCD_PLATFORM
        tristate "Platform bus based UFS Controller support"
        depends on SCSI_UFSHCD
+       depends on HAS_IOMEM
        help
        This selects the UFS host controller support. Select this if
        you have an UFS controller on Platform bus.
index fd6f84c..895e82e 100644 (file)
@@ -31,6 +31,6 @@ TRACE_EVENT(ufs_mtk_event,
 
 #undef TRACE_INCLUDE_PATH
 #undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_PATH ../../drivers/scsi/ufs/
 #define TRACE_INCLUDE_FILE ufs-mediatek-trace
 #include <trace/define_trace.h>
index 3522458..80618af 100644 (file)
@@ -70,6 +70,13 @@ static bool ufs_mtk_is_va09_supported(struct ufs_hba *hba)
        return !!(host->caps & UFS_MTK_CAP_VA09_PWR_CTRL);
 }
 
+static bool ufs_mtk_is_broken_vcc(struct ufs_hba *hba)
+{
+       struct ufs_mtk_host *host = ufshcd_get_variant(hba);
+
+       return !!(host->caps & UFS_MTK_CAP_BROKEN_VCC);
+}
+
 static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
 {
        u32 tmp;
@@ -514,6 +521,9 @@ static void ufs_mtk_init_host_caps(struct ufs_hba *hba)
        if (of_property_read_bool(np, "mediatek,ufs-disable-ah8"))
                host->caps |= UFS_MTK_CAP_DISABLE_AH8;
 
+       if (of_property_read_bool(np, "mediatek,ufs-broken-vcc"))
+               host->caps |= UFS_MTK_CAP_BROKEN_VCC;
+
        dev_info(hba->dev, "caps: 0x%x", host->caps);
 }
 
@@ -1003,6 +1013,17 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba)
 static void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba)
 {
        ufshcd_fixup_dev_quirks(hba, ufs_mtk_dev_fixups);
+
+       if (ufs_mtk_is_broken_vcc(hba) && hba->vreg_info.vcc &&
+           (hba->dev_quirks & UFS_DEVICE_QUIRK_DELAY_AFTER_LPM)) {
+               hba->vreg_info.vcc->always_on = true;
+               /*
+                * VCC will be kept always-on thus we don't
+                * need any delay during regulator operations
+                */
+               hba->dev_quirks &= ~(UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM |
+                       UFS_DEVICE_QUIRK_DELAY_AFTER_LPM);
+       }
 }
 
 static void ufs_mtk_event_notify(struct ufs_hba *hba,
index 93d3509..3f0d3bb 100644 (file)
@@ -81,6 +81,7 @@ enum ufs_mtk_host_caps {
        UFS_MTK_CAP_BOOST_CRYPT_ENGINE         = 1 << 0,
        UFS_MTK_CAP_VA09_PWR_CTRL              = 1 << 1,
        UFS_MTK_CAP_DISABLE_AH8                = 1 << 2,
+       UFS_MTK_CAP_BROKEN_VCC                 = 1 << 3,
 };
 
 struct ufs_mtk_crypt_cfg {
index d593edb..14dfda7 100644 (file)
@@ -330,7 +330,6 @@ enum {
        UFS_DEV_WRITE_BOOSTER_SUP       = BIT(8),
 };
 
-#define POWER_DESC_MAX_SIZE                    0x62
 #define POWER_DESC_MAX_ACTV_ICC_LVLS           16
 
 /* Attribute  bActiveICCLevel parameter bit masks definitions */
@@ -513,6 +512,7 @@ struct ufs_query_res {
 struct ufs_vreg {
        struct regulator *reg;
        const char *name;
+       bool always_on;
        bool enabled;
        int min_uV;
        int max_uV;
index df3a564..fadd566 100644 (file)
@@ -148,6 +148,8 @@ static int ufs_intel_common_init(struct ufs_hba *hba)
 {
        struct intel_host *host;
 
+       hba->caps |= UFSHCD_CAP_RPM_AUTOSUSPEND;
+
        host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL);
        if (!host)
                return -ENOMEM;
@@ -163,6 +165,41 @@ static void ufs_intel_common_exit(struct ufs_hba *hba)
        intel_ltr_hide(hba->dev);
 }
 
+static int ufs_intel_resume(struct ufs_hba *hba, enum ufs_pm_op op)
+{
+       /*
+        * To support S4 (suspend-to-disk) with spm_lvl other than 5, the base
+        * address registers must be restored because the restore kernel can
+        * have used different addresses.
+        */
+       ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
+                     REG_UTP_TRANSFER_REQ_LIST_BASE_L);
+       ufshcd_writel(hba, upper_32_bits(hba->utrdl_dma_addr),
+                     REG_UTP_TRANSFER_REQ_LIST_BASE_H);
+       ufshcd_writel(hba, lower_32_bits(hba->utmrdl_dma_addr),
+                     REG_UTP_TASK_REQ_LIST_BASE_L);
+       ufshcd_writel(hba, upper_32_bits(hba->utmrdl_dma_addr),
+                     REG_UTP_TASK_REQ_LIST_BASE_H);
+
+       if (ufshcd_is_link_hibern8(hba)) {
+               int ret = ufshcd_uic_hibern8_exit(hba);
+
+               if (!ret) {
+                       ufshcd_set_link_active(hba);
+               } else {
+                       dev_err(hba->dev, "%s: hibern8 exit failed %d\n",
+                               __func__, ret);
+                       /*
+                        * Force reset and restore. Any other actions can lead
+                        * to an unrecoverable state.
+                        */
+                       ufshcd_set_link_off(hba);
+               }
+       }
+
+       return 0;
+}
+
 static int ufs_intel_ehl_init(struct ufs_hba *hba)
 {
        hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
@@ -174,6 +211,7 @@ static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
        .init                   = ufs_intel_common_init,
        .exit                   = ufs_intel_common_exit,
        .link_startup_notify    = ufs_intel_link_startup_notify,
+       .resume                 = ufs_intel_resume,
 };
 
 static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
@@ -181,6 +219,7 @@ static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
        .init                   = ufs_intel_ehl_init,
        .exit                   = ufs_intel_common_exit,
        .link_startup_notify    = ufs_intel_link_startup_notify,
+       .resume                 = ufs_intel_resume,
 };
 
 #ifdef CONFIG_PM_SLEEP
@@ -207,6 +246,30 @@ static int ufshcd_pci_resume(struct device *dev)
 {
        return ufshcd_system_resume(dev_get_drvdata(dev));
 }
+
+/**
+ * ufshcd_pci_poweroff - suspend-to-disk poweroff function
+ * @dev: pointer to PCI device handle
+ *
+ * Returns 0 if successful
+ * Returns non-zero otherwise
+ */
+static int ufshcd_pci_poweroff(struct device *dev)
+{
+       struct ufs_hba *hba = dev_get_drvdata(dev);
+       int spm_lvl = hba->spm_lvl;
+       int ret;
+
+       /*
+        * For poweroff we need to set the UFS device to PowerDown mode.
+        * Force spm_lvl to ensure that.
+        */
+       hba->spm_lvl = 5;
+       ret = ufshcd_system_suspend(hba);
+       hba->spm_lvl = spm_lvl;
+       return ret;
+}
+
 #endif /* !CONFIG_PM_SLEEP */
 
 #ifdef CONFIG_PM
@@ -302,8 +365,14 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 }
 
 static const struct dev_pm_ops ufshcd_pci_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(ufshcd_pci_suspend,
-                               ufshcd_pci_resume)
+#ifdef CONFIG_PM_SLEEP
+       .suspend        = ufshcd_pci_suspend,
+       .resume         = ufshcd_pci_resume,
+       .freeze         = ufshcd_pci_suspend,
+       .thaw           = ufshcd_pci_resume,
+       .poweroff       = ufshcd_pci_poweroff,
+       .restore        = ufshcd_pci_resume,
+#endif
        SET_RUNTIME_PM_OPS(ufshcd_pci_runtime_suspend,
                           ufshcd_pci_runtime_resume,
                           ufshcd_pci_runtime_idle)
index 9902b7e..fb32d12 100644 (file)
@@ -225,6 +225,7 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba);
 static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd);
 static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag);
 static void ufshcd_hba_exit(struct ufs_hba *hba);
+static int ufshcd_clear_ua_wluns(struct ufs_hba *hba);
 static int ufshcd_probe_hba(struct ufs_hba *hba, bool async);
 static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
 static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
@@ -288,7 +289,8 @@ static inline void ufshcd_wb_config(struct ufs_hba *hba)
        if (ret)
                dev_err(hba->dev, "%s: En WB flush during H8: failed: %d\n",
                        __func__, ret);
-       ufshcd_wb_toggle_flush(hba, true);
+       if (!(hba->quirks & UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL))
+               ufshcd_wb_toggle_flush(hba, true);
 }
 
 static void ufshcd_scsi_unblock_requests(struct ufs_hba *hba)
@@ -580,6 +582,23 @@ static void ufshcd_print_pwr_info(struct ufs_hba *hba)
                 hba->pwr_info.hs_rate);
 }
 
+static void ufshcd_device_reset(struct ufs_hba *hba)
+{
+       int err;
+
+       err = ufshcd_vops_device_reset(hba);
+
+       if (!err) {
+               ufshcd_set_ufs_dev_active(hba);
+               if (ufshcd_is_wb_allowed(hba)) {
+                       hba->wb_enabled = false;
+                       hba->wb_buf_flush_enabled = false;
+               }
+       }
+       if (err != -EOPNOTSUPP)
+               ufshcd_update_evt_hist(hba, UFS_EVT_DEV_RESET, err);
+}
+
 void ufshcd_delay_us(unsigned long us, unsigned long tolerance)
 {
        if (!us)
@@ -3665,7 +3684,7 @@ static int ufshcd_dme_enable(struct ufs_hba *hba)
        ret = ufshcd_send_uic_cmd(hba, &uic_cmd);
        if (ret)
                dev_err(hba->dev,
-                       "dme-reset: error code %d\n", ret);
+                       "dme-enable: error code %d\n", ret);
 
        return ret;
 }
@@ -3964,7 +3983,7 @@ int ufshcd_link_recovery(struct ufs_hba *hba)
        spin_unlock_irqrestore(hba->host->host_lock, flags);
 
        /* Reset the attached device */
-       ufshcd_vops_device_reset(hba);
+       ufshcd_device_reset(hba);
 
        ret = ufshcd_host_reset_and_restore(hba);
 
@@ -3977,6 +3996,8 @@ int ufshcd_link_recovery(struct ufs_hba *hba)
        if (ret)
                dev_err(hba->dev, "%s: link recovery failed, err %d",
                        __func__, ret);
+       else
+               ufshcd_clear_ua_wluns(hba);
 
        return ret;
 }
@@ -4973,7 +4994,8 @@ ufshcd_transfer_rsp_status(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
                break;
        } /* end of switch */
 
-       if ((host_byte(result) != DID_OK) && !hba->silence_err_logs)
+       if ((host_byte(result) != DID_OK) &&
+           (host_byte(result) != DID_REQUEUE) && !hba->silence_err_logs)
                ufshcd_print_trs(hba, 1 << lrbp->task_tag, true);
        return result;
 }
@@ -5418,9 +5440,6 @@ static int ufshcd_wb_toggle_flush_during_h8(struct ufs_hba *hba, bool set)
 
 static inline void ufshcd_wb_toggle_flush(struct ufs_hba *hba, bool enable)
 {
-       if (hba->quirks & UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL)
-               return;
-
        if (enable)
                ufshcd_wb_buf_flush_enable(hba);
        else
@@ -5985,6 +6004,9 @@ skip_err_handling:
        ufshcd_scsi_unblock_requests(hba);
        ufshcd_err_handling_unprepare(hba);
        up(&hba->eh_sem);
+
+       if (!err && needs_reset)
+               ufshcd_clear_ua_wluns(hba);
 }
 
 /**
@@ -6279,9 +6301,13 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
                intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
        }
 
-       if (enabled_intr_status && retval == IRQ_NONE) {
-               dev_err(hba->dev, "%s: Unhandled interrupt 0x%08x\n",
-                                       __func__, intr_status);
+       if (enabled_intr_status && retval == IRQ_NONE &&
+                               !ufshcd_eh_in_progress(hba)) {
+               dev_err(hba->dev, "%s: Unhandled interrupt 0x%08x (0x%08x, 0x%08x)\n",
+                                       __func__,
+                                       intr_status,
+                                       hba->ufs_stats.last_intr_status,
+                                       enabled_intr_status);
                ufshcd_dump_regs(hba, 0, UFSHCI_REG_SPACE_SIZE, "host_regs: ");
        }
 
@@ -6325,7 +6351,10 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,
         * Even though we use wait_event() which sleeps indefinitely,
         * the maximum wait time is bounded by %TM_CMD_TIMEOUT.
         */
-       req = blk_get_request(q, REQ_OP_DRV_OUT, BLK_MQ_REQ_RESERVED);
+       req = blk_get_request(q, REQ_OP_DRV_OUT, 0);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
+
        req->end_io_data = &wait;
        free_slot = req->tag;
        WARN_ON_ONCE(free_slot < 0 || free_slot >= hba->nutmrs);
@@ -6643,19 +6672,16 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
 {
        struct Scsi_Host *host;
        struct ufs_hba *hba;
-       unsigned int tag;
        u32 pos;
        int err;
-       u8 resp = 0xF;
-       struct ufshcd_lrb *lrbp;
+       u8 resp = 0xF, lun;
        unsigned long flags;
 
        host = cmd->device->host;
        hba = shost_priv(host);
-       tag = cmd->request->tag;
 
-       lrbp = &hba->lrb[tag];
-       err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp);
+       lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
+       err = ufshcd_issue_tm_cmd(hba, lun, 0, UFS_LOGICAL_RESET, &resp);
        if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) {
                if (!err)
                        err = resp;
@@ -6664,7 +6690,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd)
 
        /* clear the commands that were pending for corresponding LUN */
        for_each_set_bit(pos, &hba->outstanding_reqs, hba->nutrs) {
-               if (hba->lrb[pos].lun == lrbp->lun) {
+               if (hba->lrb[pos].lun == lun) {
                        err = ufshcd_clear_cmd(hba, pos);
                        if (err)
                                break;
@@ -6925,13 +6951,11 @@ static int ufshcd_host_reset_and_restore(struct ufs_hba *hba)
        ufshcd_set_clk_freq(hba, true);
 
        err = ufshcd_hba_enable(hba);
-       if (err)
-               goto out;
 
        /* Establish the link again and restore the device */
-       err = ufshcd_probe_hba(hba, false);
+       if (!err)
+               err = ufshcd_probe_hba(hba, false);
 
-out:
        if (err)
                dev_err(hba->dev, "%s: Host init failed %d\n", __func__, err);
        ufshcd_update_evt_hist(hba, UFS_EVT_HOST_RESET, (u32)err);
@@ -6968,7 +6992,7 @@ static int ufshcd_reset_and_restore(struct ufs_hba *hba)
 
        do {
                /* Reset the attached device */
-               ufshcd_vops_device_reset(hba);
+               ufshcd_device_reset(hba);
 
                err = ufshcd_host_reset_and_restore(hba);
        } while (err && --retries);
@@ -7702,6 +7726,8 @@ static int ufshcd_add_lus(struct ufs_hba *hba)
        if (ret)
                goto out;
 
+       ufshcd_clear_ua_wluns(hba);
+
        /* Initialize devfreq after UFS device is detected */
        if (ufshcd_is_clkscaling_supported(hba)) {
                memcpy(&hba->clk_scaling.saved_pwr_info.info,
@@ -7903,8 +7929,6 @@ out:
                pm_runtime_put_sync(hba->dev);
                ufshcd_exit_clk_scaling(hba);
                ufshcd_hba_exit(hba);
-       } else {
-               ufshcd_clear_ua_wluns(hba);
        }
 }
 
@@ -8045,7 +8069,7 @@ static int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg)
 {
        int ret = 0;
 
-       if (!vreg || !vreg->enabled)
+       if (!vreg || !vreg->enabled || vreg->always_on)
                goto out;
 
        ret = regulator_disable(vreg->reg);
@@ -8414,13 +8438,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
         * handling context.
         */
        hba->host->eh_noresume = 1;
-       if (hba->wlun_dev_clr_ua) {
-               ret = ufshcd_send_request_sense(hba, sdp);
-               if (ret)
-                       goto out;
-               /* Unit attention condition is cleared now */
-               hba->wlun_dev_clr_ua = false;
-       }
+       ufshcd_clear_ua_wluns(hba);
 
        cmd[4] = pwr_mode << 4;
 
@@ -8441,7 +8459,7 @@ static int ufshcd_set_dev_pwr_mode(struct ufs_hba *hba,
 
        if (!ret)
                hba->curr_dev_pwr_mode = pwr_mode;
-out:
+
        scsi_device_put(sdp);
        hba->host->eh_noresume = 0;
        return ret;
@@ -8685,6 +8703,8 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
                        ufshcd_wb_need_flush(hba));
        }
 
+       flush_work(&hba->eeh_work);
+
        if (req_dev_pwr_mode != hba->curr_dev_pwr_mode) {
                if (!ufshcd_is_runtime_pm(pm_op))
                        /* ensure that bkops is disabled */
@@ -8697,8 +8717,6 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
                }
        }
 
-       flush_work(&hba->eeh_work);
-
        /*
         * In the case of DeepSleep, the device is expected to remain powered
         * with the link off, so do not check for bkops.
@@ -8747,7 +8765,7 @@ set_link_active:
         * further below.
         */
        if (ufshcd_is_ufs_dev_deepsleep(hba)) {
-               ufshcd_vops_device_reset(hba);
+               ufshcd_device_reset(hba);
                WARN_ON(!ufshcd_is_link_off(hba));
        }
        if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba))
@@ -8757,7 +8775,7 @@ set_link_active:
 set_dev_active:
        /* Can also get here needing to exit DeepSleep */
        if (ufshcd_is_ufs_dev_deepsleep(hba)) {
-               ufshcd_vops_device_reset(hba);
+               ufshcd_device_reset(hba);
                ufshcd_host_reset_and_restore(hba);
        }
        if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE))
@@ -8767,6 +8785,7 @@ enable_gating:
                ufshcd_resume_clkscaling(hba);
        hba->clk_gating.is_suspended = false;
        hba->dev_info.b_rpm_dev_flush_capable = false;
+       ufshcd_clear_ua_wluns(hba);
        ufshcd_release(hba);
 out:
        if (hba->dev_info.b_rpm_dev_flush_capable) {
@@ -8877,6 +8896,8 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
                cancel_delayed_work(&hba->rpm_dev_flush_recheck_work);
        }
 
+       ufshcd_clear_ua_wluns(hba);
+
        /* Schedule clock gating in case of no access to UFS device yet */
        ufshcd_release(hba);
 
@@ -8925,7 +8946,8 @@ int ufshcd_system_suspend(struct ufs_hba *hba)
        if ((ufs_get_pm_lvl_to_dev_pwr_mode(hba->spm_lvl) ==
             hba->curr_dev_pwr_mode) &&
            (ufs_get_pm_lvl_to_link_pwr_state(hba->spm_lvl) ==
-            hba->uic_link_state))
+            hba->uic_link_state) &&
+            !hba->dev_info.b_rpm_dev_flush_capable)
                goto out;
 
        if (pm_runtime_suspended(hba->dev)) {
@@ -9353,7 +9375,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
        }
 
        /* Reset the attached device */
-       ufshcd_vops_device_reset(hba);
+       ufshcd_device_reset(hba);
 
        ufshcd_init_crypto(hba);
 
index f8c2467..aa9ea35 100644 (file)
@@ -1218,16 +1218,12 @@ static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
                hba->vops->dbg_register_dump(hba);
 }
 
-static inline void ufshcd_vops_device_reset(struct ufs_hba *hba)
+static inline int ufshcd_vops_device_reset(struct ufs_hba *hba)
 {
-       if (hba->vops && hba->vops->device_reset) {
-               int err = hba->vops->device_reset(hba);
-
-               if (!err)
-                       ufshcd_set_ufs_dev_active(hba);
-               if (err != -EOPNOTSUPP)
-                       ufshcd_update_evt_hist(hba, UFS_EVT_DEV_RESET, err);
-       }
+       if (hba->vops && hba->vops->device_reset)
+               return hba->vops->device_reset(hba);
+
+       return -EOPNOTSUPP;
 }
 
 static inline void ufshcd_vops_config_scaling_param(struct ufs_hba *hba,
index f8e070d..a14684f 100644 (file)
@@ -214,7 +214,7 @@ int __init register_intc_controller(struct intc_desc *desc)
                        d->window[k].phys = res->start;
                        d->window[k].size = resource_size(res);
                        d->window[k].virt = ioremap(res->start,
-                                                        resource_size(res));
+                                                   resource_size(res));
                        if (!d->window[k].virt)
                                goto err2;
                }
index 9e62ba9..939915a 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/debugfs.h>
 #include "internals.h"
 
-static int intc_irq_xlate_debug(struct seq_file *m, void *priv)
+static int intc_irq_xlate_show(struct seq_file *m, void *priv)
 {
        int i;
 
@@ -37,17 +37,7 @@ static int intc_irq_xlate_debug(struct seq_file *m, void *priv)
        return 0;
 }
 
-static int intc_irq_xlate_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, intc_irq_xlate_debug, inode->i_private);
-}
-
-static const struct file_operations intc_irq_xlate_fops = {
-       .open = intc_irq_xlate_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(intc_irq_xlate);
 
 static int __init intc_irq_xlate_init(void)
 {
index c4472b6..698d21f 100644 (file)
@@ -271,8 +271,21 @@ struct soc_device * __init at91_soc_init(const struct at91_soc *socs)
        return soc_dev;
 }
 
+static const struct of_device_id at91_soc_allowed_list[] __initconst = {
+       { .compatible = "atmel,at91rm9200", },
+       { .compatible = "atmel,at91sam9", },
+       { .compatible = "atmel,sama5", },
+       { .compatible = "atmel,samv7", },
+       { }
+};
+
 static int __init atmel_soc_device_init(void)
 {
+       struct device_node *np = of_find_node_by_path("/");
+
+       if (!of_match_node(at91_soc_allowed_list, np))
+               return 0;
+
        at91_soc_init(socs);
 
        return 0;
index 497a7e0..654e924 100644 (file)
@@ -27,7 +27,7 @@
 
 static struct gen_pool *muram_pool;
 static spinlock_t cpm_muram_lock;
-static u8 __iomem *muram_vbase;
+static void __iomem *muram_vbase;
 static phys_addr_t muram_pbase;
 
 struct muram_block {
@@ -223,9 +223,9 @@ void __iomem *cpm_muram_addr(unsigned long offset)
 }
 EXPORT_SYMBOL(cpm_muram_addr);
 
-unsigned long cpm_muram_offset(void __iomem *addr)
+unsigned long cpm_muram_offset(const void __iomem *addr)
 {
-       return addr - (void __iomem *)muram_vbase;
+       return addr - muram_vbase;
 }
 EXPORT_SYMBOL(cpm_muram_offset);
 
@@ -235,6 +235,18 @@ EXPORT_SYMBOL(cpm_muram_offset);
  */
 dma_addr_t cpm_muram_dma(void __iomem *addr)
 {
-       return muram_pbase + ((u8 __iomem *)addr - muram_vbase);
+       return muram_pbase + (addr - muram_vbase);
 }
 EXPORT_SYMBOL(cpm_muram_dma);
+
+/*
+ * As cpm_muram_free, but takes the virtual address rather than the
+ * muram offset.
+ */
+void cpm_muram_free_addr(const void __iomem *addr)
+{
+       if (!addr)
+               return;
+       cpm_muram_free(cpm_muram_offset(addr));
+}
+EXPORT_SYMBOL(cpm_muram_free_addr);
index a9370f4..05812f8 100644 (file)
@@ -13,7 +13,7 @@ config SOC_IMX8M
        depends on ARCH_MXC || COMPILE_TEST
        default ARCH_MXC && ARM64
        select SOC_BUS
-       select ARM_GIC_V3 if ARCH_MXC
+       select ARM_GIC_V3 if ARCH_MXC && ARCH_MULTI_V7
        help
          If you say yes here you get support for the NXP i.MX8M family
          support, it will provide the SoC info like SoC family,
index 1217caf..9b07663 100644 (file)
@@ -140,12 +140,13 @@ struct litex_soc_ctrl_device {
        void __iomem *base;
 };
 
+#ifdef CONFIG_OF
 static const struct of_device_id litex_soc_ctrl_of_match[] = {
        {.compatible = "litex,soc-controller"},
        {},
 };
-
 MODULE_DEVICE_TABLE(of, litex_soc_ctrl_of_match);
+#endif /* CONFIG_OF */
 
 static int litex_soc_ctrl_probe(struct platform_device *pdev)
 {
index 809bfff..62ea0c9 100644 (file)
@@ -189,24 +189,26 @@ static int altera_spi_txrx(struct spi_master *master,
 
                /* send the first byte */
                altera_spi_tx_word(hw);
-       } else {
-               while (hw->count < hw->len) {
-                       altera_spi_tx_word(hw);
 
-                       for (;;) {
-                               altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
-                               if (val & ALTERA_SPI_STATUS_RRDY_MSK)
-                                       break;
+               return 1;
+       }
+
+       while (hw->count < hw->len) {
+               altera_spi_tx_word(hw);
 
-                               cpu_relax();
-                       }
+               for (;;) {
+                       altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
+                       if (val & ALTERA_SPI_STATUS_RRDY_MSK)
+                               break;
 
-                       altera_spi_rx_word(hw);
+                       cpu_relax();
                }
-               spi_finalize_current_transfer(master);
+
+               altera_spi_rx_word(hw);
        }
+       spi_finalize_current_transfer(master);
 
-       return t->len;
+       return 0;
 }
 
 static irqreturn_t altera_spi_irq(int irq, void *dev)
@@ -252,7 +254,8 @@ static int altera_spi_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev,
                                "Invalid number of chipselect: %hu\n",
                                pdata->num_chipselect);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto exit;
                }
 
                master->num_chipselect = pdata->num_chipselect;
index 70467b9..a3afd1b 100644 (file)
@@ -115,6 +115,7 @@ struct cdns_spi {
        void __iomem *regs;
        struct clk *ref_clk;
        struct clk *pclk;
+       unsigned int clk_rate;
        u32 speed_hz;
        const u8 *txbuf;
        u8 *rxbuf;
@@ -250,7 +251,7 @@ static void cdns_spi_config_clock_freq(struct spi_device *spi,
        u32 ctrl_reg, baud_rate_val;
        unsigned long frequency;
 
-       frequency = clk_get_rate(xspi->ref_clk);
+       frequency = xspi->clk_rate;
 
        ctrl_reg = cdns_spi_read(xspi, CDNS_SPI_CR);
 
@@ -558,8 +559,9 @@ static int cdns_spi_probe(struct platform_device *pdev)
        master->auto_runtime_pm = true;
        master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
 
+       xspi->clk_rate = clk_get_rate(xspi->ref_clk);
        /* Set to default valid value */
-       master->max_speed_hz = clk_get_rate(xspi->ref_clk) / 4;
+       master->max_speed_hz = xspi->clk_rate / 4;
        xspi->speed_hz = master->max_speed_hz;
 
        master->bits_per_word_mask = SPI_BPW_MASK(8);
index 9494257..6d8e0a0 100644 (file)
@@ -115,14 +115,13 @@ static void fsl_spi_chipselect(struct spi_device *spi, int value)
 {
        struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
        struct fsl_spi_platform_data *pdata;
-       bool pol = spi->mode & SPI_CS_HIGH;
        struct spi_mpc8xxx_cs   *cs = spi->controller_state;
 
        pdata = spi->dev.parent->parent->platform_data;
 
        if (value == BITBANG_CS_INACTIVE) {
                if (pdata->cs_control)
-                       pdata->cs_control(spi, !pol);
+                       pdata->cs_control(spi, false);
        }
 
        if (value == BITBANG_CS_ACTIVE) {
@@ -134,7 +133,7 @@ static void fsl_spi_chipselect(struct spi_device *spi, int value)
                fsl_spi_change_mode(spi);
 
                if (pdata->cs_control)
-                       pdata->cs_control(spi, pol);
+                       pdata->cs_control(spi, true);
        }
 }
 
index 512e925..881f645 100644 (file)
@@ -83,6 +83,7 @@ struct spi_geni_master {
        spinlock_t lock;
        int irq;
        bool cs_flag;
+       bool abort_failed;
 };
 
 static int get_spi_clk_cfg(unsigned int speed_hz,
@@ -141,8 +142,49 @@ static void handle_fifo_timeout(struct spi_master *spi,
        spin_unlock_irq(&mas->lock);
 
        time_left = wait_for_completion_timeout(&mas->abort_done, HZ);
-       if (!time_left)
+       if (!time_left) {
                dev_err(mas->dev, "Failed to cancel/abort m_cmd\n");
+
+               /*
+                * No need for a lock since SPI core has a lock and we never
+                * access this from an interrupt.
+                */
+               mas->abort_failed = true;
+       }
+}
+
+static bool spi_geni_is_abort_still_pending(struct spi_geni_master *mas)
+{
+       struct geni_se *se = &mas->se;
+       u32 m_irq, m_irq_en;
+
+       if (!mas->abort_failed)
+               return false;
+
+       /*
+        * The only known case where a transfer times out and then a cancel
+        * times out then an abort times out is if something is blocking our
+        * interrupt handler from running.  Avoid starting any new transfers
+        * until that sorts itself out.
+        */
+       spin_lock_irq(&mas->lock);
+       m_irq = readl(se->base + SE_GENI_M_IRQ_STATUS);
+       m_irq_en = readl(se->base + SE_GENI_M_IRQ_EN);
+       spin_unlock_irq(&mas->lock);
+
+       if (m_irq & m_irq_en) {
+               dev_err(mas->dev, "Interrupts pending after abort: %#010x\n",
+                       m_irq & m_irq_en);
+               return true;
+       }
+
+       /*
+        * If we're here the problem resolved itself so no need to check more
+        * on future transfers.
+        */
+       mas->abort_failed = false;
+
+       return false;
 }
 
 static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
@@ -158,10 +200,21 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
        if (set_flag == mas->cs_flag)
                return;
 
-       mas->cs_flag = set_flag;
-
        pm_runtime_get_sync(mas->dev);
+
+       if (spi_geni_is_abort_still_pending(mas)) {
+               dev_err(mas->dev, "Can't set chip select\n");
+               goto exit;
+       }
+
        spin_lock_irq(&mas->lock);
+       if (mas->cur_xfer) {
+               dev_err(mas->dev, "Can't set CS when prev xfer running\n");
+               spin_unlock_irq(&mas->lock);
+               goto exit;
+       }
+
+       mas->cs_flag = set_flag;
        reinit_completion(&mas->cs_done);
        if (set_flag)
                geni_se_setup_m_cmd(se, SPI_CS_ASSERT, 0);
@@ -170,9 +223,12 @@ static void spi_geni_set_cs(struct spi_device *slv, bool set_flag)
        spin_unlock_irq(&mas->lock);
 
        time_left = wait_for_completion_timeout(&mas->cs_done, HZ);
-       if (!time_left)
+       if (!time_left) {
+               dev_warn(mas->dev, "Timeout setting chip select\n");
                handle_fifo_timeout(spi, NULL);
+       }
 
+exit:
        pm_runtime_put(mas->dev);
 }
 
@@ -280,6 +336,9 @@ static int spi_geni_prepare_message(struct spi_master *spi,
        int ret;
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
 
+       if (spi_geni_is_abort_still_pending(mas))
+               return -EBUSY;
+
        ret = setup_fifo_params(spi_msg->spi, spi);
        if (ret)
                dev_err(mas->dev, "Couldn't select mode %d\n", ret);
@@ -354,6 +413,12 @@ static bool geni_spi_handle_tx(struct spi_geni_master *mas)
        unsigned int bytes_per_fifo_word = geni_byte_per_fifo_word(mas);
        unsigned int i = 0;
 
+       /* Stop the watermark IRQ if nothing to send */
+       if (!mas->cur_xfer) {
+               writel(0, se->base + SE_GENI_TX_WATERMARK_REG);
+               return false;
+       }
+
        max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * bytes_per_fifo_word;
        if (mas->tx_rem_bytes < max_bytes)
                max_bytes = mas->tx_rem_bytes;
@@ -396,6 +461,14 @@ static void geni_spi_handle_rx(struct spi_geni_master *mas)
                if (rx_last_byte_valid && rx_last_byte_valid < 4)
                        rx_bytes -= bytes_per_fifo_word - rx_last_byte_valid;
        }
+
+       /* Clear out the FIFO and bail if nowhere to put it */
+       if (!mas->cur_xfer) {
+               for (i = 0; i < DIV_ROUND_UP(rx_bytes, bytes_per_fifo_word); i++)
+                       readl(se->base + SE_GENI_RX_FIFOn);
+               return;
+       }
+
        if (mas->rx_rem_bytes < rx_bytes)
                rx_bytes = mas->rx_rem_bytes;
 
@@ -495,6 +568,9 @@ static int spi_geni_transfer_one(struct spi_master *spi,
 {
        struct spi_geni_master *mas = spi_master_get_devdata(spi);
 
+       if (spi_geni_is_abort_still_pending(mas))
+               return -EBUSY;
+
        /* Terminate and return success for 0 byte length transfer */
        if (!xfer->len)
                return 0;
index 471dedf..6017209 100644 (file)
@@ -493,9 +493,9 @@ static u32 stm32h7_spi_prepare_fthlv(struct stm32_spi *spi, u32 xfer_len)
 
        /* align packet size with data registers access */
        if (spi->cur_bpw > 8)
-               fthlv -= (fthlv % 2); /* multiple of 2 */
+               fthlv += (fthlv % 2) ? 1 : 0;
        else
-               fthlv -= (fthlv % 4); /* multiple of 4 */
+               fthlv += (fthlv % 4) ? (4 - (fthlv % 4)) : 0;
 
        if (!fthlv)
                fthlv = 1;
index 51d7c00..720ab34 100644 (file)
@@ -1108,6 +1108,7 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
 {
        struct spi_statistics *statm = &ctlr->statistics;
        struct spi_statistics *stats = &msg->spi->statistics;
+       u32 speed_hz = xfer->speed_hz;
        unsigned long long ms;
 
        if (spi_controller_is_slave(ctlr)) {
@@ -1116,8 +1117,11 @@ static int spi_transfer_wait(struct spi_controller *ctlr,
                        return -EINTR;
                }
        } else {
+               if (!speed_hz)
+                       speed_hz = 100000;
+
                ms = 8LL * 1000LL * xfer->len;
-               do_div(ms, xfer->speed_hz);
+               do_div(ms, speed_hz);
                ms += ms + 200; /* some tolerance */
 
                if (ms > UINT_MAX)
@@ -3378,8 +3382,9 @@ int spi_setup(struct spi_device *spi)
        if (status)
                return status;
 
-       if (!spi->max_speed_hz ||
-           spi->max_speed_hz > spi->controller->max_speed_hz)
+       if (spi->controller->max_speed_hz &&
+           (!spi->max_speed_hz ||
+            spi->max_speed_hz > spi->controller->max_speed_hz))
                spi->max_speed_hz = spi->controller->max_speed_hz;
 
        mutex_lock(&spi->controller->io_mutex);
index 859910e..8cb4d92 100644 (file)
@@ -682,6 +682,7 @@ static const struct of_device_id spidev_dt_ids[] = {
        { .compatible = "lwn,bk4" },
        { .compatible = "dh,dhcom-board" },
        { .compatible = "menlo,m53cpld" },
+       { .compatible = "cisco,spi-petra" },
        {},
 };
 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
index d99231c..80d74cc 100644 (file)
@@ -2987,7 +2987,9 @@ static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
        v32.chanlist_len = cmd->chanlist_len;
        v32.data = ptr_to_compat(cmd->data);
        v32.data_len = cmd->data_len;
-       return copy_to_user(cmd32, &v32, sizeof(v32));
+       if (copy_to_user(cmd32, &v32, sizeof(v32)))
+               return -EFAULT;
+       return 0;
 }
 
 /* Handle 32-bit COMEDI_CMD ioctl. */
index d524e92..ca3d07f 100644 (file)
@@ -901,19 +901,14 @@ static void dpaa2_switch_teardown_irqs(struct fsl_mc_device *sw_dev)
 }
 
 static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
-                                               struct switchdev_trans *trans,
                                                u8 state)
 {
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        return dpaa2_switch_port_set_stp_state(port_priv, state);
 }
 
 static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev,
-                                                  struct switchdev_trans *trans,
                                                   unsigned long flags)
 {
        if (flags & ~(BR_LEARNING | BR_FLOOD))
@@ -923,15 +918,11 @@ static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev,
 }
 
 static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev,
-                                              struct switchdev_trans *trans,
                                               unsigned long flags)
 {
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
        int err = 0;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        /* Learning is enabled per switch */
        err = dpaa2_switch_set_learning(port_priv->ethsw_data,
                                        !!(flags & BR_LEARNING));
@@ -945,22 +936,21 @@ exit:
 }
 
 static int dpaa2_switch_port_attr_set(struct net_device *netdev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans)
+                                     const struct switchdev_attr *attr)
 {
        int err = 0;
 
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-               err = dpaa2_switch_port_attr_stp_state_set(netdev, trans,
+               err = dpaa2_switch_port_attr_stp_state_set(netdev,
                                                           attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
-               err = dpaa2_switch_port_attr_br_flags_pre_set(netdev, trans,
+               err = dpaa2_switch_port_attr_br_flags_pre_set(netdev,
                                                              attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               err = dpaa2_switch_port_attr_br_flags_set(netdev, trans,
+               err = dpaa2_switch_port_attr_br_flags_set(netdev,
                                                          attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
@@ -975,54 +965,49 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev,
 }
 
 static int dpaa2_switch_port_vlans_add(struct net_device *netdev,
-                                      const struct switchdev_obj_port_vlan *vlan,
-                                      struct switchdev_trans *trans)
+                                      const struct switchdev_obj_port_vlan *vlan)
 {
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
        struct ethsw_core *ethsw = port_priv->ethsw_data;
        struct dpsw_attr *attr = &ethsw->sw_attr;
-       int vid, err = 0, new_vlans = 0;
-
-       if (switchdev_trans_ph_prepare(trans)) {
-               for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-                       if (!port_priv->ethsw_data->vlans[vid])
-                               new_vlans++;
-
-                       /* Make sure that the VLAN is not already configured
-                        * on the switch port
-                        */
-                       if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER)
-                               return -EEXIST;
-               }
+       int err = 0;
 
-               /* Check if there is space for a new VLAN */
-               err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
-                                         &ethsw->sw_attr);
-               if (err) {
-                       netdev_err(netdev, "dpsw_get_attributes err %d\n", err);
-                       return err;
-               }
-               if (attr->max_vlans - attr->num_vlans < new_vlans)
-                       return -ENOSPC;
+       /* Make sure that the VLAN is not already configured
+        * on the switch port
+        */
+       if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER)
+               return -EEXIST;
 
-               return 0;
+       /* Check if there is space for a new VLAN */
+       err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
+                                 &ethsw->sw_attr);
+       if (err) {
+               netdev_err(netdev, "dpsw_get_attributes err %d\n", err);
+               return err;
        }
+       if (attr->max_vlans - attr->num_vlans < 1)
+               return -ENOSPC;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               if (!port_priv->ethsw_data->vlans[vid]) {
-                       /* this is a new VLAN */
-                       err = dpaa2_switch_add_vlan(port_priv->ethsw_data, vid);
-                       if (err)
-                               return err;
+       /* Check if there is space for a new VLAN */
+       err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
+                                 &ethsw->sw_attr);
+       if (err) {
+               netdev_err(netdev, "dpsw_get_attributes err %d\n", err);
+               return err;
+       }
+       if (attr->max_vlans - attr->num_vlans < 1)
+               return -ENOSPC;
 
-                       port_priv->ethsw_data->vlans[vid] |= ETHSW_VLAN_GLOBAL;
-               }
-               err = dpaa2_switch_port_add_vlan(port_priv, vid, vlan->flags);
+       if (!port_priv->ethsw_data->vlans[vlan->vid]) {
+               /* this is a new VLAN */
+               err = dpaa2_switch_add_vlan(port_priv->ethsw_data, vlan->vid);
                if (err)
-                       break;
+                       return err;
+
+               port_priv->ethsw_data->vlans[vlan->vid] |= ETHSW_VLAN_GLOBAL;
        }
 
-       return err;
+       return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags);
 }
 
 static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc,
@@ -1043,15 +1028,11 @@ static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc
 }
 
 static int dpaa2_switch_port_mdb_add(struct net_device *netdev,
-                                    const struct switchdev_obj_port_mdb *mdb,
-                                    struct switchdev_trans *trans)
+                                    const struct switchdev_obj_port_mdb *mdb)
 {
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
        int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        /* Check if address is already set on this port */
        if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
                return -EEXIST;
@@ -1070,21 +1051,18 @@ static int dpaa2_switch_port_mdb_add(struct net_device *netdev,
 }
 
 static int dpaa2_switch_port_obj_add(struct net_device *netdev,
-                                    const struct switchdev_obj *obj,
-                                    struct switchdev_trans *trans)
+                                    const struct switchdev_obj *obj)
 {
        int err;
 
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
                err = dpaa2_switch_port_vlans_add(netdev,
-                                                 SWITCHDEV_OBJ_PORT_VLAN(obj),
-                                                 trans);
+                                                 SWITCHDEV_OBJ_PORT_VLAN(obj));
                break;
        case SWITCHDEV_OBJ_ID_PORT_MDB:
                err = dpaa2_switch_port_mdb_add(netdev,
-                                               SWITCHDEV_OBJ_PORT_MDB(obj),
-                                               trans);
+                                               SWITCHDEV_OBJ_PORT_MDB(obj));
                break;
        default:
                err = -EOPNOTSUPP;
@@ -1155,18 +1133,11 @@ static int dpaa2_switch_port_vlans_del(struct net_device *netdev,
                                       const struct switchdev_obj_port_vlan *vlan)
 {
        struct ethsw_port_priv *port_priv = netdev_priv(netdev);
-       int vid, err = 0;
 
        if (netif_is_bridge_master(vlan->obj.orig_dev))
                return -EOPNOTSUPP;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-               err = dpaa2_switch_port_del_vlan(port_priv, vid);
-               if (err)
-                       break;
-       }
-
-       return err;
+       return dpaa2_switch_port_del_vlan(port_priv, vlan->vid);
 }
 
 static int dpaa2_switch_port_mdb_del(struct net_device *netdev,
@@ -1216,8 +1187,7 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
 {
        int err;
 
-       err = dpaa2_switch_port_attr_set(netdev, port_attr_info->attr,
-                                        port_attr_info->trans);
+       err = dpaa2_switch_port_attr_set(netdev, port_attr_info->attr);
 
        port_attr_info->handled = true;
        return notifier_from_errno(err);
@@ -1411,8 +1381,7 @@ static int dpaa2_switch_port_obj_event(unsigned long event,
 
        switch (event) {
        case SWITCHDEV_PORT_OBJ_ADD:
-               err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj,
-                                               port_obj_info->trans);
+               err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj);
                break;
        case SWITCHDEV_PORT_OBJ_DEL:
                err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj);
index 861aedd..0d42bc6 100644 (file)
@@ -278,21 +278,24 @@ static int spmi_controller_probe(struct platform_device *pdev)
        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!iores) {
                dev_err(&pdev->dev, "can not get resource!\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto err_put_controller;
        }
 
        spmi_controller->base = devm_ioremap(&pdev->dev, iores->start,
                                             resource_size(iores));
        if (!spmi_controller->base) {
                dev_err(&pdev->dev, "can not remap base addr!\n");
-               return -EADDRNOTAVAIL;
+               ret = -EADDRNOTAVAIL;
+               goto err_put_controller;
        }
 
        ret = of_property_read_u32(pdev->dev.of_node, "spmi-channel",
                                   &spmi_controller->channel);
        if (ret) {
                dev_err(&pdev->dev, "can not get channel\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_put_controller;
        }
 
        platform_set_drvdata(pdev, spmi_controller);
@@ -309,9 +312,15 @@ static int spmi_controller_probe(struct platform_device *pdev)
        ctrl->write_cmd = spmi_write_cmd;
 
        ret = spmi_controller_add(ctrl);
-       if (ret)
-               dev_err(&pdev->dev, "spmi_add_controller failed with error %d!\n", ret);
+       if (ret) {
+               dev_err(&pdev->dev, "spmi_controller_add failed with error %d!\n", ret);
+               goto err_put_controller;
+       }
+
+       return 0;
 
+err_put_controller:
+       spmi_controller_put(ctrl);
        return ret;
 }
 
@@ -320,7 +329,7 @@ static int spmi_del_controller(struct platform_device *pdev)
        struct spmi_controller *ctrl = platform_get_drvdata(pdev);
 
        spmi_controller_remove(ctrl);
-       kfree(ctrl);
+       spmi_controller_put(ctrl);
        return 0;
 }
 
index 52b9fb1..b666cb2 100644 (file)
@@ -1062,26 +1062,6 @@ static const struct v4l2_ctrl_config ctrl_select_isp_version = {
        .def = 0,
 };
 
-#if 0 /* #ifdef CONFIG_ION */
-/*
- * Control for ISP ion device fd
- *
- * userspace will open ion device and pass the fd to kernel.
- * this fd will be used to map shared fd to buffer.
- */
-/* V4L2_CID_ATOMISP_ION_DEVICE_FD is not defined */
-static const struct v4l2_ctrl_config ctrl_ion_dev_fd = {
-       .ops = &ctrl_ops,
-       .id = V4L2_CID_ATOMISP_ION_DEVICE_FD,
-       .type = V4L2_CTRL_TYPE_INTEGER,
-       .name = "Ion Device Fd",
-       .min = -1,
-       .max = 1024,
-       .step = 1,
-       .def = ION_FD_UNSET
-};
-#endif
-
 static void atomisp_init_subdev_pipe(struct atomisp_sub_device *asd,
                                     struct atomisp_video_pipe *pipe, enum v4l2_buf_type buf_type)
 {
index b668a82..f5fbdbc 100644 (file)
@@ -367,7 +367,7 @@ hantro_reset_raw_fmt(struct hantro_ctx *ctx)
 
        hantro_reset_fmt(raw_fmt, raw_vpu_fmt);
        raw_fmt->width = encoded_fmt->width;
-       raw_fmt->width = encoded_fmt->width;
+       raw_fmt->height = encoded_fmt->height;
        if (ctx->is_encoder)
                hantro_set_fmt_out(ctx, raw_fmt);
        else
index 781c84a..de7442d 100644 (file)
@@ -203,7 +203,7 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx,
                position = cedrus_buf->codec.h264.position;
 
                sram_array[i] |= position << 1;
-               if (ref_list[i].fields & V4L2_H264_BOTTOM_FIELD_REF)
+               if (ref_list[i].fields == V4L2_H264_BOTTOM_FIELD_REF)
                        sram_array[i] |= BIT(0);
        }
 
index d241349..bc4bb43 100644 (file)
@@ -712,7 +712,7 @@ static int mtk_hsdma_probe(struct platform_device *pdev)
        ret = dma_async_device_register(dd);
        if (ret) {
                dev_err(&pdev->dev, "failed to register dma device\n");
-               return ret;
+               goto err_uninit_hsdma;
        }
 
        ret = of_dma_controller_register(pdev->dev.of_node,
@@ -728,6 +728,8 @@ static int mtk_hsdma_probe(struct platform_device *pdev)
 
 err_unregister:
        dma_async_device_unregister(dd);
+err_uninit_hsdma:
+       mtk_hsdma_uninit(hsdma);
        return ret;
 }
 
index ab5a862..f798b0c 100644 (file)
@@ -20,9 +20,9 @@ enum country_code_type_t {
        COUNTRY_CODE_MAX
 };
 
-int rtw_regd_init(struct adapter *padapter,
-       void (*reg_notifier)(struct wiphy *wiphy,
-               struct regulatory_request *request));
+void rtw_regd_init(struct wiphy *wiphy,
+                  void (*reg_notifier)(struct wiphy *wiphy,
+                                       struct regulatory_request *request));
 void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request);
 
 
index bf14172..1103231 100644 (file)
@@ -3211,9 +3211,6 @@ void rtw_cfg80211_init_wiphy(struct adapter *padapter)
                        rtw_cfg80211_init_ht_capab(&bands->ht_cap, NL80211_BAND_2GHZ, rf_type);
        }
 
-       /* init regulary domain */
-       rtw_regd_init(padapter, rtw_reg_notifier);
-
        /* copy mac_addr to wiphy */
        memcpy(wiphy->perm_addr, padapter->eeprompriv.mac_addr, ETH_ALEN);
 
@@ -3328,6 +3325,9 @@ int rtw_wdev_alloc(struct adapter *padapter, struct device *dev)
        *((struct adapter **)wiphy_priv(wiphy)) = padapter;
        rtw_cfg80211_preinit_wiphy(padapter, wiphy);
 
+       /* init regulary domain */
+       rtw_regd_init(wiphy, rtw_reg_notifier);
+
        ret = wiphy_register(wiphy);
        if (ret < 0) {
                DBG_8192C("Couldn't register wiphy device\n");
index 578b9f7..2833fc6 100644 (file)
@@ -139,15 +139,11 @@ static void _rtw_regd_init_wiphy(struct rtw_regulatory *reg,
        _rtw_reg_apply_flags(wiphy);
 }
 
-int rtw_regd_init(struct adapter *padapter,
-                 void (*reg_notifier)(struct wiphy *wiphy,
-                                      struct regulatory_request *request))
+void rtw_regd_init(struct wiphy *wiphy,
+                  void (*reg_notifier)(struct wiphy *wiphy,
+                                       struct regulatory_request *request))
 {
-       struct wiphy *wiphy = padapter->rtw_wdev->wiphy;
-
        _rtw_regd_init_wiphy(NULL, wiphy, reg_notifier);
-
-       return 0;
 }
 
 void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
index 6b171ff..a5991df 100644 (file)
@@ -562,8 +562,6 @@ tcmu_get_block_page(struct tcmu_dev *udev, uint32_t dbi)
 
 static inline void tcmu_free_cmd(struct tcmu_cmd *tcmu_cmd)
 {
-       if (tcmu_cmd->se_cmd)
-               tcmu_cmd->se_cmd->priv = NULL;
        kfree(tcmu_cmd->dbi);
        kmem_cache_free(tcmu_cmd_cache, tcmu_cmd);
 }
@@ -1174,11 +1172,12 @@ tcmu_queue_cmd(struct se_cmd *se_cmd)
                return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
 
        mutex_lock(&udev->cmdr_lock);
-       se_cmd->priv = tcmu_cmd;
        if (!(se_cmd->transport_state & CMD_T_ABORTED))
                ret = queue_cmd_ring(tcmu_cmd, &scsi_ret);
        if (ret < 0)
                tcmu_free_cmd(tcmu_cmd);
+       else
+               se_cmd->priv = tcmu_cmd;
        mutex_unlock(&udev->cmdr_lock);
        return scsi_ret;
 }
@@ -1241,6 +1240,7 @@ tcmu_tmr_notify(struct se_device *se_dev, enum tcm_tmreq_table tmf,
 
                list_del_init(&cmd->queue_entry);
                tcmu_free_cmd(cmd);
+               se_cmd->priv = NULL;
                target_complete_cmd(se_cmd, SAM_STAT_TASK_ABORTED);
                unqueued = true;
        }
@@ -1332,6 +1332,7 @@ static void tcmu_handle_completion(struct tcmu_cmd *cmd, struct tcmu_cmd_entry *
        }
 
 done:
+       se_cmd->priv = NULL;
        if (read_len_valid) {
                pr_debug("read_len = %d\n", read_len);
                target_complete_cmd_with_length(cmd->se_cmd,
@@ -1478,6 +1479,7 @@ static void tcmu_check_expired_queue_cmd(struct tcmu_cmd *cmd)
        se_cmd = cmd->se_cmd;
        tcmu_free_cmd(cmd);
 
+       se_cmd->priv = NULL;
        target_complete_cmd(se_cmd, SAM_STAT_TASK_SET_FULL);
 }
 
@@ -1592,6 +1594,7 @@ static void run_qfull_queue(struct tcmu_dev *udev, bool fail)
                         * removed then LIO core will do the right thing and
                         * fail the retry.
                         */
+                       tcmu_cmd->se_cmd->priv = NULL;
                        target_complete_cmd(tcmu_cmd->se_cmd, SAM_STAT_BUSY);
                        tcmu_free_cmd(tcmu_cmd);
                        continue;
@@ -1605,6 +1608,7 @@ static void run_qfull_queue(struct tcmu_dev *udev, bool fail)
                         * Ignore scsi_ret for now. target_complete_cmd
                         * drops it.
                         */
+                       tcmu_cmd->se_cmd->priv = NULL;
                        target_complete_cmd(tcmu_cmd->se_cmd,
                                            SAM_STAT_CHECK_CONDITION);
                        tcmu_free_cmd(tcmu_cmd);
@@ -2212,6 +2216,7 @@ static void tcmu_reset_ring(struct tcmu_dev *udev, u8 err_level)
                if (!test_bit(TCMU_CMD_BIT_EXPIRED, &cmd->flags)) {
                        WARN_ON(!cmd->se_cmd);
                        list_del_init(&cmd->queue_entry);
+                       cmd->se_cmd->priv = NULL;
                        if (err_level == 1) {
                                /*
                                 * Userspace was not able to start the
index 44e15d7..66d6f1d 100644 (file)
@@ -46,60 +46,83 @@ static int target_xcopy_gen_naa_ieee(struct se_device *dev, unsigned char *buf)
        return 0;
 }
 
-struct xcopy_dev_search_info {
-       const unsigned char *dev_wwn;
-       struct se_device *found_dev;
-};
-
+/**
+ * target_xcopy_locate_se_dev_e4_iter - compare XCOPY NAA device identifiers
+ *
+ * @se_dev: device being considered for match
+ * @dev_wwn: XCOPY requested NAA dev_wwn
+ * @return: 1 on match, 0 on no-match
+ */
 static int target_xcopy_locate_se_dev_e4_iter(struct se_device *se_dev,
-                                             void *data)
+                                             const unsigned char *dev_wwn)
 {
-       struct xcopy_dev_search_info *info = data;
        unsigned char tmp_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
        int rc;
 
-       if (!se_dev->dev_attrib.emulate_3pc)
+       if (!se_dev->dev_attrib.emulate_3pc) {
+               pr_debug("XCOPY: emulate_3pc disabled on se_dev %p\n", se_dev);
                return 0;
+       }
 
        memset(&tmp_dev_wwn[0], 0, XCOPY_NAA_IEEE_REGEX_LEN);
        target_xcopy_gen_naa_ieee(se_dev, &tmp_dev_wwn[0]);
 
-       rc = memcmp(&tmp_dev_wwn[0], info->dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
-       if (rc != 0)
-               return 0;
-
-       info->found_dev = se_dev;
-       pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
-
-       rc = target_depend_item(&se_dev->dev_group.cg_item);
+       rc = memcmp(&tmp_dev_wwn[0], dev_wwn, XCOPY_NAA_IEEE_REGEX_LEN);
        if (rc != 0) {
-               pr_err("configfs_depend_item attempt failed: %d for se_dev: %p\n",
-                      rc, se_dev);
-               return rc;
+               pr_debug("XCOPY: skip non-matching: %*ph\n",
+                        XCOPY_NAA_IEEE_REGEX_LEN, tmp_dev_wwn);
+               return 0;
        }
+       pr_debug("XCOPY 0xe4: located se_dev: %p\n", se_dev);
 
-       pr_debug("Called configfs_depend_item for se_dev: %p se_dev->se_dev_group: %p\n",
-                se_dev, &se_dev->dev_group);
        return 1;
 }
 
-static int target_xcopy_locate_se_dev_e4(const unsigned char *dev_wwn,
-                                       struct se_device **found_dev)
+static int target_xcopy_locate_se_dev_e4(struct se_session *sess,
+                                       const unsigned char *dev_wwn,
+                                       struct se_device **_found_dev,
+                                       struct percpu_ref **_found_lun_ref)
 {
-       struct xcopy_dev_search_info info;
-       int ret;
-
-       memset(&info, 0, sizeof(info));
-       info.dev_wwn = dev_wwn;
-
-       ret = target_for_each_device(target_xcopy_locate_se_dev_e4_iter, &info);
-       if (ret == 1) {
-               *found_dev = info.found_dev;
-               return 0;
-       } else {
-               pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
-               return -EINVAL;
+       struct se_dev_entry *deve;
+       struct se_node_acl *nacl;
+       struct se_lun *this_lun = NULL;
+       struct se_device *found_dev = NULL;
+
+       /* cmd with NULL sess indicates no associated $FABRIC_MOD */
+       if (!sess)
+               goto err_out;
+
+       pr_debug("XCOPY 0xe4: searching for: %*ph\n",
+                XCOPY_NAA_IEEE_REGEX_LEN, dev_wwn);
+
+       nacl = sess->se_node_acl;
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) {
+               struct se_device *this_dev;
+               int rc;
+
+               this_lun = rcu_dereference(deve->se_lun);
+               this_dev = rcu_dereference_raw(this_lun->lun_se_dev);
+
+               rc = target_xcopy_locate_se_dev_e4_iter(this_dev, dev_wwn);
+               if (rc) {
+                       if (percpu_ref_tryget_live(&this_lun->lun_ref))
+                               found_dev = this_dev;
+                       break;
+               }
        }
+       rcu_read_unlock();
+       if (found_dev == NULL)
+               goto err_out;
+
+       pr_debug("lun_ref held for se_dev: %p se_dev->se_dev_group: %p\n",
+                found_dev, &found_dev->dev_group);
+       *_found_dev = found_dev;
+       *_found_lun_ref = &this_lun->lun_ref;
+       return 0;
+err_out:
+       pr_debug_ratelimited("Unable to locate 0xe4 descriptor for EXTENDED_COPY\n");
+       return -EINVAL;
 }
 
 static int target_xcopy_parse_tiddesc_e4(struct se_cmd *se_cmd, struct xcopy_op *xop,
@@ -246,12 +269,16 @@ static int target_xcopy_parse_target_descriptors(struct se_cmd *se_cmd,
 
        switch (xop->op_origin) {
        case XCOL_SOURCE_RECV_OP:
-               rc = target_xcopy_locate_se_dev_e4(xop->dst_tid_wwn,
-                                               &xop->dst_dev);
+               rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess,
+                                               xop->dst_tid_wwn,
+                                               &xop->dst_dev,
+                                               &xop->remote_lun_ref);
                break;
        case XCOL_DEST_RECV_OP:
-               rc = target_xcopy_locate_se_dev_e4(xop->src_tid_wwn,
-                                               &xop->src_dev);
+               rc = target_xcopy_locate_se_dev_e4(se_cmd->se_sess,
+                                               xop->src_tid_wwn,
+                                               &xop->src_dev,
+                                               &xop->remote_lun_ref);
                break;
        default:
                pr_err("XCOPY CSCD descriptor IDs not found in CSCD list - "
@@ -391,18 +418,12 @@ static int xcopy_pt_get_cmd_state(struct se_cmd *se_cmd)
 
 static void xcopy_pt_undepend_remotedev(struct xcopy_op *xop)
 {
-       struct se_device *remote_dev;
-
        if (xop->op_origin == XCOL_SOURCE_RECV_OP)
-               remote_dev = xop->dst_dev;
+               pr_debug("putting dst lun_ref for %p\n", xop->dst_dev);
        else
-               remote_dev = xop->src_dev;
-
-       pr_debug("Calling configfs_undepend_item for"
-                 " remote_dev: %p remote_dev->dev_group: %p\n",
-                 remote_dev, &remote_dev->dev_group.cg_item);
+               pr_debug("putting src lun_ref for %p\n", xop->src_dev);
 
-       target_undepend_item(&remote_dev->dev_group.cg_item);
+       percpu_ref_put(xop->remote_lun_ref);
 }
 
 static void xcopy_pt_release_cmd(struct se_cmd *se_cmd)
index c56a1bd..e5f2000 100644 (file)
@@ -27,6 +27,7 @@ struct xcopy_op {
        struct se_device *dst_dev;
        unsigned char dst_tid_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
        unsigned char local_dev_wwn[XCOPY_NAA_IEEE_REGEX_LEN];
+       struct percpu_ref *remote_lun_ref;
 
        sector_t src_lba;
        sector_t dst_lba;
index c981757..780d7c4 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/tee_drv.h>
 #include <linux/types.h>
@@ -148,7 +149,8 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
                         */
                        optee_cq_wait_for_completion(&optee->call_queue, &w);
                } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
-                       might_sleep();
+                       if (need_resched())
+                               cond_resched();
                        param.a0 = res.a0;
                        param.a1 = res.a1;
                        param.a2 = res.a2;
index 8b7f941..b8c4159 100644 (file)
@@ -2316,7 +2316,7 @@ static int icm_usb4_switch_nvm_authenticate_status(struct tb_switch *sw,
 
        if (auth && auth->reply.route_hi == sw->config.route_hi &&
            auth->reply.route_lo == sw->config.route_lo) {
-               tb_dbg(tb, "NVM_AUTH found for %llx flags 0x%#x status %#x\n",
+               tb_dbg(tb, "NVM_AUTH found for %llx flags %#x status %#x\n",
                       tb_route(sw), auth->reply.hdr.flags, auth->reply.status);
                if (auth->reply.hdr.flags & ICM_FLAGS_ERROR)
                        ret = -EIO;
index 47a6e42..e15cd6b 100644 (file)
@@ -401,6 +401,20 @@ config MIPS_EJTAG_FDC_KGDB_CHAN
        help
          FDC channel number to use for KGDB.
 
+config NULL_TTY
+       tristate "NULL TTY driver"
+       help
+         Say Y here if you want a NULL TTY which simply discards messages.
+
+         This is useful to allow userspace applications which expect a console
+         device to work without modifications even when no console is
+         available or desired.
+
+         In order to use this driver, you should redirect the console to this
+         TTY, or boot the kernel with console=ttynull.
+
+         If unsure, say N.
+
 config TRACE_ROUTER
        tristate "Trace data router for MIPI P1149.7 cJTAG standard"
        depends on TRACE_SINK
index 3c1c5a9..b3ccae9 100644 (file)
@@ -2,7 +2,7 @@
 obj-$(CONFIG_TTY)              += tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o \
                                   tty_buffer.o tty_port.o tty_mutex.o \
                                   tty_ldsem.o tty_baudrate.o tty_jobctrl.o \
-                                  n_null.o ttynull.o
+                                  n_null.o
 obj-$(CONFIG_LEGACY_PTYS)      += pty.o
 obj-$(CONFIG_UNIX98_PTYS)      += pty.o
 obj-$(CONFIG_AUDIT)            += tty_audit.o
@@ -25,6 +25,7 @@ obj-$(CONFIG_ISI)             += isicom.o
 obj-$(CONFIG_MOXA_INTELLIO)    += moxa.o
 obj-$(CONFIG_MOXA_SMARTIO)     += mxser.o
 obj-$(CONFIG_NOZOMI)           += nozomi.o
+obj-$(CONFIG_NULL_TTY)         += ttynull.o
 obj-$(CONFIG_ROCKETPORT)       += rocket.o
 obj-$(CONFIG_SYNCLINK_GT)      += synclink_gt.o
 obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
index 319d68c..219e857 100644 (file)
@@ -2081,9 +2081,6 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
        return 0;
 }
 
-extern ssize_t redirected_tty_write(struct file *, const char __user *,
-                                                       size_t, loff_t *);
-
 /**
  *     job_control             -       check job control
  *     @tty: tty
@@ -2105,7 +2102,7 @@ static int job_control(struct tty_struct *tty, struct file *file)
        /* NOTE: not yet done after every sleep pending a thorough
           check of the logic of this change. -- jlc */
        /* don't stop on /dev/console */
-       if (file->f_op->write == redirected_tty_write)
+       if (file->f_op->write_iter == redirected_tty_write)
                return 0;
 
        return __tty_check_change(tty, SIGTTIN);
@@ -2309,7 +2306,7 @@ static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
        ssize_t retval = 0;
 
        /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
-       if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
+       if (L_TOSTOP(tty) && file->f_op->write_iter != redirected_tty_write) {
                retval = tty_check_change(tty);
                if (retval)
                        return retval;
index 118b299..e0c00a1 100644 (file)
@@ -648,6 +648,14 @@ static void wait_for_xmitr(struct uart_port *port)
                                  (val & STAT_TX_RDY(port)), 1, 10000);
 }
 
+static void wait_for_xmite(struct uart_port *port)
+{
+       u32 val;
+
+       readl_poll_timeout_atomic(port->membase + UART_STAT, val,
+                                 (val & STAT_TX_EMP), 1, 10000);
+}
+
 static void mvebu_uart_console_putchar(struct uart_port *port, int ch)
 {
        wait_for_xmitr(port);
@@ -675,7 +683,7 @@ static void mvebu_uart_console_write(struct console *co, const char *s,
 
        uart_console_write(port, s, count, mvebu_uart_console_putchar);
 
-       wait_for_xmitr(port);
+       wait_for_xmite(port);
 
        if (ier)
                writel(ier, port->membase + UART_CTRL(port));
index 1066eeb..328d5a7 100644 (file)
@@ -1000,6 +1000,7 @@ static int sifive_serial_probe(struct platform_device *pdev)
        /* Set up clock divider */
        ssp->clkin_rate = clk_get_rate(ssp->clk);
        ssp->baud_rate = SIFIVE_DEFAULT_BAUD_RATE;
+       ssp->port.uartclk = ssp->baud_rate * 16;
        __ssp_update_div(ssp);
 
        platform_set_drvdata(pdev, ssp);
index 8034489..48de209 100644 (file)
@@ -143,12 +143,9 @@ LIST_HEAD(tty_drivers);                    /* linked list of tty drivers */
 DEFINE_MUTEX(tty_mutex);
 
 static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
-static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
-ssize_t redirected_tty_write(struct file *, const char __user *,
-                                                       size_t, loff_t *);
+static ssize_t tty_write(struct kiocb *, struct iov_iter *);
 static __poll_t tty_poll(struct file *, poll_table *);
 static int tty_open(struct inode *, struct file *);
-long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 #ifdef CONFIG_COMPAT
 static long tty_compat_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg);
@@ -438,8 +435,7 @@ static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
        return 0;
 }
 
-static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
-                                size_t count, loff_t *ppos)
+static ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from)
 {
        return -EIO;
 }
@@ -478,7 +474,8 @@ static void tty_show_fdinfo(struct seq_file *m, struct file *file)
 static const struct file_operations tty_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
-       .write          = tty_write,
+       .write_iter     = tty_write,
+       .splice_write   = iter_file_splice_write,
        .poll           = tty_poll,
        .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
@@ -491,7 +488,8 @@ static const struct file_operations tty_fops = {
 static const struct file_operations console_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
-       .write          = redirected_tty_write,
+       .write_iter     = redirected_tty_write,
+       .splice_write   = iter_file_splice_write,
        .poll           = tty_poll,
        .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
@@ -503,7 +501,7 @@ static const struct file_operations console_fops = {
 static const struct file_operations hung_up_tty_fops = {
        .llseek         = no_llseek,
        .read           = hung_up_tty_read,
-       .write          = hung_up_tty_write,
+       .write_iter     = hung_up_tty_write,
        .poll           = hung_up_tty_poll,
        .unlocked_ioctl = hung_up_tty_ioctl,
        .compat_ioctl   = hung_up_tty_compat_ioctl,
@@ -606,9 +604,9 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
        /* This breaks for file handles being sent over AF_UNIX sockets ? */
        list_for_each_entry(priv, &tty->tty_files, list) {
                filp = priv->file;
-               if (filp->f_op->write == redirected_tty_write)
+               if (filp->f_op->write_iter == redirected_tty_write)
                        cons_filp = filp;
-               if (filp->f_op->write != tty_write)
+               if (filp->f_op->write_iter != tty_write)
                        continue;
                closecount++;
                __tty_fasync(-1, filp, 0);      /* can't block */
@@ -901,9 +899,9 @@ static inline ssize_t do_tty_write(
        ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
        struct tty_struct *tty,
        struct file *file,
-       const char __user *buf,
-       size_t count)
+       struct iov_iter *from)
 {
+       size_t count = iov_iter_count(from);
        ssize_t ret, written = 0;
        unsigned int chunk;
 
@@ -955,14 +953,20 @@ static inline ssize_t do_tty_write(
                size_t size = count;
                if (size > chunk)
                        size = chunk;
+
                ret = -EFAULT;
-               if (copy_from_user(tty->write_buf, buf, size))
+               if (copy_from_iter(tty->write_buf, size, from) != size)
                        break;
+
                ret = write(tty, file, tty->write_buf, size);
                if (ret <= 0)
                        break;
+
+               /* FIXME! Have Al check this! */
+               if (ret != size)
+                       iov_iter_revert(from, size-ret);
+
                written += ret;
-               buf += ret;
                count -= ret;
                if (!count)
                        break;
@@ -1022,9 +1026,9 @@ void tty_write_message(struct tty_struct *tty, char *msg)
  *     write method will not be invoked in parallel for each device.
  */
 
-static ssize_t tty_write(struct file *file, const char __user *buf,
-                                               size_t count, loff_t *ppos)
+static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
 {
+       struct file *file = iocb->ki_filp;
        struct tty_struct *tty = file_tty(file);
        struct tty_ldisc *ld;
        ssize_t ret;
@@ -1038,17 +1042,16 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
                tty_err(tty, "missing write_room method\n");
        ld = tty_ldisc_ref_wait(tty);
        if (!ld)
-               return hung_up_tty_write(file, buf, count, ppos);
+               return hung_up_tty_write(iocb, from);
        if (!ld->ops->write)
                ret = -EIO;
        else
-               ret = do_tty_write(ld->ops->write, tty, file, buf, count);
+               ret = do_tty_write(ld->ops->write, tty, file, from);
        tty_ldisc_deref(ld);
        return ret;
 }
 
-ssize_t redirected_tty_write(struct file *file, const char __user *buf,
-                                               size_t count, loff_t *ppos)
+ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
 {
        struct file *p = NULL;
 
@@ -1059,11 +1062,11 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
 
        if (p) {
                ssize_t res;
-               res = vfs_write(p, buf, count, &p->f_pos);
+               res = vfs_iocb_iter_write(p, iocb, iter);
                fput(p);
                return res;
        }
-       return tty_write(file, buf, count, ppos);
+       return tty_write(iocb, iter);
 }
 
 /*
@@ -2295,7 +2298,7 @@ static int tioccons(struct file *file)
 {
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
-       if (file->f_op->write == redirected_tty_write) {
+       if (file->f_op->write_iter == redirected_tty_write) {
                struct file *f;
                spin_lock(&redirect_lock);
                f = redirect;
index eced70e..17f05b7 100644 (file)
@@ -2,13 +2,6 @@
 /*
  * Copyright (C) 2019 Axis Communications AB
  *
- * The console is useful for userspace applications which expect a console
- * device to work without modifications even when no console is available
- * or desired.
- *
- * In order to use this driver, you should redirect the console to this
- * TTY, or boot the kernel with console=ttynull.
- *
  * Based on ttyprintk.c:
  *  Copyright (C) 2010 Samo Pogacnik
  */
@@ -66,17 +59,6 @@ static struct console ttynull_console = {
        .device = ttynull_device,
 };
 
-void __init register_ttynull_console(void)
-{
-       if (!ttynull_driver)
-               return;
-
-       if (add_preferred_console(ttynull_console.name, 0, NULL))
-               return;
-
-       register_console(&ttynull_console);
-}
-
 static int __init ttynull_init(void)
 {
        struct tty_driver *driver;
index 22a56c4..7990fee 100644 (file)
@@ -185,7 +185,11 @@ static int cdns_imx_probe(struct platform_device *pdev)
        }
 
        data->num_clks = ARRAY_SIZE(imx_cdns3_core_clks);
-       data->clks = (struct clk_bulk_data *)imx_cdns3_core_clks;
+       data->clks = devm_kmemdup(dev, imx_cdns3_core_clks,
+                               sizeof(imx_cdns3_core_clks), GFP_KERNEL);
+       if (!data->clks)
+               return -ENOMEM;
+
        ret = devm_clk_bulk_get(dev, data->num_clks, data->clks);
        if (ret)
                return ret;
@@ -214,20 +218,16 @@ err:
        return ret;
 }
 
-static int cdns_imx_remove_core(struct device *dev, void *data)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-
-       platform_device_unregister(pdev);
-
-       return 0;
-}
-
 static int cdns_imx_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
+       struct cdns_imx *data = dev_get_drvdata(dev);
 
-       device_for_each_child(dev, NULL, cdns_imx_remove_core);
+       pm_runtime_get_sync(dev);
+       of_platform_depopulate(dev);
+       clk_bulk_disable_unprepare(data->num_clks, data->clks);
+       pm_runtime_disable(dev);
+       pm_runtime_put_noidle(dev);
        platform_set_drvdata(pdev, NULL);
 
        return 0;
index 9e12152..8b7bc10 100644 (file)
@@ -139,9 +139,13 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
        misc_pdev = of_find_device_by_node(args.np);
        of_node_put(args.np);
 
-       if (!misc_pdev || !platform_get_drvdata(misc_pdev))
+       if (!misc_pdev)
                return ERR_PTR(-EPROBE_DEFER);
 
+       if (!platform_get_drvdata(misc_pdev)) {
+               put_device(&misc_pdev->dev);
+               return ERR_PTR(-EPROBE_DEFER);
+       }
        data->dev = &misc_pdev->dev;
 
        /*
index f52f1bc..7819057 100644 (file)
@@ -1895,6 +1895,10 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x04d8, 0xfd08),
        .driver_info = IGNORE_DEVICE,
        },
+
+       { USB_DEVICE(0x04d8, 0xf58b),
+       .driver_info = IGNORE_DEVICE,
+       },
 #endif
 
        /*Samsung phone in firmware update mode */
index 02d0cfd..508b1c3 100644 (file)
@@ -465,13 +465,23 @@ static int service_outstanding_interrupt(struct wdm_device *desc)
        if (!desc->resp_count || !--desc->resp_count)
                goto out;
 
+       if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
+               rv = -ENODEV;
+               goto out;
+       }
+       if (test_bit(WDM_RESETTING, &desc->flags)) {
+               rv = -EIO;
+               goto out;
+       }
+
        set_bit(WDM_RESPONDING, &desc->flags);
        spin_unlock_irq(&desc->iuspin);
        rv = usb_submit_urb(desc->response, GFP_KERNEL);
        spin_lock_irq(&desc->iuspin);
        if (rv) {
-               dev_err(&desc->intf->dev,
-                       "usb_submit_urb failed with result %d\n", rv);
+               if (!test_bit(WDM_DISCONNECTING, &desc->flags))
+                       dev_err(&desc->intf->dev,
+                               "usb_submit_urb failed with result %d\n", rv);
 
                /* make sure the next notification trigger a submit */
                clear_bit(WDM_RESPONDING, &desc->flags);
@@ -1027,9 +1037,9 @@ static void wdm_disconnect(struct usb_interface *intf)
        wake_up_all(&desc->wait);
        mutex_lock(&desc->rlock);
        mutex_lock(&desc->wlock);
-       kill_urbs(desc);
        cancel_work_sync(&desc->rxwork);
        cancel_work_sync(&desc->service_outs_intr);
+       kill_urbs(desc);
        mutex_unlock(&desc->wlock);
        mutex_unlock(&desc->rlock);
 
index 67cbd42..134dc20 100644 (file)
@@ -274,8 +274,25 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i
 #define usblp_reset(usblp)\
        usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_TYPE_CLASS, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0)
 
-#define usblp_hp_channel_change_request(usblp, channel, buffer) \
-       usblp_ctrl_msg(usblp, USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST, USB_TYPE_VENDOR, USB_DIR_IN, USB_RECIP_INTERFACE, channel, buffer, 1)
+static int usblp_hp_channel_change_request(struct usblp *usblp, int channel, u8 *new_channel)
+{
+       u8 *buf;
+       int ret;
+
+       buf = kzalloc(1, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = usblp_ctrl_msg(usblp, USBLP_REQ_HP_CHANNEL_CHANGE_REQUEST,
+                       USB_TYPE_VENDOR, USB_DIR_IN, USB_RECIP_INTERFACE,
+                       channel, buf, 1);
+       if (ret == 0)
+               *new_channel = buf[0];
+
+       kfree(buf);
+
+       return ret;
+}
 
 /*
  * See the description for usblp_select_alts() below for the usage
index 60886a7..ad5a0f4 100644 (file)
@@ -1649,14 +1649,12 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
        urb->status = status;
        /*
         * This function can be called in task context inside another remote
-        * coverage collection section, but KCOV doesn't support that kind of
+        * coverage collection section, but kcov doesn't support that kind of
         * recursion yet. Only collect coverage in softirq context for now.
         */
-       if (in_serving_softirq())
-               kcov_remote_start_usb((u64)urb->dev->bus->busnum);
+       kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
        urb->complete(urb);
-       if (in_serving_softirq())
-               kcov_remote_stop();
+       kcov_remote_stop_softirq();
 
        usb_anchor_resume_wakeups(anchor);
        atomic_dec(&urb->use_count);
index 2f95f08..1b241f9 100644 (file)
 
 /* Global USB2 PHY Vendor Control Register */
 #define DWC3_GUSB2PHYACC_NEWREGREQ     BIT(25)
+#define DWC3_GUSB2PHYACC_DONE          BIT(24)
 #define DWC3_GUSB2PHYACC_BUSY          BIT(23)
 #define DWC3_GUSB2PHYACC_WRITE         BIT(22)
 #define DWC3_GUSB2PHYACC_ADDR(n)       (n << 16)
index 417e053..bdf1f98 100644 (file)
@@ -754,7 +754,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
 
        ret = priv->drvdata->setup_regmaps(priv, base);
        if (ret)
-               return ret;
+               goto err_disable_clks;
 
        if (priv->vbus) {
                ret = regulator_enable(priv->vbus);
index 78cb4db..ee44321 100644 (file)
@@ -1763,6 +1763,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
                        list_for_each_entry_safe(r, t, &dep->started_list, list)
                                dwc3_gadget_move_cancelled_request(r);
 
+                       dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
                        goto out;
                }
        }
@@ -2083,6 +2085,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
 static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
 static void __dwc3_gadget_stop(struct dwc3 *dwc);
+static int __dwc3_gadget_start(struct dwc3 *dwc);
 
 static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 {
@@ -2145,6 +2148,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
                        dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) %
                                                dwc->ev_buf->length;
                }
+       } else {
+               __dwc3_gadget_start(dwc);
        }
 
        ret = dwc3_gadget_run_stop(dwc, is_on, false);
@@ -2319,10 +2324,6 @@ static int dwc3_gadget_start(struct usb_gadget *g,
        }
 
        dwc->gadget_driver      = driver;
-
-       if (pm_runtime_active(dwc->dev))
-               __dwc3_gadget_start(dwc);
-
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return 0;
@@ -2348,13 +2349,6 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
        unsigned long           flags;
 
        spin_lock_irqsave(&dwc->lock, flags);
-
-       if (pm_runtime_suspended(dwc->dev))
-               goto out;
-
-       __dwc3_gadget_stop(dwc);
-
-out:
        dwc->gadget_driver      = NULL;
        spin_unlock_irqrestore(&dwc->lock, flags);
 
index aa213c9..f23f4c9 100644 (file)
@@ -7,6 +7,8 @@
  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
  */
 
+#include <linux/delay.h>
+#include <linux/time64.h>
 #include <linux/ulpi/regs.h>
 
 #include "core.h"
                DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
                DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a))
 
-static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
+#define DWC3_ULPI_BASE_DELAY   DIV_ROUND_UP(NSEC_PER_SEC, 60000000L)
+
+static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read)
 {
-       unsigned int count = 1000;
+       unsigned long ns = 5L * DWC3_ULPI_BASE_DELAY;
+       unsigned int count = 10000;
        u32 reg;
 
+       if (addr >= ULPI_EXT_VENDOR_SPECIFIC)
+               ns += DWC3_ULPI_BASE_DELAY;
+
+       if (read)
+               ns += DWC3_ULPI_BASE_DELAY;
+
+       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+       if (reg & DWC3_GUSB2PHYCFG_SUSPHY)
+               usleep_range(1000, 1200);
+
        while (count--) {
+               ndelay(ns);
                reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
-               if (!(reg & DWC3_GUSB2PHYACC_BUSY))
+               if (reg & DWC3_GUSB2PHYACC_DONE)
                        return 0;
                cpu_relax();
        }
@@ -38,16 +54,10 @@ static int dwc3_ulpi_read(struct device *dev, u8 addr)
        u32 reg;
        int ret;
 
-       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
-       if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
-               reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-               dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
-       }
-
        reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
 
-       ret = dwc3_ulpi_busyloop(dwc);
+       ret = dwc3_ulpi_busyloop(dwc, addr, true);
        if (ret)
                return ret;
 
@@ -61,17 +71,11 @@ static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val)
        struct dwc3 *dwc = dev_get_drvdata(dev);
        u32 reg;
 
-       reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
-       if (reg & DWC3_GUSB2PHYCFG_SUSPHY) {
-               reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
-               dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
-       }
-
        reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
        reg |= DWC3_GUSB2PHYACC_WRITE | val;
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
 
-       return dwc3_ulpi_busyloop(dwc);
+       return dwc3_ulpi_busyloop(dwc, addr, false);
 }
 
 static const struct ulpi_ops dwc3_ulpi_ops = {
index 7e47e62..2d15257 100644 (file)
@@ -265,6 +265,7 @@ config USB_CONFIGFS_NCM
        depends on NET
        select USB_U_ETHER
        select USB_F_NCM
+       select CRC32
        help
          NCM is an advanced protocol for Ethernet encapsulation, allows
          grouping of several ethernet frames into one USB transfer and
@@ -314,6 +315,7 @@ config USB_CONFIGFS_EEM
        depends on NET
        select USB_U_ETHER
        select USB_F_EEM
+       select CRC32
        help
          CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
          and therefore can be supported by more hardware.  Technically ECM and
index c6d455f..1a556a6 100644 (file)
@@ -392,8 +392,11 @@ int usb_function_deactivate(struct usb_function *function)
 
        spin_lock_irqsave(&cdev->lock, flags);
 
-       if (cdev->deactivations == 0)
+       if (cdev->deactivations == 0) {
+               spin_unlock_irqrestore(&cdev->lock, flags);
                status = usb_gadget_deactivate(cdev->gadget);
+               spin_lock_irqsave(&cdev->lock, flags);
+       }
        if (status == 0)
                cdev->deactivations++;
 
@@ -424,8 +427,11 @@ int usb_function_activate(struct usb_function *function)
                status = -EINVAL;
        else {
                cdev->deactivations--;
-               if (cdev->deactivations == 0)
+               if (cdev->deactivations == 0) {
+                       spin_unlock_irqrestore(&cdev->lock, flags);
                        status = usb_gadget_activate(cdev->gadget);
+                       spin_lock_irqsave(&cdev->lock, flags);
+               }
        }
 
        spin_unlock_irqrestore(&cdev->lock, flags);
index 56051bb..36ffb43 100644 (file)
@@ -221,9 +221,16 @@ static ssize_t gadget_dev_desc_bcdUSB_store(struct config_item *item,
 
 static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page)
 {
-       char *udc_name = to_gadget_info(item)->composite.gadget_driver.udc_name;
+       struct gadget_info *gi = to_gadget_info(item);
+       char *udc_name;
+       int ret;
+
+       mutex_lock(&gi->lock);
+       udc_name = gi->composite.gadget_driver.udc_name;
+       ret = sprintf(page, "%s\n", udc_name ?: "");
+       mutex_unlock(&gi->lock);
 
-       return sprintf(page, "%s\n", udc_name ?: "");
+       return ret;
 }
 
 static int unregister_gadget(struct gadget_info *gi)
@@ -1248,9 +1255,9 @@ static void purge_configs_funcs(struct gadget_info *gi)
 
                cfg = container_of(c, struct config_usb_cfg, c);
 
-               list_for_each_entry_safe(f, tmp, &c->functions, list) {
+               list_for_each_entry_safe_reverse(f, tmp, &c->functions, list) {
 
-                       list_move_tail(&f->list, &cfg->func_list);
+                       list_move(&f->list, &cfg->func_list);
                        if (f->unbind) {
                                dev_dbg(&gi->cdev.gadget->dev,
                                        "unbind function '%s'/%p\n",
@@ -1536,7 +1543,7 @@ static const struct usb_gadget_driver configfs_driver_template = {
        .suspend        = configfs_composite_suspend,
        .resume         = configfs_composite_resume,
 
-       .max_speed      = USB_SPEED_SUPER,
+       .max_speed      = USB_SPEED_SUPER_PLUS,
        .driver = {
                .owner          = THIS_MODULE,
                .name           = "configfs-gadget",
@@ -1576,7 +1583,7 @@ static struct config_group *gadgets_make(
        gi->composite.unbind = configfs_do_nothing;
        gi->composite.suspend = NULL;
        gi->composite.resume = NULL;
-       gi->composite.max_speed = USB_SPEED_SUPER;
+       gi->composite.max_speed = USB_SPEED_SUPER_PLUS;
 
        spin_lock_init(&gi->spinlock);
        mutex_init(&gi->lock);
index 64a4112..2f1eb2e 100644 (file)
@@ -1162,6 +1162,7 @@ fail_tx_reqs:
                printer_req_free(dev->in_ep, req);
        }
 
+       usb_free_all_descriptors(f);
        return ret;
 
 }
index 3633df6..5d960b6 100644 (file)
@@ -271,7 +271,7 @@ static struct usb_endpoint_descriptor fs_epout_desc = {
 
        .bEndpointAddress = USB_DIR_OUT,
        .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .wMaxPacketSize = cpu_to_le16(1023),
+       /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 1,
 };
 
@@ -280,7 +280,7 @@ static struct usb_endpoint_descriptor hs_epout_desc = {
        .bDescriptorType = USB_DT_ENDPOINT,
 
        .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .wMaxPacketSize = cpu_to_le16(1024),
+       /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 4,
 };
 
@@ -348,7 +348,7 @@ static struct usb_endpoint_descriptor fs_epin_desc = {
 
        .bEndpointAddress = USB_DIR_IN,
        .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .wMaxPacketSize = cpu_to_le16(1023),
+       /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 1,
 };
 
@@ -357,7 +357,7 @@ static struct usb_endpoint_descriptor hs_epin_desc = {
        .bDescriptorType = USB_DT_ENDPOINT,
 
        .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC,
-       .wMaxPacketSize = cpu_to_le16(1024),
+       /* .wMaxPacketSize = DYNAMIC */
        .bInterval = 4,
 };
 
@@ -444,12 +444,28 @@ struct cntrl_range_lay3 {
        __le32  dRES;
 } __packed;
 
-static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
+static int set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
        struct usb_endpoint_descriptor *ep_desc,
-       unsigned int factor, bool is_playback)
+       enum usb_device_speed speed, bool is_playback)
 {
        int chmask, srate, ssize;
-       u16 max_packet_size;
+       u16 max_size_bw, max_size_ep;
+       unsigned int factor;
+
+       switch (speed) {
+       case USB_SPEED_FULL:
+               max_size_ep = 1023;
+               factor = 1000;
+               break;
+
+       case USB_SPEED_HIGH:
+               max_size_ep = 1024;
+               factor = 8000;
+               break;
+
+       default:
+               return -EINVAL;
+       }
 
        if (is_playback) {
                chmask = uac2_opts->p_chmask;
@@ -461,10 +477,12 @@ static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts,
                ssize = uac2_opts->c_ssize;
        }
 
-       max_packet_size = num_channels(chmask) * ssize *
+       max_size_bw = num_channels(chmask) * ssize *
                DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1)));
-       ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_packet_size,
-                               le16_to_cpu(ep_desc->wMaxPacketSize)));
+       ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw,
+                                                   max_size_ep));
+
+       return 0;
 }
 
 /* Use macro to overcome line length limitation */
@@ -670,10 +688,33 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
        }
 
        /* Calculate wMaxPacketSize according to audio bandwidth */
-       set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true);
-       set_ep_max_packet_size(uac2_opts, &fs_epout_desc, 1000, false);
-       set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true);
-       set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false);
+       ret = set_ep_max_packet_size(uac2_opts, &fs_epin_desc, USB_SPEED_FULL,
+                                    true);
+       if (ret < 0) {
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
+
+       ret = set_ep_max_packet_size(uac2_opts, &fs_epout_desc, USB_SPEED_FULL,
+                                    false);
+       if (ret < 0) {
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
+
+       ret = set_ep_max_packet_size(uac2_opts, &hs_epin_desc, USB_SPEED_HIGH,
+                                    true);
+       if (ret < 0) {
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
+
+       ret = set_ep_max_packet_size(uac2_opts, &hs_epout_desc, USB_SPEED_HIGH,
+                                    false);
+       if (ret < 0) {
+               dev_err(dev, "%s:%d Error!\n", __func__, __LINE__);
+               return ret;
+       }
 
        if (EPOUT_EN(uac2_opts)) {
                agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc);
index 31ea76a..c019f2b 100644 (file)
 #define UETH__VERSION  "29-May-2008"
 
 /* Experiments show that both Linux and Windows hosts allow up to 16k
- * frame sizes. Set the max size to 15k+52 to prevent allocating 32k
+ * frame sizes. Set the max MTU size to 15k+52 to prevent allocating 32k
  * blocks and still have efficient handling. */
-#define GETHER_MAX_ETH_FRAME_LEN 15412
+#define GETHER_MAX_MTU_SIZE 15412
+#define GETHER_MAX_ETH_FRAME_LEN (GETHER_MAX_MTU_SIZE + ETH_HLEN)
 
 struct eth_dev {
        /* lock is held while accessing port_usb
@@ -786,7 +787,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
 
        /* MTU range: 14 - 15412 */
        net->min_mtu = ETH_HLEN;
-       net->max_mtu = GETHER_MAX_ETH_FRAME_LEN;
+       net->max_mtu = GETHER_MAX_MTU_SIZE;
 
        dev->gadget = g;
        SET_NETDEV_DEV(net, &g->dev);
@@ -848,7 +849,7 @@ struct net_device *gether_setup_name_default(const char *netname)
 
        /* MTU range: 14 - 15412 */
        net->min_mtu = ETH_HLEN;
-       net->max_mtu = GETHER_MAX_ETH_FRAME_LEN;
+       net->max_mtu = GETHER_MAX_MTU_SIZE;
 
        return net;
 }
index 59be2d8..e8033e5 100644 (file)
@@ -200,8 +200,10 @@ static int acm_ms_bind(struct usb_composite_dev *cdev)
                struct usb_descriptor_header *usb_desc;
 
                usb_desc = usb_otg_descriptor_alloc(gadget);
-               if (!usb_desc)
+               if (!usb_desc) {
+                       status = -ENOMEM;
                        goto fail_string_ids;
+               }
                usb_otg_descriptor_init(gadget, usb_desc);
                otg_desc[0] = usb_desc;
                otg_desc[1] = NULL;
index 1a12aab..8c614bb 100644 (file)
@@ -90,7 +90,7 @@ config USB_BCM63XX_UDC
 
 config USB_FSL_USB2
        tristate "Freescale Highspeed USB DR Peripheral Controller"
-       depends on FSL_SOC || ARCH_MXC
+       depends on FSL_SOC
        help
           Some of Freescale PowerPC and i.MX processors have a High Speed
           Dual-Role(DR) USB controller, which supports device mode.
index f5a7ce2..a21f222 100644 (file)
@@ -23,7 +23,6 @@ obj-$(CONFIG_USB_ATMEL_USBA)  += atmel_usba_udc.o
 obj-$(CONFIG_USB_BCM63XX_UDC)  += bcm63xx_udc.o
 obj-$(CONFIG_USB_FSL_USB2)     += fsl_usb2_udc.o
 fsl_usb2_udc-y                 := fsl_udc_core.o
-fsl_usb2_udc-$(CONFIG_ARCH_MXC)        += fsl_mxc_udc.o
 obj-$(CONFIG_USB_TEGRA_XUDC)   += tegra-xudc.o
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 obj-$(CONFIG_USB_R8A66597)     += r8a66597-udc.o
index 0bd6b20..02d8bfa 100644 (file)
@@ -420,7 +420,10 @@ static void ast_vhub_stop_active_req(struct ast_vhub_ep *ep,
        u32 state, reg, loops;
 
        /* Stop DMA activity */
-       writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+       if (ep->epn.desc_mode)
+               writel(VHUB_EP_DMA_CTRL_RESET, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
+       else
+               writel(0, ep->epn.regs + AST_VHUB_EP_DMA_CTLSTAT);
 
        /* Wait for it to complete */
        for (loops = 0; loops < 1000; loops++) {
index 3e88c76..fb01ff4 100644 (file)
@@ -17,7 +17,7 @@ if USB_BDC_UDC
 comment "Platform Support"
 config USB_BDC_PCI
        tristate "BDC support for PCIe based platforms"
-       depends on USB_PCI
+       depends on USB_PCI && BROKEN
        default USB_BDC_UDC
        help
                Enable support for platforms which have BDC connected through PCIe, such as Lego3 FPGA platform.
index 5b5cfeb..ea114f9 100644 (file)
@@ -659,8 +659,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect);
  *
  * Enables the D+ (or potentially D-) pullup.  The host will start
  * enumerating this gadget when the pullup is active and a VBUS session
- * is active (the link is powered).  This pullup is always enabled unless
- * usb_gadget_disconnect() has been used to disable it.
+ * is active (the link is powered).
  *
  * Returns zero on success, else negative errno.
  */
@@ -1530,10 +1529,13 @@ static ssize_t soft_connect_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t n)
 {
        struct usb_udc          *udc = container_of(dev, struct usb_udc, dev);
+       ssize_t                 ret;
 
+       mutex_lock(&udc_lock);
        if (!udc->driver) {
                dev_err(dev, "soft-connect without a gadget driver\n");
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               goto out;
        }
 
        if (sysfs_streq(buf, "connect")) {
@@ -1544,10 +1546,14 @@ static ssize_t soft_connect_store(struct device *dev,
                usb_gadget_udc_stop(udc);
        } else {
                dev_err(dev, "unsupported command '%s'\n", buf);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
-       return n;
+       ret = n;
+out:
+       mutex_unlock(&udc_lock);
+       return ret;
 }
 static DEVICE_ATTR_WO(soft_connect);
 
index ab5e978..5706776 100644 (file)
@@ -2118,9 +2118,21 @@ static int dummy_hub_control(
                                dum_hcd->port_status &= ~USB_PORT_STAT_POWER;
                        set_link_state(dum_hcd);
                        break;
-               default:
+               case USB_PORT_FEAT_ENABLE:
+               case USB_PORT_FEAT_C_ENABLE:
+               case USB_PORT_FEAT_C_SUSPEND:
+                       /* Not allowed for USB-3 */
+                       if (hcd->speed == HCD_USB3)
+                               goto error;
+                       fallthrough;
+               case USB_PORT_FEAT_C_CONNECTION:
+               case USB_PORT_FEAT_C_RESET:
                        dum_hcd->port_status &= ~(1 << wValue);
                        set_link_state(dum_hcd);
+                       break;
+               default:
+               /* Disallow INDICATOR and C_OVER_CURRENT */
+                       goto error;
                }
                break;
        case GetHubDescriptor:
@@ -2258,17 +2270,20 @@ static int dummy_hub_control(
                        }
                        fallthrough;
                case USB_PORT_FEAT_RESET:
+                       if (!(dum_hcd->port_status & USB_PORT_STAT_CONNECTION))
+                               break;
                        /* if it's already enabled, disable */
                        if (hcd->speed == HCD_USB3) {
-                               dum_hcd->port_status = 0;
                                dum_hcd->port_status =
                                        (USB_SS_PORT_STAT_POWER |
                                         USB_PORT_STAT_CONNECTION |
                                         USB_PORT_STAT_RESET);
-                       } else
+                       } else {
                                dum_hcd->port_status &= ~(USB_PORT_STAT_ENABLE
                                        | USB_PORT_STAT_LOW_SPEED
                                        | USB_PORT_STAT_HIGH_SPEED);
+                               dum_hcd->port_status |= USB_PORT_STAT_RESET;
+                       }
                        /*
                         * We want to reset device status. All but the
                         * Self powered feature
@@ -2280,19 +2295,19 @@ static int dummy_hub_control(
                         * interval? Is it still 50msec as for HS?
                         */
                        dum_hcd->re_timeout = jiffies + msecs_to_jiffies(50);
-                       fallthrough;
-               default:
-                       if (hcd->speed == HCD_USB3) {
-                               if ((dum_hcd->port_status &
-                                    USB_SS_PORT_STAT_POWER) != 0) {
-                                       dum_hcd->port_status |= (1 << wValue);
-                               }
-                       } else
-                               if ((dum_hcd->port_status &
-                                    USB_PORT_STAT_POWER) != 0) {
-                                       dum_hcd->port_status |= (1 << wValue);
-                               }
                        set_link_state(dum_hcd);
+                       break;
+               case USB_PORT_FEAT_C_CONNECTION:
+               case USB_PORT_FEAT_C_RESET:
+               case USB_PORT_FEAT_C_ENABLE:
+               case USB_PORT_FEAT_C_SUSPEND:
+                       /* Not allowed for USB-3, and ignored for USB-2 */
+                       if (hcd->speed == HCD_USB3)
+                               goto error;
+                       break;
+               default:
+               /* Disallow TEST, INDICATOR, and C_OVER_CURRENT */
+                       goto error;
                }
                break;
        case GetPortErrorCount:
diff --git a/drivers/usb/gadget/udc/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c
deleted file mode 100644 (file)
index 5a32199..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2009
- * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
- *
- * Description:
- * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
- * driver to function correctly on these systems.
- */
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/fsl_devices.h>
-#include <linux/mod_devicetable.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-#include "fsl_usb2_udc.h"
-
-static struct clk *mxc_ahb_clk;
-static struct clk *mxc_per_clk;
-static struct clk *mxc_ipg_clk;
-
-/* workaround ENGcm09152 for i.MX35 */
-#define MX35_USBPHYCTRL_OFFSET         0x600
-#define USBPHYCTRL_OTGBASE_OFFSET      0x8
-#define USBPHYCTRL_EVDO                        (1 << 23)
-
-int fsl_udc_clk_init(struct platform_device *pdev)
-{
-       struct fsl_usb2_platform_data *pdata;
-       unsigned long freq;
-       int ret;
-
-       pdata = dev_get_platdata(&pdev->dev);
-
-       mxc_ipg_clk = devm_clk_get(&pdev->dev, "ipg");
-       if (IS_ERR(mxc_ipg_clk)) {
-               dev_err(&pdev->dev, "clk_get(\"ipg\") failed\n");
-               return PTR_ERR(mxc_ipg_clk);
-       }
-
-       mxc_ahb_clk = devm_clk_get(&pdev->dev, "ahb");
-       if (IS_ERR(mxc_ahb_clk)) {
-               dev_err(&pdev->dev, "clk_get(\"ahb\") failed\n");
-               return PTR_ERR(mxc_ahb_clk);
-       }
-
-       mxc_per_clk = devm_clk_get(&pdev->dev, "per");
-       if (IS_ERR(mxc_per_clk)) {
-               dev_err(&pdev->dev, "clk_get(\"per\") failed\n");
-               return PTR_ERR(mxc_per_clk);
-       }
-
-       clk_prepare_enable(mxc_ipg_clk);
-       clk_prepare_enable(mxc_ahb_clk);
-       clk_prepare_enable(mxc_per_clk);
-
-       /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
-       if (!strcmp(pdev->id_entry->name, "imx-udc-mx27")) {
-               freq = clk_get_rate(mxc_per_clk);
-               if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
-                   (freq < 59999000 || freq > 60001000)) {
-                       dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
-                       ret = -EINVAL;
-                       goto eclkrate;
-               }
-       }
-
-       return 0;
-
-eclkrate:
-       clk_disable_unprepare(mxc_ipg_clk);
-       clk_disable_unprepare(mxc_ahb_clk);
-       clk_disable_unprepare(mxc_per_clk);
-       mxc_per_clk = NULL;
-       return ret;
-}
-
-int fsl_udc_clk_finalize(struct platform_device *pdev)
-{
-       struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
-       int ret = 0;
-
-       /* workaround ENGcm09152 for i.MX35 */
-       if (pdata->workaround & FLS_USB2_WORKAROUND_ENGCM09152) {
-               unsigned int v;
-               struct resource *res = platform_get_resource
-                       (pdev, IORESOURCE_MEM, 0);
-               void __iomem *phy_regs = ioremap(res->start +
-                                               MX35_USBPHYCTRL_OFFSET, 512);
-               if (!phy_regs) {
-                       dev_err(&pdev->dev, "ioremap for phy address fails\n");
-                       ret = -EINVAL;
-                       goto ioremap_err;
-               }
-
-               v = readl(phy_regs + USBPHYCTRL_OTGBASE_OFFSET);
-               writel(v | USBPHYCTRL_EVDO,
-                       phy_regs + USBPHYCTRL_OTGBASE_OFFSET);
-
-               iounmap(phy_regs);
-       }
-
-
-ioremap_err:
-       /* ULPI transceivers don't need usbpll */
-       if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
-               clk_disable_unprepare(mxc_per_clk);
-               mxc_per_clk = NULL;
-       }
-
-       return ret;
-}
-
-void fsl_udc_clk_release(void)
-{
-       if (mxc_per_clk)
-               clk_disable_unprepare(mxc_per_clk);
-       clk_disable_unprepare(mxc_ahb_clk);
-       clk_disable_unprepare(mxc_ipg_clk);
-}
index e358ae1..1926b32 100644 (file)
@@ -574,6 +574,7 @@ static int ehci_run (struct usb_hcd *hcd)
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
        u32                     temp;
        u32                     hcc_params;
+       int                     rc;
 
        hcd->uses_new_polling = 1;
 
@@ -629,9 +630,20 @@ static int ehci_run (struct usb_hcd *hcd)
        down_write(&ehci_cf_port_reset_rwsem);
        ehci->rh_state = EHCI_RH_RUNNING;
        ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+
+       /* Wait until HC become operational */
        ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
        msleep(5);
+       rc = ehci_handshake(ehci, &ehci->regs->status, STS_HALT, 0, 100 * 1000);
+
        up_write(&ehci_cf_port_reset_rwsem);
+
+       if (rc) {
+               ehci_err(ehci, "USB %x.%x, controller refused to start: %d\n",
+                        ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), rc);
+               return rc;
+       }
+
        ehci->last_periodic_enable = ktime_get_real();
 
        temp = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
index 087402a..9f9ab5c 100644 (file)
@@ -345,6 +345,9 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
 
        unlink_empty_async_suspended(ehci);
 
+       /* Some Synopsys controllers mistakenly leave IAA turned on */
+       ehci_writel(ehci, STS_IAA, &ehci->regs->status);
+
        /* Any IAA cycle that started before the suspend is now invalid */
        end_iaa_cycle(ehci);
        ehci_handle_start_intr_unlinks(ehci);
index 5677b81..cf0c93a 100644 (file)
@@ -2931,6 +2931,8 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
        trb->field[0] = cpu_to_le32(field1);
        trb->field[1] = cpu_to_le32(field2);
        trb->field[2] = cpu_to_le32(field3);
+       /* make sure TRB is fully written before giving it to the controller */
+       wmb();
        trb->field[3] = cpu_to_le32(field4);
 
        trace_xhci_queue_trb(ring, trb);
index 934be16..50bb91b 100644 (file)
@@ -623,6 +623,13 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,
                                                                     enable);
                        if (err < 0)
                                break;
+
+                       /*
+                        * wait 500us for LFPS detector to be disabled before
+                        * sending ACK
+                        */
+                       if (!enable)
+                               usleep_range(500, 1000);
                }
 
                if (err < 0) {
index 91ab81c..e869405 100644 (file)
@@ -4770,19 +4770,19 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
 {
        unsigned long long timeout_ns;
 
+       if (xhci->quirks & XHCI_INTEL_HOST)
+               timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
+       else
+               timeout_ns = udev->u1_params.sel;
+
        /* Prevent U1 if service interval is shorter than U1 exit latency */
        if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
-               if (xhci_service_interval_to_ns(desc) <= udev->u1_params.mel) {
+               if (xhci_service_interval_to_ns(desc) <= timeout_ns) {
                        dev_dbg(&udev->dev, "Disable U1, ESIT shorter than exit latency\n");
                        return USB3_LPM_DISABLED;
                }
        }
 
-       if (xhci->quirks & XHCI_INTEL_HOST)
-               timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
-       else
-               timeout_ns = udev->u1_params.sel;
-
        /* The U1 timeout is encoded in 1us intervals.
         * Don't return a timeout of zero, because that's USB3_LPM_DISABLED.
         */
@@ -4834,19 +4834,19 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
 {
        unsigned long long timeout_ns;
 
+       if (xhci->quirks & XHCI_INTEL_HOST)
+               timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
+       else
+               timeout_ns = udev->u2_params.sel;
+
        /* Prevent U2 if service interval is shorter than U2 exit latency */
        if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
-               if (xhci_service_interval_to_ns(desc) <= udev->u2_params.mel) {
+               if (xhci_service_interval_to_ns(desc) <= timeout_ns) {
                        dev_dbg(&udev->dev, "Disable U2, ESIT shorter than exit latency\n");
                        return USB3_LPM_DISABLED;
                }
        }
 
-       if (xhci->quirks & XHCI_INTEL_HOST)
-               timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
-       else
-               timeout_ns = udev->u2_params.sel;
-
        /* The U2 timeout is encoded in 256us intervals */
        timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000);
        /* If the necessary timeout value is bigger than what we can set in the
index 73ebfa6..c640f98 100644 (file)
@@ -496,6 +496,9 @@ static ssize_t yurex_write(struct file *file, const char __user *user_buffer,
                timeout = schedule_timeout(YUREX_WRITE_TIMEOUT);
        finish_wait(&dev->waitq, &wait);
 
+       /* make sure URB is idle after timeout or (spurious) CMD_ACK */
+       usb_kill_urb(dev->cntl_urb);
+
        mutex_unlock(&dev->io_mutex);
 
        if (retval < 0) {
index f1201d4..e8f06b4 100644 (file)
@@ -532,23 +532,29 @@ static int iuu_uart_flush(struct usb_serial_port *port)
        struct device *dev = &port->dev;
        int i;
        int status;
-       u8 rxcmd = IUU_UART_RX;
+       u8 *rxcmd;
        struct iuu_private *priv = usb_get_serial_port_data(port);
 
        if (iuu_led(port, 0xF000, 0, 0, 0xFF) < 0)
                return -EIO;
 
+       rxcmd = kmalloc(1, GFP_KERNEL);
+       if (!rxcmd)
+               return -ENOMEM;
+
+       rxcmd[0] = IUU_UART_RX;
+
        for (i = 0; i < 2; i++) {
-               status = bulk_immediate(port, &rxcmd, 1);
+               status = bulk_immediate(port, rxcmd, 1);
                if (status != IUU_OPERATION_OK) {
                        dev_dbg(dev, "%s - uart_flush_write error\n", __func__);
-                       return status;
+                       goto out_free;
                }
 
                status = read_immediate(port, &priv->len, 1);
                if (status != IUU_OPERATION_OK) {
                        dev_dbg(dev, "%s - uart_flush_read error\n", __func__);
-                       return status;
+                       goto out_free;
                }
 
                if (priv->len > 0) {
@@ -556,12 +562,16 @@ static int iuu_uart_flush(struct usb_serial_port *port)
                        status = read_immediate(port, priv->buf, priv->len);
                        if (status != IUU_OPERATION_OK) {
                                dev_dbg(dev, "%s - uart_flush_read error\n", __func__);
-                               return status;
+                               goto out_free;
                        }
                }
        }
        dev_dbg(dev, "%s - uart_flush_read OK!\n", __func__);
        iuu_led(port, 0, 0xF000, 0, 0xFF);
+
+out_free:
+       kfree(rxcmd);
+
        return status;
 }
 
index 2c21e34..3fe9591 100644 (file)
@@ -1117,6 +1117,8 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0xff, 0xff),
          .driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM12, 0xff, 0, 0) },
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, 0x0620, 0xff, 0xff, 0x30) }, /* EM160R-GL */
+       { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, 0x0620, 0xff, 0, 0) },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x30) },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0, 0) },
        { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500Q, 0xff, 0xff, 0x10),
@@ -2057,6 +2059,7 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0105, 0xff),                     /* Fibocom NL678 series */
          .driver_info = RSVD(6) },
        { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) },                   /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */
+       { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) },                   /* LongSung M5710 */
        { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) },                   /* GosunCn GM500 RNDIS */
        { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) },                   /* GosunCn GM500 MBIM */
        { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1406, 0xff) },                   /* GosunCn GM500 ECM/NCM */
index 870e9cf..f9677a5 100644 (file)
@@ -90,6 +90,13 @@ UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999,
                USB_SC_DEVICE, USB_PR_DEVICE, NULL,
                US_FL_BROKEN_FUA),
 
+/* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */
+UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999,
+               "PNY",
+               "Pro Elite SSD",
+               USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+               US_FL_NO_ATA_1X),
+
 /* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */
 UNUSUAL_DEV(0x154b, 0xf00d, 0x0000, 0x9999,
                "PNY",
index 187690f..60d375e 100644 (file)
@@ -20,6 +20,6 @@ config TYPEC_NVIDIA_ALTMODE
          to enable support for VirtualLink devices with NVIDIA GPUs.
 
          To compile this driver as a module, choose M here: the
-         module will be called typec_displayport.
+         module will be called typec_nvidia.
 
 endmenu
index ebfd311..8f77669 100644 (file)
@@ -766,6 +766,7 @@ int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmod
                return ret;
 
        sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes");
+       kobject_uevent(&partner->dev.kobj, KOBJ_CHANGE);
 
        return 0;
 }
@@ -923,6 +924,7 @@ int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes)
                return ret;
 
        sysfs_notify(&plug->dev.kobj, NULL, "number_of_alternate_modes");
+       kobject_uevent(&plug->dev.kobj, KOBJ_CHANGE);
 
        return 0;
 }
index cf37a59..46a25b8 100644 (file)
@@ -207,10 +207,21 @@ static int
 pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_displayport_data *dp)
 {
        u8 msg[2] = { };
+       int ret;
 
        msg[0] = PMC_USB_DP_HPD;
        msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
 
+       /* Configure HPD first if HPD,IRQ comes together */
+       if (!IOM_PORT_HPD_ASSERTED(port->iom_status) &&
+           dp->status & DP_STATUS_IRQ_HPD &&
+           dp->status & DP_STATUS_HPD_STATE) {
+               msg[1] = PMC_USB_DP_HPD_LVL;
+               ret = pmc_usb_command(port, msg, sizeof(msg));
+               if (ret)
+                       return ret;
+       }
+
        if (dp->status & DP_STATUS_IRQ_HPD)
                msg[1] = PMC_USB_DP_HPD_IRQ;
 
index 66cde5e..3209b5d 100644 (file)
@@ -396,6 +396,8 @@ static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                default:
                        usbip_dbg_vhci_rh(" ClearPortFeature: default %x\n",
                                          wValue);
+                       if (wValue >= 32)
+                               goto error;
                        vhci_hcd->port_status[rhport] &= ~(1 << wValue);
                        break;
                }
index 531a00d..df82b12 100644 (file)
@@ -381,7 +381,8 @@ static void vhost_zerocopy_signal_used(struct vhost_net *net,
        }
 }
 
-static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success)
+static void vhost_zerocopy_callback(struct sk_buff *skb,
+                                   struct ubuf_info *ubuf, bool success)
 {
        struct vhost_net_ubuf_ref *ubufs = ubuf->ctx;
        struct vhost_virtqueue *vq = ubufs->vq;
@@ -827,14 +828,15 @@ static void handle_tx_copy(struct vhost_net *net, struct socket *sock)
                                msg.msg_flags &= ~MSG_MORE;
                }
 
-               /* TODO: Check specific error and bomb out unless ENOBUFS? */
                err = sock->ops->sendmsg(sock, &msg, len);
                if (unlikely(err < 0)) {
-                       vhost_discard_vq_desc(vq, 1);
-                       vhost_net_enable_vq(net, vq);
-                       break;
-               }
-               if (err != len)
+                       if (err == -EAGAIN || err == -ENOMEM || err == -ENOBUFS) {
+                               vhost_discard_vq_desc(vq, 1);
+                               vhost_net_enable_vq(net, vq);
+                               break;
+                       }
+                       pr_debug("Fail to send packet: err %d", err);
+               } else if (unlikely(err != len))
                        pr_debug("Truncated TX packet: len %d != %zd\n",
                                 err, len);
 done:
@@ -863,6 +865,7 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock)
        size_t len, total_len = 0;
        int err;
        struct vhost_net_ubuf_ref *ubufs;
+       struct ubuf_info *ubuf;
        bool zcopy_used;
        int sent_pkts = 0;
 
@@ -895,14 +898,13 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock)
 
                /* use msg_control to pass vhost zerocopy ubuf info to skb */
                if (zcopy_used) {
-                       struct ubuf_info *ubuf;
                        ubuf = nvq->ubuf_info + nvq->upend_idx;
-
                        vq->heads[nvq->upend_idx].id = cpu_to_vhost32(vq, head);
                        vq->heads[nvq->upend_idx].len = VHOST_DMA_IN_PROGRESS;
                        ubuf->callback = vhost_zerocopy_callback;
                        ubuf->ctx = nvq->ubufs;
                        ubuf->desc = nvq->upend_idx;
+                       ubuf->flags = SKBFL_ZEROCOPY_FRAG;
                        refcount_set(&ubuf->refcnt, 1);
                        msg.msg_control = &ctl;
                        ctl.type = TUN_MSG_UBUF;
@@ -923,19 +925,21 @@ static void handle_tx_zerocopy(struct vhost_net *net, struct socket *sock)
                        msg.msg_flags &= ~MSG_MORE;
                }
 
-               /* TODO: Check specific error and bomb out unless ENOBUFS? */
                err = sock->ops->sendmsg(sock, &msg, len);
                if (unlikely(err < 0)) {
                        if (zcopy_used) {
-                               vhost_net_ubuf_put(ubufs);
+                               if (vq->heads[ubuf->desc].len == VHOST_DMA_IN_PROGRESS)
+                                       vhost_net_ubuf_put(ubufs);
                                nvq->upend_idx = ((unsigned)nvq->upend_idx - 1)
                                        % UIO_MAXIOV;
                        }
-                       vhost_discard_vq_desc(vq, 1);
-                       vhost_net_enable_vq(net, vq);
-                       break;
-               }
-               if (err != len)
+                       if (err == -EAGAIN || err == -ENOMEM || err == -ENOBUFS) {
+                               vhost_discard_vq_desc(vq, 1);
+                               vhost_net_enable_vq(net, vq);
+                               break;
+                       }
+                       pr_debug("Fail to send packet: err %d", err);
+               } else if (unlikely(err != len))
                        pr_debug("Truncated TX packet: "
                                 " len %d != %zd\n", err, len);
                if (!zcopy_used)
index a483cec..5e78fb7 100644 (file)
 #define VHOST_VSOCK_PKT_WEIGHT 256
 
 enum {
-       VHOST_VSOCK_FEATURES = VHOST_FEATURES,
+       VHOST_VSOCK_FEATURES = VHOST_FEATURES |
+                              (1ULL << VIRTIO_F_ACCESS_PLATFORM)
+};
+
+enum {
+       VHOST_VSOCK_BACKEND_FEATURES = (1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2)
 };
 
 /* Used to track all the vhost_vsock instances on the system. */
@@ -94,6 +99,9 @@ vhost_transport_do_send_pkt(struct vhost_vsock *vsock,
        if (!vhost_vq_get_backend(vq))
                goto out;
 
+       if (!vq_meta_prefetch(vq))
+               goto out;
+
        /* Avoid further vmexits, we're already processing the virtqueue */
        vhost_disable_notify(&vsock->dev, vq);
 
@@ -449,6 +457,9 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work)
        if (!vhost_vq_get_backend(vq))
                goto out;
 
+       if (!vq_meta_prefetch(vq))
+               goto out;
+
        vhost_disable_notify(&vsock->dev, vq);
        do {
                u32 len;
@@ -766,8 +777,12 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)
        mutex_lock(&vsock->dev.mutex);
        if ((features & (1 << VHOST_F_LOG_ALL)) &&
            !vhost_log_access_ok(&vsock->dev)) {
-               mutex_unlock(&vsock->dev.mutex);
-               return -EFAULT;
+               goto err;
+       }
+
+       if ((features & (1ULL << VIRTIO_F_ACCESS_PLATFORM))) {
+               if (vhost_init_device_iotlb(&vsock->dev, true))
+                       goto err;
        }
 
        for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) {
@@ -778,6 +793,10 @@ static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features)
        }
        mutex_unlock(&vsock->dev.mutex);
        return 0;
+
+err:
+       mutex_unlock(&vsock->dev.mutex);
+       return -EFAULT;
 }
 
 static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
@@ -811,6 +830,18 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
                if (copy_from_user(&features, argp, sizeof(features)))
                        return -EFAULT;
                return vhost_vsock_set_features(vsock, features);
+       case VHOST_GET_BACKEND_FEATURES:
+               features = VHOST_VSOCK_BACKEND_FEATURES;
+               if (copy_to_user(argp, &features, sizeof(features)))
+                       return -EFAULT;
+               return 0;
+       case VHOST_SET_BACKEND_FEATURES:
+               if (copy_from_user(&features, argp, sizeof(features)))
+                       return -EFAULT;
+               if (features & ~VHOST_VSOCK_BACKEND_FEATURES)
+                       return -EOPNOTSUPP;
+               vhost_set_backend_features(&vsock->dev, features);
+               return 0;
        default:
                mutex_lock(&vsock->dev.mutex);
                r = vhost_dev_ioctl(&vsock->dev, ioctl, argp);
@@ -823,6 +854,34 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
        }
 }
 
+static ssize_t vhost_vsock_chr_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+       struct file *file = iocb->ki_filp;
+       struct vhost_vsock *vsock = file->private_data;
+       struct vhost_dev *dev = &vsock->dev;
+       int noblock = file->f_flags & O_NONBLOCK;
+
+       return vhost_chr_read_iter(dev, to, noblock);
+}
+
+static ssize_t vhost_vsock_chr_write_iter(struct kiocb *iocb,
+                                       struct iov_iter *from)
+{
+       struct file *file = iocb->ki_filp;
+       struct vhost_vsock *vsock = file->private_data;
+       struct vhost_dev *dev = &vsock->dev;
+
+       return vhost_chr_write_iter(dev, from);
+}
+
+static __poll_t vhost_vsock_chr_poll(struct file *file, poll_table *wait)
+{
+       struct vhost_vsock *vsock = file->private_data;
+       struct vhost_dev *dev = &vsock->dev;
+
+       return vhost_chr_poll(file, dev, wait);
+}
+
 static const struct file_operations vhost_vsock_fops = {
        .owner          = THIS_MODULE,
        .open           = vhost_vsock_dev_open,
@@ -830,6 +889,9 @@ static const struct file_operations vhost_vsock_fops = {
        .llseek         = noop_llseek,
        .unlocked_ioctl = vhost_vsock_dev_ioctl,
        .compat_ioctl   = compat_ptr_ioctl,
+       .read_iter      = vhost_vsock_chr_read_iter,
+       .write_iter     = vhost_vsock_chr_write_iter,
+       .poll           = vhost_vsock_chr_poll,
 };
 
 static struct miscdevice vhost_vsock_misc = {
index a803033..e850f79 100644 (file)
@@ -2060,16 +2060,6 @@ static struct irq_chip xen_percpu_chip __read_mostly = {
        .irq_ack                = ack_dynirq,
 };
 
-int xen_set_callback_via(uint64_t via)
-{
-       struct xen_hvm_param a;
-       a.domid = DOMID_SELF;
-       a.index = HVM_PARAM_CALLBACK_IRQ;
-       a.value = via;
-       return HYPERVISOR_hvm_op(HVMOP_set_param, &a);
-}
-EXPORT_SYMBOL_GPL(xen_set_callback_via);
-
 #ifdef CONFIG_XEN_PVHVM
 /* Vector callbacks are better than PCI interrupts to receive event
  * channel notifications because we can receive vector callbacks on any
index dd911e1..18f0ed8 100644 (file)
@@ -132,6 +132,13 @@ static int platform_pci_probe(struct pci_dev *pdev,
                        dev_warn(&pdev->dev, "request_irq failed err=%d\n", ret);
                        goto out;
                }
+               /*
+                * It doesn't strictly *have* to run on CPU0 but it sure
+                * as hell better process the event channel ports delivered
+                * to CPU0.
+                */
+               irq_set_affinity(pdev->irq, cpumask_of(0));
+
                callback_via = get_callback_via(pdev);
                ret = xen_set_callback_via(callback_via);
                if (ret) {
@@ -149,7 +156,6 @@ static int platform_pci_probe(struct pci_dev *pdev,
        ret = gnttab_init();
        if (ret)
                goto grant_out;
-       xenbus_probe(NULL);
        return 0;
 grant_out:
        gnttab_free_auto_xlat_frames();
index b0c73c5..720a7b7 100644 (file)
@@ -717,14 +717,15 @@ static long privcmd_ioctl_restrict(struct file *file, void __user *udata)
        return 0;
 }
 
-static long privcmd_ioctl_mmap_resource(struct file *file, void __user *udata)
+static long privcmd_ioctl_mmap_resource(struct file *file,
+                               struct privcmd_mmap_resource __user *udata)
 {
        struct privcmd_data *data = file->private_data;
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma;
        struct privcmd_mmap_resource kdata;
        xen_pfn_t *pfns = NULL;
-       struct xen_mem_acquire_resource xdata;
+       struct xen_mem_acquire_resource xdata = { };
        int rc;
 
        if (copy_from_user(&kdata, udata, sizeof(kdata)))
@@ -734,6 +735,22 @@ static long privcmd_ioctl_mmap_resource(struct file *file, void __user *udata)
        if (data->domid != DOMID_INVALID && data->domid != kdata.dom)
                return -EPERM;
 
+       /* Both fields must be set or unset */
+       if (!!kdata.addr != !!kdata.num)
+               return -EINVAL;
+
+       xdata.domid = kdata.dom;
+       xdata.type = kdata.type;
+       xdata.id = kdata.id;
+
+       if (!kdata.addr && !kdata.num) {
+               /* Query the size of the resource. */
+               rc = HYPERVISOR_memory_op(XENMEM_acquire_resource, &xdata);
+               if (rc)
+                       return rc;
+               return __put_user(xdata.nr_frames, &udata->num);
+       }
+
        mmap_write_lock(mm);
 
        vma = find_vma(mm, kdata.addr);
@@ -768,10 +785,6 @@ static long privcmd_ioctl_mmap_resource(struct file *file, void __user *udata)
        } else
                vma->vm_private_data = PRIV_VMA_LOCKED;
 
-       memset(&xdata, 0, sizeof(xdata));
-       xdata.domid = kdata.dom;
-       xdata.type = kdata.type;
-       xdata.id = kdata.id;
        xdata.frame = kdata.idx;
        xdata.nr_frames = kdata.num;
        set_xen_guest_handle(xdata.frame_list, pfns);
index 2a93b7c..dc15373 100644 (file)
@@ -115,6 +115,7 @@ int xenbus_probe_node(struct xen_bus_type *bus,
                      const char *type,
                      const char *nodename);
 int xenbus_probe_devices(struct xen_bus_type *bus);
+void xenbus_probe(void);
 
 void xenbus_dev_changed(const char *node, struct xen_bus_type *bus);
 
index eb5151f..e5fda02 100644 (file)
@@ -57,16 +57,8 @@ DEFINE_MUTEX(xs_response_mutex);
 static int xenbus_irq;
 static struct task_struct *xenbus_task;
 
-static DECLARE_WORK(probe_work, xenbus_probe);
-
-
 static irqreturn_t wake_waiting(int irq, void *unused)
 {
-       if (unlikely(xenstored_ready == 0)) {
-               xenstored_ready = 1;
-               schedule_work(&probe_work);
-       }
-
        wake_up(&xb_waitq);
        return IRQ_HANDLED;
 }
index 44634d9..18ffd05 100644 (file)
@@ -683,29 +683,107 @@ void unregister_xenstore_notifier(struct notifier_block *nb)
 }
 EXPORT_SYMBOL_GPL(unregister_xenstore_notifier);
 
-void xenbus_probe(struct work_struct *unused)
+void xenbus_probe(void)
 {
        xenstored_ready = 1;
 
+       /*
+        * In the HVM case, xenbus_init() deferred its call to
+        * xs_init() in case callbacks were not operational yet.
+        * So do it now.
+        */
+       if (xen_store_domain_type == XS_HVM)
+               xs_init();
+
        /* Notify others that xenstore is up */
        blocking_notifier_call_chain(&xenstore_chain, 0, NULL);
 }
-EXPORT_SYMBOL_GPL(xenbus_probe);
 
-static int __init xenbus_probe_initcall(void)
+/*
+ * Returns true when XenStore init must be deferred in order to
+ * allow the PCI platform device to be initialised, before we
+ * can actually have event channel interrupts working.
+ */
+static bool xs_hvm_defer_init_for_callback(void)
 {
-       if (!xen_domain())
-               return -ENODEV;
+#ifdef CONFIG_XEN_PVHVM
+       return xen_store_domain_type == XS_HVM &&
+               !xen_have_vector_callback;
+#else
+       return false;
+#endif
+}
 
-       if (xen_initial_domain() || xen_hvm_domain())
-               return 0;
+static int xenbus_probe_thread(void *unused)
+{
+       DEFINE_WAIT(w);
 
-       xenbus_probe(NULL);
+       /*
+        * We actually just want to wait for *any* trigger of xb_waitq,
+        * and run xenbus_probe() the moment it occurs.
+        */
+       prepare_to_wait(&xb_waitq, &w, TASK_INTERRUPTIBLE);
+       schedule();
+       finish_wait(&xb_waitq, &w);
+
+       DPRINTK("probing");
+       xenbus_probe();
        return 0;
 }
 
+static int __init xenbus_probe_initcall(void)
+{
+       /*
+        * Probe XenBus here in the XS_PV case, and also XS_HVM unless we
+        * need to wait for the platform PCI device to come up.
+        */
+       if (xen_store_domain_type == XS_PV ||
+           (xen_store_domain_type == XS_HVM &&
+            !xs_hvm_defer_init_for_callback()))
+               xenbus_probe();
+
+       /*
+        * For XS_LOCAL, spawn a thread which will wait for xenstored
+        * or a xenstore-stubdom to be started, then probe. It will be
+        * triggered when communication starts happening, by waiting
+        * on xb_waitq.
+        */
+       if (xen_store_domain_type == XS_LOCAL) {
+               struct task_struct *probe_task;
+
+               probe_task = kthread_run(xenbus_probe_thread, NULL,
+                                        "xenbus_probe");
+               if (IS_ERR(probe_task))
+                       return PTR_ERR(probe_task);
+       }
+       return 0;
+}
 device_initcall(xenbus_probe_initcall);
 
+int xen_set_callback_via(uint64_t via)
+{
+       struct xen_hvm_param a;
+       int ret;
+
+       a.domid = DOMID_SELF;
+       a.index = HVM_PARAM_CALLBACK_IRQ;
+       a.value = via;
+
+       ret = HYPERVISOR_hvm_op(HVMOP_set_param, &a);
+       if (ret)
+               return ret;
+
+       /*
+        * If xenbus_probe_initcall() deferred the xenbus_probe()
+        * due to the callback not functioning yet, we can do it now.
+        */
+       if (!xenstored_ready && xs_hvm_defer_init_for_callback())
+               xenbus_probe();
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(xen_set_callback_via);
+
 /* Set up event channel for xenstored which is run as a local process
  * (this is normally used only in dom0)
  */
@@ -818,11 +896,17 @@ static int __init xenbus_init(void)
                break;
        }
 
-       /* Initialize the interface to xenstore. */
-       err = xs_init();
-       if (err) {
-               pr_warn("Error initializing xenstore comms: %i\n", err);
-               goto out_error;
+       /*
+        * HVM domains may not have a functional callback yet. In that
+        * case let xs_init() be called from xenbus_probe(), which will
+        * get invoked at an appropriate time.
+        */
+       if (xen_store_domain_type != XS_HVM) {
+               err = xs_init();
+               if (err) {
+                       pr_warn("Error initializing xenstore comms: %i\n", err);
+                       goto out_error;
+               }
        }
 
        if ((xen_store_domain_type != XS_LOCAL) &&
index 9068d55..7bd659a 100644 (file)
@@ -350,7 +350,7 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
                                 unsigned blkoff)
 {
        union afs_xdr_dirent *dire;
-       unsigned offset, next, curr;
+       unsigned offset, next, curr, nr_slots;
        size_t nlen;
        int tmp;
 
@@ -363,13 +363,12 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
             offset < AFS_DIR_SLOTS_PER_BLOCK;
             offset = next
             ) {
-               next = offset + 1;
-
                /* skip entries marked unused in the bitmap */
                if (!(block->hdr.bitmap[offset / 8] &
                      (1 << (offset % 8)))) {
                        _debug("ENT[%zu.%u]: unused",
                               blkoff / sizeof(union afs_xdr_dir_block), offset);
+                       next = offset + 1;
                        if (offset >= curr)
                                ctx->pos = blkoff +
                                        next * sizeof(union afs_xdr_dirent);
@@ -381,35 +380,39 @@ static int afs_dir_iterate_block(struct afs_vnode *dvnode,
                nlen = strnlen(dire->u.name,
                               sizeof(*block) -
                               offset * sizeof(union afs_xdr_dirent));
+               if (nlen > AFSNAMEMAX - 1) {
+                       _debug("ENT[%zu]: name too long (len %u/%zu)",
+                              blkoff / sizeof(union afs_xdr_dir_block),
+                              offset, nlen);
+                       return afs_bad(dvnode, afs_file_error_dir_name_too_long);
+               }
 
                _debug("ENT[%zu.%u]: %s %zu \"%s\"",
                       blkoff / sizeof(union afs_xdr_dir_block), offset,
                       (offset < curr ? "skip" : "fill"),
                       nlen, dire->u.name);
 
-               /* work out where the next possible entry is */
-               for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_xdr_dirent)) {
-                       if (next >= AFS_DIR_SLOTS_PER_BLOCK) {
-                               _debug("ENT[%zu.%u]:"
-                                      " %u travelled beyond end dir block"
-                                      " (len %u/%zu)",
-                                      blkoff / sizeof(union afs_xdr_dir_block),
-                                      offset, next, tmp, nlen);
-                               return afs_bad(dvnode, afs_file_error_dir_over_end);
-                       }
-                       if (!(block->hdr.bitmap[next / 8] &
-                             (1 << (next % 8)))) {
-                               _debug("ENT[%zu.%u]:"
-                                      " %u unmarked extension (len %u/%zu)",
+               nr_slots = afs_dir_calc_slots(nlen);
+               next = offset + nr_slots;
+               if (next > AFS_DIR_SLOTS_PER_BLOCK) {
+                       _debug("ENT[%zu.%u]:"
+                              " %u extends beyond end dir block"
+                              " (len %zu)",
+                              blkoff / sizeof(union afs_xdr_dir_block),
+                              offset, next, nlen);
+                       return afs_bad(dvnode, afs_file_error_dir_over_end);
+               }
+
+               /* Check that the name-extension dirents are all allocated */
+               for (tmp = 1; tmp < nr_slots; tmp++) {
+                       unsigned int ix = offset + tmp;
+                       if (!(block->hdr.bitmap[ix / 8] & (1 << (ix % 8)))) {
+                               _debug("ENT[%zu.u]:"
+                                      " %u unmarked extension (%u/%u)",
                                       blkoff / sizeof(union afs_xdr_dir_block),
-                                      offset, next, tmp, nlen);
+                                      offset, tmp, nr_slots);
                                return afs_bad(dvnode, afs_file_error_dir_unmarked_ext);
                        }
-
-                       _debug("ENT[%zu.%u]: ext %u/%zu",
-                              blkoff / sizeof(union afs_xdr_dir_block),
-                              next, tmp, nlen);
-                       next++;
                }
 
                /* skip if starts before the current position */
index 2ffe09a..f4600c1 100644 (file)
@@ -215,8 +215,7 @@ void afs_edit_dir_add(struct afs_vnode *vnode,
        }
 
        /* Work out how many slots we're going to need. */
-       need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
-       need_slots /= AFS_DIR_DIRENT_SIZE;
+       need_slots = afs_dir_calc_slots(name->len);
 
        meta_page = kmap(page0);
        meta = &meta_page->blocks[0];
@@ -393,8 +392,7 @@ void afs_edit_dir_remove(struct afs_vnode *vnode,
        }
 
        /* Work out how many slots we're going to discard. */
-       need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
-       need_slots /= AFS_DIR_DIRENT_SIZE;
+       need_slots = afs_dir_calc_slots(name->len);
 
        meta_page = kmap(page0);
        meta = &meta_page->blocks[0];
index 94f1f39..8ca8681 100644 (file)
@@ -54,10 +54,16 @@ union afs_xdr_dirent {
                __be16          hash_next;
                __be32          vnode;
                __be32          unique;
-               u8              name[16];
-               u8              overflow[4];    /* if any char of the name (inc
-                                                * NUL) reaches here, consume
-                                                * the next dirent too */
+               u8              name[];
+               /* When determining the number of dirent slots needed to
+                * represent a directory entry, name should be assumed to be 16
+                * bytes, due to a now-standardised (mis)calculation, but it is
+                * in fact 20 bytes in size.  afs_dir_calc_slots() should be
+                * used for this.
+                *
+                * For names longer than (16 or) 20 bytes, extra slots should
+                * be annexed to this one using the extended_name format.
+                */
        } u;
        u8                      extended_name[32];
 } __packed;
@@ -96,4 +102,15 @@ struct afs_xdr_dir_page {
        union afs_xdr_dir_block blocks[AFS_DIR_BLOCKS_PER_PAGE];
 };
 
+/*
+ * Calculate the number of dirent slots required for any given name length.
+ * The calculation is made assuming the part of the name in the first slot is
+ * 16 bytes, rather than 20, but this miscalculation is now standardised.
+ */
+static inline unsigned int afs_dir_calc_slots(size_t name_len)
+{
+       name_len++; /* NUL-terminated */
+       return 1 + ((name_len + 15) / AFS_DIR_DIRENT_SIZE);
+}
+
 #endif /* XDR_FS_H */
index 9293045..3b8963e 100644 (file)
@@ -605,6 +605,8 @@ int thaw_bdev(struct block_device *bdev)
                error = thaw_super(sb);
        if (error)
                bdev->bd_fsfreeze_count++;
+       else
+               bdev->bd_fsfreeze_sb = NULL;
 out:
        mutex_unlock(&bdev->bd_fsfreeze_mutex);
        return error;
@@ -774,8 +776,11 @@ static struct kmem_cache * bdev_cachep __read_mostly;
 static struct inode *bdev_alloc_inode(struct super_block *sb)
 {
        struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, GFP_KERNEL);
+
        if (!ei)
                return NULL;
+       memset(&ei->bdev, 0, sizeof(ei->bdev));
+       ei->bdev.bd_bdi = &noop_backing_dev_info;
        return &ei->vfs_inode;
 }
 
@@ -869,14 +874,12 @@ struct block_device *bdev_alloc(struct gendisk *disk, u8 partno)
        mapping_set_gfp_mask(&inode->i_data, GFP_USER);
 
        bdev = I_BDEV(inode);
-       memset(bdev, 0, sizeof(*bdev));
        mutex_init(&bdev->bd_mutex);
        mutex_init(&bdev->bd_fsfreeze_mutex);
        spin_lock_init(&bdev->bd_size_lock);
        bdev->bd_disk = disk;
        bdev->bd_partno = partno;
        bdev->bd_inode = inode;
-       bdev->bd_bdi = &noop_backing_dev_info;
 #ifdef CONFIG_SYSFS
        INIT_LIST_HEAD(&bdev->bd_holder_disks);
 #endif
@@ -1055,7 +1058,6 @@ static void bd_finish_claiming(struct block_device *bdev, void *holder)
 /**
  * bd_abort_claiming - abort claiming of a block device
  * @bdev: block device of interest
- * @whole: whole block device
  * @holder: holder that has claimed @bdev
  *
  * Abort claiming of a block device when the exclusive open failed. This can be
@@ -1828,6 +1830,7 @@ const struct file_operations def_blk_fops = {
 /**
  * lookup_bdev  - lookup a struct block_device by name
  * @pathname:  special file representing the block device
+ * @dev:       return value of the block device's dev_t
  *
  * Get a reference to the blockdevice at @pathname in the current
  * namespace if possible and return it.  Return ERR_PTR(error)
index 02d7d7b..9cadacf 100644 (file)
@@ -3117,7 +3117,7 @@ void btrfs_backref_error_cleanup(struct btrfs_backref_cache *cache,
                list_del_init(&lower->list);
                if (lower == node)
                        node = NULL;
-               btrfs_backref_free_node(cache, lower);
+               btrfs_backref_drop_node(cache, lower);
        }
 
        btrfs_backref_cleanup_node(cache, node);
index 52f2198..0886e81 100644 (file)
@@ -2669,7 +2669,8 @@ again:
         * Go through delayed refs for all the stuff we've just kicked off
         * and then loop back (just once)
         */
-       ret = btrfs_run_delayed_refs(trans, 0);
+       if (!ret)
+               ret = btrfs_run_delayed_refs(trans, 0);
        if (!ret && loops == 0) {
                loops++;
                spin_lock(&cur_trans->dirty_bgs_lock);
index 555cbce..d9bf53d 100644 (file)
@@ -42,6 +42,15 @@ enum {
         * to an inode.
         */
        BTRFS_INODE_NO_XATTRS,
+       /*
+        * Set when we are in a context where we need to start a transaction and
+        * have dirty pages with the respective file range locked. This is to
+        * ensure that when reserving space for the transaction, if we are low
+        * on available space and need to flush delalloc, we will not flush
+        * delalloc for this inode, because that could result in a deadlock (on
+        * the file range, inode's io_tree).
+        */
+       BTRFS_INODE_NO_DELALLOC_FLUSH,
 };
 
 /* in memory btrfs inode */
index 0781089..cc89b63 100644 (file)
@@ -2555,8 +2555,14 @@ out:
  * @p:         Holds all btree nodes along the search path
  * @root:      The root node of the tree
  * @key:       The key we are looking for
- * @ins_len:   Indicates purpose of search, for inserts it is 1, for
- *             deletions it's -1. 0 for plain searches
+ * @ins_len:   Indicates purpose of search:
+ *              >0  for inserts it's size of item inserted (*)
+ *              <0  for deletions
+ *               0  for plain searches, not modifying the tree
+ *
+ *              (*) If size of item inserted doesn't include
+ *              sizeof(struct btrfs_item), then p->search_for_extension must
+ *              be set.
  * @cow:       boolean should CoW operations be performed. Must always be 1
  *             when modifying the tree.
  *
@@ -2717,6 +2723,20 @@ cow_done:
 
                if (level == 0) {
                        p->slots[level] = slot;
+                       /*
+                        * Item key already exists. In this case, if we are
+                        * allowed to insert the item (for example, in dir_item
+                        * case, item key collision is allowed), it will be
+                        * merged with the original item. Only the item size
+                        * grows, no new btrfs item will be added. If
+                        * search_for_extension is not set, ins_len already
+                        * accounts the size btrfs_item, deduct it here so leaf
+                        * space check will be correct.
+                        */
+                       if (ret == 0 && ins_len > 0 && !p->search_for_extension) {
+                               ASSERT(ins_len >= sizeof(struct btrfs_item));
+                               ins_len -= sizeof(struct btrfs_item);
+                       }
                        if (ins_len > 0 &&
                            btrfs_leaf_free_space(b) < ins_len) {
                                if (write_lock_level < 1) {
index 1d3c1e4..e6e3759 100644 (file)
@@ -131,6 +131,8 @@ enum {
         * defrag
         */
        BTRFS_FS_STATE_REMOUNTING,
+       /* Filesystem in RO mode */
+       BTRFS_FS_STATE_RO,
        /* Track if a transaction abort has been reported on this filesystem */
        BTRFS_FS_STATE_TRANS_ABORTED,
        /*
@@ -367,6 +369,12 @@ struct btrfs_path {
        unsigned int search_commit_root:1;
        unsigned int need_commit_sem:1;
        unsigned int skip_release_on_error:1;
+       /*
+        * Indicate that new item (btrfs_search_slot) is extending already
+        * existing item and ins_len contains only the data size and not item
+        * header (ie. sizeof(struct btrfs_item) is not included).
+        */
+       unsigned int search_for_extension:1;
 };
 #define BTRFS_MAX_EXTENT_ITEM_SIZE(r) ((BTRFS_LEAF_DATA_SIZE(r->fs_info) >> 4) - \
                                        sizeof(struct btrfs_item))
@@ -2885,10 +2893,26 @@ static inline int btrfs_fs_closing(struct btrfs_fs_info *fs_info)
  * If we remount the fs to be R/O or umount the fs, the cleaner needn't do
  * anything except sleeping. This function is used to check the status of
  * the fs.
+ * We check for BTRFS_FS_STATE_RO to avoid races with a concurrent remount,
+ * since setting and checking for SB_RDONLY in the superblock's flags is not
+ * atomic.
  */
 static inline int btrfs_need_cleaner_sleep(struct btrfs_fs_info *fs_info)
 {
-       return fs_info->sb->s_flags & SB_RDONLY || btrfs_fs_closing(fs_info);
+       return test_bit(BTRFS_FS_STATE_RO, &fs_info->fs_state) ||
+               btrfs_fs_closing(fs_info);
+}
+
+static inline void btrfs_set_sb_rdonly(struct super_block *sb)
+{
+       sb->s_flags |= SB_RDONLY;
+       set_bit(BTRFS_FS_STATE_RO, &btrfs_sb(sb)->fs_state);
+}
+
+static inline void btrfs_clear_sb_rdonly(struct super_block *sb)
+{
+       sb->s_flags &= ~SB_RDONLY;
+       clear_bit(BTRFS_FS_STATE_RO, &btrfs_sb(sb)->fs_state);
 }
 
 /* tree mod log functions from ctree.c */
@@ -3073,7 +3097,8 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
                               u32 min_type);
 
 int btrfs_start_delalloc_snapshot(struct btrfs_root *root);
-int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr);
+int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr,
+                              bool in_reclaim_context);
 int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
                              unsigned int extra_bits,
                              struct extent_state **cached_state);
index a98e33f..324f646 100644 (file)
@@ -715,7 +715,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
         * flush all outstanding I/O and inode extent mappings before the
         * copy operation is declared as being finished
         */
-       ret = btrfs_start_delalloc_roots(fs_info, U64_MAX);
+       ret = btrfs_start_delalloc_roots(fs_info, U64_MAX, false);
        if (ret) {
                mutex_unlock(&dev_replace->lock_finishing_cancel_unmount);
                return ret;
index 1db966b..2b8383d 100644 (file)
@@ -199,16 +199,15 @@ static struct btrfs_block_group *find_next_block_group(
 static struct btrfs_block_group *peek_discard_list(
                                        struct btrfs_discard_ctl *discard_ctl,
                                        enum btrfs_discard_state *discard_state,
-                                       int *discard_index)
+                                       int *discard_index, u64 now)
 {
        struct btrfs_block_group *block_group;
-       const u64 now = ktime_get_ns();
 
        spin_lock(&discard_ctl->lock);
 again:
        block_group = find_next_block_group(discard_ctl, now);
 
-       if (block_group && now > block_group->discard_eligible_time) {
+       if (block_group && now >= block_group->discard_eligible_time) {
                if (block_group->discard_index == BTRFS_DISCARD_INDEX_UNUSED &&
                    block_group->used != 0) {
                        if (btrfs_is_block_group_data_only(block_group))
@@ -222,12 +221,11 @@ again:
                        block_group->discard_state = BTRFS_DISCARD_EXTENTS;
                }
                discard_ctl->block_group = block_group;
+       }
+       if (block_group) {
                *discard_state = block_group->discard_state;
                *discard_index = block_group->discard_index;
-       } else {
-               block_group = NULL;
        }
-
        spin_unlock(&discard_ctl->lock);
 
        return block_group;
@@ -330,28 +328,15 @@ void btrfs_discard_queue_work(struct btrfs_discard_ctl *discard_ctl,
                btrfs_discard_schedule_work(discard_ctl, false);
 }
 
-/**
- * btrfs_discard_schedule_work - responsible for scheduling the discard work
- * @discard_ctl: discard control
- * @override: override the current timer
- *
- * Discards are issued by a delayed workqueue item.  @override is used to
- * update the current delay as the baseline delay interval is reevaluated on
- * transaction commit.  This is also maxed with any other rate limit.
- */
-void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
-                                bool override)
+static void __btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
+                                         u64 now, bool override)
 {
        struct btrfs_block_group *block_group;
-       const u64 now = ktime_get_ns();
-
-       spin_lock(&discard_ctl->lock);
 
        if (!btrfs_run_discard_work(discard_ctl))
-               goto out;
-
+               return;
        if (!override && delayed_work_pending(&discard_ctl->work))
-               goto out;
+               return;
 
        block_group = find_next_block_group(discard_ctl, now);
        if (block_group) {
@@ -393,7 +378,24 @@ void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
                mod_delayed_work(discard_ctl->discard_workers,
                                 &discard_ctl->work, nsecs_to_jiffies(delay));
        }
-out:
+}
+
+/*
+ * btrfs_discard_schedule_work - responsible for scheduling the discard work
+ * @discard_ctl:  discard control
+ * @override:     override the current timer
+ *
+ * Discards are issued by a delayed workqueue item.  @override is used to
+ * update the current delay as the baseline delay interval is reevaluated on
+ * transaction commit.  This is also maxed with any other rate limit.
+ */
+void btrfs_discard_schedule_work(struct btrfs_discard_ctl *discard_ctl,
+                                bool override)
+{
+       const u64 now = ktime_get_ns();
+
+       spin_lock(&discard_ctl->lock);
+       __btrfs_discard_schedule_work(discard_ctl, now, override);
        spin_unlock(&discard_ctl->lock);
 }
 
@@ -438,13 +440,18 @@ static void btrfs_discard_workfn(struct work_struct *work)
        int discard_index = 0;
        u64 trimmed = 0;
        u64 minlen = 0;
+       u64 now = ktime_get_ns();
 
        discard_ctl = container_of(work, struct btrfs_discard_ctl, work.work);
 
        block_group = peek_discard_list(discard_ctl, &discard_state,
-                                       &discard_index);
+                                       &discard_index, now);
        if (!block_group || !btrfs_run_discard_work(discard_ctl))
                return;
+       if (now < block_group->discard_eligible_time) {
+               btrfs_discard_schedule_work(discard_ctl, false);
+               return;
+       }
 
        /* Perform discarding */
        minlen = discard_minlen[discard_index];
@@ -474,13 +481,6 @@ static void btrfs_discard_workfn(struct work_struct *work)
                discard_ctl->discard_extent_bytes += trimmed;
        }
 
-       /*
-        * Updated without locks as this is inside the workfn and nothing else
-        * is reading the values
-        */
-       discard_ctl->prev_discard = trimmed;
-       discard_ctl->prev_discard_time = ktime_get_ns();
-
        /* Determine next steps for a block_group */
        if (block_group->discard_cursor >= btrfs_block_group_end(block_group)) {
                if (discard_state == BTRFS_DISCARD_BITMAPS) {
@@ -496,11 +496,13 @@ static void btrfs_discard_workfn(struct work_struct *work)
                }
        }
 
+       now = ktime_get_ns();
        spin_lock(&discard_ctl->lock);
+       discard_ctl->prev_discard = trimmed;
+       discard_ctl->prev_discard_time = now;
        discard_ctl->block_group = NULL;
+       __btrfs_discard_schedule_work(discard_ctl, now, false);
        spin_unlock(&discard_ctl->lock);
-
-       btrfs_discard_schedule_work(discard_ctl, false);
 }
 
 /**
index 765deef..6b35b7e 100644 (file)
@@ -1457,7 +1457,7 @@ void btrfs_check_leaked_roots(struct btrfs_fs_info *fs_info)
                root = list_first_entry(&fs_info->allocated_roots,
                                        struct btrfs_root, leak_list);
                btrfs_err(fs_info, "leaked root %s refcount %d",
-                         btrfs_root_name(root->root_key.objectid, buf),
+                         btrfs_root_name(&root->root_key, buf),
                          refcount_read(&root->refs));
                while (refcount_read(&root->refs) > 1)
                        btrfs_put_root(root);
@@ -1729,7 +1729,7 @@ static int cleaner_kthread(void *arg)
                 */
                btrfs_delete_unused_bgs(fs_info);
 sleep:
-               clear_bit(BTRFS_FS_CLEANER_RUNNING, &fs_info->flags);
+               clear_and_wake_up_bit(BTRFS_FS_CLEANER_RUNNING, &fs_info->flags);
                if (kthread_should_park())
                        kthread_parkme();
                if (kthread_should_stop())
@@ -2830,6 +2830,9 @@ static int init_mount_fs_info(struct btrfs_fs_info *fs_info, struct super_block
                return -ENOMEM;
        btrfs_init_delayed_root(fs_info->delayed_root);
 
+       if (sb_rdonly(sb))
+               set_bit(BTRFS_FS_STATE_RO, &fs_info->fs_state);
+
        return btrfs_alloc_stripe_hash_table(fs_info);
 }
 
@@ -2969,6 +2972,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info)
                }
        }
 
+       ret = btrfs_find_orphan_roots(fs_info);
 out:
        return ret;
 }
@@ -3383,10 +3387,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
                }
        }
 
-       ret = btrfs_find_orphan_roots(fs_info);
-       if (ret)
-               goto fail_qgroup;
-
        fs_info->fs_root = btrfs_get_fs_root(fs_info, BTRFS_FS_TREE_OBJECTID, true);
        if (IS_ERR(fs_info->fs_root)) {
                err = PTR_ERR(fs_info->fs_root);
@@ -4181,6 +4181,9 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
        invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
        btrfs_stop_all_workers(fs_info);
 
+       /* We shouldn't have any transaction open at this point */
+       ASSERT(list_empty(&fs_info->trans_list));
+
        clear_bit(BTRFS_FS_OPEN, &fs_info->flags);
        free_root_pointers(fs_info, true);
        btrfs_free_fs_roots(fs_info);
index 56ea380..30b1a63 100644 (file)
@@ -844,6 +844,7 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
        want = extent_ref_type(parent, owner);
        if (insert) {
                extra_size = btrfs_extent_inline_ref_size(want);
+               path->search_for_extension = 1;
                path->keep_locks = 1;
        } else
                extra_size = -1;
@@ -996,6 +997,7 @@ again:
 out:
        if (insert) {
                path->keep_locks = 0;
+               path->search_for_extension = 0;
                btrfs_unlock_up_safe(path, 1);
        }
        return err;
@@ -5547,7 +5549,15 @@ int btrfs_drop_snapshot(struct btrfs_root *root, int update_ref, int for_reloc)
                                goto out_free;
                        }
 
-                       trans = btrfs_start_transaction(tree_root, 0);
+                      /*
+                       * Use join to avoid potential EINTR from transaction
+                       * start. See wait_reserve_ticket and the whole
+                       * reservation callchain.
+                       */
+                       if (for_reloc)
+                               trans = btrfs_join_transaction(tree_root);
+                       else
+                               trans = btrfs_start_transaction(tree_root, 0);
                        if (IS_ERR(trans)) {
                                err = PTR_ERR(trans);
                                goto out_free;
index 6e3b72e..c9cee45 100644 (file)
@@ -676,9 +676,7 @@ alloc_extent_state_atomic(struct extent_state *prealloc)
 
 static void extent_io_tree_panic(struct extent_io_tree *tree, int err)
 {
-       struct inode *inode = tree->private_data;
-
-       btrfs_panic(btrfs_sb(inode->i_sb), err,
+       btrfs_panic(tree->fs_info, err,
        "locking error: extent tree was modified by another thread while locked");
 }
 
index 1545c22..6ccfc01 100644 (file)
@@ -1016,8 +1016,10 @@ again:
        }
 
        btrfs_release_path(path);
+       path->search_for_extension = 1;
        ret = btrfs_search_slot(trans, root, &file_key, path,
                                csum_size, 1);
+       path->search_for_extension = 0;
        if (ret < 0)
                goto out;
 
index 8e23780..a8e0a6b 100644 (file)
@@ -9390,7 +9390,9 @@ static struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode
  * some fairly slow code that needs optimization. This walks the list
  * of all the inodes with pending delalloc and forces them to disk.
  */
-static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot)
+static int start_delalloc_inodes(struct btrfs_root *root,
+                                struct writeback_control *wbc, bool snapshot,
+                                bool in_reclaim_context)
 {
        struct btrfs_inode *binode;
        struct inode *inode;
@@ -9398,6 +9400,7 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot
        struct list_head works;
        struct list_head splice;
        int ret = 0;
+       bool full_flush = wbc->nr_to_write == LONG_MAX;
 
        INIT_LIST_HEAD(&works);
        INIT_LIST_HEAD(&splice);
@@ -9411,6 +9414,11 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot
 
                list_move_tail(&binode->delalloc_inodes,
                               &root->delalloc_inodes);
+
+               if (in_reclaim_context &&
+                   test_bit(BTRFS_INODE_NO_DELALLOC_FLUSH, &binode->runtime_flags))
+                       continue;
+
                inode = igrab(&binode->vfs_inode);
                if (!inode) {
                        cond_resched_lock(&root->delalloc_lock);
@@ -9421,18 +9429,24 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot
                if (snapshot)
                        set_bit(BTRFS_INODE_SNAPSHOT_FLUSH,
                                &binode->runtime_flags);
-               work = btrfs_alloc_delalloc_work(inode);
-               if (!work) {
-                       iput(inode);
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               list_add_tail(&work->list, &works);
-               btrfs_queue_work(root->fs_info->flush_workers,
-                                &work->work);
-               if (*nr != U64_MAX) {
-                       (*nr)--;
-                       if (*nr == 0)
+               if (full_flush) {
+                       work = btrfs_alloc_delalloc_work(inode);
+                       if (!work) {
+                               iput(inode);
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       list_add_tail(&work->list, &works);
+                       btrfs_queue_work(root->fs_info->flush_workers,
+                                        &work->work);
+               } else {
+                       ret = sync_inode(inode, wbc);
+                       if (!ret &&
+                           test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
+                                    &BTRFS_I(inode)->runtime_flags))
+                               ret = sync_inode(inode, wbc);
+                       btrfs_add_delayed_iput(inode);
+                       if (ret || wbc->nr_to_write <= 0)
                                goto out;
                }
                cond_resched();
@@ -9458,17 +9472,29 @@ out:
 
 int btrfs_start_delalloc_snapshot(struct btrfs_root *root)
 {
+       struct writeback_control wbc = {
+               .nr_to_write = LONG_MAX,
+               .sync_mode = WB_SYNC_NONE,
+               .range_start = 0,
+               .range_end = LLONG_MAX,
+       };
        struct btrfs_fs_info *fs_info = root->fs_info;
-       u64 nr = U64_MAX;
 
        if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state))
                return -EROFS;
 
-       return start_delalloc_inodes(root, &nr, true);
+       return start_delalloc_inodes(root, &wbc, true, false);
 }
 
-int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr)
+int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr,
+                              bool in_reclaim_context)
 {
+       struct writeback_control wbc = {
+               .nr_to_write = (nr == U64_MAX) ? LONG_MAX : (unsigned long)nr,
+               .sync_mode = WB_SYNC_NONE,
+               .range_start = 0,
+               .range_end = LLONG_MAX,
+       };
        struct btrfs_root *root;
        struct list_head splice;
        int ret;
@@ -9482,6 +9508,13 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr)
        spin_lock(&fs_info->delalloc_root_lock);
        list_splice_init(&fs_info->delalloc_roots, &splice);
        while (!list_empty(&splice) && nr) {
+               /*
+                * Reset nr_to_write here so we know that we're doing a full
+                * flush.
+                */
+               if (nr == U64_MAX)
+                       wbc.nr_to_write = LONG_MAX;
+
                root = list_first_entry(&splice, struct btrfs_root,
                                        delalloc_root);
                root = btrfs_grab_root(root);
@@ -9490,9 +9523,9 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr)
                               &fs_info->delalloc_roots);
                spin_unlock(&fs_info->delalloc_root_lock);
 
-               ret = start_delalloc_inodes(root, &nr, false);
+               ret = start_delalloc_inodes(root, &wbc, false, in_reclaim_context);
                btrfs_put_root(root);
-               if (ret < 0)
+               if (ret < 0 || wbc.nr_to_write <= 0)
                        goto out;
                spin_lock(&fs_info->delalloc_root_lock);
        }
index 703212f..dde49a7 100644 (file)
@@ -4951,7 +4951,7 @@ long btrfs_ioctl(struct file *file, unsigned int
        case BTRFS_IOC_SYNC: {
                int ret;
 
-               ret = btrfs_start_delalloc_roots(fs_info, U64_MAX);
+               ret = btrfs_start_delalloc_roots(fs_info, U64_MAX, false);
                if (ret)
                        return ret;
                ret = btrfs_sync_fs(inode->i_sb, 1);
index fe5e002..aae1027 100644 (file)
@@ -26,22 +26,22 @@ static const struct root_name_map root_map[] = {
        { BTRFS_DATA_RELOC_TREE_OBJECTID,       "DATA_RELOC_TREE"       },
 };
 
-const char *btrfs_root_name(u64 objectid, char *buf)
+const char *btrfs_root_name(const struct btrfs_key *key, char *buf)
 {
        int i;
 
-       if (objectid == BTRFS_TREE_RELOC_OBJECTID) {
+       if (key->objectid == BTRFS_TREE_RELOC_OBJECTID) {
                snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN,
-                        "TREE_RELOC offset=%llu", objectid);
+                        "TREE_RELOC offset=%llu", key->offset);
                return buf;
        }
 
        for (i = 0; i < ARRAY_SIZE(root_map); i++) {
-               if (root_map[i].id == objectid)
+               if (root_map[i].id == key->objectid)
                        return root_map[i].name;
        }
 
-       snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN, "%llu", objectid);
+       snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN, "%llu", key->objectid);
        return buf;
 }
 
index 78b9938..8c3e931 100644 (file)
@@ -11,6 +11,6 @@
 
 void btrfs_print_leaf(struct extent_buffer *l);
 void btrfs_print_tree(struct extent_buffer *c, bool follow);
-const char *btrfs_root_name(u64 objectid, char *buf);
+const char *btrfs_root_name(const struct btrfs_key *key, char *buf);
 
 #endif
index fe30460..808370a 100644 (file)
@@ -3190,6 +3190,12 @@ out:
        return ret;
 }
 
+static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
+{
+       return btrfs_fs_closing(fs_info) ||
+               test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
+}
+
 static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
 {
        struct btrfs_fs_info *fs_info = container_of(work, struct btrfs_fs_info,
@@ -3198,6 +3204,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
        struct btrfs_trans_handle *trans = NULL;
        int err = -ENOMEM;
        int ret = 0;
+       bool stopped = false;
 
        path = btrfs_alloc_path();
        if (!path)
@@ -3210,7 +3217,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
        path->skip_locking = 1;
 
        err = 0;
-       while (!err && !btrfs_fs_closing(fs_info)) {
+       while (!err && !(stopped = rescan_should_stop(fs_info))) {
                trans = btrfs_start_transaction(fs_info->fs_root, 0);
                if (IS_ERR(trans)) {
                        err = PTR_ERR(trans);
@@ -3253,7 +3260,7 @@ out:
        }
 
        mutex_lock(&fs_info->qgroup_rescan_lock);
-       if (!btrfs_fs_closing(fs_info))
+       if (!stopped)
                fs_info->qgroup_flags &= ~BTRFS_QGROUP_STATUS_FLAG_RESCAN;
        if (trans) {
                ret = update_qgroup_status_item(trans);
@@ -3272,7 +3279,7 @@ out:
 
        btrfs_end_transaction(trans);
 
-       if (btrfs_fs_closing(fs_info)) {
+       if (stopped) {
                btrfs_info(fs_info, "qgroup scan paused");
        } else if (err >= 0) {
                btrfs_info(fs_info, "qgroup scan completed%s",
@@ -3530,16 +3537,6 @@ static int try_flush_qgroup(struct btrfs_root *root)
        int ret;
        bool can_commit = true;
 
-       /*
-        * We don't want to run flush again and again, so if there is a running
-        * one, we won't try to start a new flush, but exit directly.
-        */
-       if (test_and_set_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state)) {
-               wait_event(root->qgroup_flush_wait,
-                       !test_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state));
-               return 0;
-       }
-
        /*
         * If current process holds a transaction, we shouldn't flush, as we
         * assume all space reservation happens before a transaction handle is
@@ -3554,6 +3551,26 @@ static int try_flush_qgroup(struct btrfs_root *root)
            current->journal_info != BTRFS_SEND_TRANS_STUB)
                can_commit = false;
 
+       /*
+        * We don't want to run flush again and again, so if there is a running
+        * one, we won't try to start a new flush, but exit directly.
+        */
+       if (test_and_set_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state)) {
+               /*
+                * We are already holding a transaction, thus we can block other
+                * threads from flushing.  So exit right now. This increases
+                * the chance of EDQUOT for heavy load and near limit cases.
+                * But we can argue that if we're already near limit, EDQUOT is
+                * unavoidable anyway.
+                */
+               if (!can_commit)
+                       return 0;
+
+               wait_event(root->qgroup_flush_wait,
+                       !test_bit(BTRFS_ROOT_QGROUP_FLUSHING, &root->state));
+               return 0;
+       }
+
        ret = btrfs_start_delalloc_snapshot(root);
        if (ret < 0)
                goto out;
index ab80896..b03e789 100644 (file)
@@ -89,6 +89,19 @@ static int copy_inline_to_page(struct btrfs_inode *inode,
        if (ret)
                goto out_unlock;
 
+       /*
+        * After dirtying the page our caller will need to start a transaction,
+        * and if we are low on metadata free space, that can cause flushing of
+        * delalloc for all inodes in order to get metadata space released.
+        * However we are holding the range locked for the whole duration of
+        * the clone/dedupe operation, so we may deadlock if that happens and no
+        * other task releases enough space. So mark this inode as not being
+        * possible to flush to avoid such deadlock. We will clear that flag
+        * when we finish cloning all extents, since a transaction is started
+        * after finding each extent to clone.
+        */
+       set_bit(BTRFS_INODE_NO_DELALLOC_FLUSH, &inode->runtime_flags);
+
        if (comp_type == BTRFS_COMPRESS_NONE) {
                char *map;
 
@@ -549,6 +562,8 @@ process_slot:
 out:
        btrfs_free_path(path);
        kvfree(buf);
+       clear_bit(BTRFS_INODE_NO_DELALLOC_FLUSH, &BTRFS_I(inode)->runtime_flags);
+
        return ret;
 }
 
index 19b7db8..df63ef6 100644 (file)
@@ -2975,11 +2975,16 @@ static int delete_v1_space_cache(struct extent_buffer *leaf,
                return 0;
 
        for (i = 0; i < btrfs_header_nritems(leaf); i++) {
+               u8 type;
+
                btrfs_item_key_to_cpu(leaf, &key, i);
                if (key.type != BTRFS_EXTENT_DATA_KEY)
                        continue;
                ei = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
-               if (btrfs_file_extent_type(leaf, ei) == BTRFS_FILE_EXTENT_REG &&
+               type = btrfs_file_extent_type(leaf, ei);
+
+               if ((type == BTRFS_FILE_EXTENT_REG ||
+                    type == BTRFS_FILE_EXTENT_PREALLOC) &&
                    btrfs_file_extent_disk_bytenr(leaf, ei) == data_bytenr) {
                        found = true;
                        space_cache_ino = key.objectid;
index d719a27..78a3537 100644 (file)
@@ -236,6 +236,7 @@ struct waiting_dir_move {
         * after this directory is moved, we can try to rmdir the ino rmdir_ino.
         */
        u64 rmdir_ino;
+       u64 rmdir_gen;
        bool orphanized;
 };
 
@@ -316,7 +317,7 @@ static int is_waiting_for_move(struct send_ctx *sctx, u64 ino);
 static struct waiting_dir_move *
 get_waiting_dir_move(struct send_ctx *sctx, u64 ino);
 
-static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino);
+static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino, u64 gen);
 
 static int need_send_hole(struct send_ctx *sctx)
 {
@@ -2299,7 +2300,7 @@ static int get_cur_path(struct send_ctx *sctx, u64 ino, u64 gen,
 
                fs_path_reset(name);
 
-               if (is_waiting_for_rm(sctx, ino)) {
+               if (is_waiting_for_rm(sctx, ino, gen)) {
                        ret = gen_unique_name(sctx, ino, gen, name);
                        if (ret < 0)
                                goto out;
@@ -2858,8 +2859,8 @@ out:
        return ret;
 }
 
-static struct orphan_dir_info *
-add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
+static struct orphan_dir_info *add_orphan_dir_info(struct send_ctx *sctx,
+                                                  u64 dir_ino, u64 dir_gen)
 {
        struct rb_node **p = &sctx->orphan_dirs.rb_node;
        struct rb_node *parent = NULL;
@@ -2868,20 +2869,23 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
        while (*p) {
                parent = *p;
                entry = rb_entry(parent, struct orphan_dir_info, node);
-               if (dir_ino < entry->ino) {
+               if (dir_ino < entry->ino)
                        p = &(*p)->rb_left;
-               } else if (dir_ino > entry->ino) {
+               else if (dir_ino > entry->ino)
                        p = &(*p)->rb_right;
-               } else {
+               else if (dir_gen < entry->gen)
+                       p = &(*p)->rb_left;
+               else if (dir_gen > entry->gen)
+                       p = &(*p)->rb_right;
+               else
                        return entry;
-               }
        }
 
        odi = kmalloc(sizeof(*odi), GFP_KERNEL);
        if (!odi)
                return ERR_PTR(-ENOMEM);
        odi->ino = dir_ino;
-       odi->gen = 0;
+       odi->gen = dir_gen;
        odi->last_dir_index_offset = 0;
 
        rb_link_node(&odi->node, parent, p);
@@ -2889,8 +2893,8 @@ add_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
        return odi;
 }
 
-static struct orphan_dir_info *
-get_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
+static struct orphan_dir_info *get_orphan_dir_info(struct send_ctx *sctx,
+                                                  u64 dir_ino, u64 gen)
 {
        struct rb_node *n = sctx->orphan_dirs.rb_node;
        struct orphan_dir_info *entry;
@@ -2901,15 +2905,19 @@ get_orphan_dir_info(struct send_ctx *sctx, u64 dir_ino)
                        n = n->rb_left;
                else if (dir_ino > entry->ino)
                        n = n->rb_right;
+               else if (gen < entry->gen)
+                       n = n->rb_left;
+               else if (gen > entry->gen)
+                       n = n->rb_right;
                else
                        return entry;
        }
        return NULL;
 }
 
-static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino)
+static int is_waiting_for_rm(struct send_ctx *sctx, u64 dir_ino, u64 gen)
 {
-       struct orphan_dir_info *odi = get_orphan_dir_info(sctx, dir_ino);
+       struct orphan_dir_info *odi = get_orphan_dir_info(sctx, dir_ino, gen);
 
        return odi != NULL;
 }
@@ -2954,7 +2962,7 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
        key.type = BTRFS_DIR_INDEX_KEY;
        key.offset = 0;
 
-       odi = get_orphan_dir_info(sctx, dir);
+       odi = get_orphan_dir_info(sctx, dir, dir_gen);
        if (odi)
                key.offset = odi->last_dir_index_offset;
 
@@ -2985,7 +2993,7 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
 
                dm = get_waiting_dir_move(sctx, loc.objectid);
                if (dm) {
-                       odi = add_orphan_dir_info(sctx, dir);
+                       odi = add_orphan_dir_info(sctx, dir, dir_gen);
                        if (IS_ERR(odi)) {
                                ret = PTR_ERR(odi);
                                goto out;
@@ -2993,12 +3001,13 @@ static int can_rmdir(struct send_ctx *sctx, u64 dir, u64 dir_gen,
                        odi->gen = dir_gen;
                        odi->last_dir_index_offset = found_key.offset;
                        dm->rmdir_ino = dir;
+                       dm->rmdir_gen = dir_gen;
                        ret = 0;
                        goto out;
                }
 
                if (loc.objectid > send_progress) {
-                       odi = add_orphan_dir_info(sctx, dir);
+                       odi = add_orphan_dir_info(sctx, dir, dir_gen);
                        if (IS_ERR(odi)) {
                                ret = PTR_ERR(odi);
                                goto out;
@@ -3038,6 +3047,7 @@ static int add_waiting_dir_move(struct send_ctx *sctx, u64 ino, bool orphanized)
                return -ENOMEM;
        dm->ino = ino;
        dm->rmdir_ino = 0;
+       dm->rmdir_gen = 0;
        dm->orphanized = orphanized;
 
        while (*p) {
@@ -3183,7 +3193,7 @@ static int path_loop(struct send_ctx *sctx, struct fs_path *name,
        while (ino != BTRFS_FIRST_FREE_OBJECTID) {
                fs_path_reset(name);
 
-               if (is_waiting_for_rm(sctx, ino))
+               if (is_waiting_for_rm(sctx, ino, gen))
                        break;
                if (is_waiting_for_move(sctx, ino)) {
                        if (*ancestor_ino == 0)
@@ -3223,6 +3233,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
        u64 parent_ino, parent_gen;
        struct waiting_dir_move *dm = NULL;
        u64 rmdir_ino = 0;
+       u64 rmdir_gen;
        u64 ancestor;
        bool is_orphan;
        int ret;
@@ -3237,6 +3248,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
        dm = get_waiting_dir_move(sctx, pm->ino);
        ASSERT(dm);
        rmdir_ino = dm->rmdir_ino;
+       rmdir_gen = dm->rmdir_gen;
        is_orphan = dm->orphanized;
        free_waiting_dir_move(sctx, dm);
 
@@ -3273,6 +3285,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
                        dm = get_waiting_dir_move(sctx, pm->ino);
                        ASSERT(dm);
                        dm->rmdir_ino = rmdir_ino;
+                       dm->rmdir_gen = rmdir_gen;
                }
                goto out;
        }
@@ -3291,7 +3304,7 @@ static int apply_dir_move(struct send_ctx *sctx, struct pending_dir_move *pm)
                struct orphan_dir_info *odi;
                u64 gen;
 
-               odi = get_orphan_dir_info(sctx, rmdir_ino);
+               odi = get_orphan_dir_info(sctx, rmdir_ino, rmdir_gen);
                if (!odi) {
                        /* already deleted */
                        goto finish;
@@ -5499,6 +5512,21 @@ static int clone_range(struct send_ctx *sctx,
                        break;
                offset += clone_len;
                clone_root->offset += clone_len;
+
+               /*
+                * If we are cloning from the file we are currently processing,
+                * and using the send root as the clone root, we must stop once
+                * the current clone offset reaches the current eof of the file
+                * at the receiver, otherwise we would issue an invalid clone
+                * operation (source range going beyond eof) and cause the
+                * receiver to fail. So if we reach the current eof, bail out
+                * and fallback to a regular write.
+                */
+               if (clone_root->root == sctx->send_root &&
+                   clone_root->ino == sctx->cur_ino &&
+                   clone_root->offset >= sctx->cur_inode_next_write_offset)
+                       break;
+
                data_offset += clone_len;
 next:
                path->slots[0]++;
index 6409956..e834746 100644 (file)
@@ -532,7 +532,9 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info,
 
        loops = 0;
        while ((delalloc_bytes || dio_bytes) && loops < 3) {
-               btrfs_start_delalloc_roots(fs_info, items);
+               u64 nr_pages = min(delalloc_bytes, to_reclaim) >> PAGE_SHIFT;
+
+               btrfs_start_delalloc_roots(fs_info, nr_pages, true);
 
                loops++;
                if (wait_ordered && !trans) {
index 022f208..12d7d3b 100644 (file)
@@ -175,7 +175,7 @@ void __btrfs_handle_fs_error(struct btrfs_fs_info *fs_info, const char *function
        btrfs_discard_stop(fs_info);
 
        /* btrfs handle error by forcing the filesystem readonly */
-       sb->s_flags |= SB_RDONLY;
+       btrfs_set_sb_rdonly(sb);
        btrfs_info(fs_info, "forced readonly");
        /*
         * Note that a running device replace operation is not canceled here
@@ -1953,7 +1953,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                /* avoid complains from lockdep et al. */
                up(&fs_info->uuid_tree_rescan_sem);
 
-               sb->s_flags |= SB_RDONLY;
+               btrfs_set_sb_rdonly(sb);
 
                /*
                 * Setting SB_RDONLY will put the cleaner thread to
@@ -1964,10 +1964,42 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                 */
                btrfs_delete_unused_bgs(fs_info);
 
+               /*
+                * The cleaner task could be already running before we set the
+                * flag BTRFS_FS_STATE_RO (and SB_RDONLY in the superblock).
+                * We must make sure that after we finish the remount, i.e. after
+                * we call btrfs_commit_super(), the cleaner can no longer start
+                * a transaction - either because it was dropping a dead root,
+                * running delayed iputs or deleting an unused block group (the
+                * cleaner picked a block group from the list of unused block
+                * groups before we were able to in the previous call to
+                * btrfs_delete_unused_bgs()).
+                */
+               wait_on_bit(&fs_info->flags, BTRFS_FS_CLEANER_RUNNING,
+                           TASK_UNINTERRUPTIBLE);
+
+               /*
+                * We've set the superblock to RO mode, so we might have made
+                * the cleaner task sleep without running all pending delayed
+                * iputs. Go through all the delayed iputs here, so that if an
+                * unmount happens without remounting RW we don't end up at
+                * finishing close_ctree() with a non-empty list of delayed
+                * iputs.
+                */
+               btrfs_run_delayed_iputs(fs_info);
+
                btrfs_dev_replace_suspend_for_unmount(fs_info);
                btrfs_scrub_cancel(fs_info);
                btrfs_pause_balance(fs_info);
 
+               /*
+                * Pause the qgroup rescan worker if it is running. We don't want
+                * it to be still running after we are in RO mode, as after that,
+                * by the time we unmount, it might have left a transaction open,
+                * so we would leak the transaction and/or crash.
+                */
+               btrfs_qgroup_wait_for_completion(fs_info, false);
+
                ret = btrfs_commit_super(fs_info);
                if (ret)
                        goto restore;
@@ -2006,7 +2038,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
                if (ret)
                        goto restore;
 
-               sb->s_flags &= ~SB_RDONLY;
+               btrfs_clear_sb_rdonly(sb);
 
                set_bit(BTRFS_FS_OPEN, &fs_info->flags);
        }
@@ -2028,6 +2060,8 @@ restore:
        /* We've hit an error - don't reset SB_RDONLY */
        if (sb_rdonly(sb))
                old_flags |= SB_RDONLY;
+       if (!(old_flags & SB_RDONLY))
+               clear_bit(BTRFS_FS_STATE_RO, &fs_info->fs_state);
        sb->s_flags = old_flags;
        fs_info->mount_opt = old_opts;
        fs_info->compress_type = old_compress_type;
index 8ca334d..6bd97bd 100644 (file)
@@ -55,8 +55,14 @@ struct inode *btrfs_new_test_inode(void)
        struct inode *inode;
 
        inode = new_inode(test_mnt->mnt_sb);
-       if (inode)
-               inode_init_owner(inode, NULL, S_IFREG);
+       if (!inode)
+               return NULL;
+
+       inode->i_mode = S_IFREG;
+       BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
+       BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID;
+       BTRFS_I(inode)->location.offset = 0;
+       inode_init_owner(inode, NULL, S_IFREG);
 
        return inode;
 }
index 0402206..c9874b1 100644 (file)
@@ -232,11 +232,6 @@ static noinline int test_btrfs_get_extent(u32 sectorsize, u32 nodesize)
                return ret;
        }
 
-       inode->i_mode = S_IFREG;
-       BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
-       BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID;
-       BTRFS_I(inode)->location.offset = 0;
-
        fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
        if (!fs_info) {
                test_std_err(TEST_ALLOC_FS_INFO);
@@ -835,10 +830,6 @@ static int test_hole_first(u32 sectorsize, u32 nodesize)
                return ret;
        }
 
-       BTRFS_I(inode)->location.type = BTRFS_INODE_ITEM_KEY;
-       BTRFS_I(inode)->location.objectid = BTRFS_FIRST_FREE_OBJECTID;
-       BTRFS_I(inode)->location.offset = 0;
-
        fs_info = btrfs_alloc_dummy_fs_info(nodesize, sectorsize);
        if (!fs_info) {
                test_std_err(TEST_ALLOC_FS_INFO);
index 8e0f7a1..6af7f2b 100644 (file)
@@ -2264,14 +2264,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
         */
        btrfs_free_log_root_tree(trans, fs_info);
 
-       /*
-        * commit_fs_roots() can call btrfs_save_ino_cache(), which generates
-        * new delayed refs. Must handle them or qgroup can be wrong.
-        */
-       ret = btrfs_run_delayed_refs(trans, (unsigned long)-1);
-       if (ret)
-               goto unlock_tree_log;
-
        /*
         * Since fs roots are all committed, we can get a quite accurate
         * new_roots. So let's do quota accounting.
index 028e733..582061c 100644 (file)
@@ -760,6 +760,7 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf,
 {
        struct btrfs_fs_info *fs_info = leaf->fs_info;
        u64 length;
+       u64 chunk_end;
        u64 stripe_len;
        u16 num_stripes;
        u16 sub_stripes;
@@ -814,6 +815,12 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf,
                          "invalid chunk length, have %llu", length);
                return -EUCLEAN;
        }
+       if (unlikely(check_add_overflow(logical, length, &chunk_end))) {
+               chunk_err(leaf, chunk, logical,
+"invalid chunk logical start and length, have logical start %llu length %llu",
+                         logical, length);
+               return -EUCLEAN;
+       }
        if (unlikely(!is_power_of_2(stripe_len) || stripe_len != BTRFS_STRIPE_LEN)) {
                chunk_err(leaf, chunk, logical,
                          "invalid chunk stripe length: %llu",
index ee086fc..0a6de85 100644 (file)
@@ -2592,7 +2592,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
        set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE);
 
        if (seeding_dev) {
-               sb->s_flags &= ~SB_RDONLY;
+               btrfs_clear_sb_rdonly(sb);
                ret = btrfs_prepare_sprout(fs_info);
                if (ret) {
                        btrfs_abort_transaction(trans, ret);
@@ -2728,7 +2728,7 @@ error_sysfs:
        mutex_unlock(&fs_info->fs_devices->device_list_mutex);
 error_trans:
        if (seeding_dev)
-               sb->s_flags |= SB_RDONLY;
+               btrfs_set_sb_rdonly(sb);
        if (trans)
                btrfs_end_transaction(trans);
 error_free_zone:
@@ -4317,6 +4317,8 @@ int btrfs_recover_balance(struct btrfs_fs_info *fs_info)
                btrfs_warn(fs_info,
        "balance: cannot set exclusive op status, resume manually");
 
+       btrfs_release_path(path);
+
        mutex_lock(&fs_info->balance_mutex);
        BUG_ON(fs_info->balance_ctl);
        spin_lock(&fs_info->balance_lock);
index 8bda092..e027c71 100644 (file)
@@ -413,7 +413,6 @@ int cachefiles_read_or_alloc_page(struct fscache_retrieval *op,
 
        inode = d_backing_inode(object->backer);
        ASSERT(S_ISREG(inode->i_mode));
-       ASSERT(inode->i_mapping->a_ops->readpages);
 
        /* calculate the shift required to use bmap */
        shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits;
@@ -713,7 +712,6 @@ int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op,
 
        inode = d_backing_inode(object->backer);
        ASSERT(S_ISREG(inode->i_mode));
-       ASSERT(inode->i_mapping->a_ops->readpages);
 
        /* calculate the shift required to use bmap */
        shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits;
index 98c15ff..d87bd85 100644 (file)
@@ -2475,6 +2475,22 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
        return r;
 }
 
+static void encode_timestamp_and_gids(void **p,
+                                     const struct ceph_mds_request *req)
+{
+       struct ceph_timespec ts;
+       int i;
+
+       ceph_encode_timespec64(&ts, &req->r_stamp);
+       ceph_encode_copy(p, &ts, sizeof(ts));
+
+       /* gid_list */
+       ceph_encode_32(p, req->r_cred->group_info->ngroups);
+       for (i = 0; i < req->r_cred->group_info->ngroups; i++)
+               ceph_encode_64(p, from_kgid(&init_user_ns,
+                                           req->r_cred->group_info->gid[i]));
+}
+
 /*
  * called under mdsc->mutex
  */
@@ -2491,7 +2507,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
        u64 ino1 = 0, ino2 = 0;
        int pathlen1 = 0, pathlen2 = 0;
        bool freepath1 = false, freepath2 = false;
-       int len, i;
+       int len;
        u16 releases;
        void *p, *end;
        int ret;
@@ -2517,17 +2533,10 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
                goto out_free1;
        }
 
-       if (legacy) {
-               /* Old style */
-               len = sizeof(*head);
-       } else {
-               /* New style: add gid_list and any later fields */
-               len = sizeof(struct ceph_mds_request_head) + sizeof(u32) +
-                     (sizeof(u64) * req->r_cred->group_info->ngroups);
-       }
-
+       len = legacy ? sizeof(*head) : sizeof(struct ceph_mds_request_head);
        len += pathlen1 + pathlen2 + 2*(1 + sizeof(u32) + sizeof(u64)) +
                sizeof(struct ceph_timespec);
+       len += sizeof(u32) + (sizeof(u64) * req->r_cred->group_info->ngroups);
 
        /* calculate (max) length for cap releases */
        len += sizeof(struct ceph_mds_request_release) *
@@ -2548,7 +2557,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
        msg->hdr.tid = cpu_to_le64(req->r_tid);
 
        /*
-        * The old ceph_mds_request_header didn't contain a version field, and
+        * The old ceph_mds_request_head didn't contain a version field, and
         * one was added when we moved the message version from 3->4.
         */
        if (legacy) {
@@ -2609,20 +2618,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_session *session,
 
        head->num_releases = cpu_to_le16(releases);
 
-       /* time stamp */
-       {
-               struct ceph_timespec ts;
-               ceph_encode_timespec64(&ts, &req->r_stamp);
-               ceph_encode_copy(&p, &ts, sizeof(ts));
-       }
-
-       /* gid list */
-       if (!legacy) {
-               ceph_encode_32(&p, req->r_cred->group_info->ngroups);
-               for (i = 0; i < req->r_cred->group_info->ngroups; i++)
-                       ceph_encode_64(&p, from_kgid(&init_user_ns,
-                                      req->r_cred->group_info->gid[i]));
-       }
+       encode_timestamp_and_gids(&p, req);
 
        if (WARN_ON_ONCE(p > end)) {
                ceph_msg_put(msg);
@@ -2730,13 +2726,8 @@ static int __prepare_send_request(struct ceph_mds_session *session,
                /* remove cap/dentry releases from message */
                rhead->num_releases = 0;
 
-               /* time stamp */
                p = msg->front.iov_base + req->r_request_release_offset;
-               {
-                       struct ceph_timespec ts;
-                       ceph_encode_timespec64(&ts, &req->r_stamp);
-                       ceph_encode_copy(&p, &ts, sizeof(ts));
-               }
+               encode_timestamp_and_gids(&p, req);
 
                msg->front.iov_len = p - msg->front.iov_base;
                msg->hdr.front_len = cpu_to_le32(msg->front.iov_len);
@@ -5047,7 +5038,7 @@ bad:
        return;
 }
 
-static struct ceph_connection *con_get(struct ceph_connection *con)
+static struct ceph_connection *mds_get_con(struct ceph_connection *con)
 {
        struct ceph_mds_session *s = con->private;
 
@@ -5056,7 +5047,7 @@ static struct ceph_connection *con_get(struct ceph_connection *con)
        return NULL;
 }
 
-static void con_put(struct ceph_connection *con)
+static void mds_put_con(struct ceph_connection *con)
 {
        struct ceph_mds_session *s = con->private;
 
@@ -5067,7 +5058,7 @@ static void con_put(struct ceph_connection *con)
  * if the client is unresponsive for long enough, the mds will kill
  * the session entirely.
  */
-static void peer_reset(struct ceph_connection *con)
+static void mds_peer_reset(struct ceph_connection *con)
 {
        struct ceph_mds_session *s = con->private;
        struct ceph_mds_client *mdsc = s->s_mdsc;
@@ -5076,7 +5067,7 @@ static void peer_reset(struct ceph_connection *con)
        send_mds_reconnect(mdsc, s);
 }
 
-static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
+static void mds_dispatch(struct ceph_connection *con, struct ceph_msg *msg)
 {
        struct ceph_mds_session *s = con->private;
        struct ceph_mds_client *mdsc = s->s_mdsc;
@@ -5134,8 +5125,8 @@ out:
  * Note: returned pointer is the address of a structure that's
  * managed separately.  Caller must *not* attempt to free it.
  */
-static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
-                                       int *proto, int force_new)
+static struct ceph_auth_handshake *
+mds_get_authorizer(struct ceph_connection *con, int *proto, int force_new)
 {
        struct ceph_mds_session *s = con->private;
        struct ceph_mds_client *mdsc = s->s_mdsc;
@@ -5151,7 +5142,7 @@ static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
        return auth;
 }
 
-static int add_authorizer_challenge(struct ceph_connection *con,
+static int mds_add_authorizer_challenge(struct ceph_connection *con,
                                    void *challenge_buf, int challenge_buf_len)
 {
        struct ceph_mds_session *s = con->private;
@@ -5162,7 +5153,7 @@ static int add_authorizer_challenge(struct ceph_connection *con,
                                            challenge_buf, challenge_buf_len);
 }
 
-static int verify_authorizer_reply(struct ceph_connection *con)
+static int mds_verify_authorizer_reply(struct ceph_connection *con)
 {
        struct ceph_mds_session *s = con->private;
        struct ceph_mds_client *mdsc = s->s_mdsc;
@@ -5174,7 +5165,7 @@ static int verify_authorizer_reply(struct ceph_connection *con)
                NULL, NULL, NULL, NULL);
 }
 
-static int invalidate_authorizer(struct ceph_connection *con)
+static int mds_invalidate_authorizer(struct ceph_connection *con)
 {
        struct ceph_mds_session *s = con->private;
        struct ceph_mds_client *mdsc = s->s_mdsc;
@@ -5297,15 +5288,15 @@ static int mds_check_message_signature(struct ceph_msg *msg)
 }
 
 static const struct ceph_connection_operations mds_con_ops = {
-       .get = con_get,
-       .put = con_put,
-       .dispatch = dispatch,
-       .get_authorizer = get_authorizer,
-       .add_authorizer_challenge = add_authorizer_challenge,
-       .verify_authorizer_reply = verify_authorizer_reply,
-       .invalidate_authorizer = invalidate_authorizer,
-       .peer_reset = peer_reset,
+       .get = mds_get_con,
+       .put = mds_put_con,
        .alloc_msg = mds_alloc_msg,
+       .dispatch = mds_dispatch,
+       .peer_reset = mds_peer_reset,
+       .get_authorizer = mds_get_authorizer,
+       .add_authorizer_challenge = mds_add_authorizer_challenge,
+       .verify_authorizer_reply = mds_verify_authorizer_reply,
+       .invalidate_authorizer = mds_invalidate_authorizer,
        .sign_message = mds_sign_message,
        .check_message_signature = mds_check_message_signature,
        .get_auth_request = mds_get_auth_request,
index b9df855..c8ef24b 100644 (file)
@@ -2195,7 +2195,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
        if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING)
                tcon->nohandlecache = ctx->nohandlecache;
        else
-               tcon->nohandlecache = 1;
+               tcon->nohandlecache = true;
        tcon->nodelete = ctx->nodelete;
        tcon->local_lease = ctx->local_lease;
        INIT_LIST_HEAD(&tcon->pending_opens);
@@ -2628,7 +2628,7 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
        } else if (ctx)
                tcon->unix_ext = 1; /* Unix Extensions supported */
 
-       if (tcon->unix_ext == 0) {
+       if (!tcon->unix_ext) {
                cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n");
                return;
        }
@@ -3740,7 +3740,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
 
        if (!ses->binding) {
                ses->capabilities = server->capabilities;
-               if (linuxExtEnabled == 0)
+               if (!linuxExtEnabled)
                        ses->capabilities &= (~server->vals->cap_unix);
 
                if (ses->auth_key.response) {
index 6ad6ba5..0fdb0de 100644 (file)
@@ -1260,7 +1260,8 @@ void dfs_cache_del_vol(const char *fullpath)
        vi = find_vol(fullpath);
        spin_unlock(&vol_list_lock);
 
-       kref_put(&vi->refcnt, vol_release);
+       if (!IS_ERR(vi))
+               kref_put(&vi->refcnt, vol_release);
 }
 
 /**
index 0afccbb..076bcad 100644 (file)
@@ -303,8 +303,6 @@ do {                                                                        \
 int
 smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx)
 {
-       int rc = 0;
-
        memcpy(new_ctx, ctx, sizeof(*ctx));
        new_ctx->prepath = NULL;
        new_ctx->mount_options = NULL;
@@ -327,7 +325,7 @@ smb3_fs_context_dup(struct smb3_fs_context *new_ctx, struct smb3_fs_context *ctx
        DUP_CTX_STR(nodename);
        DUP_CTX_STR(iocharset);
 
-       return rc;
+       return 0;
 }
 
 static int
index 067eb44..794fc3b 100644 (file)
@@ -3248,7 +3248,7 @@ close_exit:
        free_rsp_buf(resp_buftype, rsp);
 
        /* retry close in a worker thread if this one is interrupted */
-       if (rc == -EINTR) {
+       if (is_interrupt_error(rc)) {
                int tmp_rc;
 
                tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
index 204a622..d85edf5 100644 (file)
@@ -424,7 +424,7 @@ struct smb2_rdma_transform_capabilities_context {
        __le16  TransformCount;
        __u16   Reserved1;
        __u32   Reserved2;
-       __le16  RDMATransformIds[1];
+       __le16  RDMATransformIds[];
 } __packed;
 
 /* Signing algorithms */
index e9abb41..95ef26b 100644 (file)
@@ -338,7 +338,7 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
        if (ssocket == NULL)
                return -EAGAIN;
 
-       if (signal_pending(current)) {
+       if (fatal_signal_pending(current)) {
                cifs_dbg(FYI, "signal pending before send request\n");
                return -ERESTARTSYS;
        }
@@ -429,7 +429,7 @@ unmask:
 
        if (signal_pending(current) && (total_len != send_length)) {
                cifs_dbg(FYI, "signal is pending after attempt to send\n");
-               rc = -EINTR;
+               rc = -ERESTARTSYS;
        }
 
        /* uncork it */
index 1a0a827..be79904 100644 (file)
@@ -372,20 +372,3 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
        }
        return err;
 }
-
-int __ext4_handle_dirty_super(const char *where, unsigned int line,
-                             handle_t *handle, struct super_block *sb)
-{
-       struct buffer_head *bh = EXT4_SB(sb)->s_sbh;
-       int err = 0;
-
-       ext4_superblock_csum_set(sb);
-       if (ext4_handle_valid(handle)) {
-               err = jbd2_journal_dirty_metadata(handle, bh);
-               if (err)
-                       ext4_journal_abort_handle(where, line, __func__,
-                                                 bh, handle, err);
-       } else
-               mark_buffer_dirty(bh);
-       return err;
-}
index a124c68..0d2fa42 100644 (file)
@@ -244,9 +244,6 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
                                 handle_t *handle, struct inode *inode,
                                 struct buffer_head *bh);
 
-int __ext4_handle_dirty_super(const char *where, unsigned int line,
-                             handle_t *handle, struct super_block *sb);
-
 #define ext4_journal_get_write_access(handle, bh) \
        __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh))
 #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \
@@ -257,8 +254,6 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
 #define ext4_handle_dirty_metadata(handle, inode, bh) \
        __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \
                                     (bh))
-#define ext4_handle_dirty_super(handle, sb) \
-       __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
 
 handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,
                                  int type, int blocks, int rsv_blocks,
index 4fcc21c..0a14a7c 100644 (file)
@@ -604,13 +604,13 @@ void ext4_fc_track_range(handle_t *handle, struct inode *inode, ext4_lblk_t star
        trace_ext4_fc_track_range(inode, start, end, ret);
 }
 
-static void ext4_fc_submit_bh(struct super_block *sb)
+static void ext4_fc_submit_bh(struct super_block *sb, bool is_tail)
 {
        int write_flags = REQ_SYNC;
        struct buffer_head *bh = EXT4_SB(sb)->s_fc_bh;
 
-       /* TODO: REQ_FUA | REQ_PREFLUSH is unnecessarily expensive. */
-       if (test_opt(sb, BARRIER))
+       /* Add REQ_FUA | REQ_PREFLUSH only its tail */
+       if (test_opt(sb, BARRIER) && is_tail)
                write_flags |= REQ_FUA | REQ_PREFLUSH;
        lock_buffer(bh);
        set_buffer_dirty(bh);
@@ -684,7 +684,7 @@ static u8 *ext4_fc_reserve_space(struct super_block *sb, int len, u32 *crc)
                *crc = ext4_chksum(sbi, *crc, tl, sizeof(*tl));
        if (pad_len > 0)
                ext4_fc_memzero(sb, tl + 1, pad_len, crc);
-       ext4_fc_submit_bh(sb);
+       ext4_fc_submit_bh(sb, false);
 
        ret = jbd2_fc_get_buf(EXT4_SB(sb)->s_journal, &bh);
        if (ret)
@@ -741,7 +741,7 @@ static int ext4_fc_write_tail(struct super_block *sb, u32 crc)
        tail.fc_crc = cpu_to_le32(crc);
        ext4_fc_memcpy(sb, dst, &tail.fc_crc, sizeof(tail.fc_crc), NULL);
 
-       ext4_fc_submit_bh(sb);
+       ext4_fc_submit_bh(sb, true);
 
        return 0;
 }
@@ -1268,7 +1268,7 @@ static void ext4_fc_cleanup(journal_t *journal, int full)
        list_splice_init(&sbi->s_fc_dentry_q[FC_Q_STAGING],
                                &sbi->s_fc_dentry_q[FC_Q_MAIN]);
        list_splice_init(&sbi->s_fc_q[FC_Q_STAGING],
-                               &sbi->s_fc_q[FC_Q_STAGING]);
+                               &sbi->s_fc_q[FC_Q_MAIN]);
 
        ext4_clear_mount_flag(sb, EXT4_MF_FC_COMMITTING);
        ext4_clear_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
@@ -1318,14 +1318,14 @@ static int ext4_fc_replay_unlink(struct super_block *sb, struct ext4_fc_tl *tl)
        entry.len = darg.dname_len;
        inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
 
-       if (IS_ERR_OR_NULL(inode)) {
+       if (IS_ERR(inode)) {
                jbd_debug(1, "Inode %d not found", darg.ino);
                return 0;
        }
 
        old_parent = ext4_iget(sb, darg.parent_ino,
                                EXT4_IGET_NORMAL);
-       if (IS_ERR_OR_NULL(old_parent)) {
+       if (IS_ERR(old_parent)) {
                jbd_debug(1, "Dir with inode  %d not found", darg.parent_ino);
                iput(inode);
                return 0;
@@ -1410,7 +1410,7 @@ static int ext4_fc_replay_link(struct super_block *sb, struct ext4_fc_tl *tl)
                        darg.parent_ino, darg.dname_len);
 
        inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
-       if (IS_ERR_OR_NULL(inode)) {
+       if (IS_ERR(inode)) {
                jbd_debug(1, "Inode not found.");
                return 0;
        }
@@ -1466,10 +1466,11 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
        trace_ext4_fc_replay(sb, tag, ino, 0, 0);
 
        inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
-       if (!IS_ERR_OR_NULL(inode)) {
+       if (!IS_ERR(inode)) {
                ext4_ext_clear_bb(inode);
                iput(inode);
        }
+       inode = NULL;
 
        ext4_fc_record_modified_inode(sb, ino);
 
@@ -1512,7 +1513,7 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl)
 
        /* Given that we just wrote the inode on disk, this SHOULD succeed. */
        inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL);
-       if (IS_ERR_OR_NULL(inode)) {
+       if (IS_ERR(inode)) {
                jbd_debug(1, "Inode not found.");
                return -EFSCORRUPTED;
        }
@@ -1564,7 +1565,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl)
                goto out;
 
        inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL);
-       if (IS_ERR_OR_NULL(inode)) {
+       if (IS_ERR(inode)) {
                jbd_debug(1, "inode %d not found.", darg.ino);
                inode = NULL;
                ret = -EINVAL;
@@ -1577,7 +1578,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl)
                 * dot and dot dot dirents are setup properly.
                 */
                dir = ext4_iget(sb, darg.parent_ino, EXT4_IGET_NORMAL);
-               if (IS_ERR_OR_NULL(dir)) {
+               if (IS_ERR(dir)) {
                        jbd_debug(1, "Dir %d not found.", darg.ino);
                        goto out;
                }
@@ -1653,7 +1654,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb,
 
        inode = ext4_iget(sb, le32_to_cpu(fc_add_ex->fc_ino),
                                EXT4_IGET_NORMAL);
-       if (IS_ERR_OR_NULL(inode)) {
+       if (IS_ERR(inode)) {
                jbd_debug(1, "Inode not found.");
                return 0;
        }
@@ -1777,7 +1778,7 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl)
                le32_to_cpu(lrange->fc_ino), cur, remaining);
 
        inode = ext4_iget(sb, le32_to_cpu(lrange->fc_ino), EXT4_IGET_NORMAL);
-       if (IS_ERR_OR_NULL(inode)) {
+       if (IS_ERR(inode)) {
                jbd_debug(1, "Inode %d not found", le32_to_cpu(lrange->fc_ino));
                return 0;
        }
@@ -1832,7 +1833,7 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
        for (i = 0; i < state->fc_modified_inodes_used; i++) {
                inode = ext4_iget(sb, state->fc_modified_inodes[i],
                        EXT4_IGET_NORMAL);
-               if (IS_ERR_OR_NULL(inode)) {
+               if (IS_ERR(inode)) {
                        jbd_debug(1, "Inode %d not found.",
                                state->fc_modified_inodes[i]);
                        continue;
@@ -1849,7 +1850,7 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb)
 
                        if (ret > 0) {
                                path = ext4_find_extent(inode, map.m_lblk, NULL, 0);
-                               if (!IS_ERR_OR_NULL(path)) {
+                               if (!IS_ERR(path)) {
                                        for (j = 0; j < path->p_depth; j++)
                                                ext4_mb_mark_bb(inode->i_sb,
                                                        path[j].p_block, 1, 1);
index 3ed8c04..349b27f 100644 (file)
@@ -809,9 +809,12 @@ static int ext4_sample_last_mounted(struct super_block *sb,
        err = ext4_journal_get_write_access(handle, sbi->s_sbh);
        if (err)
                goto out_journal;
-       strlcpy(sbi->s_es->s_last_mounted, cp,
+       lock_buffer(sbi->s_sbh);
+       strncpy(sbi->s_es->s_last_mounted, cp,
                sizeof(sbi->s_es->s_last_mounted));
-       ext4_handle_dirty_super(handle, sb);
+       ext4_superblock_csum_set(sb);
+       unlock_buffer(sbi->s_sbh);
+       ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
 out_journal:
        ext4_journal_stop(handle);
 out:
index 2794688..c173c84 100644 (file)
@@ -5150,9 +5150,13 @@ static int ext4_do_update_inode(handle_t *handle,
                err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh);
                if (err)
                        goto out_brelse;
+               lock_buffer(EXT4_SB(sb)->s_sbh);
                ext4_set_feature_large_file(sb);
+               ext4_superblock_csum_set(sb);
+               unlock_buffer(EXT4_SB(sb)->s_sbh);
                ext4_handle_sync(handle);
-               err = ext4_handle_dirty_super(handle, sb);
+               err = ext4_handle_dirty_metadata(handle, NULL,
+                                                EXT4_SB(sb)->s_sbh);
        }
        ext4_update_inode_fsync_trans(handle, inode, need_datasync);
 out_brelse:
index 524e134..d9665d2 100644 (file)
@@ -1157,7 +1157,10 @@ resizefs_out:
                        err = ext4_journal_get_write_access(handle, sbi->s_sbh);
                        if (err)
                                goto pwsalt_err_journal;
+                       lock_buffer(sbi->s_sbh);
                        generate_random_uuid(sbi->s_es->s_encrypt_pw_salt);
+                       ext4_superblock_csum_set(sb);
+                       unlock_buffer(sbi->s_sbh);
                        err = ext4_handle_dirty_metadata(handle, NULL,
                                                         sbi->s_sbh);
                pwsalt_err_journal:
index b17a082..cf652ba 100644 (file)
@@ -2976,14 +2976,17 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode)
            (le32_to_cpu(sbi->s_es->s_inodes_count))) {
                /* Insert this inode at the head of the on-disk orphan list */
                NEXT_ORPHAN(inode) = le32_to_cpu(sbi->s_es->s_last_orphan);
+               lock_buffer(sbi->s_sbh);
                sbi->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
+               ext4_superblock_csum_set(sb);
+               unlock_buffer(sbi->s_sbh);
                dirty = true;
        }
        list_add(&EXT4_I(inode)->i_orphan, &sbi->s_orphan);
        mutex_unlock(&sbi->s_orphan_lock);
 
        if (dirty) {
-               err = ext4_handle_dirty_super(handle, sb);
+               err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
                rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
                if (!err)
                        err = rc;
@@ -3059,9 +3062,12 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode)
                        mutex_unlock(&sbi->s_orphan_lock);
                        goto out_brelse;
                }
+               lock_buffer(sbi->s_sbh);
                sbi->s_es->s_last_orphan = cpu_to_le32(ino_next);
+               ext4_superblock_csum_set(inode->i_sb);
+               unlock_buffer(sbi->s_sbh);
                mutex_unlock(&sbi->s_orphan_lock);
-               err = ext4_handle_dirty_super(handle, inode->i_sb);
+               err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
        } else {
                struct ext4_iloc iloc2;
                struct inode *i_prev =
@@ -3593,9 +3599,6 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
                        return retval2;
                }
        }
-       brelse(ent->bh);
-       ent->bh = NULL;
-
        return retval;
 }
 
@@ -3794,6 +3797,7 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
                }
        }
 
+       old_file_type = old.de->file_type;
        if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
                ext4_handle_sync(handle);
 
@@ -3821,7 +3825,6 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
        force_reread = (new.dir->i_ino == old.dir->i_ino &&
                        ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
 
-       old_file_type = old.de->file_type;
        if (whiteout) {
                /*
                 * Do this before adding a new entry, so the old entry is sure
@@ -3919,15 +3922,19 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
        retval = 0;
 
 end_rename:
-       brelse(old.dir_bh);
-       brelse(old.bh);
-       brelse(new.bh);
        if (whiteout) {
-               if (retval)
+               if (retval) {
+                       ext4_setent(handle, &old,
+                               old.inode->i_ino, old_file_type);
                        drop_nlink(whiteout);
+               }
                unlock_new_inode(whiteout);
                iput(whiteout);
+
        }
+       brelse(old.dir_bh);
+       brelse(old.bh);
+       brelse(new.bh);
        if (handle)
                ext4_journal_stop(handle);
        return retval;
index 928700d..bd0d185 100644 (file)
@@ -899,8 +899,11 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        EXT4_SB(sb)->s_gdb_count++;
        ext4_kvfree_array_rcu(o_group_desc);
 
+       lock_buffer(EXT4_SB(sb)->s_sbh);
        le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
-       err = ext4_handle_dirty_super(handle, sb);
+       ext4_superblock_csum_set(sb);
+       unlock_buffer(EXT4_SB(sb)->s_sbh);
+       err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
        if (err)
                ext4_std_error(sb, err);
        return err;
@@ -1384,6 +1387,7 @@ static void ext4_update_super(struct super_block *sb,
        reserved_blocks *= blocks_count;
        do_div(reserved_blocks, 100);
 
+       lock_buffer(sbi->s_sbh);
        ext4_blocks_count_set(es, ext4_blocks_count(es) + blocks_count);
        ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + free_blocks);
        le32_add_cpu(&es->s_inodes_count, EXT4_INODES_PER_GROUP(sb) *
@@ -1421,6 +1425,8 @@ static void ext4_update_super(struct super_block *sb,
         * active. */
        ext4_r_blocks_count_set(es, ext4_r_blocks_count(es) +
                                reserved_blocks);
+       ext4_superblock_csum_set(sb);
+       unlock_buffer(sbi->s_sbh);
 
        /* Update the free space counts */
        percpu_counter_add(&sbi->s_freeclusters_counter,
@@ -1515,7 +1521,7 @@ static int ext4_flex_group_add(struct super_block *sb,
 
        ext4_update_super(sb, flex_gd);
 
-       err = ext4_handle_dirty_super(handle, sb);
+       err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
 
 exit_journal:
        err2 = ext4_journal_stop(handle);
@@ -1717,15 +1723,18 @@ static int ext4_group_extend_no_check(struct super_block *sb,
                goto errout;
        }
 
+       lock_buffer(EXT4_SB(sb)->s_sbh);
        ext4_blocks_count_set(es, o_blocks_count + add);
        ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + add);
+       ext4_superblock_csum_set(sb);
+       unlock_buffer(EXT4_SB(sb)->s_sbh);
        ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count,
                   o_blocks_count + add);
        /* We add the blocks to the bitmap and set the group need init bit */
        err = ext4_group_add_blocks(handle, sb, o_blocks_count, add);
        if (err)
                goto errout;
-       ext4_handle_dirty_super(handle, sb);
+       ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
        ext4_debug("freed blocks %llu through %llu\n", o_blocks_count,
                   o_blocks_count + add);
 errout:
@@ -1874,12 +1883,15 @@ static int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode)
        if (err)
                goto errout;
 
+       lock_buffer(sbi->s_sbh);
        ext4_clear_feature_resize_inode(sb);
        ext4_set_feature_meta_bg(sb);
        sbi->s_es->s_first_meta_bg =
                cpu_to_le32(num_desc_blocks(sb, sbi->s_groups_count));
+       ext4_superblock_csum_set(sb);
+       unlock_buffer(sbi->s_sbh);
 
-       err = ext4_handle_dirty_super(handle, sb);
+       err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh);
        if (err) {
                ext4_std_error(sb, err);
                goto errout;
index 2112178..9a6f987 100644 (file)
@@ -65,7 +65,8 @@ static struct ratelimit_state ext4_mount_msg_ratelimit;
 static int ext4_load_journal(struct super_block *, struct ext4_super_block *,
                             unsigned long journal_devnum);
 static int ext4_show_options(struct seq_file *seq, struct dentry *root);
-static int ext4_commit_super(struct super_block *sb, int sync);
+static void ext4_update_super(struct super_block *sb);
+static int ext4_commit_super(struct super_block *sb);
 static int ext4_mark_recovery_complete(struct super_block *sb,
                                        struct ext4_super_block *es);
 static int ext4_clear_journal_err(struct super_block *sb,
@@ -586,15 +587,12 @@ static int ext4_errno_to_code(int errno)
        return EXT4_ERR_UNKNOWN;
 }
 
-static void __save_error_info(struct super_block *sb, int error,
-                             __u32 ino, __u64 block,
-                             const char *func, unsigned int line)
+static void save_error_info(struct super_block *sb, int error,
+                           __u32 ino, __u64 block,
+                           const char *func, unsigned int line)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
 
-       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
-       if (bdev_read_only(sb->s_bdev))
-               return;
        /* We default to EFSCORRUPTED error... */
        if (error == 0)
                error = EFSCORRUPTED;
@@ -618,15 +616,6 @@ static void __save_error_info(struct super_block *sb, int error,
        spin_unlock(&sbi->s_error_lock);
 }
 
-static void save_error_info(struct super_block *sb, int error,
-                           __u32 ino, __u64 block,
-                           const char *func, unsigned int line)
-{
-       __save_error_info(sb, error, ino, block, func, line);
-       if (!bdev_read_only(sb->s_bdev))
-               ext4_commit_super(sb, 1);
-}
-
 /* Deal with the reporting of failure conditions on a filesystem such as
  * inconsistencies detected or read IO failures.
  *
@@ -647,19 +636,40 @@ static void save_error_info(struct super_block *sb, int error,
  * used to deal with unrecoverable failures such as journal IO errors or ENOMEM
  * at a critical moment in log management.
  */
-static void ext4_handle_error(struct super_block *sb, bool force_ro)
+static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
+                             __u32 ino, __u64 block,
+                             const char *func, unsigned int line)
 {
        journal_t *journal = EXT4_SB(sb)->s_journal;
+       bool continue_fs = !force_ro && test_opt(sb, ERRORS_CONT);
 
+       EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
        if (test_opt(sb, WARN_ON_ERROR))
                WARN_ON_ONCE(1);
 
-       if (sb_rdonly(sb) || (!force_ro && test_opt(sb, ERRORS_CONT)))
+       if (!continue_fs && !sb_rdonly(sb)) {
+               ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
+               if (journal)
+                       jbd2_journal_abort(journal, -EIO);
+       }
+
+       if (!bdev_read_only(sb->s_bdev)) {
+               save_error_info(sb, error, ino, block, func, line);
+               /*
+                * In case the fs should keep running, we need to writeout
+                * superblock through the journal. Due to lock ordering
+                * constraints, it may not be safe to do it right here so we
+                * defer superblock flushing to a workqueue.
+                */
+               if (continue_fs)
+                       schedule_work(&EXT4_SB(sb)->s_error_work);
+               else
+                       ext4_commit_super(sb);
+       }
+
+       if (sb_rdonly(sb) || continue_fs)
                return;
 
-       ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
-       if (journal)
-               jbd2_journal_abort(journal, -EIO);
        /*
         * We force ERRORS_RO behavior when system is rebooting. Otherwise we
         * could panic during 'reboot -f' as the underlying device got already
@@ -682,8 +692,39 @@ static void flush_stashed_error_work(struct work_struct *work)
 {
        struct ext4_sb_info *sbi = container_of(work, struct ext4_sb_info,
                                                s_error_work);
+       journal_t *journal = sbi->s_journal;
+       handle_t *handle;
 
-       ext4_commit_super(sbi->s_sb, 1);
+       /*
+        * If the journal is still running, we have to write out superblock
+        * through the journal to avoid collisions of other journalled sb
+        * updates.
+        *
+        * We use directly jbd2 functions here to avoid recursing back into
+        * ext4 error handling code during handling of previous errors.
+        */
+       if (!sb_rdonly(sbi->s_sb) && journal) {
+               handle = jbd2_journal_start(journal, 1);
+               if (IS_ERR(handle))
+                       goto write_directly;
+               if (jbd2_journal_get_write_access(handle, sbi->s_sbh)) {
+                       jbd2_journal_stop(handle);
+                       goto write_directly;
+               }
+               ext4_update_super(sbi->s_sb);
+               if (jbd2_journal_dirty_metadata(handle, sbi->s_sbh)) {
+                       jbd2_journal_stop(handle);
+                       goto write_directly;
+               }
+               jbd2_journal_stop(handle);
+               return;
+       }
+write_directly:
+       /*
+        * Write through journal failed. Write sb directly to get error info
+        * out and hope for the best.
+        */
+       ext4_commit_super(sbi->s_sb);
 }
 
 #define ext4_error_ratelimit(sb)                                       \
@@ -710,8 +751,7 @@ void __ext4_error(struct super_block *sb, const char *function,
                       sb->s_id, function, line, current->comm, &vaf);
                va_end(args);
        }
-       save_error_info(sb, error, 0, block, function, line);
-       ext4_handle_error(sb, force_ro);
+       ext4_handle_error(sb, force_ro, error, 0, block, function, line);
 }
 
 void __ext4_error_inode(struct inode *inode, const char *function,
@@ -741,9 +781,8 @@ void __ext4_error_inode(struct inode *inode, const char *function,
                               current->comm, &vaf);
                va_end(args);
        }
-       save_error_info(inode->i_sb, error, inode->i_ino, block,
-                       function, line);
-       ext4_handle_error(inode->i_sb, false);
+       ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
+                         function, line);
 }
 
 void __ext4_error_file(struct file *file, const char *function,
@@ -780,9 +819,8 @@ void __ext4_error_file(struct file *file, const char *function,
                               current->comm, path, &vaf);
                va_end(args);
        }
-       save_error_info(inode->i_sb, EFSCORRUPTED, inode->i_ino, block,
-                       function, line);
-       ext4_handle_error(inode->i_sb, false);
+       ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
+                         function, line);
 }
 
 const char *ext4_decode_error(struct super_block *sb, int errno,
@@ -849,8 +887,7 @@ void __ext4_std_error(struct super_block *sb, const char *function,
                       sb->s_id, function, line, errstr);
        }
 
-       save_error_info(sb, -errno, 0, 0, function, line);
-       ext4_handle_error(sb, false);
+       ext4_handle_error(sb, false, -errno, 0, 0, function, line);
 }
 
 void __ext4_msg(struct super_block *sb,
@@ -944,13 +981,16 @@ __acquires(bitlock)
        if (test_opt(sb, ERRORS_CONT)) {
                if (test_opt(sb, WARN_ON_ERROR))
                        WARN_ON_ONCE(1);
-               __save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
-               schedule_work(&EXT4_SB(sb)->s_error_work);
+               EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
+               if (!bdev_read_only(sb->s_bdev)) {
+                       save_error_info(sb, EFSCORRUPTED, ino, block, function,
+                                       line);
+                       schedule_work(&EXT4_SB(sb)->s_error_work);
+               }
                return;
        }
        ext4_unlock_group(sb, grp);
-       save_error_info(sb, EFSCORRUPTED, ino, block, function, line);
-       ext4_handle_error(sb, false);
+       ext4_handle_error(sb, false, EFSCORRUPTED, ino, block, function, line);
        /*
         * We only get here in the ERRORS_RO case; relocking the group
         * may be dangerous, but nothing bad will happen since the
@@ -1152,7 +1192,7 @@ static void ext4_put_super(struct super_block *sb)
                es->s_state = cpu_to_le16(sbi->s_mount_state);
        }
        if (!sb_rdonly(sb))
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
 
        rcu_read_lock();
        group_desc = rcu_dereference(sbi->s_group_desc);
@@ -2642,7 +2682,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,
        if (sbi->s_journal)
                ext4_set_feature_journal_needs_recovery(sb);
 
-       err = ext4_commit_super(sb, 1);
+       err = ext4_commit_super(sb);
 done:
        if (test_opt(sb, DEBUG))
                printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "
@@ -4868,7 +4908,7 @@ no_journal:
        if (DUMMY_ENCRYPTION_ENABLED(sbi) && !sb_rdonly(sb) &&
            !ext4_has_feature_encrypt(sb)) {
                ext4_set_feature_encrypt(sb);
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
        }
 
        /*
@@ -5418,7 +5458,7 @@ static int ext4_load_journal(struct super_block *sb,
                es->s_journal_dev = cpu_to_le32(journal_devnum);
 
                /* Make sure we flush the recovery flag to disk. */
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
        }
 
        return 0;
@@ -5428,16 +5468,14 @@ err_out:
        return err;
 }
 
-static int ext4_commit_super(struct super_block *sb, int sync)
+/* Copy state of EXT4_SB(sb) into buffer for on-disk superblock */
+static void ext4_update_super(struct super_block *sb)
 {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
-       struct ext4_super_block *es = EXT4_SB(sb)->s_es;
-       struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
-       int error = 0;
-
-       if (!sbh || block_device_ejected(sb))
-               return error;
+       struct ext4_super_block *es = sbi->s_es;
+       struct buffer_head *sbh = sbi->s_sbh;
 
+       lock_buffer(sbh);
        /*
         * If the file system is mounted read-only, don't update the
         * superblock write time.  This avoids updating the superblock
@@ -5451,17 +5489,17 @@ static int ext4_commit_super(struct super_block *sb, int sync)
        if (!(sb->s_flags & SB_RDONLY))
                ext4_update_tstamp(es, s_wtime);
        es->s_kbytes_written =
-               cpu_to_le64(EXT4_SB(sb)->s_kbytes_written +
+               cpu_to_le64(sbi->s_kbytes_written +
                    ((part_stat_read(sb->s_bdev, sectors[STAT_WRITE]) -
-                     EXT4_SB(sb)->s_sectors_written_start) >> 1));
-       if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeclusters_counter))
+                     sbi->s_sectors_written_start) >> 1));
+       if (percpu_counter_initialized(&sbi->s_freeclusters_counter))
                ext4_free_blocks_count_set(es,
-                       EXT4_C2B(EXT4_SB(sb), percpu_counter_sum_positive(
-                               &EXT4_SB(sb)->s_freeclusters_counter)));
-       if (percpu_counter_initialized(&EXT4_SB(sb)->s_freeinodes_counter))
+                       EXT4_C2B(sbi, percpu_counter_sum_positive(
+                               &sbi->s_freeclusters_counter)));
+       if (percpu_counter_initialized(&sbi->s_freeinodes_counter))
                es->s_free_inodes_count =
                        cpu_to_le32(percpu_counter_sum_positive(
-                               &EXT4_SB(sb)->s_freeinodes_counter));
+                               &sbi->s_freeinodes_counter));
        /* Copy error information to the on-disk superblock */
        spin_lock(&sbi->s_error_lock);
        if (sbi->s_add_error_count > 0) {
@@ -5502,10 +5540,20 @@ static int ext4_commit_super(struct super_block *sb, int sync)
        }
        spin_unlock(&sbi->s_error_lock);
 
-       BUFFER_TRACE(sbh, "marking dirty");
        ext4_superblock_csum_set(sb);
-       if (sync)
-               lock_buffer(sbh);
+       unlock_buffer(sbh);
+}
+
+static int ext4_commit_super(struct super_block *sb)
+{
+       struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;
+       int error = 0;
+
+       if (!sbh || block_device_ejected(sb))
+               return error;
+
+       ext4_update_super(sb);
+
        if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
                /*
                 * Oh, dear.  A previous attempt to write the
@@ -5520,17 +5568,15 @@ static int ext4_commit_super(struct super_block *sb, int sync)
                clear_buffer_write_io_error(sbh);
                set_buffer_uptodate(sbh);
        }
+       BUFFER_TRACE(sbh, "marking dirty");
        mark_buffer_dirty(sbh);
-       if (sync) {
-               unlock_buffer(sbh);
-               error = __sync_dirty_buffer(sbh,
-                       REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
-               if (buffer_write_io_error(sbh)) {
-                       ext4_msg(sb, KERN_ERR, "I/O error while writing "
-                              "superblock");
-                       clear_buffer_write_io_error(sbh);
-                       set_buffer_uptodate(sbh);
-               }
+       error = __sync_dirty_buffer(sbh,
+               REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0));
+       if (buffer_write_io_error(sbh)) {
+               ext4_msg(sb, KERN_ERR, "I/O error while writing "
+                      "superblock");
+               clear_buffer_write_io_error(sbh);
+               set_buffer_uptodate(sbh);
        }
        return error;
 }
@@ -5561,7 +5607,7 @@ static int ext4_mark_recovery_complete(struct super_block *sb,
 
        if (ext4_has_feature_journal_needs_recovery(sb) && sb_rdonly(sb)) {
                ext4_clear_feature_journal_needs_recovery(sb);
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
        }
 out:
        jbd2_journal_unlock_updates(journal);
@@ -5603,7 +5649,7 @@ static int ext4_clear_journal_err(struct super_block *sb,
 
                EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS;
                es->s_state |= cpu_to_le16(EXT4_ERROR_FS);
-               ext4_commit_super(sb, 1);
+               ext4_commit_super(sb);
 
                jbd2_journal_clear_err(journal);
                jbd2_journal_update_sb_errno(journal);
@@ -5705,7 +5751,7 @@ static int ext4_freeze(struct super_block *sb)
                ext4_clear_feature_journal_needs_recovery(sb);
        }
 
-       error = ext4_commit_super(sb, 1);
+       error = ext4_commit_super(sb);
 out:
        if (journal)
                /* we rely on upper layer to stop further updates */
@@ -5727,7 +5773,7 @@ static int ext4_unfreeze(struct super_block *sb)
                ext4_set_feature_journal_needs_recovery(sb);
        }
 
-       ext4_commit_super(sb, 1);
+       ext4_commit_super(sb);
        return 0;
 }
 
@@ -5987,7 +6033,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        }
 
        if (sbi->s_journal == NULL && !(old_sb_flags & SB_RDONLY)) {
-               err = ext4_commit_super(sb, 1);
+               err = ext4_commit_super(sb);
                if (err)
                        goto restore_opts;
        }
index 4e3b1f8..3722085 100644 (file)
@@ -792,8 +792,11 @@ static void ext4_xattr_update_super_block(handle_t *handle,
 
        BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access");
        if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) {
+               lock_buffer(EXT4_SB(sb)->s_sbh);
                ext4_set_feature_xattr(sb);
-               ext4_handle_dirty_super(handle, sb);
+               ext4_superblock_csum_set(sb);
+               unlock_buffer(EXT4_SB(sb)->s_sbh);
+               ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh);
        }
 }
 
index c0b6096..dab120b 100644 (file)
--- a/fs/file.c
+++ b/fs/file.c
@@ -21,7 +21,6 @@
 #include <linux/rcupdate.h>
 #include <linux/close_range.h>
 #include <net/sock.h>
-#include <linux/io_uring.h>
 
 unsigned int sysctl_nr_open __read_mostly = 1024*1024;
 unsigned int sysctl_nr_open_min = BITS_PER_LONG;
@@ -428,7 +427,6 @@ void exit_files(struct task_struct *tsk)
        struct files_struct * files = tsk->files;
 
        if (files) {
-               io_uring_files_cancel(files);
                task_lock(tsk);
                tsk->files = NULL;
                task_unlock(tsk);
index acfb558..c41cb88 100644 (file)
@@ -1474,21 +1474,25 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
        }
 
        /*
-        * Some filesystems may redirty the inode during the writeback
-        * due to delalloc, clear dirty metadata flags right before
-        * write_inode()
+        * If the inode has dirty timestamps and we need to write them, call
+        * mark_inode_dirty_sync() to notify the filesystem about it and to
+        * change I_DIRTY_TIME into I_DIRTY_SYNC.
         */
-       spin_lock(&inode->i_lock);
-
-       dirty = inode->i_state & I_DIRTY;
        if ((inode->i_state & I_DIRTY_TIME) &&
-           ((dirty & I_DIRTY_INODE) ||
-            wbc->sync_mode == WB_SYNC_ALL || wbc->for_sync ||
+           (wbc->sync_mode == WB_SYNC_ALL || wbc->for_sync ||
             time_after(jiffies, inode->dirtied_time_when +
                        dirtytime_expire_interval * HZ))) {
-               dirty |= I_DIRTY_TIME;
                trace_writeback_lazytime(inode);
+               mark_inode_dirty_sync(inode);
        }
+
+       /*
+        * Some filesystems may redirty the inode during the writeback
+        * due to delalloc, clear dirty metadata flags right before
+        * write_inode()
+        */
+       spin_lock(&inode->i_lock);
+       dirty = inode->i_state & I_DIRTY;
        inode->i_state &= ~dirty;
 
        /*
@@ -1509,8 +1513,6 @@ __writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 
        spin_unlock(&inode->i_lock);
 
-       if (dirty & I_DIRTY_TIME)
-               mark_inode_dirty_sync(inode);
        /* Don't write the inode if only I_DIRTY_PAGES was set */
        if (dirty & ~I_DIRTY_PAGES) {
                int err = write_inode(inode, wbc);
index 7e35283..c07913e 100644 (file)
@@ -262,6 +262,7 @@ struct io_ring_ctx {
                unsigned int            drain_next: 1;
                unsigned int            eventfd_async: 1;
                unsigned int            restricted: 1;
+               unsigned int            sqo_dead: 1;
 
                /*
                 * Ring buffer of indices into array of io_uring_sqe, which is
@@ -353,6 +354,7 @@ struct io_ring_ctx {
                unsigned                cq_entries;
                unsigned                cq_mask;
                atomic_t                cq_timeouts;
+               unsigned                cq_last_tm_flush;
                unsigned long           cq_check_overflow;
                struct wait_queue_head  cq_wait;
                struct fasync_struct    *cq_fasync;
@@ -992,6 +994,13 @@ enum io_mem_account {
        ACCT_PINNED,
 };
 
+static void __io_uring_cancel_task_requests(struct io_ring_ctx *ctx,
+                                           struct task_struct *task);
+
+static void destroy_fixed_file_ref_node(struct fixed_file_ref_node *ref_node);
+static struct fixed_file_ref_node *alloc_fixed_file_ref_node(
+                       struct io_ring_ctx *ctx);
+
 static void __io_complete_rw(struct io_kiocb *req, long res, long res2,
                             struct io_comp_state *cs);
 static void io_cqring_fill_event(struct io_kiocb *req, long res);
@@ -1016,6 +1025,7 @@ static ssize_t io_import_iovec(int rw, struct io_kiocb *req,
 static int io_setup_async_rw(struct io_kiocb *req, const struct iovec *iovec,
                             const struct iovec *fast_iov,
                             struct iov_iter *iter, bool force);
+static void io_req_drop_files(struct io_kiocb *req);
 
 static struct kmem_cache *req_cachep;
 
@@ -1039,8 +1049,7 @@ EXPORT_SYMBOL(io_uring_get_socket);
 
 static inline void io_clean_op(struct io_kiocb *req)
 {
-       if (req->flags & (REQ_F_NEED_CLEANUP | REQ_F_BUFFER_SELECTED |
-                         REQ_F_INFLIGHT))
+       if (req->flags & (REQ_F_NEED_CLEANUP | REQ_F_BUFFER_SELECTED))
                __io_clean_op(req);
 }
 
@@ -1066,8 +1075,11 @@ static bool io_match_task(struct io_kiocb *head,
                return true;
 
        io_for_each_link(req, head) {
-               if ((req->flags & REQ_F_WORK_INITIALIZED) &&
-                   (req->work.flags & IO_WQ_WORK_FILES) &&
+               if (!(req->flags & REQ_F_WORK_INITIALIZED))
+                       continue;
+               if (req->file && req->file->f_op == &io_uring_fops)
+                       return true;
+               if ((req->work.flags & IO_WQ_WORK_FILES) &&
                    req->work.identity->files == files)
                        return true;
        }
@@ -1098,6 +1110,9 @@ static void io_sq_thread_drop_mm_files(void)
 
 static int __io_sq_thread_acquire_files(struct io_ring_ctx *ctx)
 {
+       if (current->flags & PF_EXITING)
+               return -EFAULT;
+
        if (!current->files) {
                struct files_struct *files;
                struct nsproxy *nsproxy;
@@ -1125,6 +1140,8 @@ static int __io_sq_thread_acquire_mm(struct io_ring_ctx *ctx)
 {
        struct mm_struct *mm;
 
+       if (current->flags & PF_EXITING)
+               return -EFAULT;
        if (current->mm)
                return 0;
 
@@ -1338,11 +1355,6 @@ static void __io_commit_cqring(struct io_ring_ctx *ctx)
 
        /* order cqe stores with ring update */
        smp_store_release(&rings->cq.tail, ctx->cached_cq_tail);
-
-       if (wq_has_sleeper(&ctx->cq_wait)) {
-               wake_up_interruptible(&ctx->cq_wait);
-               kill_fasync(&ctx->cq_fasync, SIGIO, POLL_IN);
-       }
 }
 
 static void io_put_identity(struct io_uring_task *tctx, struct io_kiocb *req)
@@ -1385,6 +1397,8 @@ static void io_req_clean_work(struct io_kiocb *req)
                        free_fs_struct(fs);
                req->work.flags &= ~IO_WQ_WORK_FS;
        }
+       if (req->flags & REQ_F_INFLIGHT)
+               io_req_drop_files(req);
 
        io_put_identity(req->task->io_uring, req);
 }
@@ -1494,13 +1508,23 @@ static bool io_grab_identity(struct io_kiocb *req)
                        return false;
                atomic_inc(&id->files->count);
                get_nsproxy(id->nsproxy);
-               req->flags |= REQ_F_INFLIGHT;
 
-               spin_lock_irq(&ctx->inflight_lock);
-               list_add(&req->inflight_entry, &ctx->inflight_list);
-               spin_unlock_irq(&ctx->inflight_lock);
+               if (!(req->flags & REQ_F_INFLIGHT)) {
+                       req->flags |= REQ_F_INFLIGHT;
+
+                       spin_lock_irq(&ctx->inflight_lock);
+                       list_add(&req->inflight_entry, &ctx->inflight_list);
+                       spin_unlock_irq(&ctx->inflight_lock);
+               }
                req->work.flags |= IO_WQ_WORK_FILES;
        }
+       if (!(req->work.flags & IO_WQ_WORK_MM) &&
+           (def->work_flags & IO_WQ_WORK_MM)) {
+               if (id->mm != current->mm)
+                       return false;
+               mmgrab(id->mm);
+               req->work.flags |= IO_WQ_WORK_MM;
+       }
 
        return true;
 }
@@ -1509,10 +1533,8 @@ static void io_prep_async_work(struct io_kiocb *req)
 {
        const struct io_op_def *def = &io_op_defs[req->opcode];
        struct io_ring_ctx *ctx = req->ctx;
-       struct io_identity *id;
 
        io_req_init_async(req);
-       id = req->work.identity;
 
        if (req->flags & REQ_F_FORCE_ASYNC)
                req->work.flags |= IO_WQ_WORK_CONCURRENT;
@@ -1525,13 +1547,6 @@ static void io_prep_async_work(struct io_kiocb *req)
                        req->work.flags |= IO_WQ_WORK_UNBOUND;
        }
 
-       /* ->mm can never change on us */
-       if (!(req->work.flags & IO_WQ_WORK_MM) &&
-           (def->work_flags & IO_WQ_WORK_MM)) {
-               mmgrab(id->mm);
-               req->work.flags |= IO_WQ_WORK_MM;
-       }
-
        /* if we fail grabbing identity, we must COW, regrab, and retry */
        if (io_grab_identity(req))
                return;
@@ -1633,19 +1648,38 @@ static void __io_queue_deferred(struct io_ring_ctx *ctx)
 
 static void io_flush_timeouts(struct io_ring_ctx *ctx)
 {
-       while (!list_empty(&ctx->timeout_list)) {
+       u32 seq;
+
+       if (list_empty(&ctx->timeout_list))
+               return;
+
+       seq = ctx->cached_cq_tail - atomic_read(&ctx->cq_timeouts);
+
+       do {
+               u32 events_needed, events_got;
                struct io_kiocb *req = list_first_entry(&ctx->timeout_list,
                                                struct io_kiocb, timeout.list);
 
                if (io_is_timeout_noseq(req))
                        break;
-               if (req->timeout.target_seq != ctx->cached_cq_tail
-                                       - atomic_read(&ctx->cq_timeouts))
+
+               /*
+                * Since seq can easily wrap around over time, subtract
+                * the last seq at which timeouts were flushed before comparing.
+                * Assuming not more than 2^31-1 events have happened since,
+                * these subtractions won't have wrapped, so we can check if
+                * target is in [last_seq, current_seq] by comparing the two.
+                */
+               events_needed = req->timeout.target_seq - ctx->cq_last_tm_flush;
+               events_got = seq - ctx->cq_last_tm_flush;
+               if (events_got < events_needed)
                        break;
 
                list_del_init(&req->timeout.list);
                io_kill_timeout(req);
-       }
+       } while (!list_empty(&ctx->timeout_list));
+
+       ctx->cq_last_tm_flush = seq;
 }
 
 static void io_commit_cqring(struct io_ring_ctx *ctx)
@@ -1700,18 +1734,42 @@ static inline unsigned __io_cqring_events(struct io_ring_ctx *ctx)
 
 static void io_cqring_ev_posted(struct io_ring_ctx *ctx)
 {
+       /* see waitqueue_active() comment */
+       smp_mb();
+
        if (waitqueue_active(&ctx->wait))
                wake_up(&ctx->wait);
        if (ctx->sq_data && waitqueue_active(&ctx->sq_data->wait))
                wake_up(&ctx->sq_data->wait);
        if (io_should_trigger_evfd(ctx))
                eventfd_signal(ctx->cq_ev_fd, 1);
+       if (waitqueue_active(&ctx->cq_wait)) {
+               wake_up_interruptible(&ctx->cq_wait);
+               kill_fasync(&ctx->cq_fasync, SIGIO, POLL_IN);
+       }
+}
+
+static void io_cqring_ev_posted_iopoll(struct io_ring_ctx *ctx)
+{
+       /* see waitqueue_active() comment */
+       smp_mb();
+
+       if (ctx->flags & IORING_SETUP_SQPOLL) {
+               if (waitqueue_active(&ctx->wait))
+                       wake_up(&ctx->wait);
+       }
+       if (io_should_trigger_evfd(ctx))
+               eventfd_signal(ctx->cq_ev_fd, 1);
+       if (waitqueue_active(&ctx->cq_wait)) {
+               wake_up_interruptible(&ctx->cq_wait);
+               kill_fasync(&ctx->cq_fasync, SIGIO, POLL_IN);
+       }
 }
 
 /* Returns true if there are no backlogged entries after the flush */
-static bool io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force,
-                                    struct task_struct *tsk,
-                                    struct files_struct *files)
+static bool __io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force,
+                                      struct task_struct *tsk,
+                                      struct files_struct *files)
 {
        struct io_rings *rings = ctx->rings;
        struct io_kiocb *req, *tmp;
@@ -1764,6 +1822,20 @@ static bool io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force,
        return all_flushed;
 }
 
+static void io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force,
+                                    struct task_struct *tsk,
+                                    struct files_struct *files)
+{
+       if (test_bit(0, &ctx->cq_check_overflow)) {
+               /* iopoll syncs against uring_lock, not completion_lock */
+               if (ctx->flags & IORING_SETUP_IOPOLL)
+                       mutex_lock(&ctx->uring_lock);
+               __io_cqring_overflow_flush(ctx, force, tsk, files);
+               if (ctx->flags & IORING_SETUP_IOPOLL)
+                       mutex_unlock(&ctx->uring_lock);
+       }
+}
+
 static void __io_cqring_fill_event(struct io_kiocb *req, long res, long cflags)
 {
        struct io_ring_ctx *ctx = req->ctx;
@@ -2123,14 +2195,14 @@ static void __io_req_task_submit(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
 
-       if (!__io_sq_thread_acquire_mm(ctx) &&
-           !__io_sq_thread_acquire_files(ctx)) {
-               mutex_lock(&ctx->uring_lock);
+       mutex_lock(&ctx->uring_lock);
+       if (!ctx->sqo_dead &&
+           !__io_sq_thread_acquire_mm(ctx) &&
+           !__io_sq_thread_acquire_files(ctx))
                __io_queue_sqe(req, NULL);
-               mutex_unlock(&ctx->uring_lock);
-       } else {
+       else
                __io_req_task_cancel(req, -EFAULT);
-       }
+       mutex_unlock(&ctx->uring_lock);
 }
 
 static void io_req_task_submit(struct callback_head *cb)
@@ -2206,6 +2278,8 @@ static void io_req_free_batch_finish(struct io_ring_ctx *ctx,
                struct io_uring_task *tctx = rb->task->io_uring;
 
                percpu_counter_sub(&tctx->inflight, rb->task_refs);
+               if (atomic_read(&tctx->in_idle))
+                       wake_up(&tctx->wait);
                put_task_struct_many(rb->task, rb->task_refs);
                rb->task = NULL;
        }
@@ -2224,6 +2298,8 @@ static void io_req_free_batch(struct req_batch *rb, struct io_kiocb *req)
                        struct io_uring_task *tctx = rb->task->io_uring;
 
                        percpu_counter_sub(&tctx->inflight, rb->task_refs);
+                       if (atomic_read(&tctx->in_idle))
+                               wake_up(&tctx->wait);
                        put_task_struct_many(rb->task, rb->task_refs);
                }
                rb->task = req->task;
@@ -2309,20 +2385,8 @@ static void io_double_put_req(struct io_kiocb *req)
                io_free_req(req);
 }
 
-static unsigned io_cqring_events(struct io_ring_ctx *ctx, bool noflush)
+static unsigned io_cqring_events(struct io_ring_ctx *ctx)
 {
-       if (test_bit(0, &ctx->cq_check_overflow)) {
-               /*
-                * noflush == true is from the waitqueue handler, just ensure
-                * we wake up the task, and the next invocation will flush the
-                * entries. We cannot safely to it from here.
-                */
-               if (noflush)
-                       return -1U;
-
-               io_cqring_overflow_flush(ctx, false, NULL, NULL);
-       }
-
        /* See comment at the top of this file */
        smp_rmb();
        return __io_cqring_events(ctx);
@@ -2420,8 +2484,7 @@ static void io_iopoll_complete(struct io_ring_ctx *ctx, unsigned int *nr_events,
        }
 
        io_commit_cqring(ctx);
-       if (ctx->flags & IORING_SETUP_SQPOLL)
-               io_cqring_ev_posted(ctx);
+       io_cqring_ev_posted_iopoll(ctx);
        io_req_free_batch_finish(ctx, &rb);
 
        if (!list_empty(&again))
@@ -2547,7 +2610,9 @@ static int io_iopoll_check(struct io_ring_ctx *ctx, long min)
                 * If we do, we can potentially be spinning for commands that
                 * already triggered a CQE (eg in error).
                 */
-               if (io_cqring_events(ctx, false))
+               if (test_bit(0, &ctx->cq_check_overflow))
+                       __io_cqring_overflow_flush(ctx, false, NULL, NULL);
+               if (io_cqring_events(ctx))
                        break;
 
                /*
@@ -2664,6 +2729,8 @@ static bool io_rw_reissue(struct io_kiocb *req, long res)
        if ((res != -EAGAIN && res != -EOPNOTSUPP) || io_wq_current_is_worker())
                return false;
 
+       lockdep_assert_held(&req->ctx->uring_lock);
+
        ret = io_sq_thread_acquire_mm_files(req->ctx, req);
 
        if (io_resubmit_prep(req, ret)) {
@@ -3493,7 +3560,7 @@ static int io_read(struct io_kiocb *req, bool force_nonblock,
 
        /* read it all, or we did blocking attempt. no retry. */
        if (!iov_iter_count(iter) || !force_nonblock ||
-           (req->file->f_flags & O_NONBLOCK))
+           (req->file->f_flags & O_NONBLOCK) || !(req->flags & REQ_F_ISREG))
                goto done;
 
        io_size -= ret;
@@ -4413,7 +4480,6 @@ static int io_close_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
         * io_wq_work.flags, so initialize io_wq_work firstly.
         */
        io_req_init_async(req);
-       req->work.flags |= IO_WQ_WORK_NO_CANCEL;
 
        if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL))
                return -EINVAL;
@@ -4446,6 +4512,8 @@ static int io_close(struct io_kiocb *req, bool force_nonblock,
 
        /* if the file has a flush method, be safe and punt to async */
        if (close->put_file->f_op->flush && force_nonblock) {
+               /* not safe to cancel at this point */
+               req->work.flags |= IO_WQ_WORK_NO_CANCEL;
                /* was never set, but play safe */
                req->flags &= ~REQ_F_NOWAIT;
                /* avoid grabbing files - we don't need the files */
@@ -5802,6 +5870,12 @@ static int io_timeout(struct io_kiocb *req)
        tail = ctx->cached_cq_tail - atomic_read(&ctx->cq_timeouts);
        req->timeout.target_seq = tail + off;
 
+       /* Update the last seq here in case io_flush_timeouts() hasn't.
+        * This is safe because ->completion_lock is held, and submissions
+        * and completions are never mixed in the same ->completion_lock section.
+        */
+       ctx->cq_last_tm_flush = tail;
+
        /*
         * Insertion sort, ensuring the first entry in the list is always
         * the one we need first.
@@ -6096,8 +6170,10 @@ static void io_req_drop_files(struct io_kiocb *req)
        struct io_uring_task *tctx = req->task->io_uring;
        unsigned long flags;
 
-       put_files_struct(req->work.identity->files);
-       put_nsproxy(req->work.identity->nsproxy);
+       if (req->work.flags & IO_WQ_WORK_FILES) {
+               put_files_struct(req->work.identity->files);
+               put_nsproxy(req->work.identity->nsproxy);
+       }
        spin_lock_irqsave(&ctx->inflight_lock, flags);
        list_del(&req->inflight_entry);
        spin_unlock_irqrestore(&ctx->inflight_lock, flags);
@@ -6164,9 +6240,6 @@ static void __io_clean_op(struct io_kiocb *req)
                }
                req->flags &= ~REQ_F_NEED_CLEANUP;
        }
-
-       if (req->flags & REQ_F_INFLIGHT)
-               io_req_drop_files(req);
 }
 
 static int io_issue_sqe(struct io_kiocb *req, bool force_nonblock,
@@ -6385,6 +6458,15 @@ static struct file *io_file_get(struct io_submit_state *state,
                file = __io_file_get(state, fd);
        }
 
+       if (file && file->f_op == &io_uring_fops) {
+               io_req_init_async(req);
+               req->flags |= REQ_F_INFLIGHT;
+
+               spin_lock_irq(&ctx->inflight_lock);
+               list_add(&req->inflight_entry, &ctx->inflight_list);
+               spin_unlock_irq(&ctx->inflight_lock);
+       }
+
        return file;
 }
 
@@ -6822,7 +6904,7 @@ static int io_submit_sqes(struct io_ring_ctx *ctx, unsigned int nr)
 
        /* if we have a backlog and couldn't flush it all, return BUSY */
        if (test_bit(0, &ctx->sq_check_overflow)) {
-               if (!io_cqring_overflow_flush(ctx, false, NULL, NULL))
+               if (!__io_cqring_overflow_flush(ctx, false, NULL, NULL))
                        return -EBUSY;
        }
 
@@ -6924,7 +7006,8 @@ static int __io_sq_thread(struct io_ring_ctx *ctx, bool cap_entries)
                if (!list_empty(&ctx->iopoll_list))
                        io_do_iopoll(ctx, &nr_events, 0);
 
-               if (to_submit && likely(!percpu_ref_is_dying(&ctx->refs)))
+               if (to_submit && !ctx->sqo_dead &&
+                   likely(!percpu_ref_is_dying(&ctx->refs)))
                        ret = io_submit_sqes(ctx, to_submit);
                mutex_unlock(&ctx->uring_lock);
        }
@@ -7025,6 +7108,7 @@ static int io_sq_thread(void *data)
 
                if (sqt_spin || !time_after(jiffies, timeout)) {
                        io_run_task_work();
+                       io_sq_thread_drop_mm_files();
                        cond_resched();
                        if (sqt_spin)
                                timeout = jiffies + sqd->sq_thread_idle;
@@ -7062,6 +7146,7 @@ static int io_sq_thread(void *data)
        }
 
        io_run_task_work();
+       io_sq_thread_drop_mm_files();
 
        if (cur_css)
                io_sq_thread_unassociate_blkcg();
@@ -7085,7 +7170,7 @@ struct io_wait_queue {
        unsigned nr_timeouts;
 };
 
-static inline bool io_should_wake(struct io_wait_queue *iowq, bool noflush)
+static inline bool io_should_wake(struct io_wait_queue *iowq)
 {
        struct io_ring_ctx *ctx = iowq->ctx;
 
@@ -7094,7 +7179,7 @@ static inline bool io_should_wake(struct io_wait_queue *iowq, bool noflush)
         * started waiting. For timeouts, we always want to return to userspace,
         * regardless of event count.
         */
-       return io_cqring_events(ctx, noflush) >= iowq->to_wait ||
+       return io_cqring_events(ctx) >= iowq->to_wait ||
                        atomic_read(&ctx->cq_timeouts) != iowq->nr_timeouts;
 }
 
@@ -7104,11 +7189,13 @@ static int io_wake_function(struct wait_queue_entry *curr, unsigned int mode,
        struct io_wait_queue *iowq = container_of(curr, struct io_wait_queue,
                                                        wq);
 
-       /* use noflush == true, as we can't safely rely on locking context */
-       if (!io_should_wake(iowq, true))
-               return -1;
-
-       return autoremove_wake_function(curr, mode, wake_flags, key);
+       /*
+        * Cannot safely flush overflowed CQEs from here, ensure we wake up
+        * the task, and the next invocation will do it.
+        */
+       if (io_should_wake(iowq) || test_bit(0, &iowq->ctx->cq_check_overflow))
+               return autoremove_wake_function(curr, mode, wake_flags, key);
+       return -1;
 }
 
 static int io_run_task_work_sig(void)
@@ -7145,7 +7232,8 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
        int ret = 0;
 
        do {
-               if (io_cqring_events(ctx, false) >= min_events)
+               io_cqring_overflow_flush(ctx, false, NULL, NULL);
+               if (io_cqring_events(ctx) >= min_events)
                        return 0;
                if (!io_run_task_work())
                        break;
@@ -7173,6 +7261,7 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
        iowq.nr_timeouts = atomic_read(&ctx->cq_timeouts);
        trace_io_uring_cqring_wait(ctx, min_events);
        do {
+               io_cqring_overflow_flush(ctx, false, NULL, NULL);
                prepare_to_wait_exclusive(&ctx->wait, &iowq.wq,
                                                TASK_INTERRUPTIBLE);
                /* make sure we run task_work before checking for signals */
@@ -7181,8 +7270,10 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
                        continue;
                else if (ret < 0)
                        break;
-               if (io_should_wake(&iowq, false))
+               if (io_should_wake(&iowq))
                        break;
+               if (test_bit(0, &ctx->cq_check_overflow))
+                       continue;
                if (uts) {
                        timeout = schedule_timeout(timeout);
                        if (timeout == 0) {
@@ -7231,14 +7322,28 @@ static void io_file_ref_kill(struct percpu_ref *ref)
        complete(&data->done);
 }
 
+static void io_sqe_files_set_node(struct fixed_file_data *file_data,
+                                 struct fixed_file_ref_node *ref_node)
+{
+       spin_lock_bh(&file_data->lock);
+       file_data->node = ref_node;
+       list_add_tail(&ref_node->node, &file_data->ref_list);
+       spin_unlock_bh(&file_data->lock);
+       percpu_ref_get(&file_data->refs);
+}
+
 static int io_sqe_files_unregister(struct io_ring_ctx *ctx)
 {
        struct fixed_file_data *data = ctx->file_data;
-       struct fixed_file_ref_node *ref_node = NULL;
+       struct fixed_file_ref_node *backup_node, *ref_node = NULL;
        unsigned nr_tables, i;
+       int ret;
 
        if (!data)
                return -ENXIO;
+       backup_node = alloc_fixed_file_ref_node(ctx);
+       if (!backup_node)
+               return -ENOMEM;
 
        spin_lock_bh(&data->lock);
        ref_node = data->node;
@@ -7250,7 +7355,18 @@ static int io_sqe_files_unregister(struct io_ring_ctx *ctx)
 
        /* wait for all refs nodes to complete */
        flush_delayed_work(&ctx->file_put_work);
-       wait_for_completion(&data->done);
+       do {
+               ret = wait_for_completion_interruptible(&data->done);
+               if (!ret)
+                       break;
+               ret = io_run_task_work_sig();
+               if (ret < 0) {
+                       percpu_ref_resurrect(&data->refs);
+                       reinit_completion(&data->done);
+                       io_sqe_files_set_node(data, backup_node);
+                       return ret;
+               }
+       } while (1);
 
        __io_sqe_files_unregister(ctx);
        nr_tables = DIV_ROUND_UP(ctx->nr_user_files, IORING_MAX_FILES_TABLE);
@@ -7261,6 +7377,7 @@ static int io_sqe_files_unregister(struct io_ring_ctx *ctx)
        kfree(data);
        ctx->file_data = NULL;
        ctx->nr_user_files = 0;
+       destroy_fixed_file_ref_node(backup_node);
        return 0;
 }
 
@@ -7654,12 +7771,12 @@ static struct fixed_file_ref_node *alloc_fixed_file_ref_node(
 
        ref_node = kzalloc(sizeof(*ref_node), GFP_KERNEL);
        if (!ref_node)
-               return ERR_PTR(-ENOMEM);
+               return NULL;
 
        if (percpu_ref_init(&ref_node->refs, io_file_data_ref_zero,
                            0, GFP_KERNEL)) {
                kfree(ref_node);
-               return ERR_PTR(-ENOMEM);
+               return NULL;
        }
        INIT_LIST_HEAD(&ref_node->node);
        INIT_LIST_HEAD(&ref_node->file_list);
@@ -7753,16 +7870,12 @@ static int io_sqe_files_register(struct io_ring_ctx *ctx, void __user *arg,
        }
 
        ref_node = alloc_fixed_file_ref_node(ctx);
-       if (IS_ERR(ref_node)) {
+       if (!ref_node) {
                io_sqe_files_unregister(ctx);
-               return PTR_ERR(ref_node);
+               return -ENOMEM;
        }
 
-       file_data->node = ref_node;
-       spin_lock_bh(&file_data->lock);
-       list_add_tail(&ref_node->node, &file_data->ref_list);
-       spin_unlock_bh(&file_data->lock);
-       percpu_ref_get(&file_data->refs);
+       io_sqe_files_set_node(file_data, ref_node);
        return ret;
 out_fput:
        for (i = 0; i < ctx->nr_user_files; i++) {
@@ -7859,8 +7972,8 @@ static int __io_sqe_files_update(struct io_ring_ctx *ctx,
                return -EINVAL;
 
        ref_node = alloc_fixed_file_ref_node(ctx);
-       if (IS_ERR(ref_node))
-               return PTR_ERR(ref_node);
+       if (!ref_node)
+               return -ENOMEM;
 
        done = 0;
        fds = u64_to_user_ptr(up->fds);
@@ -7918,11 +8031,7 @@ static int __io_sqe_files_update(struct io_ring_ctx *ctx,
 
        if (needs_switch) {
                percpu_ref_kill(&data->node->refs);
-               spin_lock_bh(&data->lock);
-               list_add_tail(&ref_node->node, &data->ref_list);
-               data->node = ref_node;
-               spin_unlock_bh(&data->lock);
-               percpu_ref_get(&ctx->file_data->refs);
+               io_sqe_files_set_node(data, ref_node);
        } else
                destroy_fixed_file_ref_node(ref_node);
 
@@ -8602,7 +8711,8 @@ static __poll_t io_uring_poll(struct file *file, poll_table *wait)
        smp_rmb();
        if (!io_sqring_full(ctx))
                mask |= EPOLLOUT | EPOLLWRNORM;
-       if (io_cqring_events(ctx, false))
+       io_cqring_overflow_flush(ctx, false, NULL, NULL);
+       if (io_cqring_events(ctx))
                mask |= EPOLLIN | EPOLLRDNORM;
 
        return mask;
@@ -8641,7 +8751,7 @@ static void io_ring_exit_work(struct work_struct *work)
         * as nobody else will be looking for them.
         */
        do {
-               io_iopoll_try_reap_events(ctx);
+               __io_uring_cancel_task_requests(ctx, NULL);
        } while (!wait_for_completion_timeout(&ctx->ref_comp, HZ/20));
        io_ring_ctx_free(ctx);
 }
@@ -8657,10 +8767,14 @@ static void io_ring_ctx_wait_and_kill(struct io_ring_ctx *ctx)
 {
        mutex_lock(&ctx->uring_lock);
        percpu_ref_kill(&ctx->refs);
+
+       if (WARN_ON_ONCE((ctx->flags & IORING_SETUP_SQPOLL) && !ctx->sqo_dead))
+               ctx->sqo_dead = 1;
+
        /* if force is set, the ring is going away. always drop after that */
        ctx->cq_overflow_flushed = 1;
        if (ctx->rings)
-               io_cqring_overflow_flush(ctx, true, NULL, NULL);
+               __io_cqring_overflow_flush(ctx, true, NULL, NULL);
        mutex_unlock(&ctx->uring_lock);
 
        io_kill_timeouts(ctx, NULL, NULL);
@@ -8763,8 +8877,7 @@ static void io_uring_cancel_files(struct io_ring_ctx *ctx,
 
                spin_lock_irq(&ctx->inflight_lock);
                list_for_each_entry(req, &ctx->inflight_list, inflight_entry) {
-                       if (req->task != task ||
-                           req->work.identity->files != files)
+                       if (!io_match_task(req, task, files))
                                continue;
                        found = true;
                        break;
@@ -8781,6 +8894,7 @@ static void io_uring_cancel_files(struct io_ring_ctx *ctx,
                io_wq_cancel_cb(ctx->io_wq, io_cancel_task_cb, &cancel, true);
                io_poll_remove_all(ctx, task, files);
                io_kill_timeouts(ctx, task, files);
+               io_cqring_overflow_flush(ctx, true, task, files);
                /* cancellations _may_ trigger task work */
                io_run_task_work();
                schedule();
@@ -8796,9 +8910,11 @@ static void __io_uring_cancel_task_requests(struct io_ring_ctx *ctx,
                enum io_wq_cancel cret;
                bool ret = false;
 
-               cret = io_wq_cancel_cb(ctx->io_wq, io_cancel_task_cb, &cancel, true);
-               if (cret != IO_WQ_CANCEL_NOTFOUND)
-                       ret = true;
+               if (ctx->io_wq) {
+                       cret = io_wq_cancel_cb(ctx->io_wq, io_cancel_task_cb,
+                                              &cancel, true);
+                       ret |= (cret != IO_WQ_CANCEL_NOTFOUND);
+               }
 
                /* SQPOLL thread does its own polling */
                if (!(ctx->flags & IORING_SETUP_SQPOLL)) {
@@ -8817,6 +8933,17 @@ static void __io_uring_cancel_task_requests(struct io_ring_ctx *ctx,
        }
 }
 
+static void io_disable_sqo_submit(struct io_ring_ctx *ctx)
+{
+       mutex_lock(&ctx->uring_lock);
+       ctx->sqo_dead = 1;
+       mutex_unlock(&ctx->uring_lock);
+
+       /* make sure callers enter the ring to get error */
+       if (ctx->rings)
+               io_ring_set_wakeup_flag(ctx);
+}
+
 /*
  * We need to iteratively cancel requests, in case a request has dependent
  * hard links. These persist even for failure of cancelations, hence keep
@@ -8828,15 +8955,16 @@ static void io_uring_cancel_task_requests(struct io_ring_ctx *ctx,
        struct task_struct *task = current;
 
        if ((ctx->flags & IORING_SETUP_SQPOLL) && ctx->sq_data) {
+               /* for SQPOLL only sqo_task has task notes */
+               WARN_ON_ONCE(ctx->sqo_task != current);
+               io_disable_sqo_submit(ctx);
                task = ctx->sq_data->thread;
                atomic_inc(&task->io_uring->in_idle);
                io_sq_thread_park(ctx->sq_data);
        }
 
        io_cancel_defer_files(ctx, task, files);
-       io_ring_submit_lock(ctx, (ctx->flags & IORING_SETUP_IOPOLL));
        io_cqring_overflow_flush(ctx, true, task, files);
-       io_ring_submit_unlock(ctx, (ctx->flags & IORING_SETUP_IOPOLL));
 
        if (!files)
                __io_uring_cancel_task_requests(ctx, task);
@@ -8909,20 +9037,12 @@ static void io_uring_del_task_file(struct file *file)
                fput(file);
 }
 
-/*
- * Drop task note for this file if we're the only ones that hold it after
- * pending fput()
- */
-static void io_uring_attempt_task_drop(struct file *file)
+static void io_uring_remove_task_files(struct io_uring_task *tctx)
 {
-       if (!current->io_uring)
-               return;
-       /*
-        * fput() is pending, will be 2 if the only other ref is our potential
-        * task file note. If the task is exiting, drop regardless of count.
-        */
-       if (fatal_signal_pending(current) || (current->flags & PF_EXITING) ||
-           atomic_long_read(&file->f_count) == 2)
+       struct file *file;
+       unsigned long index;
+
+       xa_for_each(&tctx->xa, index, file)
                io_uring_del_task_file(file);
 }
 
@@ -8934,16 +9054,12 @@ void __io_uring_files_cancel(struct files_struct *files)
 
        /* make sure overflow events are dropped */
        atomic_inc(&tctx->in_idle);
-
-       xa_for_each(&tctx->xa, index, file) {
-               struct io_ring_ctx *ctx = file->private_data;
-
-               io_uring_cancel_task_requests(ctx, files);
-               if (files)
-                       io_uring_del_task_file(file);
-       }
-
+       xa_for_each(&tctx->xa, index, file)
+               io_uring_cancel_task_requests(file->private_data, files);
        atomic_dec(&tctx->in_idle);
+
+       if (files)
+               io_uring_remove_task_files(tctx);
 }
 
 static s64 tctx_inflight(struct io_uring_task *tctx)
@@ -8986,6 +9102,10 @@ void __io_uring_task_cancel(void)
        /* make sure overflow events are dropped */
        atomic_inc(&tctx->in_idle);
 
+       /* trigger io_disable_sqo_submit() */
+       if (tctx->sqpoll)
+               __io_uring_files_cancel(NULL);
+
        do {
                /* read completions before cancelations */
                inflight = tctx_inflight(tctx);
@@ -9005,12 +9125,44 @@ void __io_uring_task_cancel(void)
                finish_wait(&tctx->wait, &wait);
        } while (1);
 
+       finish_wait(&tctx->wait, &wait);
        atomic_dec(&tctx->in_idle);
+
+       io_uring_remove_task_files(tctx);
 }
 
 static int io_uring_flush(struct file *file, void *data)
 {
-       io_uring_attempt_task_drop(file);
+       struct io_uring_task *tctx = current->io_uring;
+       struct io_ring_ctx *ctx = file->private_data;
+
+       if (!tctx)
+               return 0;
+
+       /* we should have cancelled and erased it before PF_EXITING */
+       WARN_ON_ONCE((current->flags & PF_EXITING) &&
+                    xa_load(&tctx->xa, (unsigned long)file));
+
+       /*
+        * fput() is pending, will be 2 if the only other ref is our potential
+        * task file note. If the task is exiting, drop regardless of count.
+        */
+       if (atomic_long_read(&file->f_count) != 2)
+               return 0;
+
+       if (ctx->flags & IORING_SETUP_SQPOLL) {
+               /* there is only one file note, which is owned by sqo_task */
+               WARN_ON_ONCE(ctx->sqo_task != current &&
+                            xa_load(&tctx->xa, (unsigned long)file));
+               /* sqo_dead check is for when this happens after cancellation */
+               WARN_ON_ONCE(ctx->sqo_task == current && !ctx->sqo_dead &&
+                            !xa_load(&tctx->xa, (unsigned long)file));
+
+               io_disable_sqo_submit(ctx);
+       }
+
+       if (!(ctx->flags & IORING_SETUP_SQPOLL) || ctx->sqo_task == current)
+               io_uring_del_task_file(file);
        return 0;
 }
 
@@ -9084,8 +9236,9 @@ static unsigned long io_uring_nommu_get_unmapped_area(struct file *file,
 
 #endif /* !CONFIG_MMU */
 
-static void io_sqpoll_wait_sq(struct io_ring_ctx *ctx)
+static int io_sqpoll_wait_sq(struct io_ring_ctx *ctx)
 {
+       int ret = 0;
        DEFINE_WAIT(wait);
 
        do {
@@ -9094,6 +9247,11 @@ static void io_sqpoll_wait_sq(struct io_ring_ctx *ctx)
 
                prepare_to_wait(&ctx->sqo_sq_wait, &wait, TASK_INTERRUPTIBLE);
 
+               if (unlikely(ctx->sqo_dead)) {
+                       ret = -EOWNERDEAD;
+                       goto out;
+               }
+
                if (!io_sqring_full(ctx))
                        break;
 
@@ -9101,6 +9259,8 @@ static void io_sqpoll_wait_sq(struct io_ring_ctx *ctx)
        } while (!signal_pending(current));
 
        finish_wait(&ctx->sqo_sq_wait, &wait);
+out:
+       return ret;
 }
 
 static int io_get_ext_arg(unsigned flags, const void __user *argp, size_t *argsz,
@@ -9172,17 +9332,18 @@ SYSCALL_DEFINE6(io_uring_enter, unsigned int, fd, u32, to_submit,
         */
        ret = 0;
        if (ctx->flags & IORING_SETUP_SQPOLL) {
-               if (!list_empty_careful(&ctx->cq_overflow_list)) {
-                       bool needs_lock = ctx->flags & IORING_SETUP_IOPOLL;
+               io_cqring_overflow_flush(ctx, false, NULL, NULL);
 
-                       io_ring_submit_lock(ctx, needs_lock);
-                       io_cqring_overflow_flush(ctx, false, NULL, NULL);
-                       io_ring_submit_unlock(ctx, needs_lock);
-               }
+               ret = -EOWNERDEAD;
+               if (unlikely(ctx->sqo_dead))
+                       goto out;
                if (flags & IORING_ENTER_SQ_WAKEUP)
                        wake_up(&ctx->sq_data->wait);
-               if (flags & IORING_ENTER_SQ_WAIT)
-                       io_sqpoll_wait_sq(ctx);
+               if (flags & IORING_ENTER_SQ_WAIT) {
+                       ret = io_sqpoll_wait_sq(ctx);
+                       if (ret)
+                               goto out;
+               }
                submitted = to_submit;
        } else if (to_submit) {
                ret = io_uring_add_task_file(ctx, f.file);
@@ -9601,6 +9762,7 @@ static int io_uring_create(unsigned entries, struct io_uring_params *p,
         */
        ret = io_uring_install_fd(ctx, file);
        if (ret < 0) {
+               io_disable_sqo_submit(ctx);
                /* fput will clean it up */
                fput(file);
                return ret;
@@ -9609,6 +9771,7 @@ static int io_uring_create(unsigned entries, struct io_uring_params *p,
        trace_io_uring_create(ret, ctx, p->sq_entries, p->cq_entries, p->flags);
        return ret;
 err:
+       io_disable_sqo_submit(ctx);
        io_ring_ctx_wait_and_kill(ctx);
        return ret;
 }
index f277d02..c757193 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/pagemap.h>
 #include <linux/sched/mm.h>
 #include <linux/fsnotify.h>
+#include <linux/uio.h>
 
 #include "kernfs-internal.h"
 
@@ -180,11 +181,10 @@ static const struct seq_operations kernfs_seq_ops = {
  * it difficult to use seq_file.  Implement simplistic custom buffering for
  * bin files.
  */
-static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
-                                      char __user *user_buf, size_t count,
-                                      loff_t *ppos)
+static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
-       ssize_t len = min_t(size_t, count, PAGE_SIZE);
+       struct kernfs_open_file *of = kernfs_of(iocb->ki_filp);
+       ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE);
        const struct kernfs_ops *ops;
        char *buf;
 
@@ -210,7 +210,7 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
        of->event = atomic_read(&of->kn->attr.open->event);
        ops = kernfs_ops(of->kn);
        if (ops->read)
-               len = ops->read(of, buf, len, *ppos);
+               len = ops->read(of, buf, len, iocb->ki_pos);
        else
                len = -EINVAL;
 
@@ -220,12 +220,12 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
        if (len < 0)
                goto out_free;
 
-       if (copy_to_user(user_buf, buf, len)) {
+       if (copy_to_iter(buf, len, iter) != len) {
                len = -EFAULT;
                goto out_free;
        }
 
-       *ppos += len;
+       iocb->ki_pos += len;
 
  out_free:
        if (buf == of->prealloc_buf)
@@ -235,31 +235,14 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
        return len;
 }
 
-/**
- * kernfs_fop_read - kernfs vfs read callback
- * @file: file pointer
- * @user_buf: data to write
- * @count: number of bytes
- * @ppos: starting offset
- */
-static ssize_t kernfs_fop_read(struct file *file, char __user *user_buf,
-                              size_t count, loff_t *ppos)
+static ssize_t kernfs_fop_read_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
-       struct kernfs_open_file *of = kernfs_of(file);
-
-       if (of->kn->flags & KERNFS_HAS_SEQ_SHOW)
-               return seq_read(file, user_buf, count, ppos);
-       else
-               return kernfs_file_direct_read(of, user_buf, count, ppos);
+       if (kernfs_of(iocb->ki_filp)->kn->flags & KERNFS_HAS_SEQ_SHOW)
+               return seq_read_iter(iocb, iter);
+       return kernfs_file_read_iter(iocb, iter);
 }
 
-/**
- * kernfs_fop_write - kernfs vfs write callback
- * @file: file pointer
- * @user_buf: data to write
- * @count: number of bytes
- * @ppos: starting offset
- *
+/*
  * Copy data in from userland and pass it to the matching kernfs write
  * operation.
  *
@@ -269,20 +252,18 @@ static ssize_t kernfs_fop_read(struct file *file, char __user *user_buf,
  * modify only the the value you're changing, then write entire buffer
  * back.
  */
-static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
-                               size_t count, loff_t *ppos)
+static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter)
 {
-       struct kernfs_open_file *of = kernfs_of(file);
+       struct kernfs_open_file *of = kernfs_of(iocb->ki_filp);
+       ssize_t len = iov_iter_count(iter);
        const struct kernfs_ops *ops;
-       ssize_t len;
        char *buf;
 
        if (of->atomic_write_len) {
-               len = count;
                if (len > of->atomic_write_len)
                        return -E2BIG;
        } else {
-               len = min_t(size_t, count, PAGE_SIZE);
+               len = min_t(size_t, len, PAGE_SIZE);
        }
 
        buf = of->prealloc_buf;
@@ -293,7 +274,7 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
        if (!buf)
                return -ENOMEM;
 
-       if (copy_from_user(buf, user_buf, len)) {
+       if (copy_from_iter(buf, len, iter) != len) {
                len = -EFAULT;
                goto out_free;
        }
@@ -312,7 +293,7 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
 
        ops = kernfs_ops(of->kn);
        if (ops->write)
-               len = ops->write(of, buf, len, *ppos);
+               len = ops->write(of, buf, len, iocb->ki_pos);
        else
                len = -EINVAL;
 
@@ -320,7 +301,7 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,
        mutex_unlock(&of->mutex);
 
        if (len > 0)
-               *ppos += len;
+               iocb->ki_pos += len;
 
 out_free:
        if (buf == of->prealloc_buf)
@@ -673,7 +654,7 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
 
        /*
         * Write path needs to atomic_write_len outside active reference.
-        * Cache it in open_file.  See kernfs_fop_write() for details.
+        * Cache it in open_file.  See kernfs_fop_write_iter() for details.
         */
        of->atomic_write_len = ops->atomic_write_len;
 
@@ -960,14 +941,16 @@ void kernfs_notify(struct kernfs_node *kn)
 EXPORT_SYMBOL_GPL(kernfs_notify);
 
 const struct file_operations kernfs_file_fops = {
-       .read           = kernfs_fop_read,
-       .write          = kernfs_fop_write,
+       .read_iter      = kernfs_fop_read_iter,
+       .write_iter     = kernfs_fop_write_iter,
        .llseek         = generic_file_llseek,
        .mmap           = kernfs_fop_mmap,
        .open           = kernfs_fop_open,
        .release        = kernfs_fop_release,
        .poll           = kernfs_fop_poll,
        .fsync          = noop_fsync,
+       .splice_read    = generic_file_splice_read,
+       .splice_write   = iter_file_splice_write,
 };
 
 /**
index d2db7df..9d33909 100644 (file)
@@ -1713,8 +1713,6 @@ static int can_umount(const struct path *path, int flags)
 {
        struct mount *mnt = real_mount(path->mnt);
 
-       if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
-               return -EINVAL;
        if (!may_mount())
                return -EPERM;
        if (path->dentry != path->mnt->mnt_root)
@@ -1728,6 +1726,7 @@ static int can_umount(const struct path *path, int flags)
        return 0;
 }
 
+// caller is responsible for flags being sane
 int path_umount(struct path *path, int flags)
 {
        struct mount *mnt = real_mount(path->mnt);
@@ -1749,6 +1748,10 @@ static int ksys_umount(char __user *name, int flags)
        struct path path;
        int ret;
 
+       // basic validity checks done first
+       if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
+               return -EINVAL;
+
        if (!(flags & UMOUNT_NOFOLLOW))
                lookup_flags |= LOOKUP_FOLLOW;
        ret = user_path_at(AT_FDCWD, name, lookup_flags, &path);
index 816e142..04bf806 100644 (file)
@@ -1011,22 +1011,24 @@ nfs_delegation_find_inode_server(struct nfs_server *server,
                                 const struct nfs_fh *fhandle)
 {
        struct nfs_delegation *delegation;
-       struct inode *freeme, *res = NULL;
+       struct super_block *freeme = NULL;
+       struct inode *res = NULL;
 
        list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
                spin_lock(&delegation->lock);
                if (delegation->inode != NULL &&
                    !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
                    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
-                       freeme = igrab(delegation->inode);
-                       if (freeme && nfs_sb_active(freeme->i_sb))
-                               res = freeme;
+                       if (nfs_sb_active(server->super)) {
+                               freeme = server->super;
+                               res = igrab(delegation->inode);
+                       }
                        spin_unlock(&delegation->lock);
                        if (res != NULL)
                                return res;
                        if (freeme) {
                                rcu_read_unlock();
-                               iput(freeme);
+                               nfs_sb_deactive(freeme);
                                rcu_read_lock();
                        }
                        return ERR_PTR(-EAGAIN);
index b840d0a..62d3189 100644 (file)
@@ -136,9 +136,29 @@ struct nfs_fs_context {
        } clone_data;
 };
 
-#define nfs_errorf(fc, fmt, ...) errorf(fc, fmt, ## __VA_ARGS__)
-#define nfs_invalf(fc, fmt, ...) invalf(fc, fmt, ## __VA_ARGS__)
-#define nfs_warnf(fc, fmt, ...) warnf(fc, fmt, ## __VA_ARGS__)
+#define nfs_errorf(fc, fmt, ...) ((fc)->log.log ?              \
+       errorf(fc, fmt, ## __VA_ARGS__) :                       \
+       ({ dprintk(fmt "\n", ## __VA_ARGS__); }))
+
+#define nfs_ferrorf(fc, fac, fmt, ...) ((fc)->log.log ?                \
+       errorf(fc, fmt, ## __VA_ARGS__) :                       \
+       ({ dfprintk(fac, fmt "\n", ## __VA_ARGS__); }))
+
+#define nfs_invalf(fc, fmt, ...) ((fc)->log.log ?              \
+       invalf(fc, fmt, ## __VA_ARGS__) :                       \
+       ({ dprintk(fmt "\n", ## __VA_ARGS__);  -EINVAL; }))
+
+#define nfs_finvalf(fc, fac, fmt, ...) ((fc)->log.log ?                \
+       invalf(fc, fmt, ## __VA_ARGS__) :                       \
+       ({ dfprintk(fac, fmt "\n", ## __VA_ARGS__);  -EINVAL; }))
+
+#define nfs_warnf(fc, fmt, ...) ((fc)->log.log ?               \
+       warnf(fc, fmt, ## __VA_ARGS__) :                        \
+       ({ dprintk(fmt "\n", ## __VA_ARGS__); }))
+
+#define nfs_fwarnf(fc, fac, fmt, ...) ((fc)->log.log ?         \
+       warnf(fc, fmt, ## __VA_ARGS__) :                        \
+       ({ dfprintk(fac, fmt "\n", ## __VA_ARGS__); }))
 
 static inline struct nfs_fs_context *nfs_fc2context(const struct fs_context *fc)
 {
@@ -579,12 +599,14 @@ extern void nfs4_test_session_trunk(struct rpc_clnt *clnt,
 
 static inline struct inode *nfs_igrab_and_active(struct inode *inode)
 {
-       inode = igrab(inode);
-       if (inode != NULL && !nfs_sb_active(inode->i_sb)) {
-               iput(inode);
-               inode = NULL;
+       struct super_block *sb = inode->i_sb;
+
+       if (sb && nfs_sb_active(sb)) {
+               if (igrab(inode))
+                       return inode;
+               nfs_sb_deactive(sb);
        }
-       return inode;
+       return NULL;
 }
 
 static inline void nfs_iput_and_deactive(struct inode *inode)
index 0ce04e0..2f4679a 100644 (file)
@@ -3536,10 +3536,8 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
        trace_nfs4_close(state, &calldata->arg, &calldata->res, task->tk_status);
 
        /* Handle Layoutreturn errors */
-       if (pnfs_roc_done(task, calldata->inode,
-                               &calldata->arg.lr_args,
-                               &calldata->res.lr_res,
-                               &calldata->res.lr_ret) == -EAGAIN)
+       if (pnfs_roc_done(task, &calldata->arg.lr_args, &calldata->res.lr_res,
+                         &calldata->res.lr_ret) == -EAGAIN)
                goto out_restart;
 
        /* hmm. we are done with the inode, and in the process of freeing
@@ -6384,10 +6382,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
        trace_nfs4_delegreturn_exit(&data->args, &data->res, task->tk_status);
 
        /* Handle Layoutreturn errors */
-       if (pnfs_roc_done(task, data->inode,
-                               &data->args.lr_args,
-                               &data->res.lr_res,
-                               &data->res.lr_ret) == -EAGAIN)
+       if (pnfs_roc_done(task, &data->args.lr_args, &data->res.lr_res,
+                         &data->res.lr_ret) == -EAGAIN)
                goto out_restart;
 
        switch (task->tk_status) {
@@ -6441,10 +6437,10 @@ static void nfs4_delegreturn_release(void *calldata)
        struct nfs4_delegreturndata *data = calldata;
        struct inode *inode = data->inode;
 
+       if (data->lr.roc)
+               pnfs_roc_release(&data->lr.arg, &data->lr.res,
+                                data->res.lr_ret);
        if (inode) {
-               if (data->lr.roc)
-                       pnfs_roc_release(&data->lr.arg, &data->lr.res,
-                                       data->res.lr_ret);
                nfs_post_op_update_inode_force_wcc(inode, &data->fattr);
                nfs_iput_and_deactive(inode);
        }
@@ -6520,16 +6516,14 @@ static int _nfs4_proc_delegreturn(struct inode *inode, const struct cred *cred,
        nfs_fattr_init(data->res.fattr);
        data->timestamp = jiffies;
        data->rpc_status = 0;
-       data->lr.roc = pnfs_roc(inode, &data->lr.arg, &data->lr.res, cred);
        data->inode = nfs_igrab_and_active(inode);
-       if (data->inode) {
+       if (data->inode || issync) {
+               data->lr.roc = pnfs_roc(inode, &data->lr.arg, &data->lr.res,
+                                       cred);
                if (data->lr.roc) {
                        data->args.lr_args = &data->lr.arg;
                        data->res.lr_res = &data->lr.res;
                }
-       } else if (data->lr.roc) {
-               pnfs_roc_release(&data->lr.arg, &data->lr.res, 0);
-               data->lr.roc = false;
        }
 
        task_setup_data.callback_data = data;
@@ -7111,9 +7105,9 @@ static int _nfs4_do_setlk(struct nfs4_state *state, int cmd, struct file_lock *f
                                        data->arg.new_lock_owner, ret);
        } else
                data->cancelled = true;
+       trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
        rpc_put_task(task);
        dprintk("%s: done, ret = %d!\n", __func__, ret);
-       trace_nfs4_set_lock(fl, state, &data->res.stateid, cmd, ret);
        return ret;
 }
 
index 984cc42..d09bcfd 100644 (file)
@@ -227,7 +227,7 @@ int nfs4_try_get_tree(struct fs_context *fc)
                           fc, ctx->nfs_server.hostname,
                           ctx->nfs_server.export_path);
        if (err) {
-               nfs_errorf(fc, "NFS4: Couldn't follow remote path");
+               nfs_ferrorf(fc, MOUNT, "NFS4: Couldn't follow remote path");
                dfprintk(MOUNT, "<-- nfs4_try_get_tree() = %d [error]\n", err);
        } else {
                dfprintk(MOUNT, "<-- nfs4_try_get_tree() = 0\n");
@@ -250,7 +250,7 @@ int nfs4_get_referral_tree(struct fs_context *fc)
                            fc, ctx->nfs_server.hostname,
                            ctx->nfs_server.export_path);
        if (err) {
-               nfs_errorf(fc, "NFS4: Couldn't follow remote path");
+               nfs_ferrorf(fc, MOUNT, "NFS4: Couldn't follow remote path");
                dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = %d [error]\n", err);
        } else {
                dfprintk(MOUNT, "<-- nfs4_get_referral_tree() = 0\n");
index 07f59dc..4f274f2 100644 (file)
@@ -1152,7 +1152,7 @@ void pnfs_layoutreturn_free_lsegs(struct pnfs_layout_hdr *lo,
        LIST_HEAD(freeme);
 
        spin_lock(&inode->i_lock);
-       if (!pnfs_layout_is_valid(lo) || !arg_stateid ||
+       if (!pnfs_layout_is_valid(lo) ||
            !nfs4_stateid_match_other(&lo->plh_stateid, arg_stateid))
                goto out_unlock;
        if (stateid) {
@@ -1509,10 +1509,8 @@ out_noroc:
        return false;
 }
 
-int pnfs_roc_done(struct rpc_task *task, struct inode *inode,
-               struct nfs4_layoutreturn_args **argpp,
-               struct nfs4_layoutreturn_res **respp,
-               int *ret)
+int pnfs_roc_done(struct rpc_task *task, struct nfs4_layoutreturn_args **argpp,
+                 struct nfs4_layoutreturn_res **respp, int *ret)
 {
        struct nfs4_layoutreturn_args *arg = *argpp;
        int retval = -EAGAIN;
@@ -1545,7 +1543,7 @@ int pnfs_roc_done(struct rpc_task *task, struct inode *inode,
                return 0;
        case -NFS4ERR_OLD_STATEID:
                if (!nfs4_layout_refresh_old_stateid(&arg->stateid,
-                                       &arg->range, inode))
+                                                    &arg->range, arg->inode))
                        break;
                *ret = -NFS4ERR_NOMATCHING_LAYOUT;
                return -EAGAIN;
@@ -1560,23 +1558,28 @@ void pnfs_roc_release(struct nfs4_layoutreturn_args *args,
                int ret)
 {
        struct pnfs_layout_hdr *lo = args->layout;
-       const nfs4_stateid *arg_stateid = NULL;
+       struct inode *inode = args->inode;
        const nfs4_stateid *res_stateid = NULL;
        struct nfs4_xdr_opaque_data *ld_private = args->ld_private;
 
        switch (ret) {
        case -NFS4ERR_NOMATCHING_LAYOUT:
+               spin_lock(&inode->i_lock);
+               if (pnfs_layout_is_valid(lo) &&
+                   nfs4_stateid_match_other(&args->stateid, &lo->plh_stateid))
+                       pnfs_set_plh_return_info(lo, args->range.iomode, 0);
+               pnfs_clear_layoutreturn_waitbit(lo);
+               spin_unlock(&inode->i_lock);
                break;
        case 0:
                if (res->lrs_present)
                        res_stateid = &res->stateid;
                fallthrough;
        default:
-               arg_stateid = &args->stateid;
+               pnfs_layoutreturn_free_lsegs(lo, &args->stateid, &args->range,
+                                            res_stateid);
        }
        trace_nfs4_layoutreturn_on_close(args->inode, &args->stateid, ret);
-       pnfs_layoutreturn_free_lsegs(lo, arg_stateid, &args->range,
-                       res_stateid);
        if (ld_private && ld_private->ops && ld_private->ops->free)
                ld_private->ops->free(ld_private);
        pnfs_put_layout_hdr(lo);
@@ -2015,6 +2018,27 @@ lookup_again:
                goto lookup_again;
        }
 
+       /*
+        * Because we free lsegs when sending LAYOUTRETURN, we need to wait
+        * for LAYOUTRETURN.
+        */
+       if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
+               spin_unlock(&ino->i_lock);
+               dprintk("%s wait for layoutreturn\n", __func__);
+               lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo));
+               if (!IS_ERR(lseg)) {
+                       pnfs_put_layout_hdr(lo);
+                       dprintk("%s retrying\n", __func__);
+                       trace_pnfs_update_layout(ino, pos, count, iomode, lo,
+                                                lseg,
+                                                PNFS_UPDATE_LAYOUT_RETRY);
+                       goto lookup_again;
+               }
+               trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
+                                        PNFS_UPDATE_LAYOUT_RETURN);
+               goto out_put_layout_hdr;
+       }
+
        lseg = pnfs_find_lseg(lo, &arg, strict_iomode);
        if (lseg) {
                trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
@@ -2067,28 +2091,6 @@ lookup_again:
                nfs4_stateid_copy(&stateid, &lo->plh_stateid);
        }
 
-       /*
-        * Because we free lsegs before sending LAYOUTRETURN, we need to wait
-        * for LAYOUTRETURN even if first is true.
-        */
-       if (test_bit(NFS_LAYOUT_RETURN, &lo->plh_flags)) {
-               spin_unlock(&ino->i_lock);
-               dprintk("%s wait for layoutreturn\n", __func__);
-               lseg = ERR_PTR(pnfs_prepare_to_retry_layoutget(lo));
-               if (!IS_ERR(lseg)) {
-                       if (first)
-                               pnfs_clear_first_layoutget(lo);
-                       pnfs_put_layout_hdr(lo);
-                       dprintk("%s retrying\n", __func__);
-                       trace_pnfs_update_layout(ino, pos, count, iomode, lo,
-                                       lseg, PNFS_UPDATE_LAYOUT_RETRY);
-                       goto lookup_again;
-               }
-               trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
-                               PNFS_UPDATE_LAYOUT_RETURN);
-               goto out_put_layout_hdr;
-       }
-
        if (pnfs_layoutgets_blocked(lo)) {
                trace_pnfs_update_layout(ino, pos, count, iomode, lo, lseg,
                                PNFS_UPDATE_LAYOUT_BLOCKED);
@@ -2242,6 +2244,7 @@ static void _lgopen_prepare_attached(struct nfs4_opendata *data,
                                             &rng, GFP_KERNEL);
        if (!lgp) {
                pnfs_clear_first_layoutget(lo);
+               nfs_layoutget_end(lo);
                pnfs_put_layout_hdr(lo);
                return;
        }
index bbd3de1..d810ae6 100644 (file)
@@ -297,10 +297,8 @@ bool pnfs_roc(struct inode *ino,
                struct nfs4_layoutreturn_args *args,
                struct nfs4_layoutreturn_res *res,
                const struct cred *cred);
-int pnfs_roc_done(struct rpc_task *task, struct inode *inode,
-               struct nfs4_layoutreturn_args **argpp,
-               struct nfs4_layoutreturn_res **respp,
-               int *ret);
+int pnfs_roc_done(struct rpc_task *task, struct nfs4_layoutreturn_args **argpp,
+                 struct nfs4_layoutreturn_res **respp, int *ret);
 void pnfs_roc_release(struct nfs4_layoutreturn_args *args,
                struct nfs4_layoutreturn_res *res,
                int ret);
@@ -772,7 +770,7 @@ pnfs_roc(struct inode *ino,
 }
 
 static inline int
-pnfs_roc_done(struct rpc_task *task, struct inode *inode,
+pnfs_roc_done(struct rpc_task *task,
                struct nfs4_layoutreturn_args **argpp,
                struct nfs4_layoutreturn_res **respp,
                int *ret)
index 2efcfdd..49d3389 100644 (file)
@@ -78,22 +78,18 @@ void
 pnfs_generic_clear_request_commit(struct nfs_page *req,
                                  struct nfs_commit_info *cinfo)
 {
-       struct pnfs_layout_segment *freeme = NULL;
+       struct pnfs_commit_bucket *bucket = NULL;
 
        if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
                goto out;
        cinfo->ds->nwritten--;
-       if (list_is_singular(&req->wb_list)) {
-               struct pnfs_commit_bucket *bucket;
-
+       if (list_is_singular(&req->wb_list))
                bucket = list_first_entry(&req->wb_list,
-                                         struct pnfs_commit_bucket,
-                                         written);
-               freeme = pnfs_free_bucket_lseg(bucket);
-       }
+                                         struct pnfs_commit_bucket, written);
 out:
        nfs_request_remove_commit_list(req, cinfo);
-       pnfs_put_lseg(freeme);
+       if (bucket)
+               pnfs_put_lseg(pnfs_free_bucket_lseg(bucket));
 }
 EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit);
 
@@ -407,12 +403,16 @@ pnfs_bucket_get_committing(struct list_head *head,
                           struct pnfs_commit_bucket *bucket,
                           struct nfs_commit_info *cinfo)
 {
+       struct pnfs_layout_segment *lseg;
        struct list_head *pos;
 
        list_for_each(pos, &bucket->committing)
                cinfo->ds->ncommitting--;
        list_splice_init(&bucket->committing, head);
-       return pnfs_free_bucket_lseg(bucket);
+       lseg = pnfs_free_bucket_lseg(bucket);
+       if (!lseg)
+               lseg = pnfs_get_lseg(bucket->lseg);
+       return lseg;
 }
 
 static struct nfs_commit_data *
@@ -424,8 +424,6 @@ pnfs_bucket_fetch_commitdata(struct pnfs_commit_bucket *bucket,
        if (!data)
                return NULL;
        data->lseg = pnfs_bucket_get_committing(&data->pages, bucket, cinfo);
-       if (!data->lseg)
-               data->lseg = pnfs_get_lseg(bucket->lseg);
        return data;
 }
 
index 821db21..34b8802 100644 (file)
@@ -865,9 +865,14 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
        if (isdotent(name, namlen)) {
                if (namlen == 2) {
                        dchild = dget_parent(dparent);
-                       /* filesystem root - cannot return filehandle for ".." */
+                       /*
+                        * Don't return filehandle for ".." if we're at
+                        * the filesystem or export root:
+                        */
                        if (dchild == dparent)
                                goto out;
+                       if (dparent == exp->ex_path.dentry)
+                               goto out;
                } else
                        dchild = dget(dparent);
        } else
index 4727b7f..8d6d267 100644 (file)
 #include "pnfs.h"
 #include "trace.h"
 
+static bool inter_copy_offload_enable;
+module_param(inter_copy_offload_enable, bool, 0644);
+MODULE_PARM_DESC(inter_copy_offload_enable,
+                "Enable inter server to server copy offload. Default: false");
+
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
 #include <linux/security.h>
 
index 45ee6b1..eaaa160 100644 (file)
@@ -147,6 +147,25 @@ svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
        return p;
 }
 
+static void *
+svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, u32 len)
+{
+       __be32 *tmp;
+
+       /*
+        * The location of the decoded data item is stable,
+        * so @p is OK to use. This is the common case.
+        */
+       if (p != argp->xdr->scratch.iov_base)
+               return p;
+
+       tmp = svcxdr_tmpalloc(argp, len);
+       if (!tmp)
+               return NULL;
+       memcpy(tmp, p, len);
+       return tmp;
+}
+
 /*
  * NFSv4 basic data type decoders
  */
@@ -183,11 +202,10 @@ nfsd4_decode_opaque(struct nfsd4_compoundargs *argp, struct xdr_netobj *o)
        p = xdr_inline_decode(argp->xdr, len);
        if (!p)
                return nfserr_bad_xdr;
-       o->data = svcxdr_tmpalloc(argp, len);
+       o->data = svcxdr_savemem(argp, p, len);
        if (!o->data)
                return nfserr_jukebox;
        o->len = len;
-       memcpy(o->data, p, len);
 
        return nfs_ok;
 }
@@ -205,10 +223,9 @@ nfsd4_decode_component4(struct nfsd4_compoundargs *argp, char **namp, u32 *lenp)
        status = check_filename((char *)p, *lenp);
        if (status)
                return status;
-       *namp = svcxdr_tmpalloc(argp, *lenp);
+       *namp = svcxdr_savemem(argp, p, *lenp);
        if (!*namp)
                return nfserr_jukebox;
-       memcpy(*namp, p, *lenp);
 
        return nfs_ok;
 }
@@ -1200,10 +1217,9 @@ nfsd4_decode_putfh(struct nfsd4_compoundargs *argp, struct nfsd4_putfh *putfh)
        p = xdr_inline_decode(argp->xdr, putfh->pf_fhlen);
        if (!p)
                return nfserr_bad_xdr;
-       putfh->pf_fhval = svcxdr_tmpalloc(argp, putfh->pf_fhlen);
+       putfh->pf_fhval = svcxdr_savemem(argp, p, putfh->pf_fhlen);
        if (!putfh->pf_fhval)
                return nfserr_jukebox;
-       memcpy(putfh->pf_fhval, p, putfh->pf_fhlen);
 
        return nfs_ok;
 }
@@ -1318,24 +1334,20 @@ nfsd4_decode_setclientid(struct nfsd4_compoundargs *argp, struct nfsd4_setclient
        p = xdr_inline_decode(argp->xdr, setclientid->se_callback_netid_len);
        if (!p)
                return nfserr_bad_xdr;
-       setclientid->se_callback_netid_val = svcxdr_tmpalloc(argp,
+       setclientid->se_callback_netid_val = svcxdr_savemem(argp, p,
                                                setclientid->se_callback_netid_len);
        if (!setclientid->se_callback_netid_val)
                return nfserr_jukebox;
-       memcpy(setclientid->se_callback_netid_val, p,
-              setclientid->se_callback_netid_len);
 
        if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_addr_len) < 0)
                return nfserr_bad_xdr;
        p = xdr_inline_decode(argp->xdr, setclientid->se_callback_addr_len);
        if (!p)
                return nfserr_bad_xdr;
-       setclientid->se_callback_addr_val = svcxdr_tmpalloc(argp,
+       setclientid->se_callback_addr_val = svcxdr_savemem(argp, p,
                                                setclientid->se_callback_addr_len);
        if (!setclientid->se_callback_addr_val)
                return nfserr_jukebox;
-       memcpy(setclientid->se_callback_addr_val, p,
-              setclientid->se_callback_addr_len);
        if (xdr_stream_decode_u32(argp->xdr, &setclientid->se_callback_ident) < 0)
                return nfserr_bad_xdr;
 
@@ -1375,10 +1387,9 @@ nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify
        p = xdr_inline_decode(argp->xdr, verify->ve_attrlen);
        if (!p)
                return nfserr_bad_xdr;
-       verify->ve_attrval = svcxdr_tmpalloc(argp, verify->ve_attrlen);
+       verify->ve_attrval = svcxdr_savemem(argp, p, verify->ve_attrlen);
        if (!verify->ve_attrval)
                return nfserr_jukebox;
-       memcpy(verify->ve_attrval, p, verify->ve_attrlen);
 
        return nfs_ok;
 }
@@ -2333,10 +2344,9 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                p = xdr_inline_decode(argp->xdr, argp->taglen);
                if (!p)
                        return 0;
-               argp->tag = svcxdr_tmpalloc(argp, argp->taglen);
+               argp->tag = svcxdr_savemem(argp, p, argp->taglen);
                if (!argp->tag)
                        return 0;
-               memcpy(argp->tag, p, argp->taglen);
                max_reply += xdr_align_size(argp->taglen);
        }
 
@@ -4756,6 +4766,7 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
                            resp->rqstp->rq_vec, read->rd_vlen, maxcount, eof);
        if (nfserr)
                return nfserr;
+       xdr_truncate_encode(xdr, starting_len + 16 + xdr_align_size(*maxcount));
 
        tmp = htonl(NFS4_CONTENT_DATA);
        write_bytes_to_xdr_buf(xdr->buf, starting_len,      &tmp,   4);
@@ -4763,6 +4774,10 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp,
        write_bytes_to_xdr_buf(xdr->buf, starting_len + 4,  &tmp64, 8);
        tmp = htonl(*maxcount);
        write_bytes_to_xdr_buf(xdr->buf, starting_len + 12, &tmp,   4);
+
+       tmp = xdr_zero;
+       write_bytes_to_xdr_buf(xdr->buf, starting_len + 16 + *maxcount, &tmp,
+                              xdr_pad_size(*maxcount));
        return nfs_ok;
 }
 
@@ -4855,14 +4870,15 @@ out:
        if (nfserr && segments == 0)
                xdr_truncate_encode(xdr, starting_len);
        else {
-               tmp = htonl(eof);
-               write_bytes_to_xdr_buf(xdr->buf, starting_len,     &tmp, 4);
-               tmp = htonl(segments);
-               write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
                if (nfserr) {
                        xdr_truncate_encode(xdr, last_segment);
                        nfserr = nfs_ok;
+                       eof = 0;
                }
+               tmp = htonl(eof);
+               write_bytes_to_xdr_buf(xdr->buf, starting_len,     &tmp, 4);
+               tmp = htonl(segments);
+               write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4);
        }
 
        return nfserr;
index 00384c3..f9c9f4c 100644 (file)
 
 #define NFSDDBG_FACILITY       NFSDDBG_SVC
 
-bool inter_copy_offload_enable;
-EXPORT_SYMBOL_GPL(inter_copy_offload_enable);
-module_param(inter_copy_offload_enable, bool, 0644);
-MODULE_PARM_DESC(inter_copy_offload_enable,
-                "Enable inter server to server copy offload. Default: false");
-
 extern struct svc_program      nfsd_program;
 static int                     nfsd(void *vrqstp);
 #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
index a60ff5c..c300885 100644 (file)
@@ -568,7 +568,6 @@ struct nfsd4_copy {
        struct nfs_fh           c_fh;
        nfs4_stateid            stateid;
 };
-extern bool inter_copy_offload_enable;
 
 struct nfsd4_seek {
        /* request */
index 3e01d8f..dcab112 100644 (file)
@@ -1285,26 +1285,23 @@ fput_and_out:
        return ret;
 }
 
+#ifndef CONFIG_ARCH_SPLIT_ARG64
 SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags,
                              __u64, mask, int, dfd,
                              const char  __user *, pathname)
 {
        return do_fanotify_mark(fanotify_fd, flags, mask, dfd, pathname);
 }
+#endif
 
-#ifdef CONFIG_COMPAT
-COMPAT_SYSCALL_DEFINE6(fanotify_mark,
+#if defined(CONFIG_ARCH_SPLIT_ARG64) || defined(CONFIG_COMPAT)
+SYSCALL32_DEFINE6(fanotify_mark,
                                int, fanotify_fd, unsigned int, flags,
-                               __u32, mask0, __u32, mask1, int, dfd,
+                               SC_ARG64(mask), int, dfd,
                                const char  __user *, pathname)
 {
-       return do_fanotify_mark(fanotify_fd, flags,
-#ifdef __BIG_ENDIAN
-                               ((__u64)mask0 << 32) | mask1,
-#else
-                               ((__u64)mask1 << 32) | mask0,
-#endif
-                                dfd, pathname);
+       return do_fanotify_mark(fanotify_fd, flags, SC_VAL64(__u64, mask),
+                               dfd, pathname);
 }
 #endif
 
index c5989cf..39c9684 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1206,6 +1206,7 @@ const struct file_operations pipefifo_fops = {
        .unlocked_ioctl = pipe_ioctl,
        .release        = pipe_release,
        .fasync         = pipe_fasync,
+       .splice_write   = iter_file_splice_write,
 };
 
 /*
index 3178992..d2018f7 100644 (file)
@@ -1770,6 +1770,12 @@ static int process_sysctl_arg(char *param, char *val,
                        return 0;
        }
 
+       if (!val)
+               return -EINVAL;
+       len = strlen(val);
+       if (len == 0)
+               return -EINVAL;
+
        /*
         * To set sysctl options, we use a temporary mount of proc, look up the
         * respective sys/ file and write to it. To avoid mounting it when no
@@ -1811,7 +1817,6 @@ static int process_sysctl_arg(char *param, char *val,
                                file, param, val);
                goto out;
        }
-       len = strlen(val);
        wret = kernel_write(file, val, len, &pos);
        if (wret < 0) {
                err = wret;
index ee5a235..602e3a5 100644 (file)
@@ -1035,6 +1035,25 @@ struct clear_refs_private {
 };
 
 #ifdef CONFIG_MEM_SOFT_DIRTY
+
+#define is_cow_mapping(flags) (((flags) & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE)
+
+static inline bool pte_is_pinned(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
+{
+       struct page *page;
+
+       if (!pte_write(pte))
+               return false;
+       if (!is_cow_mapping(vma->vm_flags))
+               return false;
+       if (likely(!atomic_read(&vma->vm_mm->has_pinned)))
+               return false;
+       page = vm_normal_page(vma, addr, pte);
+       if (!page)
+               return false;
+       return page_maybe_dma_pinned(page);
+}
+
 static inline void clear_soft_dirty(struct vm_area_struct *vma,
                unsigned long addr, pte_t *pte)
 {
@@ -1049,6 +1068,8 @@ static inline void clear_soft_dirty(struct vm_area_struct *vma,
        if (pte_present(ptent)) {
                pte_t old_pte;
 
+               if (pte_is_pinned(vma, addr, ptent))
+                       return;
                old_pte = ptep_modify_prot_start(vma, addr, pte);
                ptent = pte_wrprotect(old_pte);
                ptent = pte_clear_soft_dirty(ptent);
@@ -1215,41 +1236,26 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
                        .type = type,
                };
 
+               if (mmap_write_lock_killable(mm)) {
+                       count = -EINTR;
+                       goto out_mm;
+               }
                if (type == CLEAR_REFS_MM_HIWATER_RSS) {
-                       if (mmap_write_lock_killable(mm)) {
-                               count = -EINTR;
-                               goto out_mm;
-                       }
-
                        /*
                         * Writing 5 to /proc/pid/clear_refs resets the peak
                         * resident set size to this mm's current rss value.
                         */
                        reset_mm_hiwater_rss(mm);
-                       mmap_write_unlock(mm);
-                       goto out_mm;
+                       goto out_unlock;
                }
 
-               if (mmap_read_lock_killable(mm)) {
-                       count = -EINTR;
-                       goto out_mm;
-               }
                tlb_gather_mmu(&tlb, mm, 0, -1);
                if (type == CLEAR_REFS_SOFT_DIRTY) {
                        for (vma = mm->mmap; vma; vma = vma->vm_next) {
                                if (!(vma->vm_flags & VM_SOFTDIRTY))
                                        continue;
-                               mmap_read_unlock(mm);
-                               if (mmap_write_lock_killable(mm)) {
-                                       count = -EINTR;
-                                       goto out_mm;
-                               }
-                               for (vma = mm->mmap; vma; vma = vma->vm_next) {
-                                       vma->vm_flags &= ~VM_SOFTDIRTY;
-                                       vma_set_page_prot(vma);
-                               }
-                               mmap_write_downgrade(mm);
-                               break;
+                               vma->vm_flags &= ~VM_SOFTDIRTY;
+                               vma_set_page_prot(vma);
                        }
 
                        mmu_notifier_range_init(&range, MMU_NOTIFY_SOFT_DIRTY,
@@ -1261,7 +1267,8 @@ static ssize_t clear_refs_write(struct file *file, const char __user *buf,
                if (type == CLEAR_REFS_SOFT_DIRTY)
                        mmu_notifier_invalidate_range_end(&range);
                tlb_finish_mmu(&tlb, 0, -1);
-               mmap_read_unlock(mm);
+out_unlock:
+               mmap_write_unlock(mm);
 out_mm:
                mmput(mm);
        }
index ebfebdf..37aaa83 100644 (file)
@@ -1011,14 +1011,17 @@ static int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds,
        fdcount = do_poll(head, &table, end_time);
        poll_freewait(&table);
 
+       if (!user_write_access_begin(ufds, nfds * sizeof(*ufds)))
+               goto out_fds;
+
        for (walk = head; walk; walk = walk->next) {
                struct pollfd *fds = walk->entries;
                int j;
 
-               for (j = 0; j < walk->len; j++, ufds++)
-                       if (__put_user(fds[j].revents, &ufds->revents))
-                               goto out_fds;
+               for (j = walk->len; j; fds++, ufds++, j--)
+                       unsafe_put_user(fds->revents, &ufds->revents, Efault);
        }
+       user_write_access_end();
 
        err = fdcount;
 out_fds:
@@ -1030,6 +1033,11 @@ out_fds:
        }
 
        return err;
+
+Efault:
+       user_write_access_end();
+       err = -EFAULT;
+       goto out_fds;
 }
 
 static long do_restart_poll(struct restart_block *restart_block)
index 5bef3a6..d0df217 100644 (file)
@@ -705,6 +705,7 @@ static int udf_check_vsd(struct super_block *sb)
        struct buffer_head *bh = NULL;
        int nsr = 0;
        struct udf_sb_info *sbi;
+       loff_t session_offset;
 
        sbi = UDF_SB(sb);
        if (sb->s_blocksize < sizeof(struct volStructDesc))
@@ -712,7 +713,8 @@ static int udf_check_vsd(struct super_block *sb)
        else
                sectorsize = sb->s_blocksize;
 
-       sector += (((loff_t)sbi->s_session) << sb->s_blocksize_bits);
+       session_offset = (loff_t)sbi->s_session << sb->s_blocksize_bits;
+       sector += session_offset;
 
        udf_debug("Starting at sector %u (%lu byte sectors)\n",
                  (unsigned int)(sector >> sb->s_blocksize_bits),
@@ -757,8 +759,7 @@ static int udf_check_vsd(struct super_block *sb)
 
        if (nsr > 0)
                return 1;
-       else if (!bh && sector - (sbi->s_session << sb->s_blocksize_bits) ==
-                       VSD_FIRST_SECTOR_OFFSET)
+       else if (!bh && sector - session_offset == VSD_FIRST_SECTOR_OFFSET)
                return -1;
        else
                return 0;
index ef2697b..827278f 100644 (file)
@@ -3,6 +3,7 @@ config ZONEFS_FS
        depends on BLOCK
        depends on BLK_DEV_ZONED
        select FS_IOMAP
+       select CRC32
        help
          zonefs is a simple file system which exposes zones of a zoned block
          device (e.g. host-managed or host-aware SMR disk drives) as files.
index 4365b9a..267f6df 100644 (file)
@@ -34,6 +34,7 @@ mandatory-y += kmap_size.h
 mandatory-y += kprobes.h
 mandatory-y += linkage.h
 mandatory-y += local.h
+mandatory-y += local64.h
 mandatory-y += mm-arch-hooks.h
 mandatory-y += mmiowb.h
 mandatory-y += mmu.h
index dd90c97..0e7316a 100644 (file)
  * See Documentation/atomic_bitops.txt for details.
  */
 
-static inline void set_bit(unsigned int nr, volatile unsigned long *p)
+static __always_inline void set_bit(unsigned int nr, volatile unsigned long *p)
 {
        p += BIT_WORD(nr);
        atomic_long_or(BIT_MASK(nr), (atomic_long_t *)p);
 }
 
-static inline void clear_bit(unsigned int nr, volatile unsigned long *p)
+static __always_inline void clear_bit(unsigned int nr, volatile unsigned long *p)
 {
        p += BIT_WORD(nr);
        atomic_long_andnot(BIT_MASK(nr), (atomic_long_t *)p);
 }
 
-static inline void change_bit(unsigned int nr, volatile unsigned long *p)
+static __always_inline void change_bit(unsigned int nr, volatile unsigned long *p)
 {
        p += BIT_WORD(nr);
        atomic_long_xor(BIT_MASK(nr), (atomic_long_t *)p);
index 3c3e16c..dc605c4 100644 (file)
@@ -2,9 +2,8 @@
 #ifndef __DT_APQ8016_LPASS_H
 #define __DT_APQ8016_LPASS_H
 
-#define MI2S_PRIMARY   0
-#define MI2S_SECONDARY 1
-#define MI2S_TERTIARY  2
-#define MI2S_QUATERNARY        3
+#include <dt-bindings/sound/qcom,lpass.h>
+
+/* NOTE: Use qcom,lpass.h to define any AIF ID's for LPASS */
 
 #endif /* __DT_APQ8016_LPASS_H */
diff --git a/include/dt-bindings/sound/qcom,lpass.h b/include/dt-bindings/sound/qcom,lpass.h
new file mode 100644 (file)
index 0000000..7b0b80b
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_QCOM_LPASS_H
+#define __DT_QCOM_LPASS_H
+
+#define MI2S_PRIMARY   0
+#define MI2S_SECONDARY 1
+#define MI2S_TERTIARY  2
+#define MI2S_QUATERNARY        3
+#define MI2S_QUINARY   4
+
+#define LPASS_DP_RX    5
+
+#define LPASS_MCLK0    0
+
+#endif /* __DT_QCOM_LPASS_H */
index 56ecaaf..5c1ee8b 100644 (file)
@@ -2,10 +2,8 @@
 #ifndef __DT_SC7180_LPASS_H
 #define __DT_SC7180_LPASS_H
 
-#define MI2S_PRIMARY   0
-#define MI2S_SECONDARY 1
-#define LPASS_DP_RX    2
+#include <dt-bindings/sound/qcom,lpass.h>
 
-#define LPASS_MCLK0    0
+/* NOTE: Use qcom,lpass.h to define any AIF ID's for LPASS */
 
 #endif /* __DT_APQ8016_LPASS_H */
index fc85f50..8dcb3e1 100644 (file)
@@ -13,7 +13,7 @@
 #define ARMV8_PMU_CYCLE_IDX            (ARMV8_PMU_MAX_COUNTERS - 1)
 #define ARMV8_PMU_MAX_COUNTER_PAIRS    ((ARMV8_PMU_MAX_COUNTERS + 1) >> 1)
 
-#ifdef CONFIG_KVM_ARM_PMU
+#ifdef CONFIG_HW_PERF_EVENTS
 
 struct kvm_pmc {
        u8 idx; /* index into the pmu->pmc array */
index 2630c2e..053bf05 100644 (file)
@@ -885,6 +885,13 @@ static inline int acpi_device_modalias(struct device *dev,
        return -ENODEV;
 }
 
+static inline struct platform_device *
+acpi_create_platform_device(struct acpi_device *adev,
+                           struct property_entry *properties)
+{
+       return NULL;
+}
+
 static inline bool acpi_dma_supported(struct acpi_device *adev)
 {
        return false;
index 47b0219..d705b17 100644 (file)
@@ -447,8 +447,8 @@ enum {
        BLK_MQ_REQ_NOWAIT       = (__force blk_mq_req_flags_t)(1 << 0),
        /* allocate from reserved pool */
        BLK_MQ_REQ_RESERVED     = (__force blk_mq_req_flags_t)(1 << 1),
-       /* set RQF_PREEMPT */
-       BLK_MQ_REQ_PREEMPT      = (__force blk_mq_req_flags_t)(1 << 3),
+       /* set RQF_PM */
+       BLK_MQ_REQ_PM           = (__force blk_mq_req_flags_t)(1 << 2),
 };
 
 struct request *blk_mq_alloc_request(struct request_queue *q, unsigned int op,
index 070de09..f94ee30 100644 (file)
@@ -79,9 +79,6 @@ typedef __u32 __bitwise req_flags_t;
 #define RQF_MQ_INFLIGHT                ((__force req_flags_t)(1 << 6))
 /* don't call prep for this one */
 #define RQF_DONTPREP           ((__force req_flags_t)(1 << 7))
-/* set for "ide_preempt" requests and also for requests for which the SCSI
-   "quiesce" state must be ignored. */
-#define RQF_PREEMPT            ((__force req_flags_t)(1 << 8))
 /* vaguely specified driver internal error.  Ignored by the block layer */
 #define RQF_FAILED             ((__force req_flags_t)(1 << 10))
 /* don't warn about errors */
@@ -430,8 +427,7 @@ struct request_queue {
        unsigned long           queue_flags;
        /*
         * Number of contexts that have called blk_set_pm_only(). If this
-        * counter is above zero then only RQF_PM and RQF_PREEMPT requests are
-        * processed.
+        * counter is above zero then only RQF_PM requests are processed.
         */
        atomic_t                pm_only;
 
@@ -696,6 +692,18 @@ static inline bool queue_is_mq(struct request_queue *q)
        return q->mq_ops;
 }
 
+#ifdef CONFIG_PM
+static inline enum rpm_status queue_rpm_status(struct request_queue *q)
+{
+       return q->rpm_status;
+}
+#else
+static inline enum rpm_status queue_rpm_status(struct request_queue *q)
+{
+       return RPM_ACTIVE;
+}
+#endif
+
 static inline enum blk_zoned_model
 blk_queue_zoned_model(struct request_queue *q)
 {
index 07cb5d1..1aac2af 100644 (file)
@@ -761,9 +761,15 @@ struct bpf_ctx_arg_aux {
        u32 btf_id;
 };
 
+struct btf_mod_pair {
+       struct btf *btf;
+       struct module *module;
+};
+
 struct bpf_prog_aux {
        atomic64_t refcnt;
        u32 used_map_cnt;
+       u32 used_btf_cnt;
        u32 max_ctx_offset;
        u32 max_pkt_offset;
        u32 max_tp_access;
@@ -802,6 +808,7 @@ struct bpf_prog_aux {
        const struct bpf_prog_ops *ops;
        struct bpf_map **used_maps;
        struct mutex used_maps_mutex; /* mutex for used_maps and used_map_cnt */
+       struct btf_mod_pair *used_btfs;
        struct bpf_prog *prog;
        struct user_struct *user;
        u64 load_time; /* ns since boottime */
@@ -1206,8 +1213,6 @@ void bpf_prog_sub(struct bpf_prog *prog, int i);
 void bpf_prog_inc(struct bpf_prog *prog);
 struct bpf_prog * __must_check bpf_prog_inc_not_zero(struct bpf_prog *prog);
 void bpf_prog_put(struct bpf_prog *prog);
-void __bpf_free_used_maps(struct bpf_prog_aux *aux,
-                         struct bpf_map **used_maps, u32 len);
 
 void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock);
 void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock);
@@ -1403,7 +1408,10 @@ static inline void bpf_long_memcpy(void *dst, const void *src, u32 size)
 /* verify correctness of eBPF program */
 int bpf_check(struct bpf_prog **fp, union bpf_attr *attr,
              union bpf_attr __user *uattr);
+
+#ifndef CONFIG_BPF_JIT_ALWAYS_ON
 void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
+#endif
 
 struct btf *bpf_get_btf_vmlinux(void);
 
@@ -1667,12 +1675,18 @@ bpf_base_func_proto(enum bpf_func_id func_id)
 }
 #endif /* CONFIG_BPF_SYSCALL */
 
+void __bpf_free_used_btfs(struct bpf_prog_aux *aux,
+                         struct btf_mod_pair *used_btfs, u32 len);
+
 static inline struct bpf_prog *bpf_prog_get_type(u32 ufd,
                                                 enum bpf_prog_type type)
 {
        return bpf_prog_get_type_dev(ufd, type, false);
 }
 
+void __bpf_free_used_maps(struct bpf_prog_aux *aux,
+                         struct bpf_map **used_maps, u32 len);
+
 bool bpf_prog_get_ok(struct bpf_prog *, enum bpf_prog_type *, bool);
 
 int bpf_prog_offload_compile(struct bpf_prog *prog);
index e941fe1..dfe6f85 100644 (file)
@@ -340,6 +340,7 @@ struct bpf_insn_aux_data {
 };
 
 #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
+#define MAX_USED_BTFS 64 /* max number of BTFs accessed by one BPF program */
 
 #define BPF_VERIFIER_TMP_LOG_SIZE      1024
 
@@ -398,7 +399,9 @@ struct bpf_verifier_env {
        struct bpf_verifier_state_list **explored_states; /* search pruning optimization */
        struct bpf_verifier_state_list *free_list;
        struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */
+       struct btf_mod_pair used_btfs[MAX_USED_BTFS]; /* array of BTF's used by BPF program */
        u32 used_map_cnt;               /* number of used maps */
+       u32 used_btf_cnt;               /* number of used BTF objects */
        u32 id_gen;                     /* used to generate unique reg IDs */
        bool allow_ptr_leaks;
        bool allow_ptr_to_map_access;
index d0bd226..de9430d 100644 (file)
@@ -31,6 +31,7 @@
 #define PHY_ID_BCM89610                        0x03625cd0
 
 #define PHY_ID_BCM72113                        0x35905310
+#define PHY_ID_BCM72116                        0x35905350
 #define PHY_ID_BCM7250                 0xae025280
 #define PHY_ID_BCM7255                 0xae025120
 #define PHY_ID_BCM7260                 0xae025190
index 4c200f5..7fabf14 100644 (file)
@@ -91,6 +91,9 @@ int btf_type_snprintf_show(const struct btf *btf, u32 type_id, void *obj,
 int btf_get_fd_by_id(u32 id);
 u32 btf_obj_id(const struct btf *btf);
 bool btf_is_kernel(const struct btf *btf);
+bool btf_is_module(const struct btf *btf);
+struct module *btf_try_get_module(const struct btf *btf);
+u32 btf_nr_types(const struct btf *btf);
 bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
                           const struct btf_member *m,
                           u32 expected_offset, u32 expected_size);
index 7bb66e1..e3a0be2 100644 (file)
@@ -77,9 +77,4 @@
 #define static_assert(expr, ...) __static_assert(expr, ##__VA_ARGS__, #expr)
 #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
 
-#ifdef __GENKSYMS__
-/* genksyms gets confused by _Static_assert */
-#define _Static_assert(expr, ...)
-#endif
-
 #endif /* _LINUX_BUILD_BUG_H */
diff --git a/include/linux/buildid.h b/include/linux/buildid.h
new file mode 100644 (file)
index 0000000..40232f9
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BUILDID_H
+#define _LINUX_BUILDID_H
+
+#include <linux/mm_types.h>
+
+#define BUILD_ID_SIZE_MAX 20
+
+int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
+                  __u32 *size);
+
+#endif
diff --git a/include/linux/can/bittiming.h b/include/linux/can/bittiming.h
new file mode 100644 (file)
index 0000000..707575c
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
+ */
+
+#ifndef _CAN_BITTIMING_H
+#define _CAN_BITTIMING_H
+
+#include <linux/netdevice.h>
+#include <linux/can/netlink.h>
+
+#define CAN_SYNC_SEG 1
+
+#ifdef CONFIG_CAN_CALC_BITTIMING
+int can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
+                      const struct can_bittiming_const *btc);
+#else /* !CONFIG_CAN_CALC_BITTIMING */
+static inline int
+can_calc_bittiming(struct net_device *dev, struct can_bittiming *bt,
+                  const struct can_bittiming_const *btc)
+{
+       netdev_err(dev, "bit-timing calculation not available\n");
+       return -EINVAL;
+}
+#endif /* CONFIG_CAN_CALC_BITTIMING */
+
+int can_get_bittiming(struct net_device *dev, struct can_bittiming *bt,
+                     const struct can_bittiming_const *btc,
+                     const u32 *bitrate_const,
+                     const unsigned int bitrate_const_cnt);
+
+/*
+ * can_bit_time() - Duration of one bit
+ *
+ * Please refer to ISO 11898-1:2015, section 11.3.1.1 "Bit time" for
+ * additional information.
+ *
+ * Return: the number of time quanta in one bit.
+ */
+static inline unsigned int can_bit_time(const struct can_bittiming *bt)
+{
+       return CAN_SYNC_SEG + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
+}
+
+#endif /* !_CAN_BITTIMING_H */
index 197a795..ac4d83a 100644 (file)
 #define _CAN_DEV_H
 
 #include <linux/can.h>
+#include <linux/can/bittiming.h>
 #include <linux/can/error.h>
 #include <linux/can/led.h>
+#include <linux/can/length.h>
 #include <linux/can/netlink.h>
 #include <linux/can/skb.h>
 #include <linux/netdevice.h>
@@ -82,118 +84,6 @@ struct can_priv {
 #endif
 };
 
-#define CAN_SYNC_SEG 1
-
-/*
- * can_bit_time() - Duration of one bit
- *
- * Please refer to ISO 11898-1:2015, section 11.3.1.1 "Bit time" for
- * additional information.
- *
- * Return: the number of time quanta in one bit.
- */
-static inline unsigned int can_bit_time(const struct can_bittiming *bt)
-{
-       return CAN_SYNC_SEG + bt->prop_seg + bt->phase_seg1 + bt->phase_seg2;
-}
-
-/*
- * can_cc_dlc2len(value) - convert a given data length code (dlc) of a
- * Classical CAN frame into a valid data length of max. 8 bytes.
- *
- * To be used in the CAN netdriver receive path to ensure conformance with
- * ISO 11898-1 Chapter 8.4.2.3 (DLC field)
- */
-#define can_cc_dlc2len(dlc)    (min_t(u8, (dlc), CAN_MAX_DLEN))
-
-/* Check for outgoing skbs that have not been created by the CAN subsystem */
-static inline bool can_skb_headroom_valid(struct net_device *dev,
-                                         struct sk_buff *skb)
-{
-       /* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */
-       if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv)))
-               return false;
-
-       /* af_packet does not apply CAN skb specific settings */
-       if (skb->ip_summed == CHECKSUM_NONE) {
-               /* init headroom */
-               can_skb_prv(skb)->ifindex = dev->ifindex;
-               can_skb_prv(skb)->skbcnt = 0;
-
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
-
-               /* perform proper loopback on capable devices */
-               if (dev->flags & IFF_ECHO)
-                       skb->pkt_type = PACKET_LOOPBACK;
-               else
-                       skb->pkt_type = PACKET_HOST;
-
-               skb_reset_mac_header(skb);
-               skb_reset_network_header(skb);
-               skb_reset_transport_header(skb);
-       }
-
-       return true;
-}
-
-/* Drop a given socketbuffer if it does not contain a valid CAN frame. */
-static inline bool can_dropped_invalid_skb(struct net_device *dev,
-                                         struct sk_buff *skb)
-{
-       const struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
-
-       if (skb->protocol == htons(ETH_P_CAN)) {
-               if (unlikely(skb->len != CAN_MTU ||
-                            cfd->len > CAN_MAX_DLEN))
-                       goto inval_skb;
-       } else if (skb->protocol == htons(ETH_P_CANFD)) {
-               if (unlikely(skb->len != CANFD_MTU ||
-                            cfd->len > CANFD_MAX_DLEN))
-                       goto inval_skb;
-       } else
-               goto inval_skb;
-
-       if (!can_skb_headroom_valid(dev, skb))
-               goto inval_skb;
-
-       return false;
-
-inval_skb:
-       kfree_skb(skb);
-       dev->stats.tx_dropped++;
-       return true;
-}
-
-static inline bool can_is_canfd_skb(const struct sk_buff *skb)
-{
-       /* the CAN specific type of skb is identified by its data length */
-       return skb->len == CANFD_MTU;
-}
-
-/* helper to get the data length code (DLC) for Classical CAN raw DLC access */
-static inline u8 can_get_cc_dlc(const struct can_frame *cf, const u32 ctrlmode)
-{
-       /* return len8_dlc as dlc value only if all conditions apply */
-       if ((ctrlmode & CAN_CTRLMODE_CC_LEN8_DLC) &&
-           (cf->len == CAN_MAX_DLEN) &&
-           (cf->len8_dlc > CAN_MAX_DLEN && cf->len8_dlc <= CAN_MAX_RAW_DLC))
-               return cf->len8_dlc;
-
-       /* return the payload length as dlc value */
-       return cf->len;
-}
-
-/* helper to set len and len8_dlc value for Classical CAN raw DLC access */
-static inline void can_frame_set_cc_len(struct can_frame *cf, const u8 dlc,
-                                       const u32 ctrlmode)
-{
-       /* the caller already ensured that dlc is a value from 0 .. 15 */
-       if (ctrlmode & CAN_CTRLMODE_CC_LEN8_DLC && dlc > CAN_MAX_DLEN)
-               cf->len8_dlc = dlc;
-
-       /* limit the payload length 'len' to CAN_MAX_DLEN */
-       cf->len = can_cc_dlc2len(dlc);
-}
 
 /* helper to define static CAN controller features at device creation time */
 static inline void can_set_static_ctrlmode(struct net_device *dev,
@@ -210,11 +100,7 @@ static inline void can_set_static_ctrlmode(struct net_device *dev,
                dev->mtu = CANFD_MTU;
 }
 
-/* get data length from raw data length code (DLC) */
-u8 can_fd_dlc2len(u8 dlc);
-
-/* map the sanitized data length to an appropriate data length code */
-u8 can_fd_len2dlc(u8 len);
+void can_setup(struct net_device *dev);
 
 struct net_device *alloc_candev_mqs(int sizeof_priv, unsigned int echo_skb_max,
                                    unsigned int txqs, unsigned int rxqs);
@@ -237,26 +123,18 @@ void unregister_candev(struct net_device *dev);
 int can_restart_now(struct net_device *dev);
 void can_bus_off(struct net_device *dev);
 
+const char *can_get_state_str(const enum can_state state);
 void can_change_state(struct net_device *dev, struct can_frame *cf,
                      enum can_state tx_state, enum can_state rx_state);
 
-int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
-                    unsigned int idx);
-struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx,
-                                  u8 *len_ptr);
-unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx);
-void can_free_echo_skb(struct net_device *dev, unsigned int idx);
-
 #ifdef CONFIG_OF
 void of_can_transceiver(struct net_device *dev);
 #else
 static inline void of_can_transceiver(struct net_device *dev) { }
 #endif
 
-struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf);
-struct sk_buff *alloc_canfd_skb(struct net_device *dev,
-                               struct canfd_frame **cfd);
-struct sk_buff *alloc_can_err_skb(struct net_device *dev,
-                                 struct can_frame **cf);
+extern struct rtnl_link_ops can_link_ops;
+int can_netlink_register(void);
+void can_netlink_unregister(void);
 
 #endif /* !_CAN_DEV_H */
diff --git a/include/linux/can/length.h b/include/linux/can/length.h
new file mode 100644 (file)
index 0000000..6995092
--- /dev/null
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2020 Oliver Hartkopp <socketcan@hartkopp.net>
+ * Copyright (C) 2020 Marc Kleine-Budde <kernel@pengutronix.de>
+ */
+
+#ifndef _CAN_LENGTH_H
+#define _CAN_LENGTH_H
+
+/*
+ * Size of a Classical CAN Standard Frame
+ *
+ * Name of Field                       Bits
+ * ---------------------------------------------------------
+ * Start-of-frame                      1
+ * Identifier                          11
+ * Remote transmission request (RTR)   1
+ * Identifier extension bit (IDE)      1
+ * Reserved bit (r0)                   1
+ * Data length code (DLC)              4
+ * Data field                          0...64
+ * CRC                                 15
+ * CRC delimiter                       1
+ * ACK slot                            1
+ * ACK delimiter                       1
+ * End-of-frame (EOF)                  7
+ * Inter frame spacing                 3
+ *
+ * rounded up and ignoring bitstuffing
+ */
+#define CAN_FRAME_OVERHEAD_SFF DIV_ROUND_UP(47, 8)
+
+/*
+ * Size of a Classical CAN Extended Frame
+ *
+ * Name of Field                       Bits
+ * ---------------------------------------------------------
+ * Start-of-frame                      1
+ * Identifier A                                11
+ * Substitute remote request (SRR)     1
+ * Identifier extension bit (IDE)      1
+ * Identifier B                                18
+ * Remote transmission request (RTR)   1
+ * Reserved bits (r1, r0)              2
+ * Data length code (DLC)              4
+ * Data field                          0...64
+ * CRC                                 15
+ * CRC delimiter                       1
+ * ACK slot                            1
+ * ACK delimiter                       1
+ * End-of-frame (EOF)                  7
+ * Inter frame spacing                 3
+ *
+ * rounded up and ignoring bitstuffing
+ */
+#define CAN_FRAME_OVERHEAD_EFF DIV_ROUND_UP(67, 8)
+
+/*
+ * Size of a CAN-FD Standard Frame
+ *
+ * Name of Field                       Bits
+ * ---------------------------------------------------------
+ * Start-of-frame                      1
+ * Identifier                          11
+ * Reserved bit (r1)                   1
+ * Identifier extension bit (IDE)      1
+ * Flexible data rate format (FDF)     1
+ * Reserved bit (r0)                   1
+ * Bit Rate Switch (BRS)               1
+ * Error Status Indicator (ESI)                1
+ * Data length code (DLC)              4
+ * Data field                          0...512
+ * Stuff Bit Count (SBC)               0...16: 4 20...64:5
+ * CRC                                 0...16: 17 20...64:21
+ * CRC delimiter (CD)                  1
+ * ACK slot (AS)                       1
+ * ACK delimiter (AD)                  1
+ * End-of-frame (EOF)                  7
+ * Inter frame spacing                 3
+ *
+ * assuming CRC21, rounded up and ignoring bitstuffing
+ */
+#define CANFD_FRAME_OVERHEAD_SFF DIV_ROUND_UP(61, 8)
+
+/*
+ * Size of a CAN-FD Extended Frame
+ *
+ * Name of Field                       Bits
+ * ---------------------------------------------------------
+ * Start-of-frame                      1
+ * Identifier A                                11
+ * Substitute remote request (SRR)     1
+ * Identifier extension bit (IDE)      1
+ * Identifier B                                18
+ * Reserved bit (r1)                   1
+ * Flexible data rate format (FDF)     1
+ * Reserved bit (r0)                   1
+ * Bit Rate Switch (BRS)               1
+ * Error Status Indicator (ESI)                1
+ * Data length code (DLC)              4
+ * Data field                          0...512
+ * Stuff Bit Count (SBC)               0...16: 4 20...64:5
+ * CRC                                 0...16: 17 20...64:21
+ * CRC delimiter (CD)                  1
+ * ACK slot (AS)                       1
+ * ACK delimiter (AD)                  1
+ * End-of-frame (EOF)                  7
+ * Inter frame spacing                 3
+ *
+ * assuming CRC21, rounded up and ignoring bitstuffing
+ */
+#define CANFD_FRAME_OVERHEAD_EFF DIV_ROUND_UP(80, 8)
+
+/*
+ * Maximum size of a Classical CAN frame
+ * (rounded up and ignoring bitstuffing)
+ */
+#define CAN_FRAME_LEN_MAX (CAN_FRAME_OVERHEAD_EFF + CAN_MAX_DLEN)
+
+/*
+ * Maximum size of a CAN-FD frame
+ * (rounded up and ignoring bitstuffing)
+ */
+#define CANFD_FRAME_LEN_MAX (CANFD_FRAME_OVERHEAD_EFF + CANFD_MAX_DLEN)
+
+/*
+ * can_cc_dlc2len(value) - convert a given data length code (dlc) of a
+ * Classical CAN frame into a valid data length of max. 8 bytes.
+ *
+ * To be used in the CAN netdriver receive path to ensure conformance with
+ * ISO 11898-1 Chapter 8.4.2.3 (DLC field)
+ */
+#define can_cc_dlc2len(dlc)    (min_t(u8, (dlc), CAN_MAX_DLEN))
+
+/* helper to get the data length code (DLC) for Classical CAN raw DLC access */
+static inline u8 can_get_cc_dlc(const struct can_frame *cf, const u32 ctrlmode)
+{
+       /* return len8_dlc as dlc value only if all conditions apply */
+       if ((ctrlmode & CAN_CTRLMODE_CC_LEN8_DLC) &&
+           (cf->len == CAN_MAX_DLEN) &&
+           (cf->len8_dlc > CAN_MAX_DLEN && cf->len8_dlc <= CAN_MAX_RAW_DLC))
+               return cf->len8_dlc;
+
+       /* return the payload length as dlc value */
+       return cf->len;
+}
+
+/* helper to set len and len8_dlc value for Classical CAN raw DLC access */
+static inline void can_frame_set_cc_len(struct can_frame *cf, const u8 dlc,
+                                       const u32 ctrlmode)
+{
+       /* the caller already ensured that dlc is a value from 0 .. 15 */
+       if (ctrlmode & CAN_CTRLMODE_CC_LEN8_DLC && dlc > CAN_MAX_DLEN)
+               cf->len8_dlc = dlc;
+
+       /* limit the payload length 'len' to CAN_MAX_DLEN */
+       cf->len = can_cc_dlc2len(dlc);
+}
+
+/* get data length from raw data length code (DLC) */
+u8 can_fd_dlc2len(u8 dlc);
+
+/* map the sanitized data length to an appropriate data length code */
+u8 can_fd_len2dlc(u8 len);
+
+/* calculate the CAN Frame length in bytes of a given skb */
+unsigned int can_skb_get_frame_len(const struct sk_buff *skb);
+
+/* map the data length to an appropriate data link layer length */
+static inline u8 canfd_sanitize_len(u8 len)
+{
+       return can_fd_dlc2len(can_fd_len2dlc(len));
+}
+
+#endif /* !_CAN_LENGTH_H */
index f1b3808..40882df 100644 (file)
@@ -44,7 +44,8 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload);
 int can_rx_offload_queue_sorted(struct can_rx_offload *offload,
                                struct sk_buff *skb, u32 timestamp);
 unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload,
-                                        unsigned int idx, u32 timestamp);
+                                        unsigned int idx, u32 timestamp,
+                                        unsigned int *frame_len_ptr);
 int can_rx_offload_queue_tail(struct can_rx_offload *offload,
                              struct sk_buff *skb);
 void can_rx_offload_del(struct can_rx_offload *offload);
index fc61cf4..685f34c 100644 (file)
 #include <linux/can.h>
 #include <net/sock.h>
 
+void can_flush_echo_skb(struct net_device *dev);
+int can_put_echo_skb(struct sk_buff *skb, struct net_device *dev,
+                    unsigned int idx, unsigned int frame_len);
+struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx,
+                                  u8 *len_ptr, unsigned int *frame_len_ptr);
+unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx,
+                             unsigned int *frame_len_ptr);
+void can_free_echo_skb(struct net_device *dev, unsigned int idx);
+struct sk_buff *alloc_can_skb(struct net_device *dev, struct can_frame **cf);
+struct sk_buff *alloc_canfd_skb(struct net_device *dev,
+                               struct canfd_frame **cfd);
+struct sk_buff *alloc_can_err_skb(struct net_device *dev,
+                                 struct can_frame **cf);
+
 /*
  * The struct can_skb_priv is used to transport additional information along
  * with the stored struct can(fd)_frame that can not be contained in existing
  * struct can_skb_priv - private additional data inside CAN sk_buffs
  * @ifindex:   ifindex of the first interface the CAN frame appeared on
  * @skbcnt:    atomic counter to have an unique id together with skb pointer
+ * @frame_len: length of CAN frame in data link layer
  * @cf:                align to the following CAN frame at skb->data
  */
 struct can_skb_priv {
        int ifindex;
        int skbcnt;
+       unsigned int frame_len;
        struct can_frame cf[];
 };
 
@@ -74,4 +90,68 @@ static inline struct sk_buff *can_create_echo_skb(struct sk_buff *skb)
        return nskb;
 }
 
+/* Check for outgoing skbs that have not been created by the CAN subsystem */
+static inline bool can_skb_headroom_valid(struct net_device *dev,
+                                         struct sk_buff *skb)
+{
+       /* af_packet creates a headroom of HH_DATA_MOD bytes which is fine */
+       if (WARN_ON_ONCE(skb_headroom(skb) < sizeof(struct can_skb_priv)))
+               return false;
+
+       /* af_packet does not apply CAN skb specific settings */
+       if (skb->ip_summed == CHECKSUM_NONE) {
+               /* init headroom */
+               can_skb_prv(skb)->ifindex = dev->ifindex;
+               can_skb_prv(skb)->skbcnt = 0;
+
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+               /* perform proper loopback on capable devices */
+               if (dev->flags & IFF_ECHO)
+                       skb->pkt_type = PACKET_LOOPBACK;
+               else
+                       skb->pkt_type = PACKET_HOST;
+
+               skb_reset_mac_header(skb);
+               skb_reset_network_header(skb);
+               skb_reset_transport_header(skb);
+       }
+
+       return true;
+}
+
+/* Drop a given socketbuffer if it does not contain a valid CAN frame. */
+static inline bool can_dropped_invalid_skb(struct net_device *dev,
+                                         struct sk_buff *skb)
+{
+       const struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+
+       if (skb->protocol == htons(ETH_P_CAN)) {
+               if (unlikely(skb->len != CAN_MTU ||
+                            cfd->len > CAN_MAX_DLEN))
+                       goto inval_skb;
+       } else if (skb->protocol == htons(ETH_P_CANFD)) {
+               if (unlikely(skb->len != CANFD_MTU ||
+                            cfd->len > CANFD_MAX_DLEN))
+                       goto inval_skb;
+       } else
+               goto inval_skb;
+
+       if (!can_skb_headroom_valid(dev, skb))
+               goto inval_skb;
+
+       return false;
+
+inval_skb:
+       kfree_skb(skb);
+       dev->stats.tx_dropped++;
+       return true;
+}
+
+static inline bool can_is_canfd_skb(const struct sk_buff *skb)
+{
+       /* the CAN specific type of skb is identified by its data length */
+       return skb->len == CANFD_MTU;
+}
+
 #endif /* !_CAN_SKB_H */
index f5e02f6..3989dcb 100644 (file)
@@ -33,8 +33,8 @@
 #define CEPH_MSGR2_INCARNATION_1 (0ull)
 
 #define DEFINE_MSGR2_FEATURE(bit, incarnation, name)               \
-       static const uint64_t CEPH_MSGR2_FEATURE_##name = (1ULL << bit); \
-       static const uint64_t CEPH_MSGR2_FEATUREMASK_##name =            \
+       static const uint64_t __maybe_unused CEPH_MSGR2_FEATURE_##name = (1ULL << bit); \
+       static const uint64_t __maybe_unused CEPH_MSGR2_FEATUREMASK_##name =            \
                        (1ULL << bit | CEPH_MSGR2_INCARNATION_##incarnation);
 
 #define HAVE_MSGR2_FEATURE(x, name) \
index 74c6c04..555ab0f 100644 (file)
 /* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 */
 #if GCC_VERSION < 40900
 # error Sorry, your version of GCC is too old - please use 4.9 or newer.
+#elif defined(CONFIG_ARM64) && GCC_VERSION < 50100
+/*
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63293
+ * https://lore.kernel.org/r/20210107111841.GN1551@shell.armlinux.org.uk
+ */
+# error Sorry, your version of GCC is too old - please use 5.1 or newer.
 #endif
 
 /*
index b2a3f4f..ea5e04e 100644 (file)
  */
 #define __used                          __attribute__((__used__))
 
+/*
+ *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-warn_005funused_005fresult-function-attribute
+ * clang: https://clang.llvm.org/docs/AttributeReference.html#nodiscard-warn-unused-result
+ */
+#define __must_check                    __attribute__((__warn_unused_result__))
+
 /*
  *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-weak-function-attribute
  *   gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-weak-variable-attribute
index bbaa39e..e5dd5a4 100644 (file)
@@ -121,12 +121,6 @@ struct ftrace_likely_data {
        unsigned long                   constant;
 };
 
-#ifdef CONFIG_ENABLE_MUST_CHECK
-#define __must_check           __attribute__((__warn_unused_result__))
-#else
-#define __must_check
-#endif
-
 #if defined(CC_USING_HOTPATCH)
 #define notrace                        __attribute__((hotpatch(0, 0)))
 #elif defined(CC_USING_PATCHABLE_FUNCTION_ENTRY)
index dbe78e8..20874db 100644 (file)
@@ -186,12 +186,9 @@ extern int braille_register_console(struct console *, int index,
 extern int braille_unregister_console(struct console *);
 #ifdef CONFIG_TTY
 extern void console_sysfs_notify(void);
-extern void register_ttynull_console(void);
 #else
 static inline void console_sysfs_notify(void)
 { }
-static inline void register_ttynull_console(void)
-{ }
 #endif
 extern bool console_suspend_enabled;
 
index 89bb8b8..1779f90 100644 (file)
@@ -609,6 +609,18 @@ static inline const char *dev_name(const struct device *dev)
        return kobject_name(&dev->kobj);
 }
 
+/**
+ * dev_bus_name - Return a device's bus/class name, if at all possible
+ * @dev: struct device to get the bus/class name of
+ *
+ * Will return the name of the bus/class the device is attached to.  If it is
+ * not attached to a bus/class, an empty string will be returned.
+ */
+static inline const char *dev_bus_name(const struct device *dev)
+{
+       return dev->bus ? dev->bus->name : (dev->class ? dev->class->name : "");
+}
+
 __printf(2, 3) int dev_set_name(struct device *dev, const char *name, ...);
 
 #ifdef CONFIG_NUMA
index 29d255f..90bd558 100644 (file)
@@ -150,6 +150,7 @@ void dm_bufio_set_minimum_buffers(struct dm_bufio_client *c, unsigned n);
 
 unsigned dm_bufio_get_block_size(struct dm_bufio_client *c);
 sector_t dm_bufio_get_device_size(struct dm_bufio_client *c);
+struct dm_io_client *dm_bufio_get_dm_io_client(struct dm_bufio_client *c);
 sector_t dm_bufio_get_block_number(struct dm_buffer *b);
 void *dm_bufio_get_block_data(struct dm_buffer *b);
 void *dm_bufio_get_aux_data(struct dm_buffer *b);
diff --git a/include/linux/dsa/brcm.h b/include/linux/dsa/brcm.h
new file mode 100644 (file)
index 0000000..47545a9
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (C) 2014 Broadcom Corporation
+ */
+
+/* Included by drivers/net/ethernet/broadcom/bcmsysport.c and
+ * net/dsa/tag_brcm.c
+ */
+#ifndef _NET_DSA_BRCM_H
+#define _NET_DSA_BRCM_H
+
+/* Broadcom tag specific helpers to insert and extract queue/port number */
+#define BRCM_TAG_SET_PORT_QUEUE(p, q)  ((p) << 8 | q)
+#define BRCM_TAG_GET_PORT(v)           ((v) >> 8)
+#define BRCM_TAG_GET_QUEUE(v)          ((v) & 0xff)
+
+#endif
index 29c2765..7fdce54 100644 (file)
@@ -259,15 +259,32 @@ static inline bool insn_is_zext(const struct bpf_insn *insn)
                .off   = OFF,                                   \
                .imm   = 0 })
 
-/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */
 
-#define BPF_STX_XADD(SIZE, DST, SRC, OFF)                      \
+/*
+ * Atomic operations:
+ *
+ *   BPF_ADD                  *(uint *) (dst_reg + off16) += src_reg
+ *   BPF_AND                  *(uint *) (dst_reg + off16) &= src_reg
+ *   BPF_OR                   *(uint *) (dst_reg + off16) |= src_reg
+ *   BPF_XOR                  *(uint *) (dst_reg + off16) ^= src_reg
+ *   BPF_ADD | BPF_FETCH      src_reg = atomic_fetch_add(dst_reg + off16, src_reg);
+ *   BPF_AND | BPF_FETCH      src_reg = atomic_fetch_and(dst_reg + off16, src_reg);
+ *   BPF_OR | BPF_FETCH       src_reg = atomic_fetch_or(dst_reg + off16, src_reg);
+ *   BPF_XOR | BPF_FETCH      src_reg = atomic_fetch_xor(dst_reg + off16, src_reg);
+ *   BPF_XCHG                 src_reg = atomic_xchg(dst_reg + off16, src_reg)
+ *   BPF_CMPXCHG              r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg)
+ */
+
+#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF)                 \
        ((struct bpf_insn) {                                    \
-               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD,   \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = OFF,                                   \
-               .imm   = 0 })
+               .imm   = OP })
+
+/* Legacy alias */
+#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF)
 
 /* Memory store, *(uint *) (dst_reg + off16) = imm32 */
 
@@ -886,7 +903,7 @@ void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
 u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
 #define __bpf_call_base_args \
        ((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \
-        __bpf_call_base)
+        (void *)__bpf_call_base)
 
 struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog);
 void bpf_jit_compile(struct bpf_prog *prog);
index d956987..09c6a0b 100644 (file)
@@ -533,11 +533,10 @@ struct dmar_domain {
                                        /* Domain ids per IOMMU. Use u16 since
                                         * domain ids are 16 bit wide according
                                         * to VT-d spec, section 9.3 */
-       unsigned int    auxd_refcnt;    /* Refcount of auxiliary attaching */
 
        bool has_iotlb_device;
        struct list_head devices;       /* all devices' list */
-       struct list_head auxd;          /* link to device's auxiliary list */
+       struct list_head subdevices;    /* all subdevices' list */
        struct iova_domain iovad;       /* iova's that belong to this domain */
 
        struct dma_pte  *pgd;           /* virtual address */
@@ -610,14 +609,21 @@ struct intel_iommu {
        struct dmar_drhd_unit *drhd;
 };
 
+/* Per subdevice private data */
+struct subdev_domain_info {
+       struct list_head link_phys;     /* link to phys device siblings */
+       struct list_head link_domain;   /* link to domain siblings */
+       struct device *pdev;            /* physical device derived from */
+       struct dmar_domain *domain;     /* aux-domain */
+       int users;                      /* user count */
+};
+
 /* PCI domain-device relationship */
 struct device_domain_info {
        struct list_head link;  /* link to domain siblings */
        struct list_head global; /* link to global list */
        struct list_head table; /* link to pasid table */
-       struct list_head auxiliary_domains; /* auxiliary domains
-                                            * attached to this device
-                                            */
+       struct list_head subdevices; /* subdevices sibling */
        u32 segment;            /* PCI segment number */
        u8 bus;                 /* PCI bus number */
        u8 devfn;               /* PCI devfn number */
@@ -758,6 +764,7 @@ struct intel_svm_dev {
        struct list_head list;
        struct rcu_head rcu;
        struct device *dev;
+       struct intel_iommu *iommu;
        struct svm_dev_ops *ops;
        struct iommu_sva sva;
        u32 pasid;
@@ -771,7 +778,6 @@ struct intel_svm {
        struct mmu_notifier notifier;
        struct mm_struct *mm;
 
-       struct intel_iommu *iommu;
        unsigned int flags;
        u32 pasid;
        int gpasid; /* In case that guest PASID is different from host PASID */
index dda61d1..9d1f29f 100644 (file)
@@ -31,6 +31,7 @@ struct ipv6_devconf {
        __s32           max_desync_factor;
        __s32           max_addresses;
        __s32           accept_ra_defrtr;
+       __u32           ra_defrtr_metric;
        __s32           accept_ra_min_hop_limit;
        __s32           accept_ra_pinfo;
        __s32           ignore_routes_with_linkdown;
index 5e0655f..fe1ae73 100644 (file)
@@ -35,8 +35,12 @@ struct kunit_kasan_expectation {
 #define KASAN_SHADOW_INIT 0
 #endif
 
+#ifndef PTE_HWTABLE_PTRS
+#define PTE_HWTABLE_PTRS 0
+#endif
+
 extern unsigned char kasan_early_shadow_page[PAGE_SIZE];
-extern pte_t kasan_early_shadow_pte[PTRS_PER_PTE];
+extern pte_t kasan_early_shadow_pte[PTRS_PER_PTE + PTE_HWTABLE_PTRS];
 extern pmd_t kasan_early_shadow_pmd[PTRS_PER_PMD];
 extern pud_t kasan_early_shadow_pud[PTRS_PER_PUD];
 extern p4d_t kasan_early_shadow_p4d[MAX_PTRS_PER_P4D];
index a10e847..4e3037d 100644 (file)
@@ -52,6 +52,25 @@ static inline void kcov_remote_start_usb(u64 id)
        kcov_remote_start(kcov_remote_handle(KCOV_SUBSYSTEM_USB, id));
 }
 
+/*
+ * The softirq flavor of kcov_remote_*() functions is introduced as a temporary
+ * work around for kcov's lack of nested remote coverage sections support in
+ * task context. Adding suport for nested sections is tracked in:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=210337
+ */
+
+static inline void kcov_remote_start_usb_softirq(u64 id)
+{
+       if (in_serving_softirq())
+               kcov_remote_start_usb(id);
+}
+
+static inline void kcov_remote_stop_softirq(void)
+{
+       if (in_serving_softirq())
+               kcov_remote_stop();
+}
+
 #else
 
 static inline void kcov_task_init(struct task_struct *t) {}
@@ -66,6 +85,8 @@ static inline u64 kcov_common_handle(void)
 }
 static inline void kcov_remote_start_common(u64 id) {}
 static inline void kcov_remote_start_usb(u64 id) {}
+static inline void kcov_remote_start_usb_softirq(u64 id) {}
+static inline void kcov_remote_stop_softirq(void) {}
 
 #endif /* CONFIG_KCOV */
 #endif /* _LINUX_KCOV_H */
index 85b5151..4856706 100644 (file)
        })
 
 /* acceptable for old filesystems */
-static inline bool old_valid_dev(dev_t dev)
+static __always_inline bool old_valid_dev(dev_t dev)
 {
        return MAJOR(dev) < 256 && MINOR(dev) < 256;
 }
 
-static inline u16 old_encode_dev(dev_t dev)
+static __always_inline u16 old_encode_dev(dev_t dev)
 {
        return (MAJOR(dev) << 8) | MINOR(dev);
 }
 
-static inline dev_t old_decode_dev(u16 val)
+static __always_inline dev_t old_decode_dev(u16 val)
 {
        return MKDEV((val >> 8) & 255, val & 255);
 }
 
-static inline u32 new_encode_dev(dev_t dev)
+static __always_inline u32 new_encode_dev(dev_t dev)
 {
        unsigned major = MAJOR(dev);
        unsigned minor = MINOR(dev);
        return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
 }
 
-static inline dev_t new_decode_dev(u32 dev)
+static __always_inline dev_t new_decode_dev(u32 dev)
 {
        unsigned major = (dev & 0xfff00) >> 8;
        unsigned minor = (dev & 0xff) | ((dev >> 12) & 0xfff00);
        return MKDEV(major, minor);
 }
 
-static inline u64 huge_encode_dev(dev_t dev)
+static __always_inline u64 huge_encode_dev(dev_t dev)
 {
        return new_encode_dev(dev);
 }
 
-static inline dev_t huge_decode_dev(u64 dev)
+static __always_inline dev_t huge_decode_dev(u64 dev)
 {
        return new_decode_dev(dev);
 }
 
-static inline int sysv_valid_dev(dev_t dev)
+static __always_inline int sysv_valid_dev(dev_t dev)
 {
        return MAJOR(dev) < (1<<14) && MINOR(dev) < (1<<18);
 }
 
-static inline u32 sysv_encode_dev(dev_t dev)
+static __always_inline u32 sysv_encode_dev(dev_t dev)
 {
        return MINOR(dev) | (MAJOR(dev) << 18);
 }
 
-static inline unsigned sysv_major(u32 dev)
+static __always_inline unsigned sysv_major(u32 dev)
 {
        return (dev >> 18) & 0x3fff;
 }
 
-static inline unsigned sysv_minor(u32 dev)
+static __always_inline unsigned sysv_minor(u32 dev)
 {
        return dev & 0x3ffff;
 }
index 65b81e0..2484ed9 100644 (file)
@@ -33,6 +33,9 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
                                          unsigned int cpu,
                                          const char *namefmt);
 
+void kthread_set_per_cpu(struct task_struct *k, int cpu);
+bool kthread_is_per_cpu(struct task_struct *k);
+
 /**
  * kthread_run - create and wake a thread.
  * @threadfn: the function to run until signal_pending(current).
index a12b552..73f20de 100644 (file)
@@ -230,6 +230,5 @@ static inline ktime_t ms_to_ktime(u64 ms)
 }
 
 # include <linux/timekeeping.h>
-# include <linux/timekeeping32.h>
 
 #endif
index 5d71e8a..aca4dc0 100644 (file)
@@ -35,6 +35,9 @@ struct mdiobb_ctrl {
        const struct mdiobb_ops *ops;
 };
 
+int mdiobb_read(struct mii_bus *bus, int phy, int reg);
+int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val);
+
 /* The returned bus is not yet registered with the phy layer. */
 struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl);
 
index dbd69b3..ffb787d 100644 (file)
@@ -49,7 +49,11 @@ struct mdio_device {
        unsigned int reset_assert_delay;
        unsigned int reset_deassert_delay;
 };
-#define to_mdio_device(d) container_of(d, struct mdio_device, dev)
+
+static inline struct mdio_device *to_mdio_device(const struct device *dev)
+{
+       return container_of(dev, struct mdio_device, dev);
+}
 
 /* struct mdio_driver_common: Common to all MDIO drivers */
 struct mdio_driver_common {
@@ -57,8 +61,12 @@ struct mdio_driver_common {
        int flags;
 };
 #define MDIO_DEVICE_FLAG_PHY           1
-#define to_mdio_common_driver(d) \
-       container_of(d, struct mdio_driver_common, driver)
+
+static inline struct mdio_driver_common *
+to_mdio_common_driver(const struct device_driver *driver)
+{
+       return container_of(driver, struct mdio_driver_common, driver);
+}
 
 /* struct mdio_driver: Generic MDIO driver */
 struct mdio_driver {
@@ -73,8 +81,13 @@ struct mdio_driver {
        /* Clears up any memory if needed */
        void (*remove)(struct mdio_device *mdiodev);
 };
-#define to_mdio_driver(d)                                              \
-       container_of(to_mdio_common_driver(d), struct mdio_driver, mdiodrv)
+
+static inline struct mdio_driver *
+to_mdio_driver(const struct device_driver *driver)
+{
+       return container_of(to_mdio_common_driver(driver), struct mdio_driver,
+                           mdiodrv);
+}
 
 /* device driver data */
 static inline void mdiodev_set_drvdata(struct mdio_device *mdio, void *data)
index d827bd7..eeb0b52 100644 (file)
@@ -665,7 +665,7 @@ static inline struct lruvec *mem_cgroup_page_lruvec(struct page *page,
 {
        struct mem_cgroup *memcg = page_memcg(page);
 
-       VM_WARN_ON_ONCE_PAGE(!memcg, page);
+       VM_WARN_ON_ONCE_PAGE(!memcg && !mem_cgroup_disabled(), page);
        return mem_cgroup_lruvec(memcg, pgdat);
 }
 
index f1de49d..00057ea 100644 (file)
@@ -359,6 +359,10 @@ enum mlx5_event {
        MLX5_EVENT_TYPE_MAX                = 0x100,
 };
 
+enum mlx5_driver_event {
+       MLX5_DRIVER_EVENT_TYPE_TRAP = 0,
+};
+
 enum {
        MLX5_TRACER_SUBTYPE_OWNERSHIP_CHANGE = 0x0,
        MLX5_TRACER_SUBTYPE_TRACES_AVAILABLE = 0x1,
@@ -899,6 +903,11 @@ static inline u64 get_cqe_ts(struct mlx5_cqe64 *cqe)
        return (u64)lo | ((u64)hi << 32);
 }
 
+static inline u16 get_cqe_flow_tag(struct mlx5_cqe64 *cqe)
+{
+       return be32_to_cpu(cqe->sop_drop_qpn) & 0xFFF;
+}
+
 #define MLX5_MPWQE_LOG_NUM_STRIDES_BASE        (9)
 #define MLX5_MPWQE_LOG_STRIDE_SZ_BASE  (6)
 
index f93bfe7..88197b8 100644 (file)
@@ -193,7 +193,8 @@ enum port_state_policy {
 
 enum mlx5_coredev_type {
        MLX5_COREDEV_PF,
-       MLX5_COREDEV_VF
+       MLX5_COREDEV_VF,
+       MLX5_COREDEV_SF,
 };
 
 struct mlx5_field_desc {
@@ -507,6 +508,10 @@ struct mlx5_devcom;
 struct mlx5_fw_reset;
 struct mlx5_eq_table;
 struct mlx5_irq_table;
+struct mlx5_vhca_state_notifier;
+struct mlx5_sf_dev_table;
+struct mlx5_sf_hw_table;
+struct mlx5_sf_table;
 
 struct mlx5_rate_limit {
        u32                     rate;
@@ -564,6 +569,7 @@ struct mlx5_priv {
        int                     host_pf_pages;
 
        struct mlx5_core_health health;
+       struct list_head        traps;
 
        /* start: qp staff */
        struct dentry          *qp_debugfs;
@@ -603,6 +609,15 @@ struct mlx5_priv {
 
        struct mlx5_bfreg_data          bfregs;
        struct mlx5_uars_page          *uar;
+#ifdef CONFIG_MLX5_SF
+       struct mlx5_vhca_state_notifier *vhca_state_notifier;
+       struct mlx5_sf_dev_table *sf_dev_table;
+       struct mlx5_core_dev *parent_mdev;
+#endif
+#ifdef CONFIG_MLX5_SF_MANAGER
+       struct mlx5_sf_hw_table *sf_hw_table;
+       struct mlx5_sf_table *sf_table;
+#endif
 };
 
 enum mlx5_device_state {
@@ -1072,11 +1087,26 @@ enum {
        MAX_MR_CACHE_ENTRIES
 };
 
+/* Async-atomic event notifier used by mlx5 core to forward FW
+ * evetns recived from event queue to mlx5 consumers.
+ * Optimise event queue dipatching.
+ */
 int mlx5_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb);
 int mlx5_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb);
+
+/* Async-atomic event notifier used for forwarding
+ * evetns from the event queue into the to mlx5 events dispatcher,
+ * eswitch, clock and others.
+ */
 int mlx5_eq_notifier_register(struct mlx5_core_dev *dev, struct mlx5_nb *nb);
 int mlx5_eq_notifier_unregister(struct mlx5_core_dev *dev, struct mlx5_nb *nb);
 
+/* Blocking event notifier used to forward SW events, used for slow path */
+int mlx5_blocking_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb);
+int mlx5_blocking_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb);
+int mlx5_blocking_notifier_call_chain(struct mlx5_core_dev *dev, unsigned int event,
+                                     void *data);
+
 int mlx5_core_query_vendor_id(struct mlx5_core_dev *mdev, u32 *vendor_id);
 
 int mlx5_cmd_create_vport_lag(struct mlx5_core_dev *dev);
@@ -1209,22 +1239,4 @@ static inline bool mlx5_is_roce_enabled(struct mlx5_core_dev *dev)
        return val.vbool;
 }
 
-/**
- * mlx5_core_net - Provide net namespace of the mlx5_core_dev
- * @dev: mlx5 core device
- *
- * mlx5_core_net() returns the net namespace of mlx5 core device.
- * This can be called only in below described limited context.
- * (a) When a devlink instance for mlx5_core is registered and
- *     when devlink reload operation is disabled.
- *     or
- * (b) during devlink reload reload_down() and reload_up callbacks
- *     where it is ensured that devlink instance's net namespace is
- *     stable.
- */
-static inline struct net *mlx5_core_net(struct mlx5_core_dev *dev)
-{
-       return devlink_net(priv_to_devlink(dev));
-}
-
 #endif /* MLX5_DRIVER_H */
index 8fbddec..71ae6aa 100644 (file)
@@ -842,11 +842,16 @@ struct mlx5_ifc_qos_cap_bits {
        u8         reserved_at_4[0x1];
        u8         packet_pacing_burst_bound[0x1];
        u8         packet_pacing_typical_size[0x1];
-       u8         reserved_at_7[0x4];
+       u8         reserved_at_7[0x1];
+       u8         nic_sq_scheduling[0x1];
+       u8         nic_bw_share[0x1];
+       u8         nic_rate_limit[0x1];
        u8         packet_pacing_uid[0x1];
        u8         reserved_at_c[0x14];
 
-       u8         reserved_at_20[0x20];
+       u8         reserved_at_20[0xb];
+       u8         log_max_qos_nic_queue_group[0x5];
+       u8         reserved_at_30[0x10];
 
        u8         packet_pacing_max_rate[0x20];
 
@@ -1278,9 +1283,12 @@ struct mlx5_ifc_cmd_hca_cap_bits {
 
        u8         reserved_at_a0[0x3];
        u8         ece_support[0x1];
-       u8         reserved_at_a4[0x7];
+       u8         reserved_at_a4[0x5];
+       u8         reg_c_preserve[0x1];
+       u8         reserved_at_aa[0x1];
        u8         log_max_srq[0x5];
-       u8         reserved_at_b0[0x2];
+       u8         reserved_at_b0[0x1];
+       u8         uplink_follow[0x1];
        u8         ts_cqe_to_dest_cqn[0x1];
        u8         reserved_at_b3[0xd];
 
@@ -3344,7 +3352,7 @@ struct mlx5_ifc_sqc_bits {
        u8         reserved_at_e0[0x10];
        u8         packet_pacing_rate_limit_index[0x10];
        u8         tis_lst_sz[0x10];
-       u8         reserved_at_110[0x10];
+       u8         qos_queue_group_id[0x10];
 
        u8         reserved_at_120[0x40];
 
@@ -3359,6 +3367,7 @@ enum {
        SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT = 0x1,
        SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT_TC = 0x2,
        SCHEDULING_CONTEXT_ELEMENT_TYPE_PARA_VPORT_TC = 0x3,
+       SCHEDULING_CONTEXT_ELEMENT_TYPE_QUEUE_GROUP = 0x4,
 };
 
 enum {
@@ -4802,6 +4811,7 @@ struct mlx5_ifc_query_scheduling_element_out_bits {
 
 enum {
        SCHEDULING_HIERARCHY_E_SWITCH = 0x2,
+       SCHEDULING_HIERARCHY_NIC = 0x3,
 };
 
 struct mlx5_ifc_query_scheduling_element_in_bits {
index 5299b90..ecdf8a8 100644 (file)
@@ -216,6 +216,13 @@ int overcommit_kbytes_handler(struct ctl_table *, int, void *, size_t *,
                loff_t *);
 int overcommit_policy_handler(struct ctl_table *, int, void *, size_t *,
                loff_t *);
+/*
+ * Any attempt to mark this function as static leads to build failure
+ * when CONFIG_DEBUG_INFO_BTF is enabled because __add_to_page_cache_locked()
+ * is referred to by BPF code. This must be visible for error injection.
+ */
+int __add_to_page_cache_locked(struct page *page, struct address_space *mapping,
+               pgoff_t index, gfp_t gfp, void **shadowp);
 
 #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n))
 
@@ -2432,8 +2439,9 @@ extern int __meminit early_pfn_to_nid(unsigned long pfn);
 #endif
 
 extern void set_dma_reserve(unsigned long new_dma_reserve);
-extern void memmap_init_zone(unsigned long, int, unsigned long, unsigned long,
-               enum meminit_context, struct vmem_altmap *, int migratetype);
+extern void memmap_init_zone(unsigned long, int, unsigned long,
+               unsigned long, unsigned long, enum meminit_context,
+               struct vmem_altmap *, int migratetype);
 extern void setup_per_zone_wmarks(void);
 extern int __meminit init_per_zone_wmark_min(void);
 extern void mem_init(void);
index 934de56..c06d6aa 100644 (file)
@@ -84,6 +84,7 @@ enum {
        NETIF_F_GRO_FRAGLIST_BIT,       /* Fraglist GRO */
 
        NETIF_F_HW_MACSEC_BIT,          /* Offload MACsec operations */
+       NETIF_F_GRO_UDP_FWD_BIT,        /* Allow UDP GRO for forwarding */
 
        /*
         * Add your fresh new feature above and remember to update
@@ -157,6 +158,7 @@ enum {
 #define NETIF_F_GRO_FRAGLIST   __NETIF_F(GRO_FRAGLIST)
 #define NETIF_F_GSO_FRAGLIST   __NETIF_F(GSO_FRAGLIST)
 #define NETIF_F_HW_MACSEC      __NETIF_F(HW_MACSEC)
+#define NETIF_F_GRO_UDP_FWD    __NETIF_F(GRO_UDP_FWD)
 
 /* Finds the next feature with the highest number of the range of start till 0.
  */
@@ -234,7 +236,7 @@ static inline int find_next_netdev_feature(u64 feature, unsigned long start)
 #define NETIF_F_SOFT_FEATURES  (NETIF_F_GSO | NETIF_F_GRO)
 
 /* Changeable features with no special hardware requirements that defaults to off. */
-#define NETIF_F_SOFT_FEATURES_OFF      NETIF_F_GRO_FRAGLIST
+#define NETIF_F_SOFT_FEATURES_OFF      (NETIF_F_GRO_FRAGLIST | NETIF_F_GRO_UDP_FWD)
 
 #define NETIF_F_VLAN_FEATURES  (NETIF_F_HW_VLAN_CTAG_FILTER | \
                                 NETIF_F_HW_VLAN_CTAG_RX | \
index 259be67..e9e7ada 100644 (file)
@@ -376,7 +376,6 @@ enum gro_result {
        GRO_MERGED_FREE,
        GRO_HELD,
        GRO_NORMAL,
-       GRO_DROP,
        GRO_CONSUMED,
 };
 typedef enum gro_result gro_result_t;
@@ -859,6 +858,7 @@ enum tc_setup_type {
        TC_SETUP_QDISC_ETS,
        TC_SETUP_QDISC_TBF,
        TC_SETUP_QDISC_FIFO,
+       TC_SETUP_QDISC_HTB,
 };
 
 /* These structures hold the attributes of bpf state that are being passed
@@ -1213,19 +1213,6 @@ struct netdev_net_notifier {
  *                              struct netdev_phys_item_id *ppid)
  *     Called to get the parent ID of the physical port of this device.
  *
- * void (*ndo_udp_tunnel_add)(struct net_device *dev,
- *                           struct udp_tunnel_info *ti);
- *     Called by UDP tunnel to notify a driver about the UDP port and socket
- *     address family that a UDP tunnel is listnening to. It is called only
- *     when a new port starts listening. The operation is protected by the
- *     RTNL.
- *
- * void (*ndo_udp_tunnel_del)(struct net_device *dev,
- *                           struct udp_tunnel_info *ti);
- *     Called by UDP tunnel to notify the driver about a UDP port and socket
- *     address family that the UDP tunnel is not listening to anymore. The
- *     operation is protected by the RTNL.
- *
  * void* (*ndo_dfwd_add_station)(struct net_device *pdev,
  *                              struct net_device *dev)
  *     Called by upper layer devices to accelerate switching or other
@@ -1412,6 +1399,8 @@ struct net_device_ops {
        struct net_device*      (*ndo_get_xmit_slave)(struct net_device *dev,
                                                      struct sk_buff *skb,
                                                      bool all_slaves);
+       struct net_device*      (*ndo_sk_get_lower_dev)(struct net_device *dev,
+                                                       struct sock *sk);
        netdev_features_t       (*ndo_fix_features)(struct net_device *dev,
                                                    netdev_features_t features);
        int                     (*ndo_set_features)(struct net_device *dev,
@@ -1464,10 +1453,6 @@ struct net_device_ops {
                                                          struct netdev_phys_item_id *ppid);
        int                     (*ndo_get_phys_port_name)(struct net_device *dev,
                                                          char *name, size_t len);
-       void                    (*ndo_udp_tunnel_add)(struct net_device *dev,
-                                                     struct udp_tunnel_info *ti);
-       void                    (*ndo_udp_tunnel_del)(struct net_device *dev,
-                                                     struct udp_tunnel_info *ti);
        void*                   (*ndo_dfwd_add_station)(struct net_device *pdev,
                                                        struct net_device *dev);
        void                    (*ndo_dfwd_del_station)(struct net_device *pdev,
@@ -1873,7 +1858,6 @@ struct net_device {
        unsigned long           mem_end;
        unsigned long           mem_start;
        unsigned long           base_addr;
-       int                     irq;
 
        /*
         *      Some hardware also needs these fields (state,dev_list,
@@ -1895,6 +1879,23 @@ struct net_device {
                struct list_head lower;
        } adj_list;
 
+       /* Read-mostly cache-line for fast-path access */
+       unsigned int            flags;
+       unsigned int            priv_flags;
+       const struct net_device_ops *netdev_ops;
+       int                     ifindex;
+       unsigned short          gflags;
+       unsigned short          hard_header_len;
+
+       /* Note : dev->mtu is often read without holding a lock.
+        * Writers usually hold RTNL.
+        * It is recommended to use READ_ONCE() to annotate the reads,
+        * and to use WRITE_ONCE() to annotate the writes.
+        */
+       unsigned int            mtu;
+       unsigned short          needed_headroom;
+       unsigned short          needed_tailroom;
+
        netdev_features_t       features;
        netdev_features_t       hw_features;
        netdev_features_t       wanted_features;
@@ -1903,10 +1904,15 @@ struct net_device {
        netdev_features_t       mpls_features;
        netdev_features_t       gso_partial_features;
 
-       int                     ifindex;
+       unsigned int            min_mtu;
+       unsigned int            max_mtu;
+       unsigned short          type;
+       unsigned char           min_header_len;
+       unsigned char           name_assign_type;
+
        int                     group;
 
-       struct net_device_stats stats;
+       struct net_device_stats stats; /* not used by modern drivers */
 
        atomic_long_t           rx_dropped;
        atomic_long_t           tx_dropped;
@@ -1920,7 +1926,6 @@ struct net_device {
        const struct iw_handler_def *wireless_handlers;
        struct iw_public_data   *wireless_data;
 #endif
-       const struct net_device_ops *netdev_ops;
        const struct ethtool_ops *ethtool_ops;
 #ifdef CONFIG_NET_L3_MASTER_DEV
        const struct l3mdev_ops *l3mdev_ops;
@@ -1939,34 +1944,12 @@ struct net_device {
 
        const struct header_ops *header_ops;
 
-       unsigned int            flags;
-       unsigned int            priv_flags;
-
-       unsigned short          gflags;
-       unsigned short          padded;
-
        unsigned char           operstate;
        unsigned char           link_mode;
 
        unsigned char           if_port;
        unsigned char           dma;
 
-       /* Note : dev->mtu is often read without holding a lock.
-        * Writers usually hold RTNL.
-        * It is recommended to use READ_ONCE() to annotate the reads,
-        * and to use WRITE_ONCE() to annotate the writes.
-        */
-       unsigned int            mtu;
-       unsigned int            min_mtu;
-       unsigned int            max_mtu;
-       unsigned short          type;
-       unsigned short          hard_header_len;
-       unsigned char           min_header_len;
-       unsigned char           name_assign_type;
-
-       unsigned short          needed_headroom;
-       unsigned short          needed_tailroom;
-
        /* Interface address info. */
        unsigned char           perm_addr[MAX_ADDR_LEN];
        unsigned char           addr_assign_type;
@@ -1977,7 +1960,10 @@ struct net_device {
        unsigned short          neigh_priv_len;
        unsigned short          dev_id;
        unsigned short          dev_port;
+       unsigned short          padded;
+
        spinlock_t              addr_list_lock;
+       int                     irq;
 
        struct netdev_hw_addr_list      uc;
        struct netdev_hw_addr_list      mc;
@@ -2633,6 +2619,7 @@ enum netdev_lag_hash {
        NETDEV_LAG_HASH_L23,
        NETDEV_LAG_HASH_E23,
        NETDEV_LAG_HASH_E34,
+       NETDEV_LAG_HASH_VLAN_SRCMAC,
        NETDEV_LAG_HASH_UNKNOWN,
 };
 
@@ -2876,6 +2863,8 @@ int init_dummy_netdev(struct net_device *dev);
 struct net_device *netdev_get_xmit_slave(struct net_device *dev,
                                         struct sk_buff *skb,
                                         bool all_slaves);
+struct net_device *netdev_sk_get_lowest_dev(struct net_device *dev,
+                                           struct sock *sk);
 struct net_device *dev_get_by_index(struct net *net, int ifindex);
 struct net_device *__dev_get_by_index(struct net *net, int ifindex);
 struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex);
index d925359..bfed36e 100644 (file)
@@ -116,6 +116,9 @@ enum {
        NVME_REG_BPMBL  = 0x0048,       /* Boot Partition Memory Buffer
                                         * Location
                                         */
+       NVME_REG_CMBMSC = 0x0050,       /* Controller Memory Buffer Memory
+                                        * Space Control
+                                        */
        NVME_REG_PMRCAP = 0x0e00,       /* Persistent Memory Capabilities */
        NVME_REG_PMRCTL = 0x0e04,       /* Persistent Memory Region Control */
        NVME_REG_PMRSTS = 0x0e08,       /* Persistent Memory Region Status */
@@ -135,6 +138,7 @@ enum {
 #define NVME_CAP_CSS(cap)      (((cap) >> 37) & 0xff)
 #define NVME_CAP_MPSMIN(cap)   (((cap) >> 48) & 0xf)
 #define NVME_CAP_MPSMAX(cap)   (((cap) >> 52) & 0xf)
+#define NVME_CAP_CMBS(cap)     (((cap) >> 57) & 0x1)
 
 #define NVME_CMB_BIR(cmbloc)   ((cmbloc) & 0x7)
 #define NVME_CMB_OFST(cmbloc)  (((cmbloc) >> 12) & 0xfffff)
@@ -192,6 +196,8 @@ enum {
        NVME_CSTS_SHST_OCCUR    = 1 << 2,
        NVME_CSTS_SHST_CMPLT    = 2 << 2,
        NVME_CSTS_SHST_MASK     = 3 << 2,
+       NVME_CMBMSC_CRE         = 1 << 0,
+       NVME_CMBMSC_CMSE        = 1 << 1,
 };
 
 struct nvme_id_power_state {
index bf79667..5054802 100644 (file)
@@ -163,8 +163,6 @@ int arm_pmu_acpi_probe(armpmu_init_fn init_fn);
 static inline int arm_pmu_acpi_probe(armpmu_init_fn init_fn) { return 0; }
 #endif
 
-bool arm_pmu_irq_is_nmi(void);
-
 /* Internal functions only for core arm_pmu code */
 struct arm_pmu *armpmu_alloc(void);
 struct arm_pmu *armpmu_alloc_atomic(void);
index 9effb51..bc323fb 100644 (file)
@@ -104,6 +104,7 @@ extern const int phy_10gbit_features_array[1];
  * @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
  * @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
  * @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
+ * @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
  * @PHY_INTERFACE_MODE_1000BASEX: 1000 BaseX
  * @PHY_INTERFACE_MODE_2500BASEX: 2500 BaseX
  * @PHY_INTERFACE_MODE_RXAUI: Reduced XAUI
@@ -135,6 +136,7 @@ typedef enum {
        PHY_INTERFACE_MODE_MOCA,
        PHY_INTERFACE_MODE_QSGMII,
        PHY_INTERFACE_MODE_TRGMII,
+       PHY_INTERFACE_MODE_100BASEX,
        PHY_INTERFACE_MODE_1000BASEX,
        PHY_INTERFACE_MODE_2500BASEX,
        PHY_INTERFACE_MODE_RXAUI,
@@ -217,6 +219,8 @@ static inline const char *phy_modes(phy_interface_t interface)
                return "usxgmii";
        case PHY_INTERFACE_MODE_10GKR:
                return "10gbase-kr";
+       case PHY_INTERFACE_MODE_100BASEX:
+               return "100base-x";
        default:
                return "unknown";
        }
@@ -644,8 +648,11 @@ struct phy_device {
        const struct macsec_ops *macsec_ops;
 #endif
 };
-#define to_phy_device(d) container_of(to_mdio_device(d), \
-                                     struct phy_device, mdio)
+
+static inline struct phy_device *to_phy_device(const struct device *dev)
+{
+       return container_of(to_mdio_device(dev), struct phy_device, mdio);
+}
 
 /**
  * struct phy_tdr_config - Configuration of a TDR raw test
index 4d58dc8..e339b48 100644 (file)
@@ -470,7 +470,7 @@ static inline void *qed_chain_consume(struct qed_chain *p_chain)
 /**
  * @brief qed_chain_reset - Resets the chain to its start state
  *
- * @param p_chain pointer to a previously allocted chain
+ * @param p_chain pointer to a previously allocated chain
  */
 static inline void qed_chain_reset(struct qed_chain *p_chain)
 {
index de08264..fd02c5f 100644 (file)
@@ -86,6 +86,12 @@ void rcu_sched_clock_irq(int user);
 void rcu_report_dead(unsigned int cpu);
 void rcutree_migrate_callbacks(int cpu);
 
+#ifdef CONFIG_TASKS_RCU_GENERIC
+void rcu_init_tasks_generic(void);
+#else
+static inline void rcu_init_tasks_generic(void) { }
+#endif
+
 #ifdef CONFIG_RCU_STALL_COMMON
 void rcu_sysrq_start(void);
 void rcu_sysrq_end(void);
index 2024944..20e84a8 100644 (file)
@@ -331,6 +331,12 @@ regulator_get_exclusive(struct device *dev, const char *id)
        return ERR_PTR(-ENODEV);
 }
 
+static inline struct regulator *__must_check
+devm_regulator_get_exclusive(struct device *dev, const char *id)
+{
+       return ERR_PTR(-ENODEV);
+}
+
 static inline struct regulator *__must_check
 regulator_get_optional(struct device *dev, const char *id)
 {
@@ -486,6 +492,11 @@ static inline int regulator_get_voltage(struct regulator *regulator)
        return -EINVAL;
 }
 
+static inline int regulator_sync_voltage(struct regulator *regulator)
+{
+       return -EINVAL;
+}
+
 static inline int regulator_is_supported_voltage(struct regulator *regulator,
                                   int min_uV, int max_uV)
 {
@@ -578,6 +589,25 @@ static inline int devm_regulator_unregister_notifier(struct regulator *regulator
        return 0;
 }
 
+static inline int regulator_suspend_enable(struct regulator_dev *rdev,
+                                          suspend_state_t state)
+{
+       return -EINVAL;
+}
+
+static inline int regulator_suspend_disable(struct regulator_dev *rdev,
+                                           suspend_state_t state)
+{
+       return -EINVAL;
+}
+
+static inline int regulator_set_suspend_voltage(struct regulator *regulator,
+                                               int min_uV, int max_uV,
+                                               suspend_state_t state)
+{
+       return -EINVAL;
+}
+
 static inline void *regulator_get_drvdata(struct regulator *regulator)
 {
        return NULL;
index 6470516..82b2115 100644 (file)
@@ -3,8 +3,6 @@
 
 struct notifier_block;
 
-#if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON)
-
 /**
  * enum qcom_ssr_notify_type - Startup/Shutdown events related to a remoteproc
  * processor.
@@ -26,6 +24,8 @@ struct qcom_ssr_notify_data {
        bool crashed;
 };
 
+#if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON)
+
 void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb);
 int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb);
 
index 9874f6f..1ac79bc 100644 (file)
@@ -44,6 +44,9 @@
 #define SZ_2G                          0x80000000
 
 #define SZ_4G                          _AC(0x100000000, ULL)
+#define SZ_8G                          _AC(0x200000000, ULL)
+#define SZ_16G                         _AC(0x400000000, ULL)
+#define SZ_32G                         _AC(0x800000000, ULL)
 #define SZ_64T                         _AC(0x400000000000, ULL)
 
 #endif /* __LINUX_SIZES_H__ */
index 333bcdc..9313b5a 100644 (file)
@@ -366,7 +366,7 @@ static inline void skb_frag_size_sub(skb_frag_t *frag, int delta)
 static inline bool skb_frag_must_loop(struct page *p)
 {
 #if defined(CONFIG_HIGHMEM)
-       if (PageHighMem(p))
+       if (IS_ENABLED(CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP) || PageHighMem(p))
                return true;
 #endif
        return false;
@@ -430,28 +430,32 @@ enum {
        /* device driver is going to provide hardware time stamp */
        SKBTX_IN_PROGRESS = 1 << 2,
 
-       /* device driver supports TX zero-copy buffers */
-       SKBTX_DEV_ZEROCOPY = 1 << 3,
-
        /* generate wifi status information (where possible) */
        SKBTX_WIFI_STATUS = 1 << 4,
 
-       /* This indicates at least one fragment might be overwritten
-        * (as in vmsplice(), sendfile() ...)
-        * If we need to compute a TX checksum, we'll need to copy
-        * all frags to avoid possible bad checksum
-        */
-       SKBTX_SHARED_FRAG = 1 << 5,
-
        /* generate software time stamp when entering packet scheduling */
        SKBTX_SCHED_TSTAMP = 1 << 6,
 };
 
-#define SKBTX_ZEROCOPY_FRAG    (SKBTX_DEV_ZEROCOPY | SKBTX_SHARED_FRAG)
 #define SKBTX_ANY_SW_TSTAMP    (SKBTX_SW_TSTAMP    | \
                                 SKBTX_SCHED_TSTAMP)
 #define SKBTX_ANY_TSTAMP       (SKBTX_HW_TSTAMP | SKBTX_ANY_SW_TSTAMP)
 
+/* Definitions for flags in struct skb_shared_info */
+enum {
+       /* use zcopy routines */
+       SKBFL_ZEROCOPY_ENABLE = BIT(0),
+
+       /* This indicates at least one fragment might be overwritten
+        * (as in vmsplice(), sendfile() ...)
+        * If we need to compute a TX checksum, we'll need to copy
+        * all frags to avoid possible bad checksum
+        */
+       SKBFL_SHARED_FRAG = BIT(1),
+};
+
+#define SKBFL_ZEROCOPY_FRAG    (SKBFL_ZEROCOPY_ENABLE | SKBFL_SHARED_FRAG)
+
 /*
  * The callback notifies userspace to release buffers when skb DMA is done in
  * lower device, the skb last reference should be 0 when calling this.
@@ -461,7 +465,8 @@ enum {
  * The desc field is used to track userspace buffer index.
  */
 struct ubuf_info {
-       void (*callback)(struct ubuf_info *, bool zerocopy_success);
+       void (*callback)(struct sk_buff *, struct ubuf_info *,
+                        bool zerocopy_success);
        union {
                struct {
                        unsigned long desc;
@@ -475,6 +480,7 @@ struct ubuf_info {
                };
        };
        refcount_t refcnt;
+       u8 flags;
 
        struct mmpin {
                struct user_struct *user;
@@ -487,19 +493,14 @@ struct ubuf_info {
 int mm_account_pinned_pages(struct mmpin *mmp, size_t size);
 void mm_unaccount_pinned_pages(struct mmpin *mmp);
 
-struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size);
-struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size,
-                                       struct ubuf_info *uarg);
+struct ubuf_info *msg_zerocopy_alloc(struct sock *sk, size_t size);
+struct ubuf_info *msg_zerocopy_realloc(struct sock *sk, size_t size,
+                                      struct ubuf_info *uarg);
 
-static inline void sock_zerocopy_get(struct ubuf_info *uarg)
-{
-       refcount_inc(&uarg->refcnt);
-}
+void msg_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref);
 
-void sock_zerocopy_put(struct ubuf_info *uarg);
-void sock_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref);
-
-void sock_zerocopy_callback(struct ubuf_info *uarg, bool success);
+void msg_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *uarg,
+                          bool success);
 
 int skb_zerocopy_iter_dgram(struct sk_buff *skb, struct msghdr *msg, int len);
 int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
@@ -510,7 +511,7 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
  * the end of the header data, ie. at skb->end.
  */
 struct skb_shared_info {
-       __u8            __unused;
+       __u8            flags;
        __u8            meta_len;
        __u8            nr_frags;
        __u8            tx_flags;
@@ -1203,6 +1204,7 @@ struct skb_seq_state {
        struct sk_buff  *root_skb;
        struct sk_buff  *cur_skb;
        __u8            *frag_data;
+       __u32           frag_off;
 };
 
 void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from,
@@ -1351,8 +1353,8 @@ void
 skb_flow_dissect_ct(const struct sk_buff *skb,
                    struct flow_dissector *flow_dissector,
                    void *target_container,
-                   u16 *ctinfo_map,
-                   size_t mapsize);
+                   u16 *ctinfo_map, size_t mapsize,
+                   bool post_ct);
 void
 skb_flow_dissect_tunnel_info(const struct sk_buff *skb,
                             struct flow_dissector *flow_dissector,
@@ -1437,11 +1439,22 @@ static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb)
 
 static inline struct ubuf_info *skb_zcopy(struct sk_buff *skb)
 {
-       bool is_zcopy = skb && skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY;
+       bool is_zcopy = skb && skb_shinfo(skb)->flags & SKBFL_ZEROCOPY_ENABLE;
 
        return is_zcopy ? skb_uarg(skb) : NULL;
 }
 
+static inline void net_zcopy_get(struct ubuf_info *uarg)
+{
+       refcount_inc(&uarg->refcnt);
+}
+
+static inline void skb_zcopy_init(struct sk_buff *skb, struct ubuf_info *uarg)
+{
+       skb_shinfo(skb)->destructor_arg = uarg;
+       skb_shinfo(skb)->flags |= uarg->flags;
+}
+
 static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg,
                                 bool *have_ref)
 {
@@ -1449,16 +1462,15 @@ static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg,
                if (unlikely(have_ref && *have_ref))
                        *have_ref = false;
                else
-                       sock_zerocopy_get(uarg);
-               skb_shinfo(skb)->destructor_arg = uarg;
-               skb_shinfo(skb)->tx_flags |= SKBTX_ZEROCOPY_FRAG;
+                       net_zcopy_get(uarg);
+               skb_zcopy_init(skb, uarg);
        }
 }
 
 static inline void skb_zcopy_set_nouarg(struct sk_buff *skb, void *val)
 {
        skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t) val | 0x1UL);
-       skb_shinfo(skb)->tx_flags |= SKBTX_ZEROCOPY_FRAG;
+       skb_shinfo(skb)->flags |= SKBFL_ZEROCOPY_FRAG;
 }
 
 static inline bool skb_zcopy_is_nouarg(struct sk_buff *skb)
@@ -1471,33 +1483,32 @@ static inline void *skb_zcopy_get_nouarg(struct sk_buff *skb)
        return (void *)((uintptr_t) skb_shinfo(skb)->destructor_arg & ~0x1UL);
 }
 
-/* Release a reference on a zerocopy structure */
-static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy)
+static inline void net_zcopy_put(struct ubuf_info *uarg)
 {
-       struct ubuf_info *uarg = skb_zcopy(skb);
+       if (uarg)
+               uarg->callback(NULL, uarg, true);
+}
 
+static inline void net_zcopy_put_abort(struct ubuf_info *uarg, bool have_uref)
+{
        if (uarg) {
-               if (skb_zcopy_is_nouarg(skb)) {
-                       /* no notification callback */
-               } else if (uarg->callback == sock_zerocopy_callback) {
-                       uarg->zerocopy = uarg->zerocopy && zerocopy;
-                       sock_zerocopy_put(uarg);
-               } else {
-                       uarg->callback(uarg, zerocopy);
-               }
-
-               skb_shinfo(skb)->tx_flags &= ~SKBTX_ZEROCOPY_FRAG;
+               if (uarg->callback == msg_zerocopy_callback)
+                       msg_zerocopy_put_abort(uarg, have_uref);
+               else if (have_uref)
+                       net_zcopy_put(uarg);
        }
 }
 
-/* Abort a zerocopy operation and revert zckey on error in send syscall */
-static inline void skb_zcopy_abort(struct sk_buff *skb)
+/* Release a reference on a zerocopy structure */
+static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy_success)
 {
        struct ubuf_info *uarg = skb_zcopy(skb);
 
        if (uarg) {
-               sock_zerocopy_put_abort(uarg, false);
-               skb_shinfo(skb)->tx_flags &= ~SKBTX_ZEROCOPY_FRAG;
+               if (!skb_zcopy_is_nouarg(skb))
+                       uarg->callback(skb, uarg, zerocopy_success);
+
+               skb_shinfo(skb)->flags &= ~SKBFL_ZEROCOPY_FRAG;
        }
 }
 
@@ -2776,7 +2787,7 @@ static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask)
        if (likely(!skb_zcopy(skb)))
                return 0;
        if (!skb_zcopy_is_nouarg(skb) &&
-           skb_uarg(skb)->callback == sock_zerocopy_callback)
+           skb_uarg(skb)->callback == msg_zerocopy_callback)
                return 0;
        return skb_copy_ubufs(skb, gfp_mask);
 }
@@ -3323,7 +3334,7 @@ static inline int skb_linearize(struct sk_buff *skb)
 static inline bool skb_has_shared_frag(const struct sk_buff *skb)
 {
        return skb_is_nonlinear(skb) &&
-              skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG;
+              skb_shinfo(skb)->flags & SKBFL_SHARED_FRAG;
 }
 
 /**
@@ -3848,7 +3859,7 @@ static inline bool skb_defer_rx_timestamp(struct sk_buff *skb)
 void skb_complete_tx_timestamp(struct sk_buff *skb,
                               struct skb_shared_hwtstamps *hwtstamps);
 
-void __skb_tstamp_tx(struct sk_buff *orig_skb,
+void __skb_tstamp_tx(struct sk_buff *orig_skb, const struct sk_buff *ack_skb,
                     struct skb_shared_hwtstamps *hwtstamps,
                     struct sock *sk, int tstype);
 
@@ -4610,6 +4621,11 @@ static inline void skb_reset_redirect(struct sk_buff *skb)
 #endif
 }
 
+static inline bool skb_csum_is_sctp(struct sk_buff *skb)
+{
+       return skb->csum_not_inet;
+}
+
 static inline void skb_set_kcov_handle(struct sk_buff *skb,
                                       const u64 kcov_handle)
 {
index e600bae..afd4721 100644 (file)
@@ -11,6 +11,8 @@
 struct device;
 struct firmware;
 
+#if IS_ENABLED(CONFIG_QCOM_MDT_LOADER)
+
 ssize_t qcom_mdt_get_size(const struct firmware *fw);
 int qcom_mdt_load(struct device *dev, const struct firmware *fw,
                  const char *fw_name, int pas_id, void *mem_region,
@@ -23,4 +25,37 @@ int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw,
                          phys_addr_t *reloc_base);
 void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len);
 
+#else /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */
+
+static inline ssize_t qcom_mdt_get_size(const struct firmware *fw)
+{
+       return -ENODEV;
+}
+
+static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw,
+                               const char *fw_name, int pas_id,
+                               void *mem_region, phys_addr_t mem_phys,
+                               size_t mem_size, phys_addr_t *reloc_base)
+{
+       return -ENODEV;
+}
+
+static inline int qcom_mdt_load_no_init(struct device *dev,
+                                       const struct firmware *fw,
+                                       const char *fw_name, int pas_id,
+                                       void *mem_region, phys_addr_t mem_phys,
+                                       size_t mem_size,
+                                       phys_addr_t *reloc_base)
+{
+       return -ENODEV;
+}
+
+static inline void *qcom_mdt_read_metadata(const struct firmware *fw,
+                                          size_t *data_len)
+{
+       return ERR_PTR(-ENODEV);
+}
+
+#endif /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */
+
 #endif
index f3929af..7688bc9 100644 (file)
@@ -251,6 +251,30 @@ static inline int is_syscall_trace_event(struct trace_event_call *tp_event)
        static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
 #endif /* __SYSCALL_DEFINEx */
 
+/* For split 64-bit arguments on 32-bit architectures */
+#ifdef __LITTLE_ENDIAN
+#define SC_ARG64(name) u32, name##_lo, u32, name##_hi
+#else
+#define SC_ARG64(name) u32, name##_hi, u32, name##_lo
+#endif
+#define SC_VAL64(type, name) ((type) name##_hi << 32 | name##_lo)
+
+#ifdef CONFIG_COMPAT
+#define SYSCALL32_DEFINE1 COMPAT_SYSCALL_DEFINE1
+#define SYSCALL32_DEFINE2 COMPAT_SYSCALL_DEFINE2
+#define SYSCALL32_DEFINE3 COMPAT_SYSCALL_DEFINE3
+#define SYSCALL32_DEFINE4 COMPAT_SYSCALL_DEFINE4
+#define SYSCALL32_DEFINE5 COMPAT_SYSCALL_DEFINE5
+#define SYSCALL32_DEFINE6 COMPAT_SYSCALL_DEFINE6
+#else
+#define SYSCALL32_DEFINE1 SYSCALL_DEFINE1
+#define SYSCALL32_DEFINE2 SYSCALL_DEFINE2
+#define SYSCALL32_DEFINE3 SYSCALL_DEFINE3
+#define SYSCALL32_DEFINE4 SYSCALL_DEFINE4
+#define SYSCALL32_DEFINE5 SYSCALL_DEFINE5
+#define SYSCALL32_DEFINE6 SYSCALL_DEFINE6
+#endif
+
 /*
  * Called before coming back to user-mode. Returning to user-mode with an
  * address limit different than USER_DS can allow to overwrite kernel memory.
index 2f87377..48d8a36 100644 (file)
@@ -496,7 +496,8 @@ static inline u32 tcp_saved_syn_len(const struct saved_syn *saved_syn)
 }
 
 struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
-                                              const struct sk_buff *orig_skb);
+                                              const struct sk_buff *orig_skb,
+                                              const struct sk_buff *ack_skb);
 
 static inline u16 tcp_mss_clamp(const struct tcp_sock *tp, u16 mss)
 {
diff --git a/include/linux/timekeeping32.h b/include/linux/timekeeping32.h
deleted file mode 100644 (file)
index 266017f..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef _LINUX_TIMEKEEPING32_H
-#define _LINUX_TIMEKEEPING32_H
-/*
- * These interfaces are all based on the old timespec type
- * and should get replaced with the timespec64 based versions
- * over time so we can remove the file here.
- */
-
-static inline unsigned long get_seconds(void)
-{
-       return ktime_get_real_seconds();
-}
-
-#endif
index c873f47..37803f3 100644 (file)
@@ -421,6 +421,7 @@ extern void tty_kclose(struct tty_struct *tty);
 extern int tty_dev_name_to_number(const char *name, dev_t *number);
 extern int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout);
 extern void tty_ldisc_unlock(struct tty_struct *tty);
+extern ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *);
 #else
 static inline void tty_kref_put(struct tty_struct *tty)
 { }
index 88a7673..cfbfd6f 100644 (file)
@@ -81,6 +81,8 @@ struct usbnet {
 #              define EVENT_LINK_CHANGE        11
 #              define EVENT_SET_RX_MODE        12
 #              define EVENT_NO_IP_ALIGN        13
+       u32                     rx_speed;       /* in bps - NOT Mbps */
+       u32                     tx_speed;       /* in bps - NOT Mbps */
 };
 
 static inline struct usb_driver *driver_of(struct usb_interface *intf)
index be36cbd..3eb2022 100644 (file)
@@ -520,7 +520,7 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat,
                        u32 width, u32 height);
 
 /**
- * v4l2_get_link_rate - Get link rate from transmitter
+ * v4l2_get_link_freq - Get link rate from transmitter
  *
  * @handler: The transmitter's control handler
  * @mul: The multiplier between pixel rate and link frequency. Bits per pixel on
@@ -537,7 +537,7 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat,
  *     -ENOENT: Link frequency or pixel rate control not found
  *     -EINVAL: Invalid link frequency value
  */
-s64 v4l2_get_link_rate(struct v4l2_ctrl_handler *handler, unsigned int mul,
+s64 v4l2_get_link_freq(struct v4l2_ctrl_handler *handler, unsigned int mul,
                       unsigned int div);
 
 static inline u64 v4l2_buffer_get_timestamp(const struct v4l2_buffer *buf)
index 55dab60..761c0e3 100644 (file)
@@ -186,10 +186,13 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
                    struct nlattr *est, char *name, int ovr, int bind,
                    struct tc_action *actions[], size_t *attr_size,
                    bool rtnl_held, struct netlink_ext_ack *extack);
+struct tc_action_ops *tc_action_load_ops(char *name, struct nlattr *nla,
+                                        bool rtnl_held,
+                                        struct netlink_ext_ack *extack);
 struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
                                    struct nlattr *nla, struct nlattr *est,
                                    char *name, int ovr, int bind,
-                                   bool rtnl_held,
+                                   struct tc_action_ops *ops, bool rtnl_held,
                                    struct netlink_ext_ack *extack);
 int tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[], int bind,
                    int ref, bool terse);
index adc3da7..019e998 100644 (file)
@@ -89,6 +89,8 @@
 #define BOND_XFRM_FEATURES (NETIF_F_HW_ESP | NETIF_F_HW_ESP_TX_CSUM | \
                            NETIF_F_GSO_ESP)
 
+#define BOND_TLS_FEATURES (NETIF_F_HW_TLS_TX | NETIF_F_HW_TLS_RX)
+
 #ifdef CONFIG_NET_POLL_CONTROLLER
 extern atomic_t netpoll_block_tx;
 
@@ -265,6 +267,8 @@ struct bond_vlan_tag {
        unsigned short  vlan_id;
 };
 
+bool bond_sk_check(struct bonding *bond);
+
 /**
  * Returns NULL if the net_device does not belong to any of the bond's slaves
  *
index 9a4bbcc..4741d71 100644 (file)
@@ -1460,6 +1460,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @RATE_INFO_FLAGS_DMG: 60GHz MCS
  * @RATE_INFO_FLAGS_HE_MCS: HE MCS information
  * @RATE_INFO_FLAGS_EDMG: 60GHz MCS in EDMG mode
+ * @RATE_INFO_FLAGS_EXTENDED_SC_DMG: 60GHz extended SC MCS
  */
 enum rate_info_flags {
        RATE_INFO_FLAGS_MCS                     = BIT(0),
@@ -1468,6 +1469,7 @@ enum rate_info_flags {
        RATE_INFO_FLAGS_DMG                     = BIT(3),
        RATE_INFO_FLAGS_HE_MCS                  = BIT(4),
        RATE_INFO_FLAGS_EDMG                    = BIT(5),
+       RATE_INFO_FLAGS_EXTENDED_SC_DMG         = BIT(6),
 };
 
 /**
@@ -1756,7 +1758,7 @@ struct cfg80211_sar_specs {
 
 
 /**
- * @struct cfg80211_sar_chan_ranges - sar frequency ranges
+ * struct cfg80211_sar_freq_ranges - sar frequency ranges
  * @start_freq:  start range edge frequency
  * @end_freq:    end range edge frequency
  */
@@ -3630,9 +3632,10 @@ struct mgmt_frame_regs {
  * All callbacks except where otherwise noted should return 0
  * on success or a negative error code.
  *
- * All operations are currently invoked under rtnl for consistency with the
- * wireless extensions but this is subject to reevaluation as soon as this
- * code is used more widely and we have a first user without wext.
+ * All operations are invoked with the wiphy mutex held. The RTNL may be
+ * held in addition (due to wireless extensions) but this cannot be relied
+ * upon except in cases where documented below. Note that due to ordering,
+ * the RTNL also cannot be acquired in any handlers.
  *
  * @suspend: wiphy device needs to be suspended. The variable @wow will
  *     be %NULL or contain the enabled Wake-on-Wireless triggers that are
@@ -3647,11 +3650,14 @@ struct mgmt_frame_regs {
  *     the new netdev in the wiphy's network namespace! Returns the struct
  *     wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
  *     also set the address member in the wdev.
+ *     This additionally holds the RTNL to be able to do netdev changes.
  *
  * @del_virtual_intf: remove the virtual interface
+ *     This additionally holds the RTNL to be able to do netdev changes.
  *
  * @change_virtual_intf: change type/configuration of virtual interface,
  *     keep the struct wireless_dev's iftype updated.
+ *     This additionally holds the RTNL to be able to do netdev changes.
  *
  * @add_key: add a key with the given parameters. @mac_addr will be %NULL
  *     when adding a group key.
@@ -3972,6 +3978,8 @@ struct mgmt_frame_regs {
  *     This callback may sleep.
  * @reset_tid_config: Reset TID specific configuration for the peer, for the
  *     given TIDs. This callback may sleep.
+ *
+ * @set_sar_specs: Update the SAR (TX power) settings.
  */
 struct cfg80211_ops {
        int     (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -4739,6 +4747,7 @@ struct wiphy_iftype_akm_suites {
 
 /**
  * struct wiphy - wireless hardware description
+ * @mtx: mutex for the data (structures) of this device
  * @reg_notifier: the driver's regulatory notification callback,
  *     note that if your driver uses wiphy_apply_custom_regulatory()
  *     the reg_notifier's request can be passed as NULL
@@ -4929,8 +4938,11 @@ struct wiphy_iftype_akm_suites {
  * @max_data_retry_count: maximum supported per TID retry count for
  *     configuration through the %NL80211_TID_CONFIG_ATTR_RETRY_SHORT and
  *     %NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes
+ * @sar_capa: SAR control capabilities
  */
 struct wiphy {
+       struct mutex mtx;
+
        /* assign these fields before you register the wiphy */
 
        u8 perm_addr[ETH_ALEN];
@@ -5183,6 +5195,37 @@ static inline struct wiphy *wiphy_new(const struct cfg80211_ops *ops,
  */
 int wiphy_register(struct wiphy *wiphy);
 
+/* this is a define for better error reporting (file/line) */
+#define lockdep_assert_wiphy(wiphy) lockdep_assert_held(&(wiphy)->mtx)
+
+/**
+ * rcu_dereference_wiphy - rcu_dereference with debug checking
+ * @wiphy: the wiphy to check the locking on
+ * @p: The pointer to read, prior to dereferencing
+ *
+ * Do an rcu_dereference(p), but check caller either holds rcu_read_lock()
+ * or RTNL. Note: Please prefer wiphy_dereference() or rcu_dereference().
+ */
+#define rcu_dereference_wiphy(wiphy, p)                                \
+        rcu_dereference_check(p, lockdep_is_held(&wiphy->mtx))
+
+/**
+ * wiphy_dereference - fetch RCU pointer when updates are prevented by wiphy mtx
+ * @wiphy: the wiphy to check the locking on
+ * @p: The pointer to read, prior to dereferencing
+ *
+ * Return the value of the specified RCU-protected pointer, but omit the
+ * READ_ONCE(), because caller holds the wiphy mutex used for updates.
+ */
+#define wiphy_dereference(wiphy, p)                            \
+        rcu_dereference_protected(p, lockdep_is_held(&wiphy->mtx))
+
+/**
+ * get_wiphy_regdom - get custom regdomain for the given wiphy
+ * @wiphy: the wiphy to get the regdomain from
+ */
+const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);
+
 /**
  * wiphy_unregister - deregister a wiphy from cfg80211
  *
@@ -5207,6 +5250,35 @@ struct cfg80211_internal_bss;
 struct cfg80211_cached_keys;
 struct cfg80211_cqm_config;
 
+/**
+ * wiphy_lock - lock the wiphy
+ * @wiphy: the wiphy to lock
+ *
+ * This is mostly exposed so it can be done around registering and
+ * unregistering netdevs that aren't created through cfg80211 calls,
+ * since that requires locking in cfg80211 when the notifiers is
+ * called, but that cannot differentiate which way it's called.
+ *
+ * When cfg80211 ops are called, the wiphy is already locked.
+ */
+static inline void wiphy_lock(struct wiphy *wiphy)
+       __acquires(&wiphy->mtx)
+{
+       mutex_lock(&wiphy->mtx);
+       __acquire(&wiphy->mtx);
+}
+
+/**
+ * wiphy_unlock - unlock the wiphy again
+ * @wiphy: the wiphy to unlock
+ */
+static inline void wiphy_unlock(struct wiphy *wiphy)
+       __releases(&wiphy->mtx)
+{
+       __release(&wiphy->mtx);
+       mutex_unlock(&wiphy->mtx);
+}
+
 /**
  * struct wireless_dev - wireless device state
  *
@@ -5214,7 +5286,10 @@ struct cfg80211_cqm_config;
  * that uses the ieee80211_ptr field in struct net_device (this
  * is intentional so it can be allocated along with the netdev.)
  * It need not be registered then as netdev registration will
- * be intercepted by cfg80211 to see the new wireless device.
+ * be intercepted by cfg80211 to see the new wireless device,
+ * however, drivers must lock the wiphy before registering or
+ * unregistering netdevs if they pre-create any netdevs (in ops
+ * called from cfg80211, the wiphy is already locked.)
  *
  * For non-netdev uses, it must also be allocated by the driver
  * in response to the cfg80211 callbacks that require it, as
@@ -5223,6 +5298,7 @@ struct cfg80211_cqm_config;
  *
  * @wiphy: pointer to hardware description
  * @iftype: interface type
+ * @registered: is this wdev already registered with cfg80211
  * @list: (private) Used to collect the interfaces
  * @netdev: (private) Used to reference back to the netdev, may be %NULL
  * @identifier: (private) Identifier used in nl80211 to identify this
@@ -5306,7 +5382,7 @@ struct wireless_dev {
 
        struct mutex mtx;
 
-       bool use_4addr, is_running;
+       bool use_4addr, is_running, registered;
 
        u8 address[ETH_ALEN] __aligned(sizeof(u16));
 
@@ -5975,18 +6051,18 @@ int regulatory_set_wiphy_regd(struct wiphy *wiphy,
                              struct ieee80211_regdomain *rd);
 
 /**
- * regulatory_set_wiphy_regd_sync_rtnl - set regdom for self-managed drivers
+ * regulatory_set_wiphy_regd_sync - set regdom for self-managed drivers
  * @wiphy: the wireless device we want to process the regulatory domain on
  * @rd: the regulatory domain information to use for this wiphy
  *
- * This functions requires the RTNL to be held and applies the new regdomain
- * synchronously to this wiphy. For more details see
- * regulatory_set_wiphy_regd().
+ * This functions requires the RTNL and the wiphy mutex to be held and
+ * applies the new regdomain synchronously to this wiphy. For more details
+ * see regulatory_set_wiphy_regd().
  *
  * Return: 0 on success. -EINVAL, -EPERM
  */
-int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
-                                       struct ieee80211_regdomain *rd);
+int regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
+                                  struct ieee80211_regdomain *rd);
 
 /**
  * wiphy_apply_custom_regulatory - apply a custom driver regulatory domain
@@ -6104,7 +6180,7 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid);
 void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid);
 
 /**
- * cfg80211_sched_scan_stopped_rtnl - notify that the scheduled scan has stopped
+ * cfg80211_sched_scan_stopped_locked - notify that the scheduled scan has stopped
  *
  * @wiphy: the wiphy on which the scheduled scan stopped
  * @reqid: identifier for the related scheduled scan request
@@ -6112,9 +6188,9 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid);
  * The driver can call this function to inform cfg80211 that the
  * scheduled scan had to be stopped, for whatever reason.  The driver
  * is then called back via the sched_scan_stop operation when done.
- * This function should be called with rtnl locked.
+ * This function should be called with the wiphy mutex held.
  */
-void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid);
+void cfg80211_sched_scan_stopped_locked(struct wiphy *wiphy, u64 reqid);
 
 /**
  * cfg80211_inform_bss_frame_data - inform cfg80211 of a received BSS frame
@@ -7551,7 +7627,7 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
  * also checks if IR-relaxation conditions apply, to allow beaconing under
  * more permissive conditions.
  *
- * Requires the RTNL to be held.
+ * Requires the wiphy mutex to be held.
  */
 bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
                                   struct cfg80211_chan_def *chandef,
@@ -7649,18 +7725,45 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate);
  * cfg80211_unregister_wdev - remove the given wdev
  * @wdev: struct wireless_dev to remove
  *
- * Call this function only for wdevs that have no netdev assigned,
- * e.g. P2P Devices. It removes the device from the list so that
- * it can no longer be used. It is necessary to call this function
- * even when cfg80211 requests the removal of the interface by
- * calling the del_virtual_intf() callback. The function must also
- * be called when the driver wishes to unregister the wdev, e.g.
- * when the device is unbound from the driver.
+ * This function removes the device so it can no longer be used. It is necessary
+ * to call this function even when cfg80211 requests the removal of the device
+ * by calling the del_virtual_intf() callback. The function must also be called
+ * when the driver wishes to unregister the wdev, e.g. when the hardware device
+ * is unbound from the driver.
  *
- * Requires the RTNL to be held.
+ * Requires the RTNL and wiphy mutex to be held.
  */
 void cfg80211_unregister_wdev(struct wireless_dev *wdev);
 
+/**
+ * cfg80211_register_netdevice - register the given netdev
+ * @dev: the netdev to register
+ *
+ * Note: In contexts coming from cfg80211 callbacks, you must call this rather
+ * than register_netdevice(), unregister_netdev() is impossible as the RTNL is
+ * held. Otherwise, both register_netdevice() and register_netdev() are usable
+ * instead as well.
+ *
+ * Requires the RTNL and wiphy mutex to be held.
+ */
+int cfg80211_register_netdevice(struct net_device *dev);
+
+/**
+ * cfg80211_unregister_netdevice - unregister the given netdev
+ * @dev: the netdev to register
+ *
+ * Note: In contexts coming from cfg80211 callbacks, you must call this rather
+ * than unregister_netdevice(), unregister_netdev() is impossible as the RTNL
+ * is held. Otherwise, both unregister_netdevice() and unregister_netdev() are
+ * usable instead as well.
+ *
+ * Requires the RTNL and wiphy mutex to be held.
+ */
+static inline void cfg80211_unregister_netdevice(struct net_device *dev)
+{
+       cfg80211_unregister_wdev(dev->ieee80211_ptr);
+}
+
 /**
  * struct cfg80211_ft_event_params - FT Information Elements
  * @ies: FT IEs
index f466819..47b4b06 100644 (file)
@@ -93,6 +93,18 @@ struct devlink_port_pci_vf_attrs {
        u8 external:1;
 };
 
+/**
+ * struct devlink_port_pci_sf_attrs - devlink port's PCI SF attributes
+ * @controller: Associated controller number
+ * @sf: Associated PCI SF for of the PCI PF for this port.
+ * @pf: Associated PCI PF number for this port.
+ */
+struct devlink_port_pci_sf_attrs {
+       u32 controller;
+       u32 sf;
+       u16 pf;
+};
+
 /**
  * struct devlink_port_attrs - devlink port object
  * @flavour: flavour of the port
@@ -103,6 +115,7 @@ struct devlink_port_pci_vf_attrs {
  * @phys: physical port attributes
  * @pci_pf: PCI PF port attributes
  * @pci_vf: PCI VF port attributes
+ * @pci_sf: PCI SF port attributes
  */
 struct devlink_port_attrs {
        u8 split:1,
@@ -114,6 +127,7 @@ struct devlink_port_attrs {
                struct devlink_port_phys_attrs phys;
                struct devlink_port_pci_pf_attrs pci_pf;
                struct devlink_port_pci_vf_attrs pci_vf;
+               struct devlink_port_pci_sf_attrs pci_sf;
        };
 };
 
@@ -138,6 +152,17 @@ struct devlink_port {
        struct mutex reporters_lock; /* Protects reporter_list */
 };
 
+struct devlink_port_new_attrs {
+       enum devlink_port_flavour flavour;
+       unsigned int port_index;
+       u32 controller;
+       u32 sfnum;
+       u16 pfnum;
+       u8 port_index_valid:1,
+          controller_valid:1,
+          sfnum_valid:1;
+};
+
 struct devlink_sb_pool_info {
        enum devlink_sb_pool_type pool_type;
        u32 size;
@@ -380,6 +405,8 @@ struct devlink_resource {
 
 #define DEVLINK_RESOURCE_ID_PARENT_TOP 0
 
+#define DEVLINK_RESOURCE_GENERIC_NAME_PORTS "physical_ports"
+
 #define __DEVLINK_PARAM_MAX_STRING_VALUE 32
 enum devlink_param_type {
        DEVLINK_PARAM_TYPE_U8,
@@ -836,6 +863,7 @@ enum devlink_trap_generic_id {
        DEVLINK_TRAP_GENERIC_ID_GTP_PARSING,
        DEVLINK_TRAP_GENERIC_ID_ESP_PARSING,
        DEVLINK_TRAP_GENERIC_ID_BLACKHOLE_NEXTHOP,
+       DEVLINK_TRAP_GENERIC_ID_DMAC_FILTER,
 
        /* Add new generic trap IDs above */
        __DEVLINK_TRAP_GENERIC_ID_MAX,
@@ -1061,6 +1089,8 @@ enum devlink_trap_group_generic_id {
        "esp_parsing"
 #define DEVLINK_TRAP_GENERIC_NAME_BLACKHOLE_NEXTHOP \
        "blackhole_nexthop"
+#define DEVLINK_TRAP_GENERIC_NAME_DMAC_FILTER \
+       "dest_mac_filter"
 
 #define DEVLINK_TRAP_GROUP_GENERIC_NAME_L2_DROPS \
        "l2_drops"
@@ -1348,6 +1378,79 @@ struct devlink_ops {
        int (*port_function_hw_addr_set)(struct devlink *devlink, struct devlink_port *port,
                                         const u8 *hw_addr, int hw_addr_len,
                                         struct netlink_ext_ack *extack);
+       /**
+        * port_new() - Add a new port function of a specified flavor
+        * @devlink: Devlink instance
+        * @attrs: attributes of the new port
+        * @extack: extack for reporting error messages
+        * @new_port_index: index of the new port
+        *
+        * Devlink core will call this device driver function upon user request
+        * to create a new port function of a specified flavor and optional
+        * attributes
+        *
+        * Notes:
+        *      - Called without devlink instance lock being held. Drivers must
+        *        implement own means of synchronization
+        *      - On success, drivers must register a port with devlink core
+        *
+        * Return: 0 on success, negative value otherwise.
+        */
+       int (*port_new)(struct devlink *devlink,
+                       const struct devlink_port_new_attrs *attrs,
+                       struct netlink_ext_ack *extack,
+                       unsigned int *new_port_index);
+       /**
+        * port_del() - Delete a port function
+        * @devlink: Devlink instance
+        * @port_index: port function index to delete
+        * @extack: extack for reporting error messages
+        *
+        * Devlink core will call this device driver function upon user request
+        * to delete a previously created port function
+        *
+        * Notes:
+        *      - Called without devlink instance lock being held. Drivers must
+        *        implement own means of synchronization
+        *      - On success, drivers must unregister the corresponding devlink
+        *        port
+        *
+        * Return: 0 on success, negative value otherwise.
+        */
+       int (*port_del)(struct devlink *devlink, unsigned int port_index,
+                       struct netlink_ext_ack *extack);
+       /**
+        * port_fn_state_get() - Get the state of a port function
+        * @devlink: Devlink instance
+        * @port: The devlink port
+        * @state: Admin configured state
+        * @opstate: Current operational state
+        * @extack: extack for reporting error messages
+        *
+        * Reports the admin and operational state of a devlink port function
+        *
+        * Return: 0 on success, negative value otherwise.
+        */
+       int (*port_fn_state_get)(struct devlink *devlink,
+                                struct devlink_port *port,
+                                enum devlink_port_fn_state *state,
+                                enum devlink_port_fn_opstate *opstate,
+                                struct netlink_ext_ack *extack);
+       /**
+        * port_fn_state_set() - Set the admin state of a port function
+        * @devlink: Devlink instance
+        * @port: The devlink port
+        * @state: Admin state
+        * @extack: extack for reporting error messages
+        *
+        * Set the admin state of a devlink port function
+        *
+        * Return: 0 on success, negative value otherwise.
+        */
+       int (*port_fn_state_set)(struct devlink *devlink,
+                                struct devlink_port *port,
+                                enum devlink_port_fn_state state,
+                                struct netlink_ext_ack *extack);
 };
 
 static inline void *devlink_priv(struct devlink *devlink)
@@ -1404,6 +1507,8 @@ void devlink_port_attrs_pci_pf_set(struct devlink_port *devlink_port, u32 contro
                                   u16 pf, bool external);
 void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 controller,
                                   u16 pf, u16 vf, bool external);
+void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port,
+                                  u32 controller, u16 pf, u32 sf);
 int devlink_sb_register(struct devlink *devlink, unsigned int sb_index,
                        u32 size, u16 ingress_pools_count,
                        u16 egress_pools_count, u16 ingress_tc_count,
index 4e60d26..2f5435d 100644 (file)
@@ -46,6 +46,7 @@ struct phylink_link_state;
 #define DSA_TAG_PROTO_AR9331_VALUE             16
 #define DSA_TAG_PROTO_RTL4_A_VALUE             17
 #define DSA_TAG_PROTO_HELLCREEK_VALUE          18
+#define DSA_TAG_PROTO_XRS700X_VALUE            19
 
 enum dsa_tag_protocol {
        DSA_TAG_PROTO_NONE              = DSA_TAG_PROTO_NONE_VALUE,
@@ -67,6 +68,7 @@ enum dsa_tag_protocol {
        DSA_TAG_PROTO_AR9331            = DSA_TAG_PROTO_AR9331_VALUE,
        DSA_TAG_PROTO_RTL4_A            = DSA_TAG_PROTO_RTL4_A_VALUE,
        DSA_TAG_PROTO_HELLCREEK         = DSA_TAG_PROTO_HELLCREEK_VALUE,
+       DSA_TAG_PROTO_XRS700X           = DSA_TAG_PROTO_XRS700X_VALUE,
 };
 
 struct packet_type;
@@ -149,8 +151,41 @@ struct dsa_switch_tree {
 
        /* List of DSA links composing the routing table */
        struct list_head rtable;
+
+       /* Maps offloaded LAG netdevs to a zero-based linear ID for
+        * drivers that need it.
+        */
+       struct net_device **lags;
+       unsigned int lags_len;
 };
 
+#define dsa_lags_foreach_id(_id, _dst)                         \
+       for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++)      \
+               if ((_dst)->lags[(_id)])
+
+#define dsa_lag_foreach_port(_dp, _dst, _lag)                  \
+       list_for_each_entry((_dp), &(_dst)->ports, list)        \
+               if ((_dp)->lag_dev == (_lag))
+
+static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst,
+                                            unsigned int id)
+{
+       return dst->lags[id];
+}
+
+static inline int dsa_lag_id(struct dsa_switch_tree *dst,
+                            struct net_device *lag)
+{
+       unsigned int id;
+
+       dsa_lags_foreach_id(id, dst) {
+               if (dsa_lag_dev(dst, id) == lag)
+                       return id;
+       }
+
+       return -ENODEV;
+}
+
 /* TC matchall action types */
 enum dsa_port_mall_action_type {
        DSA_PORT_MALL_MIRROR,
@@ -220,6 +255,8 @@ struct dsa_port {
        bool                    devlink_port_setup;
        struct phylink          *pl;
        struct phylink_config   pl_config;
+       struct net_device       *lag_dev;
+       bool                    lag_tx_enabled;
 
        struct list_head list;
 
@@ -319,6 +356,11 @@ struct dsa_switch {
         */
        bool                    untag_bridge_pvid;
 
+       /* Let DSA manage the FDB entries towards the CPU, based on the
+        * software bridge database.
+        */
+       bool                    assisted_learning_on_cpu_port;
+
        /* In case vlan_filtering_is_global is set, the VLAN awareness state
         * should be retrieved from here and not from the per-port settings.
         */
@@ -335,6 +377,14 @@ struct dsa_switch {
         */
        bool                    mtu_enforcement_ingress;
 
+       /* Drivers that benefit from having an ID associated with each
+        * offloaded LAG should set this to the maximum number of
+        * supported IDs. DSA will then maintain a mapping of _at
+        * least_ these many IDs, accessible to drivers via
+        * dsa_lag_id().
+        */
+       unsigned int            num_lag_ids;
+
        size_t num_ports;
 };
 
@@ -477,7 +527,7 @@ struct dsa_switch_ops {
        void    (*phylink_fixed_state)(struct dsa_switch *ds, int port,
                                       struct phylink_link_state *state);
        /*
-        * ethtool hardware statistics.
+        * Port statistics counters.
         */
        void    (*get_strings)(struct dsa_switch *ds, int port,
                               u32 stringset, uint8_t *data);
@@ -486,6 +536,8 @@ struct dsa_switch_ops {
        int     (*get_sset_count)(struct dsa_switch *ds, int port, int sset);
        void    (*get_ethtool_phy_stats)(struct dsa_switch *ds,
                                         int port, uint64_t *data);
+       void    (*get_stats64)(struct dsa_switch *ds, int port,
+                                  struct rtnl_link_stats64 *s);
 
        /*
         * ethtool Wake-on-LAN
@@ -560,12 +612,9 @@ struct dsa_switch_ops {
         * VLAN support
         */
        int     (*port_vlan_filtering)(struct dsa_switch *ds, int port,
-                                      bool vlan_filtering,
-                                      struct switchdev_trans *trans);
-       int (*port_vlan_prepare)(struct dsa_switch *ds, int port,
+                                      bool vlan_filtering);
+       int     (*port_vlan_add)(struct dsa_switch *ds, int port,
                                 const struct switchdev_obj_port_vlan *vlan);
-       void (*port_vlan_add)(struct dsa_switch *ds, int port,
-                             const struct switchdev_obj_port_vlan *vlan);
        int     (*port_vlan_del)(struct dsa_switch *ds, int port,
                                 const struct switchdev_obj_port_vlan *vlan);
        /*
@@ -581,10 +630,8 @@ struct dsa_switch_ops {
        /*
         * Multicast database
         */
-       int (*port_mdb_prepare)(struct dsa_switch *ds, int port,
+       int     (*port_mdb_add)(struct dsa_switch *ds, int port,
                                const struct switchdev_obj_port_mdb *mdb);
-       void (*port_mdb_add)(struct dsa_switch *ds, int port,
-                            const struct switchdev_obj_port_mdb *mdb);
        int     (*port_mdb_del)(struct dsa_switch *ds, int port,
                                const struct switchdev_obj_port_mdb *mdb);
        /*
@@ -624,6 +671,13 @@ struct dsa_switch_ops {
        void    (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index,
                                          int sw_index, int port,
                                          struct net_device *br);
+       int     (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index,
+                                       int port);
+       int     (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index,
+                                     int port, struct net_device *lag,
+                                     struct netdev_lag_upper_info *info);
+       int     (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index,
+                                      int port, struct net_device *lag);
 
        /*
         * PTP functionality
@@ -645,6 +699,40 @@ struct dsa_switch_ops {
        int     (*devlink_info_get)(struct dsa_switch *ds,
                                    struct devlink_info_req *req,
                                    struct netlink_ext_ack *extack);
+       int     (*devlink_sb_pool_get)(struct dsa_switch *ds,
+                                      unsigned int sb_index, u16 pool_index,
+                                      struct devlink_sb_pool_info *pool_info);
+       int     (*devlink_sb_pool_set)(struct dsa_switch *ds, unsigned int sb_index,
+                                      u16 pool_index, u32 size,
+                                      enum devlink_sb_threshold_type threshold_type,
+                                      struct netlink_ext_ack *extack);
+       int     (*devlink_sb_port_pool_get)(struct dsa_switch *ds, int port,
+                                           unsigned int sb_index, u16 pool_index,
+                                           u32 *p_threshold);
+       int     (*devlink_sb_port_pool_set)(struct dsa_switch *ds, int port,
+                                           unsigned int sb_index, u16 pool_index,
+                                           u32 threshold,
+                                           struct netlink_ext_ack *extack);
+       int     (*devlink_sb_tc_pool_bind_get)(struct dsa_switch *ds, int port,
+                                              unsigned int sb_index, u16 tc_index,
+                                              enum devlink_sb_pool_type pool_type,
+                                              u16 *p_pool_index, u32 *p_threshold);
+       int     (*devlink_sb_tc_pool_bind_set)(struct dsa_switch *ds, int port,
+                                              unsigned int sb_index, u16 tc_index,
+                                              enum devlink_sb_pool_type pool_type,
+                                              u16 pool_index, u32 threshold,
+                                              struct netlink_ext_ack *extack);
+       int     (*devlink_sb_occ_snapshot)(struct dsa_switch *ds,
+                                          unsigned int sb_index);
+       int     (*devlink_sb_occ_max_clear)(struct dsa_switch *ds,
+                                           unsigned int sb_index);
+       int     (*devlink_sb_occ_port_pool_get)(struct dsa_switch *ds, int port,
+                                               unsigned int sb_index, u16 pool_index,
+                                               u32 *p_cur, u32 *p_max);
+       int     (*devlink_sb_occ_tc_port_bind_get)(struct dsa_switch *ds, int port,
+                                                  unsigned int sb_index, u16 tc_index,
+                                                  enum devlink_sb_pool_type pool_type,
+                                                  u32 *p_cur, u32 *p_max);
 
        /*
         * MTU change functionality. Switches can also adjust their MRU through
@@ -655,6 +743,16 @@ struct dsa_switch_ops {
        int     (*port_change_mtu)(struct dsa_switch *ds, int port,
                                   int new_mtu);
        int     (*port_max_mtu)(struct dsa_switch *ds, int port);
+
+       /*
+        * LAG integration
+        */
+       int     (*port_lag_change)(struct dsa_switch *ds, int port);
+       int     (*port_lag_join)(struct dsa_switch *ds, int port,
+                                struct net_device *lag,
+                                struct netdev_lag_upper_info *info);
+       int     (*port_lag_leave)(struct dsa_switch *ds, int port,
+                                 struct net_device *lag);
 };
 
 #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes)           \
@@ -828,57 +926,15 @@ static inline int dsa_switch_resume(struct dsa_switch *ds)
 }
 #endif /* CONFIG_PM_SLEEP */
 
-enum dsa_notifier_type {
-       DSA_PORT_REGISTER,
-       DSA_PORT_UNREGISTER,
-};
-
-struct dsa_notifier_info {
-       struct net_device *dev;
-};
-
-struct dsa_notifier_register_info {
-       struct dsa_notifier_info info;  /* must be first */
-       struct net_device *master;
-       unsigned int port_number;
-       unsigned int switch_number;
-};
-
-static inline struct net_device *
-dsa_notifier_info_to_dev(const struct dsa_notifier_info *info)
-{
-       return info->dev;
-}
-
 #if IS_ENABLED(CONFIG_NET_DSA)
-int register_dsa_notifier(struct notifier_block *nb);
-int unregister_dsa_notifier(struct notifier_block *nb);
-int call_dsa_notifiers(unsigned long val, struct net_device *dev,
-                      struct dsa_notifier_info *info);
+bool dsa_slave_dev_check(const struct net_device *dev);
 #else
-static inline int register_dsa_notifier(struct notifier_block *nb)
+static inline bool dsa_slave_dev_check(const struct net_device *dev)
 {
-       return 0;
-}
-
-static inline int unregister_dsa_notifier(struct notifier_block *nb)
-{
-       return 0;
-}
-
-static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev,
-                                    struct dsa_notifier_info *info)
-{
-       return NOTIFY_DONE;
+       return false;
 }
 #endif
 
-/* Broadcom tag specific helpers to insert and extract queue/port number */
-#define BRCM_TAG_SET_PORT_QUEUE(p, q)  ((p) << 8 | q)
-#define BRCM_TAG_GET_PORT(v)           ((v) >> 8)
-#define BRCM_TAG_GET_QUEUE(v)          ((v) & 0xff)
-
-
 netdev_tx_t dsa_enqueue_skb(struct sk_buff *skb, struct net_device *dev);
 int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data);
 int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
index 123b1e9..e6bd8eb 100644 (file)
@@ -245,6 +245,7 @@ struct flow_action_entry {
                        unsigned long cookie;
                        u32 mark;
                        u32 labels[4];
+                       bool orig_dir;
                } ct_metadata;
                struct {                                /* FLOW_ACTION_MPLS_PUSH */
                        u32             label;
index e39f3f8..2eccbbd 100644 (file)
@@ -19,8 +19,6 @@ struct fq_tin;
  * @flowchain: can be linked to fq_tin's new_flows or old_flows. Used for DRR++
  *     (deficit round robin) based round robin queuing similar to the one
  *     found in net/sched/sch_fq_codel.c
- * @backlogchain: can be linked to other fq_flow and fq. Used to keep track of
- *     fat flows and efficient head-dropping if packet limit is reached
  * @queue: sk_buff queue to hold packets
  * @backlog: number of bytes pending in the queue. The number of packets can be
  *     found in @queue.qlen
@@ -29,7 +27,6 @@ struct fq_tin;
 struct fq_flow {
        struct fq_tin *tin;
        struct list_head flowchain;
-       struct list_head backlogchain;
        struct sk_buff_head queue;
        u32 backlog;
        int deficit;
@@ -47,6 +44,8 @@ struct fq_flow {
 struct fq_tin {
        struct list_head new_flows;
        struct list_head old_flows;
+       struct list_head tin_list;
+       struct fq_flow default_flow;
        u32 backlog_bytes;
        u32 backlog_packets;
        u32 overlimit;
@@ -59,14 +58,14 @@ struct fq_tin {
 /**
  * struct fq - main container for fair queuing purposes
  *
- * @backlogs: linked to fq_flows. Used to maintain fat flows for efficient
- *     head-dropping when @backlog reaches @limit
  * @limit: max number of packets that can be queued across all flows
  * @backlog: number of packets queued across all flows
  */
 struct fq {
        struct fq_flow *flows;
-       struct list_head backlogs;
+       unsigned long *flows_bitmap;
+
+       struct list_head tin_backlog;
        spinlock_t lock;
        u32 flows_cnt;
        u32 limit;
index e73d74d..a5f67a2 100644 (file)
 
 /* functions that are embedded into includer */
 
-static void fq_adjust_removal(struct fq *fq,
-                             struct fq_flow *flow,
-                             struct sk_buff *skb)
+
+static void
+__fq_adjust_removal(struct fq *fq, struct fq_flow *flow, unsigned int packets,
+                   unsigned int bytes, unsigned int truesize)
 {
        struct fq_tin *tin = flow->tin;
+       int idx;
 
-       tin->backlog_bytes -= skb->len;
-       tin->backlog_packets--;
-       flow->backlog -= skb->len;
-       fq->backlog--;
-       fq->memory_usage -= skb->truesize;
-}
+       tin->backlog_bytes -= bytes;
+       tin->backlog_packets -= packets;
+       flow->backlog -= bytes;
+       fq->backlog -= packets;
+       fq->memory_usage -= truesize;
 
-static void fq_rejigger_backlog(struct fq *fq, struct fq_flow *flow)
-{
-       struct fq_flow *i;
+       if (flow->backlog)
+               return;
 
-       if (flow->backlog == 0) {
-               list_del_init(&flow->backlogchain);
-       } else {
-               i = flow;
+       if (flow == &tin->default_flow) {
+               list_del_init(&tin->tin_list);
+               return;
+       }
 
-               list_for_each_entry_continue(i, &fq->backlogs, backlogchain)
-                       if (i->backlog < flow->backlog)
-                               break;
+       idx = flow - fq->flows;
+       __clear_bit(idx, fq->flows_bitmap);
+}
 
-               list_move_tail(&flow->backlogchain,
-                              &i->backlogchain);
-       }
+static void fq_adjust_removal(struct fq *fq,
+                             struct fq_flow *flow,
+                             struct sk_buff *skb)
+{
+       __fq_adjust_removal(fq, flow, 1, skb->len, skb->truesize);
 }
 
 static struct sk_buff *fq_flow_dequeue(struct fq *fq,
@@ -54,11 +56,37 @@ static struct sk_buff *fq_flow_dequeue(struct fq *fq,
                return NULL;
 
        fq_adjust_removal(fq, flow, skb);
-       fq_rejigger_backlog(fq, flow);
 
        return skb;
 }
 
+static int fq_flow_drop(struct fq *fq, struct fq_flow *flow,
+                       fq_skb_free_t free_func)
+{
+       unsigned int packets = 0, bytes = 0, truesize = 0;
+       struct fq_tin *tin = flow->tin;
+       struct sk_buff *skb;
+       int pending;
+
+       lockdep_assert_held(&fq->lock);
+
+       pending = min_t(int, 32, skb_queue_len(&flow->queue) / 2);
+       do {
+               skb = __skb_dequeue(&flow->queue);
+               if (!skb)
+                       break;
+
+               packets++;
+               bytes += skb->len;
+               truesize += skb->truesize;
+               free_func(fq, tin, flow, skb);
+       } while (packets < pending);
+
+       __fq_adjust_removal(fq, flow, packets, bytes, truesize);
+
+       return packets;
+}
+
 static struct sk_buff *fq_tin_dequeue(struct fq *fq,
                                      struct fq_tin *tin,
                                      fq_tin_dequeue_t dequeue_func)
@@ -115,8 +143,7 @@ static u32 fq_flow_idx(struct fq *fq, struct sk_buff *skb)
 
 static struct fq_flow *fq_flow_classify(struct fq *fq,
                                        struct fq_tin *tin, u32 idx,
-                                       struct sk_buff *skb,
-                                       fq_flow_get_default_t get_default_func)
+                                       struct sk_buff *skb)
 {
        struct fq_flow *flow;
 
@@ -124,7 +151,7 @@ static struct fq_flow *fq_flow_classify(struct fq *fq,
 
        flow = &fq->flows[idx];
        if (flow->tin && flow->tin != tin) {
-               flow = get_default_func(fq, tin, idx, skb);
+               flow = &tin->default_flow;
                tin->collisions++;
                fq->collisions++;
        }
@@ -135,36 +162,56 @@ static struct fq_flow *fq_flow_classify(struct fq *fq,
        return flow;
 }
 
-static void fq_recalc_backlog(struct fq *fq,
-                             struct fq_tin *tin,
-                             struct fq_flow *flow)
+static struct fq_flow *fq_find_fattest_flow(struct fq *fq)
 {
-       struct fq_flow *i;
+       struct fq_tin *tin;
+       struct fq_flow *flow = NULL;
+       u32 len = 0;
+       int i;
 
-       if (list_empty(&flow->backlogchain))
-               list_add_tail(&flow->backlogchain, &fq->backlogs);
+       for_each_set_bit(i, fq->flows_bitmap, fq->flows_cnt) {
+               struct fq_flow *cur = &fq->flows[i];
+               unsigned int cur_len;
 
-       i = flow;
-       list_for_each_entry_continue_reverse(i, &fq->backlogs,
-                                            backlogchain)
-               if (i->backlog > flow->backlog)
-                       break;
+               cur_len = cur->backlog;
+               if (cur_len <= len)
+                       continue;
+
+               flow = cur;
+               len = cur_len;
+       }
+
+       list_for_each_entry(tin, &fq->tin_backlog, tin_list) {
+               unsigned int cur_len = tin->default_flow.backlog;
 
-       list_move(&flow->backlogchain, &i->backlogchain);
+               if (cur_len <= len)
+                       continue;
+
+               flow = &tin->default_flow;
+               len = cur_len;
+       }
+
+       return flow;
 }
 
 static void fq_tin_enqueue(struct fq *fq,
                           struct fq_tin *tin, u32 idx,
                           struct sk_buff *skb,
-                          fq_skb_free_t free_func,
-                          fq_flow_get_default_t get_default_func)
+                          fq_skb_free_t free_func)
 {
        struct fq_flow *flow;
        bool oom;
 
        lockdep_assert_held(&fq->lock);
 
-       flow = fq_flow_classify(fq, tin, idx, skb, get_default_func);
+       flow = fq_flow_classify(fq, tin, idx, skb);
+
+       if (!flow->backlog) {
+               if (flow != &tin->default_flow)
+                       __set_bit(idx, fq->flows_bitmap);
+               else if (list_empty(&tin->tin_list))
+                       list_add(&tin->tin_list, &fq->tin_backlog);
+       }
 
        flow->tin = tin;
        flow->backlog += skb->len;
@@ -173,8 +220,6 @@ static void fq_tin_enqueue(struct fq *fq,
        fq->memory_usage += skb->truesize;
        fq->backlog++;
 
-       fq_recalc_backlog(fq, tin, flow);
-
        if (list_empty(&flow->flowchain)) {
                flow->deficit = fq->quantum;
                list_add_tail(&flow->flowchain,
@@ -184,18 +229,13 @@ static void fq_tin_enqueue(struct fq *fq,
        __skb_queue_tail(&flow->queue, skb);
        oom = (fq->memory_usage > fq->memory_limit);
        while (fq->backlog > fq->limit || oom) {
-               flow = list_first_entry_or_null(&fq->backlogs,
-                                               struct fq_flow,
-                                               backlogchain);
+               flow = fq_find_fattest_flow(fq);
                if (!flow)
                        return;
 
-               skb = fq_flow_dequeue(fq, flow);
-               if (!skb)
+               if (!fq_flow_drop(fq, flow, free_func))
                        return;
 
-               free_func(fq, flow->tin, flow, skb);
-
                flow->tin->overlimit++;
                fq->overlimit++;
                if (oom) {
@@ -224,8 +264,6 @@ static void fq_flow_filter(struct fq *fq,
                fq_adjust_removal(fq, flow, skb);
                free_func(fq, tin, flow, skb);
        }
-
-       fq_rejigger_backlog(fq, flow);
 }
 
 static void fq_tin_filter(struct fq *fq,
@@ -248,16 +286,18 @@ static void fq_flow_reset(struct fq *fq,
                          struct fq_flow *flow,
                          fq_skb_free_t free_func)
 {
+       struct fq_tin *tin = flow->tin;
        struct sk_buff *skb;
 
        while ((skb = fq_flow_dequeue(fq, flow)))
-               free_func(fq, flow->tin, flow, skb);
+               free_func(fq, tin, flow, skb);
 
-       if (!list_empty(&flow->flowchain))
+       if (!list_empty(&flow->flowchain)) {
                list_del_init(&flow->flowchain);
-
-       if (!list_empty(&flow->backlogchain))
-               list_del_init(&flow->backlogchain);
+               if (list_empty(&tin->new_flows) &&
+                   list_empty(&tin->old_flows))
+                       list_del_init(&tin->tin_list);
+       }
 
        flow->tin = NULL;
 
@@ -283,6 +323,7 @@ static void fq_tin_reset(struct fq *fq,
                fq_flow_reset(fq, flow, free_func);
        }
 
+       WARN_ON_ONCE(!list_empty(&tin->tin_list));
        WARN_ON_ONCE(tin->backlog_bytes);
        WARN_ON_ONCE(tin->backlog_packets);
 }
@@ -290,7 +331,6 @@ static void fq_tin_reset(struct fq *fq,
 static void fq_flow_init(struct fq_flow *flow)
 {
        INIT_LIST_HEAD(&flow->flowchain);
-       INIT_LIST_HEAD(&flow->backlogchain);
        __skb_queue_head_init(&flow->queue);
 }
 
@@ -298,6 +338,8 @@ static void fq_tin_init(struct fq_tin *tin)
 {
        INIT_LIST_HEAD(&tin->new_flows);
        INIT_LIST_HEAD(&tin->old_flows);
+       INIT_LIST_HEAD(&tin->tin_list);
+       fq_flow_init(&tin->default_flow);
 }
 
 static int fq_init(struct fq *fq, int flows_cnt)
@@ -305,8 +347,8 @@ static int fq_init(struct fq *fq, int flows_cnt)
        int i;
 
        memset(fq, 0, sizeof(fq[0]));
-       INIT_LIST_HEAD(&fq->backlogs);
        spin_lock_init(&fq->lock);
+       INIT_LIST_HEAD(&fq->tin_backlog);
        fq->flows_cnt = max_t(u32, flows_cnt, 1);
        fq->quantum = 300;
        fq->limit = 8192;
@@ -316,6 +358,14 @@ static int fq_init(struct fq *fq, int flows_cnt)
        if (!fq->flows)
                return -ENOMEM;
 
+       fq->flows_bitmap = kcalloc(BITS_TO_LONGS(fq->flows_cnt), sizeof(long),
+                                  GFP_KERNEL);
+       if (!fq->flows_bitmap) {
+               kvfree(fq->flows);
+               fq->flows = NULL;
+               return -ENOMEM;
+       }
+
        for (i = 0; i < fq->flows_cnt; i++)
                fq_flow_init(&fq->flows[i]);
 
@@ -332,6 +382,9 @@ static void fq_reset(struct fq *fq,
 
        kvfree(fq->flows);
        fq->flows = NULL;
+
+       kfree(fq->flows_bitmap);
+       fq->flows_bitmap = NULL;
 }
 
 #endif
index 7338b38..c11f80f 100644 (file)
@@ -76,6 +76,8 @@ struct inet_connection_sock_af_ops {
  * @icsk_ext_hdr_len:     Network protocol overhead (IP/IPv6 options)
  * @icsk_ack:             Delayed ACK control data
  * @icsk_mtup;            MTU probing control data
+ * @icsk_probes_tstamp:    Probe timestamp (cleared by non-zero window ack)
+ * @icsk_user_timeout:    TCP_USER_TIMEOUT value
  */
 struct inet_connection_sock {
        /* inet_sock has to be the first member! */
@@ -129,6 +131,7 @@ struct inet_connection_sock {
 
                u32               probe_timestamp;
        } icsk_mtup;
+       u32                       icsk_probes_tstamp;
        u32                       icsk_user_timeout;
 
        u64                       icsk_ca_priv[104 / sizeof(u64)];
@@ -138,7 +141,6 @@ struct inet_connection_sock {
 #define ICSK_TIME_RETRANS      1       /* Retransmit timer */
 #define ICSK_TIME_DACK         2       /* Delayed ack timer */
 #define ICSK_TIME_PROBE0       3       /* Zero window probe timer */
-#define ICSK_TIME_EARLY_RETRANS 4      /* Early retransmit timer */
 #define ICSK_TIME_LOSS_PROBE   5       /* Tail loss probe timer */
 #define ICSK_TIME_REO_TIMEOUT  6       /* Reordering timer */
 
@@ -224,8 +226,7 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what,
        }
 
        if (what == ICSK_TIME_RETRANS || what == ICSK_TIME_PROBE0 ||
-           what == ICSK_TIME_EARLY_RETRANS || what == ICSK_TIME_LOSS_PROBE ||
-           what == ICSK_TIME_REO_TIMEOUT) {
+           what == ICSK_TIME_LOSS_PROBE || what == ICSK_TIME_REO_TIMEOUT) {
                icsk->icsk_pending = what;
                icsk->icsk_timeout = jiffies + when;
                sk_reset_timer(sk, &icsk->icsk_retransmit_timer, icsk->icsk_timeout);
index 2a52777..f51a118 100644 (file)
@@ -174,7 +174,8 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
                                     struct net_device *dev);
 struct fib6_info *rt6_add_dflt_router(struct net *net,
                                     const struct in6_addr *gwaddr,
-                                    struct net_device *dev, unsigned int pref);
+                                    struct net_device *dev, unsigned int pref,
+                                    u32 defrtr_usr_metric);
 
 void rt6_purge_dflt_routers(struct net *net);
 
index 9259ce2..ff06246 100644 (file)
@@ -128,11 +128,12 @@ struct iucv_sock {
        u8                      flags;
        u16                     msglimit;
        u16                     msglimit_peer;
+       atomic_t                skbs_in_xmit;
        atomic_t                msg_sent;
        atomic_t                msg_recv;
        atomic_t                pendings;
        int                     transport;
-       void                    (*sk_txnotify)(struct sk_buff *skb,
+       void                    (*sk_txnotify)(struct sock *sk,
                                               enum iucv_tx_notify n);
 };
 
index ccc3d1f..eee7344 100644 (file)
@@ -92,6 +92,7 @@ struct lapb_cb {
        unsigned short          n2, n2count;
        unsigned short          t1, t2;
        struct timer_list       t1timer, t2timer;
+       bool                    t1timer_stop, t2timer_stop;
 
        /* Internal control information */
        struct sk_buff_head     write_queue;
@@ -103,6 +104,7 @@ struct lapb_cb {
        struct lapb_frame       frmr_data;
        unsigned char           frmr_type;
 
+       spinlock_t              lock;
        refcount_t              refcnt;
 };
 
index d315740..2d1d629 100644 (file)
@@ -1296,6 +1296,8 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
  *     the "0-length PSDU" field included there.  The value for it is
  *     in &struct ieee80211_rx_status.  Note that if this value isn't
  *     known the frame shouldn't be reported.
+ * @RX_FLAG_8023: the frame has an 802.3 header (decap offload performed by
+ *     hardware or driver)
  */
 enum mac80211_rx_flags {
        RX_FLAG_MMIC_ERROR              = BIT(0),
@@ -1328,6 +1330,7 @@ enum mac80211_rx_flags {
        RX_FLAG_RADIOTAP_HE_MU          = BIT(27),
        RX_FLAG_RADIOTAP_LSIG           = BIT(28),
        RX_FLAG_NO_PSDU                 = BIT(29),
+       RX_FLAG_8023                    = BIT(30),
 };
 
 /**
@@ -1649,11 +1652,15 @@ enum ieee80211_vif_flags {
  *     The driver supports sending frames passed as 802.3 frames by mac80211.
  *     It must also support sending 802.11 packets for the same interface.
  * @IEEE80211_OFFLOAD_ENCAP_4ADDR: support 4-address mode encapsulation offload
+ * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled
+ *     The driver supports passing received 802.11 frames as 802.3 frames to
+ *     mac80211.
  */
 
 enum ieee80211_offload_flags {
        IEEE80211_OFFLOAD_ENCAP_ENABLED         = BIT(0),
        IEEE80211_OFFLOAD_ENCAP_4ADDR           = BIT(1),
+       IEEE80211_OFFLOAD_DECAP_ENABLED         = BIT(2),
 };
 
 /**
@@ -2389,6 +2396,9 @@ struct ieee80211_txq {
  * @IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD: Hardware supports tx encapsulation
  *     offload
  *
+ * @IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD: Hardware supports rx decapsulation
+ *     offload
+ *
  * @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
  */
 enum ieee80211_hw_flags {
@@ -2442,6 +2452,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_SUPPORTS_ONLY_HE_MULTI_BSSID,
        IEEE80211_HW_AMPDU_KEYBORDER_SUPPORT,
        IEEE80211_HW_SUPPORTS_TX_ENCAP_OFFLOAD,
+       IEEE80211_HW_SUPPORTS_RX_DECAP_OFFLOAD,
 
        /* keep last, obviously */
        NUM_IEEE80211_HW_FLAGS
@@ -3880,6 +3891,9 @@ enum ieee80211_reconfig_type {
  *     This callback may sleep.
  * @sta_set_4addr: Called to notify the driver when a station starts/stops using
  *     4-address mode
+ * @set_sar_specs: Update the SAR (TX power) settings.
+ * @sta_set_decap_offload: Called to notify the driver when a station is allowed
+ *     to use rx decapsulation offload
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -4197,6 +4211,9 @@ struct ieee80211_ops {
                              struct ieee80211_sta *sta, bool enabled);
        int (*set_sar_specs)(struct ieee80211_hw *hw,
                             const struct cfg80211_sar_specs *sar);
+       void (*sta_set_decap_offload)(struct ieee80211_hw *hw,
+                                     struct ieee80211_vif *vif,
+                                     struct ieee80211_sta *sta, bool enabled);
 };
 
 /**
@@ -5512,7 +5529,7 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
                                                void *data);
 
 /**
- * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
+ * ieee80211_iterate_active_interfaces_mtx - iterate active interfaces
  *
  * This function iterates over the interfaces associated with a given
  * hardware that are currently active and calls the callback for them.
@@ -5523,12 +5540,12 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
  * @iterator: the iterator function to call, cannot sleep
  * @data: first argument of the iterator function
  */
-void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
-                                             u32 iter_flags,
-                                             void (*iterator)(void *data,
+void ieee80211_iterate_active_interfaces_mtx(struct ieee80211_hw *hw,
+                                            u32 iter_flags,
+                                            void (*iterator)(void *data,
                                                u8 *mac,
                                                struct ieee80211_vif *vif),
-                                             void *data);
+                                            void *data);
 
 /**
  * ieee80211_iterate_stations_atomic - iterate stations
index f4af836..4b6ecf5 100644 (file)
@@ -721,6 +721,8 @@ void *nft_set_elem_init(const struct nft_set *set,
                        const struct nft_set_ext_tmpl *tmpl,
                        const u32 *key, const u32 *key_end, const u32 *data,
                        u64 timeout, u64 expiration, gfp_t gfp);
+int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
+                           struct nft_expr *expr_array[]);
 void nft_set_elem_destroy(const struct nft_set *set, void *elem,
                          bool destroy_expr);
 
index 226930d..7bc057a 100644 (file)
@@ -66,7 +66,12 @@ struct nh_info {
 struct nh_grp_entry {
        struct nexthop  *nh;
        u8              weight;
-       atomic_t        upper_bound;
+
+       union {
+               struct {
+                       atomic_t        upper_bound;
+               } mpath;
+       };
 
        struct list_head nh_list;
        struct nexthop  *nh_parent;  /* nexthop of group with this entry */
@@ -109,6 +114,11 @@ enum nexthop_event_type {
        NEXTHOP_EVENT_REPLACE,
 };
 
+enum nh_notifier_info_type {
+       NH_NOTIFIER_INFO_TYPE_SINGLE,
+       NH_NOTIFIER_INFO_TYPE_GRP,
+};
+
 struct nh_notifier_single_info {
        struct net_device *dev;
        u8 gw_family;
@@ -137,7 +147,7 @@ struct nh_notifier_info {
        struct net *net;
        struct netlink_ext_ack *extack;
        u32 id;
-       bool is_grp;
+       enum nh_notifier_info_type type;
        union {
                struct nh_notifier_single_info *nh;
                struct nh_notifier_grp_info *nh_grp;
index 0f2a9c4..255e4f4 100644 (file)
@@ -783,6 +783,42 @@ struct tc_mq_qopt_offload {
        };
 };
 
+enum tc_htb_command {
+       /* Root */
+       TC_HTB_CREATE, /* Initialize HTB offload. */
+       TC_HTB_DESTROY, /* Destroy HTB offload. */
+
+       /* Classes */
+       /* Allocate qid and create leaf. */
+       TC_HTB_LEAF_ALLOC_QUEUE,
+       /* Convert leaf to inner, preserve and return qid, create new leaf. */
+       TC_HTB_LEAF_TO_INNER,
+       /* Delete leaf, while siblings remain. */
+       TC_HTB_LEAF_DEL,
+       /* Delete leaf, convert parent to leaf, preserving qid. */
+       TC_HTB_LEAF_DEL_LAST,
+       /* TC_HTB_LEAF_DEL_LAST, but delete driver data on hardware errors. */
+       TC_HTB_LEAF_DEL_LAST_FORCE,
+       /* Modify parameters of a node. */
+       TC_HTB_NODE_MODIFY,
+
+       /* Class qdisc */
+       TC_HTB_LEAF_QUERY_QUEUE, /* Query qid by classid. */
+};
+
+struct tc_htb_qopt_offload {
+       struct netlink_ext_ack *extack;
+       enum tc_htb_command command;
+       u16 classid;
+       u32 parent_classid;
+       u16 qid;
+       u16 moved_qid;
+       u64 rate;
+       u64 ceil;
+};
+
+#define TC_HTB_CLASSID_ROOT U32_MAX
+
 enum tc_red_command {
        TC_RED_REPLACE,
        TC_RED_DESTROY,
index fc45544..932f0d7 100644 (file)
@@ -168,12 +168,14 @@ static inline void red_set_vars(struct red_vars *v)
        v->qcount       = -1;
 }
 
-static inline bool red_check_params(u32 qth_min, u32 qth_max, u8 Wlog)
+static inline bool red_check_params(u32 qth_min, u32 qth_max, u8 Wlog, u8 Scell_log)
 {
        if (fls(qth_min) + Wlog > 32)
                return false;
        if (fls(qth_max) + Wlog > 32)
                return false;
+       if (Scell_log >= 32)
+               return false;
        if (qth_max < qth_min)
                return false;
        return true;
index 639e465..070f01b 100644 (file)
@@ -210,7 +210,8 @@ struct Qdisc_class_ops {
        int                     (*change)(struct Qdisc *, u32, u32,
                                        struct nlattr **, unsigned long *,
                                        struct netlink_ext_ack *);
-       int                     (*delete)(struct Qdisc *, unsigned long);
+       int                     (*delete)(struct Qdisc *, unsigned long,
+                                         struct netlink_ext_ack *);
        void                    (*walk)(struct Qdisc *, struct qdisc_walker * arg);
 
        /* Filter manipulation */
@@ -388,6 +389,7 @@ struct qdisc_skb_cb {
 #define QDISC_CB_PRIV_LEN 20
        unsigned char           data[QDISC_CB_PRIV_LEN];
        u16                     mru;
+       bool                    post_ct;
 };
 
 typedef void tcf_chain_head_change_t(struct tcf_proto *tp_head, void *priv);
@@ -551,14 +553,20 @@ static inline struct net_device *qdisc_dev(const struct Qdisc *qdisc)
        return qdisc->dev_queue->dev;
 }
 
-static inline void sch_tree_lock(const struct Qdisc *q)
+static inline void sch_tree_lock(struct Qdisc *q)
 {
-       spin_lock_bh(qdisc_root_sleeping_lock(q));
+       if (q->flags & TCQ_F_MQROOT)
+               spin_lock_bh(qdisc_lock(q));
+       else
+               spin_lock_bh(qdisc_root_sleeping_lock(q));
 }
 
-static inline void sch_tree_unlock(const struct Qdisc *q)
+static inline void sch_tree_unlock(struct Qdisc *q)
 {
-       spin_unlock_bh(qdisc_root_sleeping_lock(q));
+       if (q->flags & TCQ_F_MQROOT)
+               spin_unlock_bh(qdisc_lock(q));
+       else
+               spin_unlock_bh(qdisc_root_sleeping_lock(q));
 }
 
 extern struct Qdisc noop_qdisc;
index bdc4323..129d200 100644 (file)
@@ -1921,10 +1921,13 @@ static inline void sk_set_txhash(struct sock *sk)
        sk->sk_txhash = net_tx_rndhash();
 }
 
-static inline void sk_rethink_txhash(struct sock *sk)
+static inline bool sk_rethink_txhash(struct sock *sk)
 {
-       if (sk->sk_txhash)
+       if (sk->sk_txhash) {
                sk_set_txhash(sk);
+               return true;
+       }
+       return false;
 }
 
 static inline struct dst_entry *
@@ -1947,12 +1950,10 @@ sk_dst_get(struct sock *sk)
        return dst;
 }
 
-static inline void dst_negative_advice(struct sock *sk)
+static inline void __dst_negative_advice(struct sock *sk)
 {
        struct dst_entry *ndst, *dst = __sk_dst_get(sk);
 
-       sk_rethink_txhash(sk);
-
        if (dst && dst->ops->negative_advice) {
                ndst = dst->ops->negative_advice(dst);
 
@@ -1964,6 +1965,12 @@ static inline void dst_negative_advice(struct sock *sk)
        }
 }
 
+static inline void dst_negative_advice(struct sock *sk)
+{
+       sk_rethink_txhash(sk);
+       __dst_negative_advice(sk);
+}
+
 static inline void
 __sk_dst_set(struct sock *sk, struct dst_entry *dst)
 {
index 99cd538..88fcac1 100644 (file)
 #define SWITCHDEV_F_SKIP_EOPNOTSUPP    BIT(1)
 #define SWITCHDEV_F_DEFER              BIT(2)
 
-struct switchdev_trans {
-       bool ph_prepare;
-};
-
-static inline bool switchdev_trans_ph_prepare(struct switchdev_trans *trans)
-{
-       return trans && trans->ph_prepare;
-}
-
-static inline bool switchdev_trans_ph_commit(struct switchdev_trans *trans)
-{
-       return trans && !trans->ph_prepare;
-}
-
 enum switchdev_attr_id {
        SWITCHDEV_ATTR_ID_UNDEFINED,
        SWITCHDEV_ATTR_ID_PORT_STP_STATE,
@@ -97,8 +83,7 @@ struct switchdev_obj {
 struct switchdev_obj_port_vlan {
        struct switchdev_obj obj;
        u16 flags;
-       u16 vid_begin;
-       u16 vid_end;
+       u16 vid;
 };
 
 #define SWITCHDEV_OBJ_PORT_VLAN(OBJ) \
@@ -234,14 +219,12 @@ struct switchdev_notifier_fdb_info {
 struct switchdev_notifier_port_obj_info {
        struct switchdev_notifier_info info; /* must be first */
        const struct switchdev_obj *obj;
-       struct switchdev_trans *trans;
        bool handled;
 };
 
 struct switchdev_notifier_port_attr_info {
        struct switchdev_notifier_info info; /* must be first */
        const struct switchdev_attr *attr;
-       struct switchdev_trans *trans;
        bool handled;
 };
 
@@ -289,7 +272,6 @@ int switchdev_handle_port_obj_add(struct net_device *dev,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*add_cb)(struct net_device *dev,
                                      const struct switchdev_obj *obj,
-                                     struct switchdev_trans *trans,
                                      struct netlink_ext_ack *extack));
 int switchdev_handle_port_obj_del(struct net_device *dev,
                        struct switchdev_notifier_port_obj_info *port_obj_info,
@@ -301,8 +283,7 @@ int switchdev_handle_port_attr_set(struct net_device *dev,
                        struct switchdev_notifier_port_attr_info *port_attr_info,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*set_cb)(struct net_device *dev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans));
+                                     const struct switchdev_attr *attr));
 #else
 
 static inline void switchdev_deferred_process(void)
@@ -373,7 +354,6 @@ switchdev_handle_port_obj_add(struct net_device *dev,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*add_cb)(struct net_device *dev,
                                      const struct switchdev_obj *obj,
-                                     struct switchdev_trans *trans,
                                      struct netlink_ext_ack *extack))
 {
        return 0;
@@ -394,8 +374,7 @@ switchdev_handle_port_attr_set(struct net_device *dev,
                        struct switchdev_notifier_port_attr_info *port_attr_info,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*set_cb)(struct net_device *dev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans))
+                                     const struct switchdev_attr *attr))
 {
        return 0;
 }
index 78d13c8..25bbada 100644 (file)
@@ -630,6 +630,7 @@ static inline void tcp_clear_xmit_timers(struct sock *sk)
 
 unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu);
 unsigned int tcp_current_mss(struct sock *sk);
+u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when);
 
 /* Bound MSS / TSO packet size with the half of the window */
 static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize)
@@ -2060,7 +2061,7 @@ void tcp_mark_skb_lost(struct sock *sk, struct sk_buff *skb);
 void tcp_newreno_mark_lost(struct sock *sk, bool snd_una_advanced);
 extern s32 tcp_rack_skb_timeout(struct tcp_sock *tp, struct sk_buff *skb,
                                u32 reo_wnd);
-extern void tcp_rack_mark_lost(struct sock *sk);
+extern bool tcp_rack_mark_lost(struct sock *sk);
 extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq,
                             u64 xmit_time);
 extern void tcp_rack_reo_timeout(struct sock *sk);
index 2ea453d..282d10e 100644 (file)
@@ -129,12 +129,16 @@ void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type);
 static inline void udp_tunnel_get_rx_info(struct net_device *dev)
 {
        ASSERT_RTNL();
+       if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
+               return;
        call_netdevice_notifiers(NETDEV_UDP_TUNNEL_PUSH_INFO, dev);
 }
 
 static inline void udp_tunnel_drop_rx_info(struct net_device *dev)
 {
        ASSERT_RTNL();
+       if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
+               return;
        call_netdevice_notifiers(NETDEV_UDP_TUNNEL_DROP_INFO, dev);
 }
 
@@ -323,6 +327,8 @@ udp_tunnel_nic_set_port_priv(struct net_device *dev, unsigned int table,
 static inline void
 udp_tunnel_nic_add_port(struct net_device *dev, struct udp_tunnel_info *ti)
 {
+       if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
+               return;
        if (udp_tunnel_nic_ops)
                udp_tunnel_nic_ops->add_port(dev, ti);
 }
@@ -330,6 +336,8 @@ udp_tunnel_nic_add_port(struct net_device *dev, struct udp_tunnel_info *ti)
 static inline void
 udp_tunnel_nic_del_port(struct net_device *dev, struct udp_tunnel_info *ti)
 {
+       if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
+               return;
        if (udp_tunnel_nic_ops)
                udp_tunnel_nic_ops->del_port(dev, ti);
 }
index 600acb3..0cf3976 100644 (file)
@@ -76,6 +76,25 @@ struct xdp_buff {
        u32 frame_sz; /* frame size to deduce data_hard_end/reserved tailroom*/
 };
 
+static __always_inline void
+xdp_init_buff(struct xdp_buff *xdp, u32 frame_sz, struct xdp_rxq_info *rxq)
+{
+       xdp->frame_sz = frame_sz;
+       xdp->rxq = rxq;
+}
+
+static __always_inline void
+xdp_prepare_buff(struct xdp_buff *xdp, unsigned char *hard_start,
+                int headroom, int data_len, const bool meta_valid)
+{
+       unsigned char *data = hard_start + headroom;
+
+       xdp->data_hard_start = hard_start;
+       xdp->data = data;
+       xdp->data_end = data + data_len;
+       xdp->data_meta = meta_valid ? data : data + 1;
+}
+
 /* Reserve memory area at end-of data area.
  *
  * This macro reserves tailroom in the XDP buffer by limiting the
index 4f4e93b..cc17bc9 100644 (file)
@@ -58,10 +58,6 @@ struct xdp_sock {
 
        struct xsk_queue *tx ____cacheline_aligned_in_smp;
        struct list_head tx_list;
-       /* Mutual exclusion of NAPI TX thread and sendmsg error paths
-        * in the SKB destructor callback.
-        */
-       spinlock_t tx_completion_lock;
        /* Protects generic receive. */
        spinlock_t rx_lock;
 
index 01755b8..eaa8386 100644 (file)
@@ -73,6 +73,11 @@ struct xsk_buff_pool {
        bool dma_need_sync;
        bool unaligned;
        void *addrs;
+       /* Mutual exclusion of the completion ring in the SKB mode. Two cases to protect:
+        * NAPI TX thread and sendmsg error paths in the SKB destructor callback and when
+        * sockets share a single cq when the same netdev and queue id is shared.
+        */
+       spinlock_t cq_lock;
        struct xdp_buff_xsk *free_heads[];
 };
 
index 3feddfe..4925a1b 100644 (file)
 #define QE_NUM_OF_BRGS 16
 #define QE_NUM_OF_PORTS        1024
 
-/* Memory partitions
-*/
-#define MEM_PART_SYSTEM                0
-#define MEM_PART_SECONDARY     1
-#define MEM_PART_MURAM         2
-
 /* Clocks and BRGs */
 enum qe_clock {
        QE_CLK_NONE = 0,
@@ -102,8 +96,9 @@ s32 cpm_muram_alloc(unsigned long size, unsigned long align);
 void cpm_muram_free(s32 offset);
 s32 cpm_muram_alloc_fixed(unsigned long offset, unsigned long size);
 void __iomem *cpm_muram_addr(unsigned long offset);
-unsigned long cpm_muram_offset(void __iomem *addr);
+unsigned long cpm_muram_offset(const void __iomem *addr);
 dma_addr_t cpm_muram_dma(void __iomem *addr);
+void cpm_muram_free_addr(const void __iomem *addr);
 #else
 static inline s32 cpm_muram_alloc(unsigned long size,
                                  unsigned long align)
@@ -126,7 +121,7 @@ static inline void __iomem *cpm_muram_addr(unsigned long offset)
        return NULL;
 }
 
-static inline unsigned long cpm_muram_offset(void __iomem *addr)
+static inline unsigned long cpm_muram_offset(const void __iomem *addr)
 {
        return -ENOSYS;
 }
@@ -135,6 +130,9 @@ static inline dma_addr_t cpm_muram_dma(void __iomem *addr)
 {
        return 0;
 }
+static inline void cpm_muram_free_addr(const void __iomem *addr)
+{
+}
 #endif /* defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE) */
 
 /* QE PIO */
@@ -239,6 +237,7 @@ static inline int qe_alive_during_sleep(void)
 #define qe_muram_addr cpm_muram_addr
 #define qe_muram_offset cpm_muram_offset
 #define qe_muram_dma cpm_muram_dma
+#define qe_muram_free_addr cpm_muram_free_addr
 
 #ifdef CONFIG_PPC32
 #define qe_iowrite8(val, addr)     out_8(addr, val)
index dc4e794..9696a5b 100644 (file)
@@ -146,7 +146,6 @@ struct ucc_fast_info {
        resource_size_t regs;
        int irq;
        u32 uccm_mask;
-       int bd_mem_part;
        int brkpt_support;
        int grant_support;
        int tsa;
index 2f4cd32..cdc33fa 100644 (file)
@@ -98,6 +98,7 @@
 #define IFH_REW_OP_TWO_STEP_PTP                0x3
 #define IFH_REW_OP_ORIGIN_PTP          0x5
 
+#define OCELOT_NUM_TC                  8
 #define OCELOT_TAG_LEN                 16
 #define OCELOT_SHORT_PREFIX_LEN                4
 #define OCELOT_LONG_PREFIX_LEN         16
@@ -563,6 +564,8 @@ struct ocelot_ops {
        int (*netdev_to_port)(struct net_device *dev);
        int (*reset)(struct ocelot *ocelot);
        u16 (*wm_enc)(u16 value);
+       u16 (*wm_dec)(u16 value);
+       void (*wm_stat)(u32 val, u32 *inuse, u32 *maxuse);
 };
 
 struct ocelot_vcap_block {
@@ -576,6 +579,18 @@ struct ocelot_vlan {
        u16 vid;
 };
 
+enum ocelot_sb {
+       OCELOT_SB_BUF,
+       OCELOT_SB_REF,
+       OCELOT_SB_NUM,
+};
+
+enum ocelot_sb_pool {
+       OCELOT_SB_POOL_ING,
+       OCELOT_SB_POOL_EGR,
+       OCELOT_SB_POOL_NUM,
+};
+
 struct ocelot_port {
        struct ocelot                   *ocelot;
 
@@ -599,6 +614,8 @@ struct ocelot_port {
 
 struct ocelot {
        struct device                   *dev;
+       struct devlink                  *devlink;
+       struct devlink_port             *devlink_ports;
 
        const struct ocelot_ops         *ops;
        struct regmap                   *targets[TARGET_MAX];
@@ -607,7 +624,9 @@ struct ocelot {
        const struct ocelot_stat_layout *stats_layout;
        unsigned int                    num_stats;
 
-       int                             shared_queue_sz;
+       u32                             pool_size[OCELOT_SB_NUM][OCELOT_SB_POOL_NUM];
+       int                             packet_buffer_size;
+       int                             num_frame_refs;
        int                             num_mact_rows;
 
        struct net_device               *hw_bridge_dev;
@@ -739,8 +758,7 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
 void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
 void ocelot_adjust_link(struct ocelot *ocelot, int port,
                        struct phy_device *phydev);
-int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled,
-                              struct switchdev_trans *trans);
+int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled);
 void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
 int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
                            struct net_device *bridge);
@@ -778,4 +796,38 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
 int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
                        const struct switchdev_obj_port_mdb *mdb);
 
+int ocelot_devlink_sb_register(struct ocelot *ocelot);
+void ocelot_devlink_sb_unregister(struct ocelot *ocelot);
+int ocelot_sb_pool_get(struct ocelot *ocelot, unsigned int sb_index,
+                      u16 pool_index,
+                      struct devlink_sb_pool_info *pool_info);
+int ocelot_sb_pool_set(struct ocelot *ocelot, unsigned int sb_index,
+                      u16 pool_index, u32 size,
+                      enum devlink_sb_threshold_type threshold_type,
+                      struct netlink_ext_ack *extack);
+int ocelot_sb_port_pool_get(struct ocelot *ocelot, int port,
+                           unsigned int sb_index, u16 pool_index,
+                           u32 *p_threshold);
+int ocelot_sb_port_pool_set(struct ocelot *ocelot, int port,
+                           unsigned int sb_index, u16 pool_index,
+                           u32 threshold, struct netlink_ext_ack *extack);
+int ocelot_sb_tc_pool_bind_get(struct ocelot *ocelot, int port,
+                              unsigned int sb_index, u16 tc_index,
+                              enum devlink_sb_pool_type pool_type,
+                              u16 *p_pool_index, u32 *p_threshold);
+int ocelot_sb_tc_pool_bind_set(struct ocelot *ocelot, int port,
+                              unsigned int sb_index, u16 tc_index,
+                              enum devlink_sb_pool_type pool_type,
+                              u16 pool_index, u32 threshold,
+                              struct netlink_ext_ack *extack);
+int ocelot_sb_occ_snapshot(struct ocelot *ocelot, unsigned int sb_index);
+int ocelot_sb_occ_max_clear(struct ocelot *ocelot, unsigned int sb_index);
+int ocelot_sb_occ_port_pool_get(struct ocelot *ocelot, int port,
+                               unsigned int sb_index, u16 pool_index,
+                               u32 *p_cur, u32 *p_max);
+int ocelot_sb_occ_tc_port_bind_get(struct ocelot *ocelot, int port,
+                                  unsigned int sb_index, u16 tc_index,
+                                  enum devlink_sb_pool_type pool_type,
+                                  u32 *p_cur, u32 *p_max);
+
 #endif
index a814bc2..9731895 100644 (file)
 
 #define QSYS_RES_STAT_GSZ                                 0x8
 
-#define QSYS_RES_STAT_INUSE(x)                            (((x) << 12) & GENMASK(23, 12))
-#define QSYS_RES_STAT_INUSE_M                             GENMASK(23, 12)
-#define QSYS_RES_STAT_INUSE_X(x)                          (((x) & GENMASK(23, 12)) >> 12)
-#define QSYS_RES_STAT_MAXUSE(x)                           ((x) & GENMASK(11, 0))
-#define QSYS_RES_STAT_MAXUSE_M                            GENMASK(11, 0)
+#define QSYS_MMGT_EQ_CTRL_FP_FREE_CNT(x)                  ((x) & GENMASK(15, 0))
+#define QSYS_MMGT_EQ_CTRL_FP_FREE_CNT_M                   GENMASK(15, 0)
 
 #define QSYS_EVENTS_CORE_EV_FDC(x)                        (((x) << 2) & GENMASK(4, 2))
 #define QSYS_EVENTS_CORE_EV_FDC_M                         GENMASK(4, 2)
diff --git a/include/soc/nps/common.h b/include/soc/nps/common.h
deleted file mode 100644 (file)
index 8c18dc6..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses.  You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      - Redistributions of source code must retain the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer.
- *
- *      - Redistributions in binary form must reproduce the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef SOC_NPS_COMMON_H
-#define SOC_NPS_COMMON_H
-
-#ifdef CONFIG_SMP
-#define NPS_IPI_IRQ                                    5
-#endif
-
-#define NPS_HOST_REG_BASE                      0xF6000000
-
-#define NPS_MSU_BLKID                          0x018
-
-#define CTOP_INST_RSPI_GIC_0_R12               0x3C56117E
-#define CTOP_INST_MOV2B_FLIP_R3_B1_B2_INST     0x5B60
-#define CTOP_INST_MOV2B_FLIP_R3_B1_B2_LIMM     0x00010422
-
-#ifndef AUX_IENABLE
-#define AUX_IENABLE                            0x40c
-#endif
-
-#define CTOP_AUX_IACK                          (0xFFFFF800 + 0x088)
-
-#ifndef __ASSEMBLY__
-
-/* In order to increase compilation test coverage */
-#ifdef CONFIG_ARC
-static inline void nps_ack_gic(void)
-{
-       __asm__ __volatile__ (
-       "       .word %0\n"
-       :
-       : "i"(CTOP_INST_RSPI_GIC_0_R12)
-       : "memory");
-}
-#else
-static inline void nps_ack_gic(void) { }
-#define write_aux_reg(r, v)
-#define read_aux_reg(r) 0
-#endif
-
-/* CPU global ID */
-struct global_id {
-       union {
-               struct {
-#ifdef CONFIG_EZNPS_MTM_EXT
-                       u32 __reserved:20, cluster:4, core:4, thread:4;
-#else
-                       u32 __reserved:24, cluster:4, core:4;
-#endif
-               };
-               u32 value;
-       };
-};
-
-/*
- * Convert logical to physical CPU IDs
- *
- * The conversion swap bits 1 and 2 of cluster id (out of 4 bits)
- * Now quad of logical clusters id's are adjacent physically,
- * and not like the id's physically came with each cluster.
- * Below table is 4x4 mesh of core clusters as it layout on chip.
- * Cluster ids are in format: logical (physical)
- *
- *    -----------------   ------------------
- * 3 |  5 (3)   7 (7)  | | 13 (11)   15 (15)|
- *
- * 2 |  4 (2)   6 (6)  | | 12 (10)   14 (14)|
- *    -----------------   ------------------
- * 1 |  1 (1)   3 (5)  | |  9  (9)   11 (13)|
- *
- * 0 |  0 (0)   2 (4)  | |  8  (8)   10 (12)|
- *    -----------------   ------------------
- *       0       1            2        3
- */
-static inline int nps_cluster_logic_to_phys(int cluster)
-{
-#ifdef __arc__
-        __asm__ __volatile__(
-       "       mov r3,%0\n"
-       "       .short %1\n"
-       "       .word %2\n"
-       "       mov %0,r3\n"
-       : "+r"(cluster)
-       : "i"(CTOP_INST_MOV2B_FLIP_R3_B1_B2_INST),
-         "i"(CTOP_INST_MOV2B_FLIP_R3_B1_B2_LIMM)
-       : "r3");
-#endif
-
-       return cluster;
-}
-
-#define NPS_CPU_TO_CLUSTER_NUM(cpu) \
-       ({ struct global_id gid; gid.value = cpu; \
-               nps_cluster_logic_to_phys(gid.cluster); })
-
-struct nps_host_reg_address {
-       union {
-               struct {
-                       u32 base:8, cl_x:4, cl_y:4,
-                       blkid:6, reg:8, __reserved:2;
-               };
-               u32 value;
-       };
-};
-
-struct nps_host_reg_address_non_cl {
-       union {
-               struct {
-                       u32 base:7, blkid:11, reg:12, __reserved:2;
-               };
-               u32 value;
-       };
-};
-
-static inline void *nps_host_reg_non_cl(u32 blkid, u32 reg)
-{
-       struct nps_host_reg_address_non_cl reg_address;
-
-       reg_address.value = NPS_HOST_REG_BASE;
-       reg_address.blkid = blkid;
-       reg_address.reg = reg;
-
-       return (void *)reg_address.value;
-}
-
-static inline void *nps_host_reg(u32 cpu, u32 blkid, u32 reg)
-{
-       struct nps_host_reg_address reg_address;
-       u32 cl = NPS_CPU_TO_CLUSTER_NUM(cpu);
-
-       reg_address.value = NPS_HOST_REG_BASE;
-       reg_address.cl_x  = (cl >> 2) & 0x3;
-       reg_address.cl_y  = cl & 0x3;
-       reg_address.blkid = blkid;
-       reg_address.reg   = reg;
-
-       return (void *)reg_address.value;
-}
-#endif /* __ASSEMBLY__ */
-
-#endif /* SOC_NPS_COMMON_H */
diff --git a/include/soc/nps/mtm.h b/include/soc/nps/mtm.h
deleted file mode 100644 (file)
index d2f5e7e..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses.  You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- *     Redistribution and use in source and binary forms, with or
- *     without modification, are permitted provided that the following
- *     conditions are met:
- *
- *      - Redistributions of source code must retain the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer.
- *
- *      - Redistributions in binary form must reproduce the above
- *        copyright notice, this list of conditions and the following
- *        disclaimer in the documentation and/or other materials
- *        provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef SOC_NPS_MTM_H
-#define SOC_NPS_MTM_H
-
-#define CTOP_INST_HWSCHD_OFF_R3                 0x3B6F00BF
-#define CTOP_INST_HWSCHD_RESTORE_R3             0x3E6F70C3
-
-static inline void hw_schd_save(unsigned int *flags)
-{
-       __asm__ __volatile__(
-       "       .word %1\n"
-       "       st r3,[%0]\n"
-       :
-       : "r"(flags), "i"(CTOP_INST_HWSCHD_OFF_R3)
-       : "r3", "memory");
-}
-
-static inline void hw_schd_restore(unsigned int flags)
-{
-       __asm__ __volatile__(
-       "       mov r3, %0\n"
-       "       .word %1\n"
-       :
-       : "r"(flags), "i"(CTOP_INST_HWSCHD_RESTORE_R3)
-       : "r3");
-}
-
-#endif /* SOC_NPS_MTM_H */
index 2336bf9..2e1200d 100644 (file)
@@ -229,7 +229,7 @@ typedef int (*snd_pcm_hw_rule_func_t)(struct snd_pcm_hw_params *params,
 struct snd_pcm_hw_rule {
        unsigned int cond;
        int var;
-       int deps[4];
+       int deps[5];
 
        snd_pcm_hw_rule_func_t func;
        void *private;
index 4eef374..4a5cc8c 100644 (file)
@@ -231,6 +231,7 @@ enum afs_file_error {
        afs_file_error_dir_bad_magic,
        afs_file_error_dir_big,
        afs_file_error_dir_missing_page,
+       afs_file_error_dir_name_too_long,
        afs_file_error_dir_over_end,
        afs_file_error_dir_small,
        afs_file_error_dir_unmarked_ext,
@@ -488,6 +489,7 @@ enum afs_cb_break_reason {
        EM(afs_file_error_dir_bad_magic,        "DIR_BAD_MAGIC")        \
        EM(afs_file_error_dir_big,              "DIR_BIG")              \
        EM(afs_file_error_dir_missing_page,     "DIR_MISSING_PAGE")     \
+       EM(afs_file_error_dir_name_too_long,    "DIR_NAME_TOO_LONG")    \
        EM(afs_file_error_dir_over_end,         "DIR_ENT_OVER_END")     \
        EM(afs_file_error_dir_small,            "DIR_SMALL")            \
        EM(afs_file_error_dir_unmarked_ext,     "DIR_UNMARKED_EXT")     \
index 5039af6..cbe3e15 100644 (file)
@@ -366,7 +366,7 @@ TRACE_EVENT(sched_process_wait,
 );
 
 /*
- * Tracepoint for do_fork:
+ * Tracepoint for kernel_clone:
  */
 TRACE_EVENT(sched_process_fork,
 
index 58994e0..6f89c27 100644 (file)
@@ -1424,13 +1424,61 @@ TRACE_EVENT(rpcb_unregister,
        )
 );
 
+/* Record an xdr_buf containing a fully-formed RPC message */
+DECLARE_EVENT_CLASS(svc_xdr_msg_class,
+       TP_PROTO(
+               const struct xdr_buf *xdr
+       ),
+
+       TP_ARGS(xdr),
+
+       TP_STRUCT__entry(
+               __field(u32, xid)
+               __field(const void *, head_base)
+               __field(size_t, head_len)
+               __field(const void *, tail_base)
+               __field(size_t, tail_len)
+               __field(unsigned int, page_len)
+               __field(unsigned int, msg_len)
+       ),
+
+       TP_fast_assign(
+               __be32 *p = (__be32 *)xdr->head[0].iov_base;
+
+               __entry->xid = be32_to_cpu(*p);
+               __entry->head_base = p;
+               __entry->head_len = xdr->head[0].iov_len;
+               __entry->tail_base = xdr->tail[0].iov_base;
+               __entry->tail_len = xdr->tail[0].iov_len;
+               __entry->page_len = xdr->page_len;
+               __entry->msg_len = xdr->len;
+       ),
+
+       TP_printk("xid=0x%08x head=[%p,%zu] page=%u tail=[%p,%zu] len=%u",
+               __entry->xid,
+               __entry->head_base, __entry->head_len, __entry->page_len,
+               __entry->tail_base, __entry->tail_len, __entry->msg_len
+       )
+);
+
+#define DEFINE_SVCXDRMSG_EVENT(name)                                   \
+               DEFINE_EVENT(svc_xdr_msg_class,                         \
+                               svc_xdr_##name,                         \
+                               TP_PROTO(                               \
+                                       const struct xdr_buf *xdr       \
+                               ),                                      \
+                               TP_ARGS(xdr))
+
+DEFINE_SVCXDRMSG_EVENT(recvfrom);
+
+/* Record an xdr_buf containing arbitrary data, tagged with an XID */
 DECLARE_EVENT_CLASS(svc_xdr_buf_class,
        TP_PROTO(
-               const struct svc_rqst *rqst,
+               __be32 xid,
                const struct xdr_buf *xdr
        ),
 
-       TP_ARGS(rqst, xdr),
+       TP_ARGS(xid, xdr),
 
        TP_STRUCT__entry(
                __field(u32, xid)
@@ -1443,7 +1491,7 @@ DECLARE_EVENT_CLASS(svc_xdr_buf_class,
        ),
 
        TP_fast_assign(
-               __entry->xid = be32_to_cpu(rqst->rq_xid);
+               __entry->xid = be32_to_cpu(xid);
                __entry->head_base = xdr->head[0].iov_base;
                __entry->head_len = xdr->head[0].iov_len;
                __entry->tail_base = xdr->tail[0].iov_base;
@@ -1463,12 +1511,11 @@ DECLARE_EVENT_CLASS(svc_xdr_buf_class,
                DEFINE_EVENT(svc_xdr_buf_class,                         \
                                svc_xdr_##name,                         \
                                TP_PROTO(                               \
-                                       const struct svc_rqst *rqst,    \
+                                       __be32 xid,                     \
                                        const struct xdr_buf *xdr       \
                                ),                                      \
-                               TP_ARGS(rqst, xdr))
+                               TP_ARGS(xid, xdr))
 
-DEFINE_SVCXDRBUF_EVENT(recvfrom);
 DEFINE_SVCXDRBUF_EVENT(sendto);
 
 /*
index 52e8bcb..cf7399f 100644 (file)
@@ -213,7 +213,7 @@ struct cache_sb_disk {
                __le16          keys;
        };
        __le64                  d[SB_JOURNAL_BUCKETS];  /* journal buckets */
-       __le16                  bucket_size_hi;
+       __le16                  obso_bucket_size_hi;    /* obsoleted */
 };
 
 /*
index 77d7c1b..c001766 100644 (file)
@@ -19,7 +19,8 @@
 
 /* ld/ldx fields */
 #define BPF_DW         0x18    /* double word (64-bit) */
-#define BPF_XADD       0xc0    /* exclusive add */
+#define BPF_ATOMIC     0xc0    /* atomic memory ops - op type in immediate */
+#define BPF_XADD       0xc0    /* exclusive add - legacy name */
 
 /* alu/jmp fields */
 #define BPF_MOV                0xb0    /* mov reg to reg */
 #define BPF_CALL       0x80    /* function call */
 #define BPF_EXIT       0x90    /* function return */
 
+/* atomic op type fields (stored in immediate) */
+#define BPF_FETCH      0x01    /* not an opcode on its own, used to build others */
+#define BPF_XCHG       (0xe0 | BPF_FETCH)      /* atomic exchange */
+#define BPF_CMPXCHG    (0xf0 | BPF_FETCH)      /* atomic compare-and-write */
+
 /* Register numbers */
 enum {
        BPF_REG_0 = 0,
@@ -2448,7 +2454,7 @@ union bpf_attr {
  *             running simultaneously.
  *
  *             A user should care about the synchronization by himself.
- *             For example, by using the **BPF_STX_XADD** instruction to alter
+ *             For example, by using the **BPF_ATOMIC** instructions to alter
  *             the shared data.
  *     Return
  *             A pointer to the local storage area.
@@ -2993,10 +2999,10 @@ union bpf_attr {
  *             string length is larger than *size*, just *size*-1 bytes are
  *             copied and the last byte is set to NUL.
  *
- *             On success, the length of the copied string is returned. This
- *             makes this helper useful in tracing programs for reading
- *             strings, and more importantly to get its length at runtime. See
- *             the following snippet:
+ *             On success, returns the number of bytes that were written,
+ *             including the terminal NUL. This makes this helper useful in
+ *             tracing programs for reading strings, and more importantly to
+ *             get its length at runtime. See the following snippet:
  *
  *             ::
  *
@@ -3024,7 +3030,7 @@ union bpf_attr {
  *             **->mm->env_start**: using this helper and the return value,
  *             one can quickly iterate at the right offset of the memory area.
  *     Return
- *             On success, the strictly positive length of the string,
+ *             On success, the strictly positive length of the output string,
  *             including the trailing NUL character. On error, a negative
  *             value.
  *
index cf89c31..f6008b2 100644 (file)
@@ -200,6 +200,10 @@ enum devlink_port_flavour {
        DEVLINK_PORT_FLAVOUR_UNUSED, /* Port which exists in the switch, but
                                      * is not used in any way.
                                      */
+       DEVLINK_PORT_FLAVOUR_PCI_SF, /* Represents eswitch port
+                                     * for the PCI SF. It is an internal
+                                     * port that faces the PCI SF.
+                                     */
 };
 
 enum devlink_param_cmode {
@@ -529,6 +533,7 @@ enum devlink_attr {
        DEVLINK_ATTR_RELOAD_ACTION_INFO,        /* nested */
        DEVLINK_ATTR_RELOAD_ACTION_STATS,       /* nested */
 
+       DEVLINK_ATTR_PORT_PCI_SF_NUMBER,        /* u32 */
        /* add new attributes above here, update the policy in devlink.c */
 
        __DEVLINK_ATTR_MAX,
@@ -578,9 +583,29 @@ enum devlink_resource_unit {
 enum devlink_port_function_attr {
        DEVLINK_PORT_FUNCTION_ATTR_UNSPEC,
        DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR,     /* binary */
+       DEVLINK_PORT_FN_ATTR_STATE,     /* u8 */
+       DEVLINK_PORT_FN_ATTR_OPSTATE,   /* u8 */
 
        __DEVLINK_PORT_FUNCTION_ATTR_MAX,
        DEVLINK_PORT_FUNCTION_ATTR_MAX = __DEVLINK_PORT_FUNCTION_ATTR_MAX - 1
 };
 
+enum devlink_port_fn_state {
+       DEVLINK_PORT_FN_STATE_INACTIVE,
+       DEVLINK_PORT_FN_STATE_ACTIVE,
+};
+
+/**
+ * enum devlink_port_fn_opstate - indicates operational state of the function
+ * @DEVLINK_PORT_FN_OPSTATE_ATTACHED: Driver is attached to the function.
+ * For graceful tear down of the function, after inactivation of the
+ * function, user should wait for operational state to turn DETACHED.
+ * @DEVLINK_PORT_FN_OPSTATE_DETACHED: Driver is detached from the function.
+ * It is safe to delete the port.
+ */
+enum devlink_port_fn_opstate {
+       DEVLINK_PORT_FN_OPSTATE_DETACHED,
+       DEVLINK_PORT_FN_OPSTATE_ATTACHED,
+};
+
 #endif /* _UAPI_LINUX_DEVLINK_H_ */
index 79f9191..62aff78 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef _UAPI_LINUX_GTP_H_
 #define _UAPI_LINUX_GTP_H_
 
+#include <linux/types.h>
+
 #define GTP_GENL_MCGRP_NAME    "gtp"
 
 enum gtp_genl_cmds {
@@ -34,4 +36,14 @@ enum gtp_attrs {
 };
 #define GTPA_MAX (__GTPA_MAX + 1)
 
+enum {
+       GTP_METADATA_V1
+};
+
+struct gtpu_metadata {
+       __u8    ver;
+       __u8    flags;
+       __u8    type;
+};
+
 #endif /* _UAPI_LINUX_GTP_H_ */
index 45f3750..e8eb4ad 100644 (file)
@@ -94,6 +94,7 @@
 #define BOND_XMIT_POLICY_LAYER23       2 /* layer 2+3 (IP ^ MAC) */
 #define BOND_XMIT_POLICY_ENCAP23       3 /* encapsulated layer 2+3 */
 #define BOND_XMIT_POLICY_ENCAP34       4 /* encapsulated layer 3+4 */
+#define BOND_XMIT_POLICY_VLAN_SRCMAC   5 /* vlan + source MAC */
 
 /* 802.3ad port state definitions (43.4.2.2 in the 802.3ad standard) */
 #define LACP_STATE_LACP_ACTIVITY   0x1
index 874cc12..eb8018c 100644 (file)
@@ -75,8 +75,9 @@ struct rtnl_link_stats {
  *
  * @rx_dropped: Number of packets received but not processed,
  *   e.g. due to lack of resources or unsupported protocol.
- *   For hardware interfaces this counter should not include packets
- *   dropped by the device which are counted separately in
+ *   For hardware interfaces this counter may include packets discarded
+ *   due to L2 address filtering but should not include packets dropped
+ *   by the device due to buffer exhaustion which are counted separately in
  *   @rx_missed_errors (since procfs folds those two counters together).
  *
  * @tx_dropped: Number of packets dropped on their way to transmission,
@@ -524,6 +525,8 @@ enum {
        IFLA_BRPORT_BACKUP_PORT,
        IFLA_BRPORT_MRP_RING_OPEN,
        IFLA_BRPORT_MRP_IN_OPEN,
+       IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
+       IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
        __IFLA_BRPORT_MAX
 };
 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
@@ -808,6 +811,7 @@ enum {
        IFLA_GTP_FD1,
        IFLA_GTP_PDP_HASHSIZE,
        IFLA_GTP_ROLE,
+       IFLA_GTP_COLLECT_METADATA,
        __IFLA_GTP_MAX,
 };
 #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
index 7d91055..802da67 100644 (file)
@@ -176,6 +176,7 @@ enum {
 #define TUNNEL_VXLAN_OPT       __cpu_to_be16(0x1000)
 #define TUNNEL_NOCACHE         __cpu_to_be16(0x2000)
 #define TUNNEL_ERSPAN_OPT      __cpu_to_be16(0x4000)
+#define TUNNEL_GTPU_OPT                __cpu_to_be16(0x8000)
 
 #define TUNNEL_OPTIONS_PRESENT \
                (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT)
index 13e8751..7060377 100644 (file)
@@ -189,6 +189,7 @@ enum {
        DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
        DEVCONF_NDISC_TCLASS,
        DEVCONF_RPL_SEG_ENABLED,
+       DEVCONF_RA_DEFRTR_METRIC,
        DEVCONF_MAX
 };
 
index 886802b..374c678 100644 (file)
@@ -251,6 +251,7 @@ struct kvm_hyperv_exit {
 #define KVM_EXIT_X86_RDMSR        29
 #define KVM_EXIT_X86_WRMSR        30
 #define KVM_EXIT_DIRTY_RING_FULL  31
+#define KVM_EXIT_AP_RESET_HOLD    32
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -573,6 +574,7 @@ struct kvm_vapic_addr {
 #define KVM_MP_STATE_CHECK_STOP        6
 #define KVM_MP_STATE_OPERATING         7
 #define KVM_MP_STATE_LOAD              8
+#define KVM_MP_STATE_AP_RESET_HOLD     9
 
 struct kvm_mp_state {
        __u32 mp_state;
index 9762660..3674a45 100644 (file)
@@ -82,6 +82,7 @@ enum {
        MPTCP_PM_CMD_FLUSH_ADDRS,
        MPTCP_PM_CMD_SET_LIMITS,
        MPTCP_PM_CMD_GET_LIMITS,
+       MPTCP_PM_CMD_SET_FLAGS,
 
        __MPTCP_PM_CMD_AFTER_LAST
 };
index 9744773..bd4424d 100644 (file)
@@ -71,90 +71,4 @@ enum br_mrp_sub_tlv_header_type {
        BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR = 0x3,
 };
 
-struct br_mrp_tlv_hdr {
-       __u8 type;
-       __u8 length;
-};
-
-struct br_mrp_sub_tlv_hdr {
-       __u8 type;
-       __u8 length;
-};
-
-struct br_mrp_end_hdr {
-       struct br_mrp_tlv_hdr hdr;
-};
-
-struct br_mrp_common_hdr {
-       __be16 seq_id;
-       __u8 domain[MRP_DOMAIN_UUID_LENGTH];
-};
-
-struct br_mrp_ring_test_hdr {
-       __be16 prio;
-       __u8 sa[ETH_ALEN];
-       __be16 port_role;
-       __be16 state;
-       __be16 transitions;
-       __be32 timestamp;
-};
-
-struct br_mrp_ring_topo_hdr {
-       __be16 prio;
-       __u8 sa[ETH_ALEN];
-       __be16 interval;
-};
-
-struct br_mrp_ring_link_hdr {
-       __u8 sa[ETH_ALEN];
-       __be16 port_role;
-       __be16 interval;
-       __be16 blocked;
-};
-
-struct br_mrp_sub_opt_hdr {
-       __u8 type;
-       __u8 manufacture_data[MRP_MANUFACTURE_DATA_LENGTH];
-};
-
-struct br_mrp_test_mgr_nack_hdr {
-       __be16 prio;
-       __u8 sa[ETH_ALEN];
-       __be16 other_prio;
-       __u8 other_sa[ETH_ALEN];
-};
-
-struct br_mrp_test_prop_hdr {
-       __be16 prio;
-       __u8 sa[ETH_ALEN];
-       __be16 other_prio;
-       __u8 other_sa[ETH_ALEN];
-};
-
-struct br_mrp_oui_hdr {
-       __u8 oui[MRP_OUI_LENGTH];
-};
-
-struct br_mrp_in_test_hdr {
-       __be16 id;
-       __u8 sa[ETH_ALEN];
-       __be16 port_role;
-       __be16 state;
-       __be16 transitions;
-       __be32 timestamp;
-};
-
-struct br_mrp_in_topo_hdr {
-       __u8 sa[ETH_ALEN];
-       __be16 id;
-       __be16 interval;
-};
-
-struct br_mrp_in_link_hdr {
-       __u8 sa[ETH_ALEN];
-       __be16 port_role;
-       __be16 id;
-       __be16 interval;
-};
-
 #endif
index 28b6ee5..b1633e7 100644 (file)
@@ -293,6 +293,7 @@ enum nft_rule_compat_attributes {
  * @NFT_SET_EVAL: set can be updated from the evaluation path
  * @NFT_SET_OBJECT: set contains stateful objects
  * @NFT_SET_CONCAT: set contains a concatenation
+ * @NFT_SET_EXPR: set contains expressions
  */
 enum nft_set_flags {
        NFT_SET_ANONYMOUS               = 0x1,
@@ -303,6 +304,7 @@ enum nft_set_flags {
        NFT_SET_EVAL                    = 0x20,
        NFT_SET_OBJECT                  = 0x40,
        NFT_SET_CONCAT                  = 0x80,
+       NFT_SET_EXPR                    = 0x100,
 };
 
 /**
@@ -706,6 +708,7 @@ enum nft_dynset_ops {
 
 enum nft_dynset_flags {
        NFT_DYNSET_F_INV        = (1 << 0),
+       NFT_DYNSET_F_EXPR       = (1 << 1),
 };
 
 /**
index b15e344..cb6f841 100644 (file)
@@ -386,7 +386,8 @@ struct perf_event_attr {
                                aux_output     :  1, /* generate AUX records instead of events */
                                cgroup         :  1, /* include cgroup events */
                                text_poke      :  1, /* include text poke events */
-                               __reserved_1   : 30;
+                               build_id       :  1, /* use build id in mmap2 events */
+                               __reserved_1   : 29;
 
        union {
                __u32           wakeup_events;    /* wakeup every n events */
@@ -659,6 +660,22 @@ struct perf_event_mmap_page {
        __u64   aux_size;
 };
 
+/*
+ * The current state of perf_event_header::misc bits usage:
+ * ('|' used bit, '-' unused bit)
+ *
+ *  012         CDEF
+ *  |||---------||||
+ *
+ *  Where:
+ *    0-2     CPUMODE_MASK
+ *
+ *    C       PROC_MAP_PARSE_TIMEOUT
+ *    D       MMAP_DATA / COMM_EXEC / FORK_EXEC / SWITCH_OUT
+ *    E       MMAP_BUILD_ID / EXACT_IP / SCHED_OUT_PREEMPT
+ *    F       (reserved)
+ */
+
 #define PERF_RECORD_MISC_CPUMODE_MASK          (7 << 0)
 #define PERF_RECORD_MISC_CPUMODE_UNKNOWN       (0 << 0)
 #define PERF_RECORD_MISC_KERNEL                        (1 << 0)
@@ -690,6 +707,7 @@ struct perf_event_mmap_page {
  *
  *   PERF_RECORD_MISC_EXACT_IP           - PERF_RECORD_SAMPLE of precise events
  *   PERF_RECORD_MISC_SWITCH_OUT_PREEMPT - PERF_RECORD_SWITCH* events
+ *   PERF_RECORD_MISC_MMAP_BUILD_ID      - PERF_RECORD_MMAP2 event
  *
  *
  * PERF_RECORD_MISC_EXACT_IP:
@@ -699,9 +717,13 @@ struct perf_event_mmap_page {
  *
  * PERF_RECORD_MISC_SWITCH_OUT_PREEMPT:
  *   Indicates that thread was preempted in TASK_RUNNING state.
+ *
+ * PERF_RECORD_MISC_MMAP_BUILD_ID:
+ *   Indicates that mmap2 event carries build id data.
  */
 #define PERF_RECORD_MISC_EXACT_IP              (1 << 14)
 #define PERF_RECORD_MISC_SWITCH_OUT_PREEMPT    (1 << 14)
+#define PERF_RECORD_MISC_MMAP_BUILD_ID         (1 << 14)
 /*
  * Reserve the last bit to indicate some extended misc field
  */
@@ -915,10 +937,20 @@ enum perf_event_type {
         *      u64                             addr;
         *      u64                             len;
         *      u64                             pgoff;
-        *      u32                             maj;
-        *      u32                             min;
-        *      u64                             ino;
-        *      u64                             ino_generation;
+        *      union {
+        *              struct {
+        *                      u32             maj;
+        *                      u32             min;
+        *                      u64             ino;
+        *                      u64             ino_generation;
+        *              };
+        *              struct {
+        *                      u8              build_id_size;
+        *                      u8              __reserved_1;
+        *                      u16             __reserved_2;
+        *                      u8              build_id[20];
+        *              };
+        *      };
         *      u32                             prot, flags;
         *      char                            filename[];
         *      struct sample_id                sample_id;
index ee95f42..afe6836 100644 (file)
@@ -591,6 +591,8 @@ enum {
        TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED = 1 << 1, /* Part of an existing connection. */
        TCA_FLOWER_KEY_CT_FLAGS_RELATED = 1 << 2, /* Related to an established connection. */
        TCA_FLOWER_KEY_CT_FLAGS_TRACKED = 1 << 3, /* Conntrack has occurred. */
+       TCA_FLOWER_KEY_CT_FLAGS_INVALID = 1 << 4, /* Conntrack is invalid. */
+       TCA_FLOWER_KEY_CT_FLAGS_REPLY = 1 << 5, /* Packet is in the reply direction. */
 };
 
 enum {
index 9e7c2c6..79a699f 100644 (file)
@@ -434,6 +434,7 @@ enum {
        TCA_HTB_RATE64,
        TCA_HTB_CEIL64,
        TCA_HTB_PAD,
+       TCA_HTB_OFFLOAD,
        __TCA_HTB_MAX,
 };
 
index 8dbecb3..1cc5ce0 100644 (file)
@@ -116,7 +116,7 @@ struct pppol2tp_ioc_stats {
 #define PPPIOCGCHAN    _IOR('t', 55, int)      /* get ppp channel number */
 #define PPPIOCGL2TPSTATS _IOR('t', 54, struct pppol2tp_ioc_stats)
 #define PPPIOCBRIDGECHAN _IOW('t', 53, int)    /* bridge one channel to another */
-#define PPPIOCUNBRIDGECHAN _IO('t', 54)        /* unbridge channel */
+#define PPPIOCUNBRIDGECHAN _IO('t', 52)        /* unbridge channel */
 
 #define SIOCGPPPSTATS   (SIOCDEVPRIVATE + 0)
 #define SIOCGPPPVER     (SIOCDEVPRIVATE + 1)   /* NEVER change this!! */
index 1dccb55..708addd 100644 (file)
@@ -28,10 +28,10 @@ struct ipv6_rpl_sr_hdr {
                pad:4,
                reserved1:16;
 #elif defined(__BIG_ENDIAN_BITFIELD)
-       __u32   reserved:20,
+       __u32   cmpri:4,
+               cmpre:4,
                pad:4,
-               cmpri:4,
-               cmpre:4;
+               reserved:20;
 #else
 #error  "Please fix <asm/byteorder.h>"
 #endif
index 458179d..1e05d3c 100644 (file)
@@ -571,6 +571,7 @@ enum {
        NET_IPV6_ACCEPT_SOURCE_ROUTE=25,
        NET_IPV6_ACCEPT_RA_FROM_LOCAL=26,
        NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
+       NET_IPV6_RA_DEFRTR_METRIC=28,
        __NET_IPV6_MAX
 };
 
index 13ceeb3..42fc5a6 100644 (file)
@@ -51,7 +51,7 @@ struct tcphdr {
                fin:1;
 #else
 #error "Adjust your <asm/byteorder.h> defines"
-#endif 
+#endif
        __be16  window;
        __sum16 check;
        __be16  urg_ptr;
@@ -62,14 +62,14 @@ struct tcphdr {
  *  (union is compatible to any of its members)
  *  This means this part of the code is -fstrict-aliasing safe now.
  */
-union tcp_word_hdr { 
+union tcp_word_hdr {
        struct tcphdr hdr;
-       __be32            words[5];
-}; 
+       __be32        words[5];
+};
 
-#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words [3]) 
+#define tcp_flag_word(tp) (((union tcp_word_hdr *)(tp))->words[3])
 
-enum { 
+enum {
        TCP_FLAG_CWR = __constant_cpu_to_be32(0x00800000),
        TCP_FLAG_ECE = __constant_cpu_to_be32(0x00400000),
        TCP_FLAG_URG = __constant_cpu_to_be32(0x00200000),
@@ -80,7 +80,7 @@ enum {
        TCP_FLAG_FIN = __constant_cpu_to_be32(0x00010000),
        TCP_RESERVED_BITS = __constant_cpu_to_be32(0x0F000000),
        TCP_DATA_OFFSET = __constant_cpu_to_be32(0xF0000000)
-}; 
+};
 
 /*
  * TCP general constants
@@ -103,8 +103,8 @@ enum {
 #define TCP_QUICKACK           12      /* Block/reenable quick acks */
 #define TCP_CONGESTION         13      /* Congestion control algorithm */
 #define TCP_MD5SIG             14      /* TCP MD5 Signature (RFC2385) */
-#define TCP_THIN_LINEAR_TIMEOUTS 16      /* Use linear timeouts for thin streams*/
-#define TCP_THIN_DUPACK         17      /* Fast retrans. after 1 dupack */
+#define TCP_THIN_LINEAR_TIMEOUTS 16    /* Use linear timeouts for thin streams*/
+#define TCP_THIN_DUPACK                17      /* Fast retrans. after 1 dupack */
 #define TCP_USER_TIMEOUT       18      /* How long for loss retry before timeout */
 #define TCP_REPAIR             19      /* TCP sock is under repair right now */
 #define TCP_REPAIR_QUEUE       20
@@ -314,6 +314,7 @@ enum {
        TCP_NLA_TIMEOUT_REHASH, /* Timeout-triggered rehash attempts */
        TCP_NLA_BYTES_NOTSENT,  /* Bytes in write queue not yet sent */
        TCP_NLA_EDT,            /* Earliest departure time (CLOCK_MONOTONIC) */
+       TCP_NLA_TTL,            /* TTL or hop limit of a packet received */
 };
 
 /* for TCP_MD5SIG socket option */
@@ -353,5 +354,9 @@ struct tcp_zerocopy_receive {
        __u64 copybuf_address;  /* in: copybuf address (small reads) */
        __s32 copybuf_len; /* in/out: copybuf bytes avail/used or error */
        __u32 flags; /* in: flags */
+       __u64 msg_control; /* ancillary data */
+       __u64 msg_controllen;
+       __u32 msg_flags;
+       /* __u32 hole;  Next we must add >1 u32 otherwise length checks fail. */
 };
 #endif /* _UAPI_LINUX_TCP_H */
index 00850b9..a38454d 100644 (file)
@@ -176,7 +176,7 @@ struct v4l2_subdev_capability {
 };
 
 /* The v4l2 sub-device video device node is registered in read-only mode. */
-#define V4L2_SUBDEV_CAP_RO_SUBDEV              BIT(0)
+#define V4L2_SUBDEV_CAP_RO_SUBDEV              0x00000001
 
 /* Backwards compatibility define --- to be removed */
 #define v4l2_subdev_edid v4l2_edid
index 8c15a7d..dba3827 100644 (file)
@@ -279,6 +279,7 @@ enum hl_device_status {
  * HL_INFO_CLK_THROTTLE_REASON - Retrieve clock throttling reason
  * HL_INFO_SYNC_MANAGER  - Retrieve sync manager info per dcore
  * HL_INFO_TOTAL_ENERGY  - Retrieve total energy consumption
+ * HL_INFO_PLL_FREQUENCY - Retrieve PLL frequency
  */
 #define HL_INFO_HW_IP_INFO             0
 #define HL_INFO_HW_EVENTS              1
@@ -425,6 +426,8 @@ struct hl_info_sync_manager {
  * @ctx_device_in_reset_drop_cnt: context dropped due to device in reset
  * @total_max_cs_in_flight_drop_cnt: total dropped due to maximum CS in-flight
  * @ctx_max_cs_in_flight_drop_cnt: context dropped due to maximum CS in-flight
+ * @total_validation_drop_cnt: total dropped due to validation error
+ * @ctx_validation_drop_cnt: context dropped due to validation error
  */
 struct hl_info_cs_counters {
        __u64 total_out_of_mem_drop_cnt;
@@ -437,6 +440,8 @@ struct hl_info_cs_counters {
        __u64 ctx_device_in_reset_drop_cnt;
        __u64 total_max_cs_in_flight_drop_cnt;
        __u64 ctx_max_cs_in_flight_drop_cnt;
+       __u64 total_validation_drop_cnt;
+       __u64 ctx_validation_drop_cnt;
 };
 
 enum gaudi_dcores {
index f8b638c..901a4fd 100644 (file)
@@ -133,6 +133,13 @@ enum pvrdma_wc_flags {
        PVRDMA_WC_FLAGS_MAX             = PVRDMA_WC_WITH_NETWORK_HDR_TYPE,
 };
 
+enum pvrdma_network_type {
+       PVRDMA_NETWORK_IB,
+       PVRDMA_NETWORK_ROCE_V1 = PVRDMA_NETWORK_IB,
+       PVRDMA_NETWORK_IPV4,
+       PVRDMA_NETWORK_IPV6
+};
+
 struct pvrdma_alloc_ucontext_resp {
        __u32 qp_tab_size;
        __u32 reserved;
index 00c7235..2c43b0e 100644 (file)
@@ -192,7 +192,7 @@ void xs_suspend_cancel(void);
 
 struct work_struct;
 
-void xenbus_probe(struct work_struct *);
+void xenbus_probe(void);
 
 #define XENBUS_IS_ERR_READ(str) ({                     \
        if (!IS_ERR(str) && strlen(str) == 0) {         \
index 6feee7f..c68d784 100644 (file)
@@ -1480,14 +1480,8 @@ void __init console_on_rootfs(void)
        struct file *file = filp_open("/dev/console", O_RDWR, 0);
 
        if (IS_ERR(file)) {
-               pr_err("Warning: unable to open an initial console. Fallback to ttynull.\n");
-               register_ttynull_console();
-
-               file = filp_open("/dev/console", O_RDWR, 0);
-               if (IS_ERR(file)) {
-                       pr_err("Warning: Failed to add ttynull console. No stdin, stdout, and stderr for the init process!\n");
-                       return;
-               }
+               pr_err("Warning: unable to open an initial console.\n");
+               return;
        }
        init_dup(file);
        init_dup(file);
@@ -1518,6 +1512,7 @@ static noinline void __init kernel_init_freeable(void)
 
        init_mm_internals();
 
+       rcu_init_tasks_generic();
        do_pre_smp_initcalls();
        lockup_detector_init();
 
index 6edff97..2f05973 100644 (file)
@@ -176,14 +176,14 @@ BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode,
         * bpf_local_storage_update expects the owner to have a
         * valid storage pointer.
         */
-       if (!inode_storage_ptr(inode))
+       if (!inode || !inode_storage_ptr(inode))
                return (unsigned long)NULL;
 
        sdata = inode_storage_lookup(inode, map, true);
        if (sdata)
                return (unsigned long)sdata->data;
 
-       /* This helper must only called from where the inode is gurranteed
+       /* This helper must only called from where the inode is guaranteed
         * to have a refcount and cannot be freed.
         */
        if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) {
@@ -200,7 +200,10 @@ BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode,
 BPF_CALL_2(bpf_inode_storage_delete,
           struct bpf_map *, map, struct inode *, inode)
 {
-       /* This helper must only called from where the inode is gurranteed
+       if (!inode)
+               return -EINVAL;
+
+       /* This helper must only called from where the inode is guaranteed
         * to have a refcount and cannot be freed.
         */
        return inode_storage_delete(inode, map);
index 4ef1959..e0da025 100644 (file)
@@ -218,7 +218,7 @@ BPF_CALL_4(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *,
         * bpf_local_storage_update expects the owner to have a
         * valid storage pointer.
         */
-       if (!task_storage_ptr(task))
+       if (!task || !task_storage_ptr(task))
                return (unsigned long)NULL;
 
        sdata = task_storage_lookup(task, map, true);
@@ -243,6 +243,9 @@ BPF_CALL_4(bpf_task_storage_get, struct bpf_map *, map, struct task_struct *,
 BPF_CALL_2(bpf_task_storage_delete, struct bpf_map *, map, struct task_struct *,
           task)
 {
+       if (!task)
+               return -EINVAL;
+
        /* This helper must only be called from places where the lifetime of the task
         * is guaranteed. Either by being refcounted or by being protected
         * by an RCU read-side critical section.
index 8d6bdb4..8962f98 100644 (file)
@@ -458,7 +458,7 @@ static bool btf_type_is_datasec(const struct btf_type *t)
        return BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC;
 }
 
-static u32 btf_nr_types_total(const struct btf *btf)
+u32 btf_nr_types(const struct btf *btf)
 {
        u32 total = 0;
 
@@ -476,7 +476,7 @@ s32 btf_find_by_name_kind(const struct btf *btf, const char *name, u8 kind)
        const char *tname;
        u32 i, total;
 
-       total = btf_nr_types_total(btf);
+       total = btf_nr_types(btf);
        for (i = 1; i < total; i++) {
                t = btf_type_by_id(btf, i);
                if (BTF_INFO_KIND(t->info) != kind)
@@ -4172,7 +4172,7 @@ static int btf_parse_hdr(struct btf_verifier_env *env)
                return -ENOTSUPP;
        }
 
-       if (btf_data_size == hdr->hdr_len) {
+       if (!btf->base_btf && btf_data_size == hdr->hdr_len) {
                btf_verifier_log(env, "No data");
                return -EINVAL;
        }
@@ -5743,6 +5743,11 @@ bool btf_is_kernel(const struct btf *btf)
        return btf->kernel_btf;
 }
 
+bool btf_is_module(const struct btf *btf)
+{
+       return btf->kernel_btf && strcmp(btf->name, "vmlinux") != 0;
+}
+
 static int btf_id_cmp_func(const void *a, const void *b)
 {
        const int *pa = a, *pb = b;
@@ -5877,3 +5882,25 @@ static int __init btf_module_init(void)
 
 fs_initcall(btf_module_init);
 #endif /* CONFIG_DEBUG_INFO_BTF_MODULES */
+
+struct module *btf_try_get_module(const struct btf *btf)
+{
+       struct module *res = NULL;
+#ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+       struct btf_module *btf_mod, *tmp;
+
+       mutex_lock(&btf_module_mutex);
+       list_for_each_entry_safe(btf_mod, tmp, &btf_modules, list) {
+               if (btf_mod->btf != btf)
+                       continue;
+
+               if (try_module_get(btf_mod->module))
+                       res = btf_mod->module;
+
+               break;
+       }
+       mutex_unlock(&btf_module_mutex);
+#endif
+
+       return res;
+}
index 6ec088a..96555a8 100644 (file)
@@ -1391,12 +1391,13 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
                if (ctx.optlen != 0) {
                        *optlen = ctx.optlen;
                        *kernel_optval = ctx.optval;
+                       /* export and don't free sockopt buf */
+                       return 0;
                }
        }
 
 out:
-       if (ret)
-               sockopt_free_buf(&ctx);
+       sockopt_free_buf(&ctx);
        return ret;
 }
 
index 261f869..5bbd488 100644 (file)
@@ -1309,8 +1309,8 @@ EXPORT_SYMBOL_GPL(__bpf_call_base);
        INSN_3(STX, MEM,  H),                   \
        INSN_3(STX, MEM,  W),                   \
        INSN_3(STX, MEM,  DW),                  \
-       INSN_3(STX, XADD, W),                   \
-       INSN_3(STX, XADD, DW),                  \
+       INSN_3(STX, ATOMIC, W),                 \
+       INSN_3(STX, ATOMIC, DW),                \
        /*   Immediate based. */                \
        INSN_3(ST, MEM, B),                     \
        INSN_3(ST, MEM, H),                     \
@@ -1618,13 +1618,59 @@ out:
        LDX_PROBE(DW, 8)
 #undef LDX_PROBE
 
-       STX_XADD_W: /* lock xadd *(u32 *)(dst_reg + off16) += src_reg */
-               atomic_add((u32) SRC, (atomic_t *)(unsigned long)
-                          (DST + insn->off));
-               CONT;
-       STX_XADD_DW: /* lock xadd *(u64 *)(dst_reg + off16) += src_reg */
-               atomic64_add((u64) SRC, (atomic64_t *)(unsigned long)
-                            (DST + insn->off));
+#define ATOMIC_ALU_OP(BOP, KOP)                                                \
+               case BOP:                                               \
+                       if (BPF_SIZE(insn->code) == BPF_W)              \
+                               atomic_##KOP((u32) SRC, (atomic_t *)(unsigned long) \
+                                            (DST + insn->off));        \
+                       else                                            \
+                               atomic64_##KOP((u64) SRC, (atomic64_t *)(unsigned long) \
+                                              (DST + insn->off));      \
+                       break;                                          \
+               case BOP | BPF_FETCH:                                   \
+                       if (BPF_SIZE(insn->code) == BPF_W)              \
+                               SRC = (u32) atomic_fetch_##KOP(         \
+                                       (u32) SRC,                      \
+                                       (atomic_t *)(unsigned long) (DST + insn->off)); \
+                       else                                            \
+                               SRC = (u64) atomic64_fetch_##KOP(       \
+                                       (u64) SRC,                      \
+                                       (atomic64_t *)(unsigned long) (DST + insn->off)); \
+                       break;
+
+       STX_ATOMIC_DW:
+       STX_ATOMIC_W:
+               switch (IMM) {
+               ATOMIC_ALU_OP(BPF_ADD, add)
+               ATOMIC_ALU_OP(BPF_AND, and)
+               ATOMIC_ALU_OP(BPF_OR, or)
+               ATOMIC_ALU_OP(BPF_XOR, xor)
+#undef ATOMIC_ALU_OP
+
+               case BPF_XCHG:
+                       if (BPF_SIZE(insn->code) == BPF_W)
+                               SRC = (u32) atomic_xchg(
+                                       (atomic_t *)(unsigned long) (DST + insn->off),
+                                       (u32) SRC);
+                       else
+                               SRC = (u64) atomic64_xchg(
+                                       (atomic64_t *)(unsigned long) (DST + insn->off),
+                                       (u64) SRC);
+                       break;
+               case BPF_CMPXCHG:
+                       if (BPF_SIZE(insn->code) == BPF_W)
+                               BPF_R0 = (u32) atomic_cmpxchg(
+                                       (atomic_t *)(unsigned long) (DST + insn->off),
+                                       (u32) BPF_R0, (u32) SRC);
+                       else
+                               BPF_R0 = (u64) atomic64_cmpxchg(
+                                       (atomic64_t *)(unsigned long) (DST + insn->off),
+                                       (u64) BPF_R0, (u64) SRC);
+                       break;
+
+               default:
+                       goto default_label;
+               }
                CONT;
 
        default_label:
@@ -1634,7 +1680,8 @@ out:
                 *
                 * Note, verifier whitelists all opcodes in bpf_opcode_in_insntable().
                 */
-               pr_warn("BPF interpreter: unknown opcode %02x\n", insn->code);
+               pr_warn("BPF interpreter: unknown opcode %02x (imm: 0x%x)\n",
+                       insn->code, insn->imm);
                BUG_ON(1);
                return 0;
 }
@@ -2119,6 +2166,28 @@ static void bpf_free_used_maps(struct bpf_prog_aux *aux)
        kfree(aux->used_maps);
 }
 
+void __bpf_free_used_btfs(struct bpf_prog_aux *aux,
+                         struct btf_mod_pair *used_btfs, u32 len)
+{
+#ifdef CONFIG_BPF_SYSCALL
+       struct btf_mod_pair *btf_mod;
+       u32 i;
+
+       for (i = 0; i < len; i++) {
+               btf_mod = &used_btfs[i];
+               if (btf_mod->module)
+                       module_put(btf_mod->module);
+               btf_put(btf_mod->btf);
+       }
+#endif
+}
+
+static void bpf_free_used_btfs(struct bpf_prog_aux *aux)
+{
+       __bpf_free_used_btfs(aux, aux->used_btfs, aux->used_btf_cnt);
+       kfree(aux->used_btfs);
+}
+
 static void bpf_prog_free_deferred(struct work_struct *work)
 {
        struct bpf_prog_aux *aux;
@@ -2126,6 +2195,7 @@ static void bpf_prog_free_deferred(struct work_struct *work)
 
        aux = container_of(work, struct bpf_prog_aux, work);
        bpf_free_used_maps(aux);
+       bpf_free_used_btfs(aux);
        if (bpf_prog_is_dev_bound(aux))
                bpf_prog_offload_destroy(aux->prog);
 #ifdef CONFIG_PERF_EVENTS
index b44d8c4..19ff8fe 100644 (file)
@@ -80,6 +80,13 @@ const char *const bpf_alu_string[16] = {
        [BPF_END >> 4]  = "endian",
 };
 
+static const char *const bpf_atomic_alu_string[16] = {
+       [BPF_ADD >> 4]  = "add",
+       [BPF_AND >> 4]  = "and",
+       [BPF_OR >> 4]  = "or",
+       [BPF_XOR >> 4]  = "or",
+};
+
 static const char *const bpf_ldst_string[] = {
        [BPF_W >> 3]  = "u32",
        [BPF_H >> 3]  = "u16",
@@ -153,14 +160,44 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
                                bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
                                insn->dst_reg,
                                insn->off, insn->src_reg);
-               else if (BPF_MODE(insn->code) == BPF_XADD)
-                       verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) += r%d\n",
+               else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
+                        (insn->imm == BPF_ADD || insn->imm == BPF_ADD ||
+                         insn->imm == BPF_OR || insn->imm == BPF_XOR)) {
+                       verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d\n",
+                               insn->code,
+                               bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
+                               insn->dst_reg, insn->off,
+                               bpf_alu_string[BPF_OP(insn->imm) >> 4],
+                               insn->src_reg);
+               } else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
+                          (insn->imm == (BPF_ADD | BPF_FETCH) ||
+                           insn->imm == (BPF_AND | BPF_FETCH) ||
+                           insn->imm == (BPF_OR | BPF_FETCH) ||
+                           insn->imm == (BPF_XOR | BPF_FETCH))) {
+                       verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\n",
+                               insn->code, insn->src_reg,
+                               BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
+                               bpf_atomic_alu_string[BPF_OP(insn->imm) >> 4],
+                               bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
+                               insn->dst_reg, insn->off, insn->src_reg);
+               } else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
+                          insn->imm == BPF_CMPXCHG) {
+                       verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\n",
                                insn->code,
+                               BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
                                bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
                                insn->dst_reg, insn->off,
                                insn->src_reg);
-               else
+               } else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
+                          insn->imm == BPF_XCHG) {
+                       verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\n",
+                               insn->code, insn->src_reg,
+                               BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
+                               bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
+                               insn->dst_reg, insn->off, insn->src_reg);
+               } else {
                        verbose(cbs->private_data, "BUG_%02x\n", insn->code);
+               }
        } else if (class == BPF_ST) {
                if (BPF_MODE(insn->code) != BPF_MEM) {
                        verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
index 7e84820..c1ac7f9 100644 (file)
@@ -152,6 +152,7 @@ static void htab_init_buckets(struct bpf_htab *htab)
                        lockdep_set_class(&htab->buckets[i].lock,
                                          &htab->lockdep_key);
                }
+               cond_resched();
        }
 }
 
index bd8a318..41ca280 100644 (file)
@@ -108,7 +108,7 @@ BPF_CALL_2(bpf_map_peek_elem, struct bpf_map *, map, void *, value)
 }
 
 const struct bpf_func_proto bpf_map_peek_elem_proto = {
-       .func           = bpf_map_pop_elem,
+       .func           = bpf_map_peek_elem,
        .gpl_only       = false,
        .ret_type       = RET_INTEGER,
        .arg1_type      = ARG_CONST_MAP_PTR,
index b7ff879..5d872a7 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /* Copyright (c) 2020 Facebook */
-#include <argp.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
index aea96b6..cabaf7d 100644 (file)
@@ -7,10 +7,9 @@
 #include <linux/kernel.h>
 #include <linux/stacktrace.h>
 #include <linux/perf_event.h>
-#include <linux/elf.h>
-#include <linux/pagemap.h>
 #include <linux/irq_work.h>
 #include <linux/btf_ids.h>
+#include <linux/buildid.h>
 #include "percpu_freelist.h"
 
 #define STACK_CREATE_FLAG_MASK                                 \
@@ -143,140 +142,6 @@ free_smap:
        return ERR_PTR(err);
 }
 
-#define BPF_BUILD_ID 3
-/*
- * Parse build id from the note segment. This logic can be shared between
- * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
- * identical.
- */
-static inline int stack_map_parse_build_id(void *page_addr,
-                                          unsigned char *build_id,
-                                          void *note_start,
-                                          Elf32_Word note_size)
-{
-       Elf32_Word note_offs = 0, new_offs;
-
-       /* check for overflow */
-       if (note_start < page_addr || note_start + note_size < note_start)
-               return -EINVAL;
-
-       /* only supports note that fits in the first page */
-       if (note_start + note_size > page_addr + PAGE_SIZE)
-               return -EINVAL;
-
-       while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
-               Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
-
-               if (nhdr->n_type == BPF_BUILD_ID &&
-                   nhdr->n_namesz == sizeof("GNU") &&
-                   nhdr->n_descsz > 0 &&
-                   nhdr->n_descsz <= BPF_BUILD_ID_SIZE) {
-                       memcpy(build_id,
-                              note_start + note_offs +
-                              ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
-                              nhdr->n_descsz);
-                       memset(build_id + nhdr->n_descsz, 0,
-                              BPF_BUILD_ID_SIZE - nhdr->n_descsz);
-                       return 0;
-               }
-               new_offs = note_offs + sizeof(Elf32_Nhdr) +
-                       ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
-               if (new_offs <= note_offs)  /* overflow */
-                       break;
-               note_offs = new_offs;
-       }
-       return -EINVAL;
-}
-
-/* Parse build ID from 32-bit ELF */
-static int stack_map_get_build_id_32(void *page_addr,
-                                    unsigned char *build_id)
-{
-       Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
-       Elf32_Phdr *phdr;
-       int i;
-
-       /* only supports phdr that fits in one page */
-       if (ehdr->e_phnum >
-           (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
-               return -EINVAL;
-
-       phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
-
-       for (i = 0; i < ehdr->e_phnum; ++i) {
-               if (phdr[i].p_type == PT_NOTE &&
-                   !stack_map_parse_build_id(page_addr, build_id,
-                                             page_addr + phdr[i].p_offset,
-                                             phdr[i].p_filesz))
-                       return 0;
-       }
-       return -EINVAL;
-}
-
-/* Parse build ID from 64-bit ELF */
-static int stack_map_get_build_id_64(void *page_addr,
-                                    unsigned char *build_id)
-{
-       Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
-       Elf64_Phdr *phdr;
-       int i;
-
-       /* only supports phdr that fits in one page */
-       if (ehdr->e_phnum >
-           (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
-               return -EINVAL;
-
-       phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
-
-       for (i = 0; i < ehdr->e_phnum; ++i) {
-               if (phdr[i].p_type == PT_NOTE &&
-                   !stack_map_parse_build_id(page_addr, build_id,
-                                             page_addr + phdr[i].p_offset,
-                                             phdr[i].p_filesz))
-                       return 0;
-       }
-       return -EINVAL;
-}
-
-/* Parse build ID of ELF file mapped to vma */
-static int stack_map_get_build_id(struct vm_area_struct *vma,
-                                 unsigned char *build_id)
-{
-       Elf32_Ehdr *ehdr;
-       struct page *page;
-       void *page_addr;
-       int ret;
-
-       /* only works for page backed storage  */
-       if (!vma->vm_file)
-               return -EINVAL;
-
-       page = find_get_page(vma->vm_file->f_mapping, 0);
-       if (!page)
-               return -EFAULT; /* page not mapped */
-
-       ret = -EINVAL;
-       page_addr = kmap_atomic(page);
-       ehdr = (Elf32_Ehdr *)page_addr;
-
-       /* compare magic x7f "ELF" */
-       if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
-               goto out;
-
-       /* only support executable file and shared object file */
-       if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
-               goto out;
-
-       if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
-               ret = stack_map_get_build_id_32(page_addr, build_id);
-       else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
-               ret = stack_map_get_build_id_64(page_addr, build_id);
-out:
-       kunmap_atomic(page_addr);
-       put_page(page);
-       return ret;
-}
-
 static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
                                          u64 *ips, u32 trace_nr, bool user)
 {
@@ -317,18 +182,18 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs,
                for (i = 0; i < trace_nr; i++) {
                        id_offs[i].status = BPF_STACK_BUILD_ID_IP;
                        id_offs[i].ip = ips[i];
-                       memset(id_offs[i].build_id, 0, BPF_BUILD_ID_SIZE);
+                       memset(id_offs[i].build_id, 0, BUILD_ID_SIZE_MAX);
                }
                return;
        }
 
        for (i = 0; i < trace_nr; i++) {
                vma = find_vma(current->mm, ips[i]);
-               if (!vma || stack_map_get_build_id(vma, id_offs[i].build_id)) {
+               if (!vma || build_id_parse(vma, id_offs[i].build_id, NULL)) {
                        /* per entry fall back to ips */
                        id_offs[i].status = BPF_STACK_BUILD_ID_IP;
                        id_offs[i].ip = ips[i];
-                       memset(id_offs[i].build_id, 0, BPF_BUILD_ID_SIZE);
+                       memset(id_offs[i].build_id, 0, BUILD_ID_SIZE_MAX);
                        continue;
                }
                id_offs[i].offset = (vma->vm_pgoff << PAGE_SHIFT) + ips[i]
index 4caf06f..e5999d8 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/fs.h>
 #include <linux/license.h>
 #include <linux/filter.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/idr.h>
 #include <linux/cred.h>
@@ -2713,7 +2712,6 @@ out_unlock:
 out_put_prog:
        if (tgt_prog_fd && tgt_prog)
                bpf_prog_put(tgt_prog);
-       bpf_prog_put(prog);
        return err;
 }
 
@@ -2826,7 +2824,10 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
                        tp_name = prog->aux->attach_func_name;
                        break;
                }
-               return bpf_tracing_prog_attach(prog, 0, 0);
+               err = bpf_tracing_prog_attach(prog, 0, 0);
+               if (err >= 0)
+                       return err;
+               goto out_put_prog;
        case BPF_PROG_TYPE_RAW_TRACEPOINT:
        case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
                if (strncpy_from_user(buf,
index e73c075..175b7b4 100644 (file)
@@ -37,7 +37,7 @@ retry:
                if (!task) {
                        ++*tid;
                        goto retry;
-               } else if (skip_if_dup_files && task->tgid != task->pid &&
+               } else if (skip_if_dup_files && !thread_group_leader(task) &&
                           task->files == task->group_leader->files) {
                        put_task_struct(task);
                        task = NULL;
@@ -151,13 +151,14 @@ again:
                curr_task = info->task;
                curr_fd = info->fd;
        } else {
-               curr_task = task_seq_get_next(ns, &curr_tid, true);
-               if (!curr_task) {
-                       info->task = NULL;
-                       return NULL;
-               }
-
-               /* set info->task and info->tid */
+                curr_task = task_seq_get_next(ns, &curr_tid, true);
+                if (!curr_task) {
+                        info->task = NULL;
+                        info->tid = curr_tid;
+                        return NULL;
+                }
+
+                /* set info->task and info->tid */
                info->task = curr_task;
                if (curr_tid == info->tid) {
                        curr_fd = info->fd;
index 17270b8..785d253 100644 (file)
@@ -2217,6 +2217,8 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
        case PTR_TO_RDWR_BUF:
        case PTR_TO_RDWR_BUF_OR_NULL:
        case PTR_TO_PERCPU_BTF_ID:
+       case PTR_TO_MEM:
+       case PTR_TO_MEM_OR_NULL:
                return true;
        default:
                return false;
@@ -3604,13 +3606,30 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
        return err;
 }
 
-static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn)
+static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn)
 {
+       int load_reg;
        int err;
 
-       if ((BPF_SIZE(insn->code) != BPF_W && BPF_SIZE(insn->code) != BPF_DW) ||
-           insn->imm != 0) {
-               verbose(env, "BPF_XADD uses reserved fields\n");
+       switch (insn->imm) {
+       case BPF_ADD:
+       case BPF_ADD | BPF_FETCH:
+       case BPF_AND:
+       case BPF_AND | BPF_FETCH:
+       case BPF_OR:
+       case BPF_OR | BPF_FETCH:
+       case BPF_XOR:
+       case BPF_XOR | BPF_FETCH:
+       case BPF_XCHG:
+       case BPF_CMPXCHG:
+               break;
+       default:
+               verbose(env, "BPF_ATOMIC uses invalid atomic opcode %02x\n", insn->imm);
+               return -EINVAL;
+       }
+
+       if (BPF_SIZE(insn->code) != BPF_W && BPF_SIZE(insn->code) != BPF_DW) {
+               verbose(env, "invalid atomic operand size\n");
                return -EINVAL;
        }
 
@@ -3624,6 +3643,13 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins
        if (err)
                return err;
 
+       if (insn->imm == BPF_CMPXCHG) {
+               /* Check comparison of R0 with memory location */
+               err = check_reg_arg(env, BPF_REG_0, SRC_OP);
+               if (err)
+                       return err;
+       }
+
        if (is_pointer_value(env, insn->src_reg)) {
                verbose(env, "R%d leaks addr into mem\n", insn->src_reg);
                return -EACCES;
@@ -3633,21 +3659,38 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins
            is_pkt_reg(env, insn->dst_reg) ||
            is_flow_key_reg(env, insn->dst_reg) ||
            is_sk_reg(env, insn->dst_reg)) {
-               verbose(env, "BPF_XADD stores into R%d %s is not allowed\n",
+               verbose(env, "BPF_ATOMIC stores into R%d %s is not allowed\n",
                        insn->dst_reg,
                        reg_type_str[reg_state(env, insn->dst_reg)->type]);
                return -EACCES;
        }
 
-       /* check whether atomic_add can read the memory */
+       /* check whether we can read the memory */
        err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
                               BPF_SIZE(insn->code), BPF_READ, -1, true);
        if (err)
                return err;
 
-       /* check whether atomic_add can write into the same memory */
-       return check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
-                               BPF_SIZE(insn->code), BPF_WRITE, -1, true);
+       /* check whether we can write into the same memory */
+       err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off,
+                              BPF_SIZE(insn->code), BPF_WRITE, -1, true);
+       if (err)
+               return err;
+
+       if (!(insn->imm & BPF_FETCH))
+               return 0;
+
+       if (insn->imm == BPF_CMPXCHG)
+               load_reg = BPF_REG_0;
+       else
+               load_reg = insn->src_reg;
+
+       /* check and record load of old value */
+       err = check_reg_arg(env, load_reg, DST_OP);
+       if (err)
+               return err;
+
+       return 0;
 }
 
 static int __check_stack_boundary(struct bpf_verifier_env *env, u32 regno,
@@ -4319,7 +4362,7 @@ skip_type_check:
                        err = mark_chain_precision(env, regno);
        } else if (arg_type_is_alloc_size(arg_type)) {
                if (!tnum_is_const(reg->var_off)) {
-                       verbose(env, "R%d unbounded size, use 'var &= const' or 'if (var < const)'\n",
+                       verbose(env, "R%d is not a known constant'\n",
                                regno);
                        return -EACCES;
                }
@@ -5311,7 +5354,7 @@ static bool signed_add_overflows(s64 a, s64 b)
        return res < a;
 }
 
-static bool signed_add32_overflows(s64 a, s64 b)
+static bool signed_add32_overflows(s32 a, s32 b)
 {
        /* Do the add in u32, where overflow is well-defined */
        s32 res = (s32)((u32)a + (u32)b);
@@ -5321,7 +5364,7 @@ static bool signed_add32_overflows(s64 a, s64 b)
        return res < a;
 }
 
-static bool signed_sub_overflows(s32 a, s32 b)
+static bool signed_sub_overflows(s64 a, s64 b)
 {
        /* Do the sub in u64, where overflow is well-defined */
        s64 res = (s64)((u64)a - (u64)b);
@@ -5333,7 +5376,7 @@ static bool signed_sub_overflows(s32 a, s32 b)
 
 static bool signed_sub32_overflows(s32 a, s32 b)
 {
-       /* Do the sub in u64, where overflow is well-defined */
+       /* Do the sub in u32, where overflow is well-defined */
        s32 res = (s32)((u32)a - (u32)b);
 
        if (b < 0)
@@ -9524,14 +9567,19 @@ static int do_check(struct bpf_verifier_env *env)
                } else if (class == BPF_STX) {
                        enum bpf_reg_type *prev_dst_type, dst_reg_type;
 
-                       if (BPF_MODE(insn->code) == BPF_XADD) {
-                               err = check_xadd(env, env->insn_idx, insn);
+                       if (BPF_MODE(insn->code) == BPF_ATOMIC) {
+                               err = check_atomic(env, env->insn_idx, insn);
                                if (err)
                                        return err;
                                env->insn_idx++;
                                continue;
                        }
 
+                       if (BPF_MODE(insn->code) != BPF_MEM || insn->imm != 0) {
+                               verbose(env, "BPF_STX uses reserved fields\n");
+                               return -EINVAL;
+                       }
+
                        /* check src1 operand */
                        err = check_reg_arg(env, insn->src_reg, SRC_OP);
                        if (err)
@@ -9703,6 +9751,36 @@ process_bpf_exit:
        return 0;
 }
 
+static int find_btf_percpu_datasec(struct btf *btf)
+{
+       const struct btf_type *t;
+       const char *tname;
+       int i, n;
+
+       /*
+        * Both vmlinux and module each have their own ".data..percpu"
+        * DATASECs in BTF. So for module's case, we need to skip vmlinux BTF
+        * types to look at only module's own BTF types.
+        */
+       n = btf_nr_types(btf);
+       if (btf_is_module(btf))
+               i = btf_nr_types(btf_vmlinux);
+       else
+               i = 1;
+
+       for(; i < n; i++) {
+               t = btf_type_by_id(btf, i);
+               if (BTF_INFO_KIND(t->info) != BTF_KIND_DATASEC)
+                       continue;
+
+               tname = btf_name_by_offset(btf, t->name_off);
+               if (!strcmp(tname, ".data..percpu"))
+                       return i;
+       }
+
+       return -ENOENT;
+}
+
 /* replace pseudo btf_id with kernel symbol address */
 static int check_pseudo_btf_id(struct bpf_verifier_env *env,
                               struct bpf_insn *insn,
@@ -9710,48 +9788,57 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
 {
        const struct btf_var_secinfo *vsi;
        const struct btf_type *datasec;
+       struct btf_mod_pair *btf_mod;
        const struct btf_type *t;
        const char *sym_name;
        bool percpu = false;
        u32 type, id = insn->imm;
+       struct btf *btf;
        s32 datasec_id;
        u64 addr;
-       int i;
+       int i, btf_fd, err;
 
-       if (!btf_vmlinux) {
-               verbose(env, "kernel is missing BTF, make sure CONFIG_DEBUG_INFO_BTF=y is specified in Kconfig.\n");
-               return -EINVAL;
-       }
-
-       if (insn[1].imm != 0) {
-               verbose(env, "reserved field (insn[1].imm) is used in pseudo_btf_id ldimm64 insn.\n");
-               return -EINVAL;
+       btf_fd = insn[1].imm;
+       if (btf_fd) {
+               btf = btf_get_by_fd(btf_fd);
+               if (IS_ERR(btf)) {
+                       verbose(env, "invalid module BTF object FD specified.\n");
+                       return -EINVAL;
+               }
+       } else {
+               if (!btf_vmlinux) {
+                       verbose(env, "kernel is missing BTF, make sure CONFIG_DEBUG_INFO_BTF=y is specified in Kconfig.\n");
+                       return -EINVAL;
+               }
+               btf = btf_vmlinux;
+               btf_get(btf);
        }
 
-       t = btf_type_by_id(btf_vmlinux, id);
+       t = btf_type_by_id(btf, id);
        if (!t) {
                verbose(env, "ldimm64 insn specifies invalid btf_id %d.\n", id);
-               return -ENOENT;
+               err = -ENOENT;
+               goto err_put;
        }
 
        if (!btf_type_is_var(t)) {
-               verbose(env, "pseudo btf_id %d in ldimm64 isn't KIND_VAR.\n",
-                       id);
-               return -EINVAL;
+               verbose(env, "pseudo btf_id %d in ldimm64 isn't KIND_VAR.\n", id);
+               err = -EINVAL;
+               goto err_put;
        }
 
-       sym_name = btf_name_by_offset(btf_vmlinux, t->name_off);
+       sym_name = btf_name_by_offset(btf, t->name_off);
        addr = kallsyms_lookup_name(sym_name);
        if (!addr) {
                verbose(env, "ldimm64 failed to find the address for kernel symbol '%s'.\n",
                        sym_name);
-               return -ENOENT;
+               err = -ENOENT;
+               goto err_put;
        }
 
-       datasec_id = btf_find_by_name_kind(btf_vmlinux, ".data..percpu",
-                                          BTF_KIND_DATASEC);
+       datasec_id = find_btf_percpu_datasec(btf);
        if (datasec_id > 0) {
-               datasec = btf_type_by_id(btf_vmlinux, datasec_id);
+               datasec = btf_type_by_id(btf, datasec_id);
                for_each_vsi(i, datasec, vsi) {
                        if (vsi->type == id) {
                                percpu = true;
@@ -9764,10 +9851,10 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
        insn[1].imm = addr >> 32;
 
        type = t->type;
-       t = btf_type_skip_modifiers(btf_vmlinux, type, NULL);
+       t = btf_type_skip_modifiers(btf, type, NULL);
        if (percpu) {
                aux->btf_var.reg_type = PTR_TO_PERCPU_BTF_ID;
-               aux->btf_var.btf = btf_vmlinux;
+               aux->btf_var.btf = btf;
                aux->btf_var.btf_id = type;
        } else if (!btf_type_is_struct(t)) {
                const struct btf_type *ret;
@@ -9775,21 +9862,54 @@ static int check_pseudo_btf_id(struct bpf_verifier_env *env,
                u32 tsize;
 
                /* resolve the type size of ksym. */
-               ret = btf_resolve_size(btf_vmlinux, t, &tsize);
+               ret = btf_resolve_size(btf, t, &tsize);
                if (IS_ERR(ret)) {
-                       tname = btf_name_by_offset(btf_vmlinux, t->name_off);
+                       tname = btf_name_by_offset(btf, t->name_off);
                        verbose(env, "ldimm64 unable to resolve the size of type '%s': %ld\n",
                                tname, PTR_ERR(ret));
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto err_put;
                }
                aux->btf_var.reg_type = PTR_TO_MEM;
                aux->btf_var.mem_size = tsize;
        } else {
                aux->btf_var.reg_type = PTR_TO_BTF_ID;
-               aux->btf_var.btf = btf_vmlinux;
+               aux->btf_var.btf = btf;
                aux->btf_var.btf_id = type;
        }
+
+       /* check whether we recorded this BTF (and maybe module) already */
+       for (i = 0; i < env->used_btf_cnt; i++) {
+               if (env->used_btfs[i].btf == btf) {
+                       btf_put(btf);
+                       return 0;
+               }
+       }
+
+       if (env->used_btf_cnt >= MAX_USED_BTFS) {
+               err = -E2BIG;
+               goto err_put;
+       }
+
+       btf_mod = &env->used_btfs[env->used_btf_cnt];
+       btf_mod->btf = btf;
+       btf_mod->module = NULL;
+
+       /* if we reference variables from kernel module, bump its refcount */
+       if (btf_is_module(btf)) {
+               btf_mod->module = btf_try_get_module(btf);
+               if (!btf_mod->module) {
+                       err = -ENXIO;
+                       goto err_put;
+               }
+       }
+
+       env->used_btf_cnt++;
+
        return 0;
+err_put:
+       btf_put(btf);
+       return err;
 }
 
 static int check_map_prealloc(struct bpf_map *map)
@@ -9936,13 +10056,6 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
                        return -EINVAL;
                }
 
-               if (BPF_CLASS(insn->code) == BPF_STX &&
-                   ((BPF_MODE(insn->code) != BPF_MEM &&
-                     BPF_MODE(insn->code) != BPF_XADD) || insn->imm != 0)) {
-                       verbose(env, "BPF_STX uses reserved fields\n");
-                       return -EINVAL;
-               }
-
                if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
                        struct bpf_insn_aux_data *aux;
                        struct bpf_map *map;
@@ -10086,6 +10199,13 @@ static void release_maps(struct bpf_verifier_env *env)
                             env->used_map_cnt);
 }
 
+/* drop refcnt of maps used by the rejected program */
+static void release_btfs(struct bpf_verifier_env *env)
+{
+       __bpf_free_used_btfs(env->prog->aux, env->used_btfs,
+                            env->used_btf_cnt);
+}
+
 /* convert pseudo BPF_LD_IMM64 into generic BPF_LD_IMM64 */
 static void convert_pseudo_ld_imm64(struct bpf_verifier_env *env)
 {
@@ -12098,7 +12218,10 @@ skip_full_check:
                goto err_release_maps;
        }
 
-       if (ret == 0 && env->used_map_cnt) {
+       if (ret)
+               goto err_release_maps;
+
+       if (env->used_map_cnt) {
                /* if program passed verifier, update used_maps in bpf_prog_info */
                env->prog->aux->used_maps = kmalloc_array(env->used_map_cnt,
                                                          sizeof(env->used_maps[0]),
@@ -12112,15 +12235,29 @@ skip_full_check:
                memcpy(env->prog->aux->used_maps, env->used_maps,
                       sizeof(env->used_maps[0]) * env->used_map_cnt);
                env->prog->aux->used_map_cnt = env->used_map_cnt;
+       }
+       if (env->used_btf_cnt) {
+               /* if program passed verifier, update used_btfs in bpf_prog_aux */
+               env->prog->aux->used_btfs = kmalloc_array(env->used_btf_cnt,
+                                                         sizeof(env->used_btfs[0]),
+                                                         GFP_KERNEL);
+               if (!env->prog->aux->used_btfs) {
+                       ret = -ENOMEM;
+                       goto err_release_maps;
+               }
 
+               memcpy(env->prog->aux->used_btfs, env->used_btfs,
+                      sizeof(env->used_btfs[0]) * env->used_btf_cnt);
+               env->prog->aux->used_btf_cnt = env->used_btf_cnt;
+       }
+       if (env->used_map_cnt || env->used_btf_cnt) {
                /* program is valid. Convert pseudo bpf_ld_imm64 into generic
                 * bpf_ld_imm64 instructions
                 */
                convert_pseudo_ld_imm64(env);
        }
 
-       if (ret == 0)
-               adjust_btf_func(env);
+       adjust_btf_func(env);
 
 err_release_maps:
        if (!env->prog->aux->used_maps)
@@ -12128,6 +12265,8 @@ err_release_maps:
                 * them now. Otherwise free_used_maps() will release them.
                 */
                release_maps(env);
+       if (!env->prog->aux->used_btfs)
+               release_btfs(env);
 
        /* extension progs temporarily inherit the attach_type of their targets
           for verification purposes, so set it back to zero before returning
index 191c329..32596fd 100644 (file)
@@ -908,6 +908,8 @@ int cgroup1_parse_param(struct fs_context *fc, struct fs_parameter *param)
        opt = fs_parse(fc, cgroup1_fs_parameters, param, &result);
        if (opt == -ENOPARAM) {
                if (strcmp(param->key, "source") == 0) {
+                       if (fc->source)
+                               return invalf(fc, "Multiple sources not supported");
                        fc->source = param->string;
                        param->string = NULL;
                        return 0;
index fefa219..6138457 100644 (file)
@@ -244,7 +244,7 @@ bool cgroup_ssid_enabled(int ssid)
  *
  * The default hierarchy is the v2 interface of cgroup and this function
  * can be used to test whether a cgroup is on the default hierarchy for
- * cases where a subsystem should behave differnetly depending on the
+ * cases where a subsystem should behave differently depending on the
  * interface version.
  *
  * List of changed behaviors:
@@ -262,7 +262,7 @@ bool cgroup_ssid_enabled(int ssid)
  *   "cgroup.procs" instead.
  *
  * - "cgroup.procs" is not sorted.  pids will be unique unless they got
- *   recycled inbetween reads.
+ *   recycled in-between reads.
  *
  * - "release_agent" and "notify_on_release" are removed.  Replacement
  *   notification mechanism will be implemented.
@@ -342,7 +342,7 @@ static bool cgroup_is_mixable(struct cgroup *cgrp)
        return !cgroup_parent(cgrp);
 }
 
-/* can @cgrp become a thread root? should always be true for a thread root */
+/* can @cgrp become a thread root? Should always be true for a thread root */
 static bool cgroup_can_be_thread_root(struct cgroup *cgrp)
 {
        /* mixables don't care */
@@ -527,7 +527,7 @@ static struct cgroup_subsys_state *cgroup_e_css_by_mask(struct cgroup *cgrp,
  * the root css is returned, so this function always returns a valid css.
  *
  * The returned css is not guaranteed to be online, and therefore it is the
- * callers responsiblity to tryget a reference for it.
+ * callers responsibility to try get a reference for it.
  */
 struct cgroup_subsys_state *cgroup_e_css(struct cgroup *cgrp,
                                         struct cgroup_subsys *ss)
@@ -699,7 +699,7 @@ EXPORT_SYMBOL_GPL(of_css);
                        ;                                               \
                else
 
-/* walk live descendants in preorder */
+/* walk live descendants in pre order */
 #define cgroup_for_each_live_descendant_pre(dsct, d_css, cgrp)         \
        css_for_each_descendant_pre((d_css), cgroup_css((cgrp), NULL))  \
                if (({ lockdep_assert_held(&cgroup_mutex);              \
@@ -933,7 +933,7 @@ void put_css_set_locked(struct css_set *cset)
 
        WARN_ON_ONCE(!list_empty(&cset->threaded_csets));
 
-       /* This css_set is dead. unlink it and release cgroup and css refs */
+       /* This css_set is dead. Unlink it and release cgroup and css refs */
        for_each_subsys(ss, ssid) {
                list_del(&cset->e_cset_node[ssid]);
                css_put(cset->subsys[ssid]);
@@ -1058,7 +1058,7 @@ static struct css_set *find_existing_css_set(struct css_set *old_cset,
 
        /*
         * Build the set of subsystem state objects that we want to see in the
-        * new css_set. while subsystems can change globally, the entries here
+        * new css_set. While subsystems can change globally, the entries here
         * won't change, so no need for locking.
         */
        for_each_subsys(ss, i) {
@@ -1148,7 +1148,7 @@ static void link_css_set(struct list_head *tmp_links, struct css_set *cset,
 
        /*
         * Always add links to the tail of the lists so that the lists are
-        * in choronological order.
+        * in chronological order.
         */
        list_move_tail(&link->cset_link, &cgrp->cset_links);
        list_add_tail(&link->cgrp_link, &cset->cgrp_links);
@@ -3654,7 +3654,7 @@ static ssize_t cgroup_freeze_write(struct kernfs_open_file *of,
 
 static int cgroup_file_open(struct kernfs_open_file *of)
 {
-       struct cftype *cft = of->kn->priv;
+       struct cftype *cft = of_cft(of);
 
        if (cft->open)
                return cft->open(of);
@@ -3663,7 +3663,7 @@ static int cgroup_file_open(struct kernfs_open_file *of)
 
 static void cgroup_file_release(struct kernfs_open_file *of)
 {
-       struct cftype *cft = of->kn->priv;
+       struct cftype *cft = of_cft(of);
 
        if (cft->release)
                cft->release(of);
@@ -3674,7 +3674,7 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
 {
        struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
        struct cgroup *cgrp = of->kn->parent->priv;
-       struct cftype *cft = of->kn->priv;
+       struct cftype *cft = of_cft(of);
        struct cgroup_subsys_state *css;
        int ret;
 
@@ -3724,7 +3724,7 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
 
 static __poll_t cgroup_file_poll(struct kernfs_open_file *of, poll_table *pt)
 {
-       struct cftype *cft = of->kn->priv;
+       struct cftype *cft = of_cft(of);
 
        if (cft->poll)
                return cft->poll(of, pt);
@@ -4134,7 +4134,7 @@ struct cgroup_subsys_state *css_next_child(struct cgroup_subsys_state *pos,
         * implies that if we observe !CSS_RELEASED on @pos in this RCU
         * critical section, the one pointed to by its next pointer is
         * guaranteed to not have finished its RCU grace period even if we
-        * have dropped rcu_read_lock() inbetween iterations.
+        * have dropped rcu_read_lock() in-between iterations.
         *
         * If @pos has CSS_RELEASED set, its next pointer can't be
         * dereferenced; however, as each css is given a monotonically
@@ -4382,7 +4382,7 @@ static struct css_set *css_task_iter_next_css_set(struct css_task_iter *it)
 }
 
 /**
- * css_task_iter_advance_css_set - advance a task itererator to the next css_set
+ * css_task_iter_advance_css_set - advance a task iterator to the next css_set
  * @it: the iterator to advance
  *
  * Advance @it to the next css_set to walk.
@@ -6308,7 +6308,7 @@ struct cgroup_subsys_state *css_from_id(int id, struct cgroup_subsys *ss)
  *
  * Find the cgroup at @path on the default hierarchy, increment its
  * reference count and return it.  Returns pointer to the found cgroup on
- * success, ERR_PTR(-ENOENT) if @path doens't exist and ERR_PTR(-ENOTDIR)
+ * success, ERR_PTR(-ENOENT) if @path doesn't exist and ERR_PTR(-ENOTDIR)
  * if @path points to a non-directory.
  */
 struct cgroup *cgroup_get_from_path(const char *path)
index 53d688b..eb0029c 100644 (file)
@@ -81,7 +81,6 @@ CONFIG_INPUT_JOYSTICK=y
 CONFIG_INPUT_MISC=y
 CONFIG_INPUT_TABLET=y
 CONFIG_INPUT_UINPUT=y
-CONFIG_ION=y
 CONFIG_JOYSTICK_XPAD=y
 CONFIG_JOYSTICK_XPAD_FF=y
 CONFIG_JOYSTICK_XPAD_LEDS=y
index 55d1879..c37401e 100644 (file)
@@ -53,6 +53,7 @@
 #include <linux/min_heap.h>
 #include <linux/highmem.h>
 #include <linux/pgtable.h>
+#include <linux/buildid.h>
 
 #include "internal.h"
 
@@ -397,6 +398,7 @@ static atomic_t nr_ksymbol_events __read_mostly;
 static atomic_t nr_bpf_events __read_mostly;
 static atomic_t nr_cgroup_events __read_mostly;
 static atomic_t nr_text_poke_events __read_mostly;
+static atomic_t nr_build_id_events __read_mostly;
 
 static LIST_HEAD(pmus);
 static DEFINE_MUTEX(pmus_lock);
@@ -4673,6 +4675,8 @@ static void unaccount_event(struct perf_event *event)
                dec = true;
        if (event->attr.mmap || event->attr.mmap_data)
                atomic_dec(&nr_mmap_events);
+       if (event->attr.build_id)
+               atomic_dec(&nr_build_id_events);
        if (event->attr.comm)
                atomic_dec(&nr_comm_events);
        if (event->attr.namespaces)
@@ -8046,6 +8050,8 @@ struct perf_mmap_event {
        u64                     ino;
        u64                     ino_generation;
        u32                     prot, flags;
+       u8                      build_id[BUILD_ID_SIZE_MAX];
+       u32                     build_id_size;
 
        struct {
                struct perf_event_header        header;
@@ -8077,6 +8083,7 @@ static void perf_event_mmap_output(struct perf_event *event,
        struct perf_sample_data sample;
        int size = mmap_event->event_id.header.size;
        u32 type = mmap_event->event_id.header.type;
+       bool use_build_id;
        int ret;
 
        if (!perf_event_mmap_match(event, data))
@@ -8101,13 +8108,25 @@ static void perf_event_mmap_output(struct perf_event *event,
        mmap_event->event_id.pid = perf_event_pid(event, current);
        mmap_event->event_id.tid = perf_event_tid(event, current);
 
+       use_build_id = event->attr.build_id && mmap_event->build_id_size;
+
+       if (event->attr.mmap2 && use_build_id)
+               mmap_event->event_id.header.misc |= PERF_RECORD_MISC_MMAP_BUILD_ID;
+
        perf_output_put(&handle, mmap_event->event_id);
 
        if (event->attr.mmap2) {
-               perf_output_put(&handle, mmap_event->maj);
-               perf_output_put(&handle, mmap_event->min);
-               perf_output_put(&handle, mmap_event->ino);
-               perf_output_put(&handle, mmap_event->ino_generation);
+               if (use_build_id) {
+                       u8 size[4] = { (u8) mmap_event->build_id_size, 0, 0, 0 };
+
+                       __output_copy(&handle, size, 4);
+                       __output_copy(&handle, mmap_event->build_id, BUILD_ID_SIZE_MAX);
+               } else {
+                       perf_output_put(&handle, mmap_event->maj);
+                       perf_output_put(&handle, mmap_event->min);
+                       perf_output_put(&handle, mmap_event->ino);
+                       perf_output_put(&handle, mmap_event->ino_generation);
+               }
                perf_output_put(&handle, mmap_event->prot);
                perf_output_put(&handle, mmap_event->flags);
        }
@@ -8236,6 +8255,9 @@ got_name:
 
        mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size;
 
+       if (atomic_read(&nr_build_id_events))
+               build_id_parse(vma, mmap_event->build_id, &mmap_event->build_id_size);
+
        perf_iterate_sb(perf_event_mmap_output,
                       mmap_event,
                       NULL);
@@ -11172,6 +11194,8 @@ static void account_event(struct perf_event *event)
                inc = true;
        if (event->attr.mmap || event->attr.mmap_data)
                atomic_inc(&nr_mmap_events);
+       if (event->attr.build_id)
+               atomic_inc(&nr_build_id_events);
        if (event->attr.comm)
                atomic_inc(&nr_comm_events);
        if (event->attr.namespaces)
index 3594291..04029e3 100644 (file)
@@ -63,6 +63,7 @@
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
+#include <linux/io_uring.h>
 
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
@@ -776,6 +777,7 @@ void __noreturn do_exit(long code)
                schedule();
        }
 
+       io_uring_files_cancel(tsk->files);
        exit_signals(tsk);  /* sets PF_EXITING */
 
        /* sync mm's RSS info before statistics gathering */
index 37720a6..d66cd10 100644 (file)
@@ -819,9 +819,8 @@ void __init fork_init(void)
        init_task.signal->rlim[RLIMIT_SIGPENDING] =
                init_task.signal->rlim[RLIMIT_NPROC];
 
-       for (i = 0; i < UCOUNT_COUNTS; i++) {
+       for (i = 0; i < UCOUNT_COUNTS; i++)
                init_user_ns.ucount_max[i] = max_threads/2;
-       }
 
 #ifdef CONFIG_VMAP_STACK
        cpuhp_setup_state(CPUHP_BP_PREPARE_DYN, "fork:vm_stack_cache",
@@ -1654,9 +1653,8 @@ static inline void init_task_pid_links(struct task_struct *task)
 {
        enum pid_type type;
 
-       for (type = PIDTYPE_PID; type < PIDTYPE_MAX; ++type) {
+       for (type = PIDTYPE_PID; type < PIDTYPE_MAX; ++type)
                INIT_HLIST_NODE(&task->pid_links[type]);
-       }
 }
 
 static inline void
index c47d101..45a13eb 100644 (file)
@@ -763,6 +763,29 @@ static struct futex_pi_state *alloc_pi_state(void)
        return pi_state;
 }
 
+static void pi_state_update_owner(struct futex_pi_state *pi_state,
+                                 struct task_struct *new_owner)
+{
+       struct task_struct *old_owner = pi_state->owner;
+
+       lockdep_assert_held(&pi_state->pi_mutex.wait_lock);
+
+       if (old_owner) {
+               raw_spin_lock(&old_owner->pi_lock);
+               WARN_ON(list_empty(&pi_state->list));
+               list_del_init(&pi_state->list);
+               raw_spin_unlock(&old_owner->pi_lock);
+       }
+
+       if (new_owner) {
+               raw_spin_lock(&new_owner->pi_lock);
+               WARN_ON(!list_empty(&pi_state->list));
+               list_add(&pi_state->list, &new_owner->pi_state_list);
+               pi_state->owner = new_owner;
+               raw_spin_unlock(&new_owner->pi_lock);
+       }
+}
+
 static void get_pi_state(struct futex_pi_state *pi_state)
 {
        WARN_ON_ONCE(!refcount_inc_not_zero(&pi_state->refcount));
@@ -785,17 +808,11 @@ static void put_pi_state(struct futex_pi_state *pi_state)
         * and has cleaned up the pi_state already
         */
        if (pi_state->owner) {
-               struct task_struct *owner;
                unsigned long flags;
 
                raw_spin_lock_irqsave(&pi_state->pi_mutex.wait_lock, flags);
-               owner = pi_state->owner;
-               if (owner) {
-                       raw_spin_lock(&owner->pi_lock);
-                       list_del_init(&pi_state->list);
-                       raw_spin_unlock(&owner->pi_lock);
-               }
-               rt_mutex_proxy_unlock(&pi_state->pi_mutex, owner);
+               pi_state_update_owner(pi_state, NULL);
+               rt_mutex_proxy_unlock(&pi_state->pi_mutex);
                raw_spin_unlock_irqrestore(&pi_state->pi_mutex.wait_lock, flags);
        }
 
@@ -941,7 +958,8 @@ static inline void exit_pi_state_list(struct task_struct *curr) { }
  *     FUTEX_OWNER_DIED bit. See [4]
  *
  * [10] There is no transient state which leaves owner and user space
- *     TID out of sync.
+ *     TID out of sync. Except one error case where the kernel is denied
+ *     write access to the user address, see fixup_pi_state_owner().
  *
  *
  * Serialization and lifetime rules:
@@ -1521,26 +1539,15 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_
                        ret = -EINVAL;
        }
 
-       if (ret)
-               goto out_unlock;
-
-       /*
-        * This is a point of no return; once we modify the uval there is no
-        * going back and subsequent operations must not fail.
-        */
-
-       raw_spin_lock(&pi_state->owner->pi_lock);
-       WARN_ON(list_empty(&pi_state->list));
-       list_del_init(&pi_state->list);
-       raw_spin_unlock(&pi_state->owner->pi_lock);
-
-       raw_spin_lock(&new_owner->pi_lock);
-       WARN_ON(!list_empty(&pi_state->list));
-       list_add(&pi_state->list, &new_owner->pi_state_list);
-       pi_state->owner = new_owner;
-       raw_spin_unlock(&new_owner->pi_lock);
-
-       postunlock = __rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q);
+       if (!ret) {
+               /*
+                * This is a point of no return; once we modified the uval
+                * there is no going back and subsequent operations must
+                * not fail.
+                */
+               pi_state_update_owner(pi_state, new_owner);
+               postunlock = __rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q);
+       }
 
 out_unlock:
        raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
@@ -2323,18 +2330,13 @@ static void unqueue_me_pi(struct futex_q *q)
        spin_unlock(q->lock_ptr);
 }
 
-static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
-                               struct task_struct *argowner)
+static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
+                                 struct task_struct *argowner)
 {
        struct futex_pi_state *pi_state = q->pi_state;
-       u32 uval, curval, newval;
        struct task_struct *oldowner, *newowner;
-       u32 newtid;
-       int ret, err = 0;
-
-       lockdep_assert_held(q->lock_ptr);
-
-       raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
+       u32 uval, curval, newval, newtid;
+       int err = 0;
 
        oldowner = pi_state->owner;
 
@@ -2368,14 +2370,12 @@ retry:
                         * We raced against a concurrent self; things are
                         * already fixed up. Nothing to do.
                         */
-                       ret = 0;
-                       goto out_unlock;
+                       return 0;
                }
 
                if (__rt_mutex_futex_trylock(&pi_state->pi_mutex)) {
-                       /* We got the lock after all, nothing to fix. */
-                       ret = 0;
-                       goto out_unlock;
+                       /* We got the lock. pi_state is correct. Tell caller. */
+                       return 1;
                }
 
                /*
@@ -2402,8 +2402,7 @@ retry:
                         * We raced against a concurrent self; things are
                         * already fixed up. Nothing to do.
                         */
-                       ret = 0;
-                       goto out_unlock;
+                       return 1;
                }
                newowner = argowner;
        }
@@ -2433,22 +2432,9 @@ retry:
         * We fixed up user space. Now we need to fix the pi_state
         * itself.
         */
-       if (pi_state->owner != NULL) {
-               raw_spin_lock(&pi_state->owner->pi_lock);
-               WARN_ON(list_empty(&pi_state->list));
-               list_del_init(&pi_state->list);
-               raw_spin_unlock(&pi_state->owner->pi_lock);
-       }
+       pi_state_update_owner(pi_state, newowner);
 
-       pi_state->owner = newowner;
-
-       raw_spin_lock(&newowner->pi_lock);
-       WARN_ON(!list_empty(&pi_state->list));
-       list_add(&pi_state->list, &newowner->pi_state_list);
-       raw_spin_unlock(&newowner->pi_lock);
-       raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
-
-       return 0;
+       return argowner == current;
 
        /*
         * In order to reschedule or handle a page fault, we need to drop the
@@ -2469,17 +2455,16 @@ handle_err:
 
        switch (err) {
        case -EFAULT:
-               ret = fault_in_user_writeable(uaddr);
+               err = fault_in_user_writeable(uaddr);
                break;
 
        case -EAGAIN:
                cond_resched();
-               ret = 0;
+               err = 0;
                break;
 
        default:
                WARN_ON_ONCE(1);
-               ret = err;
                break;
        }
 
@@ -2489,17 +2474,44 @@ handle_err:
        /*
         * Check if someone else fixed it for us:
         */
-       if (pi_state->owner != oldowner) {
-               ret = 0;
-               goto out_unlock;
-       }
+       if (pi_state->owner != oldowner)
+               return argowner == current;
 
-       if (ret)
-               goto out_unlock;
+       /* Retry if err was -EAGAIN or the fault in succeeded */
+       if (!err)
+               goto retry;
 
-       goto retry;
+       /*
+        * fault_in_user_writeable() failed so user state is immutable. At
+        * best we can make the kernel state consistent but user state will
+        * be most likely hosed and any subsequent unlock operation will be
+        * rejected due to PI futex rule [10].
+        *
+        * Ensure that the rtmutex owner is also the pi_state owner despite
+        * the user space value claiming something different. There is no
+        * point in unlocking the rtmutex if current is the owner as it
+        * would need to wait until the next waiter has taken the rtmutex
+        * to guarantee consistent state. Keep it simple. Userspace asked
+        * for this wreckaged state.
+        *
+        * The rtmutex has an owner - either current or some other
+        * task. See the EAGAIN loop above.
+        */
+       pi_state_update_owner(pi_state, rt_mutex_owner(&pi_state->pi_mutex));
 
-out_unlock:
+       return err;
+}
+
+static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
+                               struct task_struct *argowner)
+{
+       struct futex_pi_state *pi_state = q->pi_state;
+       int ret;
+
+       lockdep_assert_held(q->lock_ptr);
+
+       raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
+       ret = __fixup_pi_state_owner(uaddr, q, argowner);
        raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
        return ret;
 }
@@ -2523,8 +2535,6 @@ static long futex_wait_restart(struct restart_block *restart);
  */
 static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
 {
-       int ret = 0;
-
        if (locked) {
                /*
                 * Got the lock. We might not be the anticipated owner if we
@@ -2535,8 +2545,8 @@ static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
                 * stable state, anything else needs more attention.
                 */
                if (q->pi_state->owner != current)
-                       ret = fixup_pi_state_owner(uaddr, q, current);
-               return ret ? ret : locked;
+                       return fixup_pi_state_owner(uaddr, q, current);
+               return 1;
        }
 
        /*
@@ -2547,23 +2557,17 @@ static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
         * Another speculative read; pi_state->owner == current is unstable
         * but needs our attention.
         */
-       if (q->pi_state->owner == current) {
-               ret = fixup_pi_state_owner(uaddr, q, NULL);
-               return ret;
-       }
+       if (q->pi_state->owner == current)
+               return fixup_pi_state_owner(uaddr, q, NULL);
 
        /*
         * Paranoia check. If we did not take the lock, then we should not be
-        * the owner of the rt_mutex.
+        * the owner of the rt_mutex. Warn and establish consistent state.
         */
-       if (rt_mutex_owner(&q->pi_state->pi_mutex) == current) {
-               printk(KERN_ERR "fixup_owner: ret = %d pi-mutex: %p "
-                               "pi-state %p\n", ret,
-                               q->pi_state->pi_mutex.owner,
-                               q->pi_state->owner);
-       }
+       if (WARN_ON_ONCE(rt_mutex_owner(&q->pi_state->pi_mutex) == current))
+               return fixup_pi_state_owner(uaddr, q, current);
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -2771,7 +2775,6 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags,
                         ktime_t *time, int trylock)
 {
        struct hrtimer_sleeper timeout, *to;
-       struct futex_pi_state *pi_state = NULL;
        struct task_struct *exiting = NULL;
        struct rt_mutex_waiter rt_waiter;
        struct futex_hash_bucket *hb;
@@ -2907,23 +2910,8 @@ no_block:
        if (res)
                ret = (res < 0) ? res : 0;
 
-       /*
-        * If fixup_owner() faulted and was unable to handle the fault, unlock
-        * it and return the fault to userspace.
-        */
-       if (ret && (rt_mutex_owner(&q.pi_state->pi_mutex) == current)) {
-               pi_state = q.pi_state;
-               get_pi_state(pi_state);
-       }
-
        /* Unqueue and drop the lock */
        unqueue_me_pi(&q);
-
-       if (pi_state) {
-               rt_mutex_futex_unlock(&pi_state->pi_mutex);
-               put_pi_state(pi_state);
-       }
-
        goto out;
 
 out_unlock_put_key:
@@ -3183,7 +3171,6 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
                                 u32 __user *uaddr2)
 {
        struct hrtimer_sleeper timeout, *to;
-       struct futex_pi_state *pi_state = NULL;
        struct rt_mutex_waiter rt_waiter;
        struct futex_hash_bucket *hb;
        union futex_key key2 = FUTEX_KEY_INIT;
@@ -3261,16 +3248,17 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
                if (q.pi_state && (q.pi_state->owner != current)) {
                        spin_lock(q.lock_ptr);
                        ret = fixup_pi_state_owner(uaddr2, &q, current);
-                       if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
-                               pi_state = q.pi_state;
-                               get_pi_state(pi_state);
-                       }
                        /*
                         * Drop the reference to the pi state which
                         * the requeue_pi() code acquired for us.
                         */
                        put_pi_state(q.pi_state);
                        spin_unlock(q.lock_ptr);
+                       /*
+                        * Adjust the return value. It's either -EFAULT or
+                        * success (1) but the caller expects 0 for success.
+                        */
+                       ret = ret < 0 ? ret : 0;
                }
        } else {
                struct rt_mutex *pi_mutex;
@@ -3301,25 +3289,10 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
                if (res)
                        ret = (res < 0) ? res : 0;
 
-               /*
-                * If fixup_pi_state_owner() faulted and was unable to handle
-                * the fault, unlock the rt_mutex and return the fault to
-                * userspace.
-                */
-               if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
-                       pi_state = q.pi_state;
-                       get_pi_state(pi_state);
-               }
-
                /* Unqueue and drop the lock. */
                unqueue_me_pi(&q);
        }
 
-       if (pi_state) {
-               rt_mutex_futex_unlock(&pi_state->pi_mutex);
-               put_pi_state(pi_state);
-       }
-
        if (ret == -EINTR) {
                /*
                 * We've already been requeued, but cannot restart by calling
index ab8567f..dec3f73 100644 (file)
@@ -2859,3 +2859,4 @@ bool irq_check_status_bit(unsigned int irq, unsigned int bitmask)
        rcu_read_unlock();
        return res;
 }
+EXPORT_SYMBOL_GPL(irq_check_status_bit);
index 2c0c4d6..dc0e2d7 100644 (file)
@@ -402,7 +402,7 @@ int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
        struct msi_domain_ops *ops = info->ops;
        struct irq_data *irq_data;
        struct msi_desc *desc;
-       msi_alloc_info_t arg;
+       msi_alloc_info_t arg = { };
        int i, ret, virq;
        bool can_reserve;
 
index a5eceec..1578973 100644 (file)
@@ -294,7 +294,7 @@ static int kthread(void *_create)
        do_exit(ret);
 }
 
-/* called from do_fork() to get node information for about to be created task */
+/* called from kernel_clone() to get node information for about to be created task */
 int tsk_fork_get_node(struct task_struct *tsk)
 {
 #ifdef CONFIG_NUMA
@@ -493,11 +493,36 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
                return p;
        kthread_bind(p, cpu);
        /* CPU hotplug need to bind once again when unparking the thread. */
-       set_bit(KTHREAD_IS_PER_CPU, &to_kthread(p)->flags);
        to_kthread(p)->cpu = cpu;
        return p;
 }
 
+void kthread_set_per_cpu(struct task_struct *k, int cpu)
+{
+       struct kthread *kthread = to_kthread(k);
+       if (!kthread)
+               return;
+
+       WARN_ON_ONCE(!(k->flags & PF_NO_SETAFFINITY));
+
+       if (cpu < 0) {
+               clear_bit(KTHREAD_IS_PER_CPU, &kthread->flags);
+               return;
+       }
+
+       kthread->cpu = cpu;
+       set_bit(KTHREAD_IS_PER_CPU, &kthread->flags);
+}
+
+bool kthread_is_per_cpu(struct task_struct *k)
+{
+       struct kthread *kthread = to_kthread(k);
+       if (!kthread)
+               return false;
+
+       return test_bit(KTHREAD_IS_PER_CPU, &kthread->flags);
+}
+
 /**
  * kthread_unpark - unpark a thread created by kthread_create().
  * @k:         thread created by kthread_create().
index c1418b4..bdaf482 100644 (file)
@@ -79,7 +79,7 @@ module_param(lock_stat, int, 0644);
 DEFINE_PER_CPU(unsigned int, lockdep_recursion);
 EXPORT_PER_CPU_SYMBOL_GPL(lockdep_recursion);
 
-static inline bool lockdep_enabled(void)
+static __always_inline bool lockdep_enabled(void)
 {
        if (!debug_locks)
                return false;
@@ -5271,12 +5271,15 @@ static void __lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie cookie
 /*
  * Check whether we follow the irq-flags state precisely:
  */
-static void check_flags(unsigned long flags)
+static noinstr void check_flags(unsigned long flags)
 {
 #if defined(CONFIG_PROVE_LOCKING) && defined(CONFIG_DEBUG_LOCKDEP)
        if (!debug_locks)
                return;
 
+       /* Get the warning out..  */
+       instrumentation_begin();
+
        if (irqs_disabled_flags(flags)) {
                if (DEBUG_LOCKS_WARN_ON(lockdep_hardirqs_enabled())) {
                        printk("possible reason: unannotated irqs-off.\n");
@@ -5304,6 +5307,8 @@ static void check_flags(unsigned long flags)
 
        if (!debug_locks)
                print_irqtrace_events(current);
+
+       instrumentation_end();
 #endif
 }
 
index cfdd5b9..2f8cd61 100644 (file)
@@ -1716,8 +1716,7 @@ void rt_mutex_init_proxy_locked(struct rt_mutex *lock,
  * possible because it belongs to the pi_state which is about to be freed
  * and it is not longer visible to other tasks.
  */
-void rt_mutex_proxy_unlock(struct rt_mutex *lock,
-                          struct task_struct *proxy_owner)
+void rt_mutex_proxy_unlock(struct rt_mutex *lock)
 {
        debug_rt_mutex_proxy_unlock(lock);
        rt_mutex_set_owner(lock, NULL);
index d1d62f9..ca6fb48 100644 (file)
@@ -133,8 +133,7 @@ enum rtmutex_chainwalk {
 extern struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock);
 extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock,
                                       struct task_struct *proxy_owner);
-extern void rt_mutex_proxy_unlock(struct rt_mutex *lock,
-                                 struct task_struct *proxy_owner);
+extern void rt_mutex_proxy_unlock(struct rt_mutex *lock);
 extern void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter);
 extern int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
                                     struct rt_mutex_waiter *waiter,
index ffdd0dc..5a95c68 100644 (file)
@@ -1291,11 +1291,16 @@ static size_t info_print_prefix(const struct printk_info  *info, bool syslog,
  * done:
  *
  *   - Add prefix for each line.
+ *   - Drop truncated lines that no longer fit into the buffer.
  *   - Add the trailing newline that has been removed in vprintk_store().
- *   - Drop truncated lines that do not longer fit into the buffer.
+ *   - Add a string terminator.
+ *
+ * Since the produced string is always terminated, the maximum possible
+ * return value is @r->text_buf_size - 1;
  *
  * Return: The length of the updated/prepared text, including the added
- * prefixes and the newline. The dropped line(s) are not counted.
+ * prefixes and the newline. The terminator is not counted. The dropped
+ * line(s) are not counted.
  */
 static size_t record_print_text(struct printk_record *r, bool syslog,
                                bool time)
@@ -1338,26 +1343,31 @@ static size_t record_print_text(struct printk_record *r, bool syslog,
 
                /*
                 * Truncate the text if there is not enough space to add the
-                * prefix and a trailing newline.
+                * prefix and a trailing newline and a terminator.
                 */
-               if (len + prefix_len + text_len + 1 > buf_size) {
+               if (len + prefix_len + text_len + 1 + 1 > buf_size) {
                        /* Drop even the current line if no space. */
-                       if (len + prefix_len + line_len + 1 > buf_size)
+                       if (len + prefix_len + line_len + 1 + 1 > buf_size)
                                break;
 
-                       text_len = buf_size - len - prefix_len - 1;
+                       text_len = buf_size - len - prefix_len - 1 - 1;
                        truncated = true;
                }
 
                memmove(text + prefix_len, text, text_len);
                memcpy(text, prefix, prefix_len);
 
+               /*
+                * Increment the prepared length to include the text and
+                * prefix that were just moved+copied. Also increment for the
+                * newline at the end of this line. If this is the last line,
+                * there is no newline, but it will be added immediately below.
+                */
                len += prefix_len + line_len + 1;
-
                if (text_len == line_len) {
                        /*
-                        * Add the trailing newline removed in
-                        * vprintk_store().
+                        * This is the last line. Add the trailing newline
+                        * removed in vprintk_store().
                         */
                        text[prefix_len + line_len] = '\n';
                        break;
@@ -1382,6 +1392,14 @@ static size_t record_print_text(struct printk_record *r, bool syslog,
                text_len -= line_len + 1;
        }
 
+       /*
+        * If a buffer was provided, it will be terminated. Space for the
+        * string terminator is guaranteed to be available. The terminator is
+        * not counted in the return value.
+        */
+       if (buf_size > 0)
+               r->text_buf[len] = 0;
+
        return len;
 }
 
@@ -3427,7 +3445,7 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
        while (prb_read_valid_info(prb, seq, &info, &line_count)) {
                if (r.info->seq >= dumper->next_seq)
                        break;
-               l += get_record_print_text_size(&info, line_count, true, time);
+               l += get_record_print_text_size(&info, line_count, syslog, time);
                seq = r.info->seq + 1;
        }
 
@@ -3437,7 +3455,7 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
                                                &info, &line_count)) {
                if (r.info->seq >= dumper->next_seq)
                        break;
-               l -= get_record_print_text_size(&info, line_count, true, time);
+               l -= get_record_print_text_size(&info, line_count, syslog, time);
                seq = r.info->seq + 1;
        }
 
index 6704f06..8a7b736 100644 (file)
@@ -1718,7 +1718,7 @@ static bool copy_data(struct prb_data_ring *data_ring,
 
        /* Caller interested in the line count? */
        if (line_count)
-               *line_count = count_lines(data, data_size);
+               *line_count = count_lines(data, len);
 
        /* Caller interested in the data content? */
        if (!buf || !buf_size)
index 35bdcfd..3660755 100644 (file)
@@ -241,7 +241,7 @@ static int __noreturn rcu_tasks_kthread(void *arg)
        }
 }
 
-/* Spawn RCU-tasks grace-period kthread, e.g., at core_initcall() time. */
+/* Spawn RCU-tasks grace-period kthread. */
 static void __init rcu_spawn_tasks_kthread_generic(struct rcu_tasks *rtp)
 {
        struct task_struct *t;
@@ -564,7 +564,6 @@ static int __init rcu_spawn_tasks_kthread(void)
        rcu_spawn_tasks_kthread_generic(&rcu_tasks);
        return 0;
 }
-core_initcall(rcu_spawn_tasks_kthread);
 
 #if !defined(CONFIG_TINY_RCU)
 void show_rcu_tasks_classic_gp_kthread(void)
@@ -692,7 +691,6 @@ static int __init rcu_spawn_tasks_rude_kthread(void)
        rcu_spawn_tasks_kthread_generic(&rcu_tasks_rude);
        return 0;
 }
-core_initcall(rcu_spawn_tasks_rude_kthread);
 
 #if !defined(CONFIG_TINY_RCU)
 void show_rcu_tasks_rude_gp_kthread(void)
@@ -968,6 +966,11 @@ static void rcu_tasks_trace_pregp_step(void)
 static void rcu_tasks_trace_pertask(struct task_struct *t,
                                    struct list_head *hop)
 {
+       // During early boot when there is only the one boot CPU, there
+       // is no idle task for the other CPUs. Just return.
+       if (unlikely(t == NULL))
+               return;
+
        WRITE_ONCE(t->trc_reader_special.b.need_qs, false);
        WRITE_ONCE(t->trc_reader_checked, false);
        t->trc_ipi_to_cpu = -1;
@@ -1193,7 +1196,6 @@ static int __init rcu_spawn_tasks_trace_kthread(void)
        rcu_spawn_tasks_kthread_generic(&rcu_tasks_trace);
        return 0;
 }
-core_initcall(rcu_spawn_tasks_trace_kthread);
 
 #if !defined(CONFIG_TINY_RCU)
 void show_rcu_tasks_trace_gp_kthread(void)
@@ -1222,6 +1224,21 @@ void show_rcu_tasks_gp_kthreads(void)
 }
 #endif /* #ifndef CONFIG_TINY_RCU */
 
+void __init rcu_init_tasks_generic(void)
+{
+#ifdef CONFIG_TASKS_RCU
+       rcu_spawn_tasks_kthread();
+#endif
+
+#ifdef CONFIG_TASKS_RUDE_RCU
+       rcu_spawn_tasks_rude_kthread();
+#endif
+
+#ifdef CONFIG_TASKS_TRACE_RCU
+       rcu_spawn_tasks_trace_kthread();
+#endif
+}
+
 #else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
 static inline void rcu_tasks_bootup_oddness(void) {}
 void show_rcu_tasks_gp_kthreads(void) {}
index 15d2562..ff74fca 100644 (file)
@@ -1796,13 +1796,28 @@ static inline bool rq_has_pinned_tasks(struct rq *rq)
  */
 static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
 {
+       /* When not in the task's cpumask, no point in looking further. */
        if (!cpumask_test_cpu(cpu, p->cpus_ptr))
                return false;
 
-       if (is_per_cpu_kthread(p) || is_migration_disabled(p))
+       /* migrate_disabled() must be allowed to finish. */
+       if (is_migration_disabled(p))
                return cpu_online(cpu);
 
-       return cpu_active(cpu);
+       /* Non kernel threads are not allowed during either online or offline. */
+       if (!(p->flags & PF_KTHREAD))
+               return cpu_active(cpu);
+
+       /* KTHREAD_IS_PER_CPU is always allowed. */
+       if (kthread_is_per_cpu(p))
+               return cpu_online(cpu);
+
+       /* Regular kernel threads don't get to stay during offline. */
+       if (cpu_rq(cpu)->balance_push)
+               return false;
+
+       /* But are allowed during online. */
+       return cpu_online(cpu);
 }
 
 /*
@@ -2327,7 +2342,9 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 
        if (p->flags & PF_KTHREAD || is_migration_disabled(p)) {
                /*
-                * Kernel threads are allowed on online && !active CPUs.
+                * Kernel threads are allowed on online && !active CPUs,
+                * however, during cpu-hot-unplug, even these might get pushed
+                * away if not KTHREAD_IS_PER_CPU.
                 *
                 * Specifically, migration_disabled() tasks must not fail the
                 * cpumask_any_and_distribute() pick below, esp. so on
@@ -2371,16 +2388,6 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 
        __do_set_cpus_allowed(p, new_mask, flags);
 
-       if (p->flags & PF_KTHREAD) {
-               /*
-                * For kernel threads that do indeed end up on online &&
-                * !active we want to ensure they are strict per-CPU threads.
-                */
-               WARN_ON(cpumask_intersects(new_mask, cpu_online_mask) &&
-                       !cpumask_intersects(new_mask, cpu_active_mask) &&
-                       p->nr_cpus_allowed != 1);
-       }
-
        return affine_move_task(rq, p, &rf, dest_cpu, flags);
 
 out:
@@ -3121,6 +3128,13 @@ bool cpus_share_cache(int this_cpu, int that_cpu)
 
 static inline bool ttwu_queue_cond(int cpu, int wake_flags)
 {
+       /*
+        * Do not complicate things with the async wake_list while the CPU is
+        * in hotplug state.
+        */
+       if (!cpu_active(cpu))
+               return false;
+
        /*
         * If the CPU does not share cache, then queue the task on the
         * remote rqs wakelist to avoid accessing remote data.
@@ -7276,8 +7290,14 @@ static void balance_push(struct rq *rq)
        /*
         * Both the cpu-hotplug and stop task are in this case and are
         * required to complete the hotplug process.
+        *
+        * XXX: the idle task does not match kthread_is_per_cpu() due to
+        * histerical raisins.
         */
-       if (is_per_cpu_kthread(push_task) || is_migration_disabled(push_task)) {
+       if (rq->idle == push_task ||
+           ((push_task->flags & PF_KTHREAD) && kthread_is_per_cpu(push_task)) ||
+           is_migration_disabled(push_task)) {
+
                /*
                 * If this is the idle task on the outgoing CPU try to wake
                 * up the hotplug control thread which might wait for the
@@ -7309,7 +7329,7 @@ static void balance_push(struct rq *rq)
        /*
         * At this point need_resched() is true and we'll take the loop in
         * schedule(). The next pick is obviously going to be the stop task
-        * which is_per_cpu_kthread() and will push this task away.
+        * which kthread_is_per_cpu() and will push this task away.
         */
        raw_spin_lock(&rq->lock);
 }
@@ -7320,10 +7340,13 @@ static void balance_push_set(int cpu, bool on)
        struct rq_flags rf;
 
        rq_lock_irqsave(rq, &rf);
-       if (on)
+       rq->balance_push = on;
+       if (on) {
+               WARN_ON_ONCE(rq->balance_callback);
                rq->balance_callback = &balance_push_callback;
-       else
+       } else if (rq->balance_callback == &balance_push_callback) {
                rq->balance_callback = NULL;
+       }
        rq_unlock_irqrestore(rq, &rf);
 }
 
@@ -7441,6 +7464,10 @@ int sched_cpu_activate(unsigned int cpu)
        struct rq *rq = cpu_rq(cpu);
        struct rq_flags rf;
 
+       /*
+        * Make sure that when the hotplug state machine does a roll-back
+        * we clear balance_push. Ideally that would happen earlier...
+        */
        balance_push_set(cpu, false);
 
 #ifdef CONFIG_SCHED_SMT
@@ -7483,17 +7510,27 @@ int sched_cpu_deactivate(unsigned int cpu)
        int ret;
 
        set_cpu_active(cpu, false);
+
+       /*
+        * From this point forward, this CPU will refuse to run any task that
+        * is not: migrate_disable() or KTHREAD_IS_PER_CPU, and will actively
+        * push those tasks away until this gets cleared, see
+        * sched_cpu_dying().
+        */
+       balance_push_set(cpu, true);
+
        /*
-        * We've cleared cpu_active_mask, wait for all preempt-disabled and RCU
-        * users of this state to go away such that all new such users will
-        * observe it.
+        * We've cleared cpu_active_mask / set balance_push, wait for all
+        * preempt-disabled and RCU users of this state to go away such that
+        * all new such users will observe it.
+        *
+        * Specifically, we rely on ttwu to no longer target this CPU, see
+        * ttwu_queue_cond() and is_cpu_allowed().
         *
         * Do sync before park smpboot threads to take care the rcu boost case.
         */
        synchronize_rcu();
 
-       balance_push_set(cpu, true);
-
        rq_lock_irqsave(rq, &rf);
        if (rq->rd) {
                update_rq_clock(rq);
@@ -7574,6 +7611,25 @@ static void calc_load_migrate(struct rq *rq)
                atomic_long_add(delta, &calc_load_tasks);
 }
 
+static void dump_rq_tasks(struct rq *rq, const char *loglvl)
+{
+       struct task_struct *g, *p;
+       int cpu = cpu_of(rq);
+
+       lockdep_assert_held(&rq->lock);
+
+       printk("%sCPU%d enqueued tasks (%u total):\n", loglvl, cpu, rq->nr_running);
+       for_each_process_thread(g, p) {
+               if (task_cpu(p) != cpu)
+                       continue;
+
+               if (!task_on_rq_queued(p))
+                       continue;
+
+               printk("%s\tpid: %d, name: %s\n", loglvl, p->pid, p->comm);
+       }
+}
+
 int sched_cpu_dying(unsigned int cpu)
 {
        struct rq *rq = cpu_rq(cpu);
@@ -7583,9 +7639,18 @@ int sched_cpu_dying(unsigned int cpu)
        sched_tick_stop(cpu);
 
        rq_lock_irqsave(rq, &rf);
-       BUG_ON(rq->nr_running != 1 || rq_has_pinned_tasks(rq));
+       if (rq->nr_running != 1 || rq_has_pinned_tasks(rq)) {
+               WARN(true, "Dying CPU not properly vacated!");
+               dump_rq_tasks(rq, KERN_WARNING);
+       }
        rq_unlock_irqrestore(rq, &rf);
 
+       /*
+        * Now that the CPU is offline, make sure we're welcome
+        * to new tasks once we come back up.
+        */
+       balance_push_set(cpu, false);
+
        calc_load_migrate(rq);
        update_max_interval();
        nohz_balance_exit_idle(rq);
index 12ada79..bb09988 100644 (file)
@@ -975,6 +975,7 @@ struct rq {
        unsigned long           cpu_capacity_orig;
 
        struct callback_head    *balance_callback;
+       unsigned char           balance_push;
 
        unsigned char           nohz_idle_balance;
        unsigned char           idle_balance;
index 5736c55..5ad8566 100644 (file)
@@ -2550,6 +2550,9 @@ bool get_signal(struct ksignal *ksig)
        struct signal_struct *signal = current->signal;
        int signr;
 
+       if (unlikely(current->task_works))
+               task_work_run();
+
        /*
         * For non-generic architectures, check for TIF_NOTIFY_SIGNAL so
         * that the arch handlers don't all have to do it. If we get here
@@ -3701,7 +3704,8 @@ static bool access_pidfd_pidns(struct pid *pid)
        return true;
 }
 
-static int copy_siginfo_from_user_any(kernel_siginfo_t *kinfo, siginfo_t *info)
+static int copy_siginfo_from_user_any(kernel_siginfo_t *kinfo,
+               siginfo_t __user *info)
 {
 #ifdef CONFIG_COMPAT
        /*
index 2efe1e2..f25208e 100644 (file)
@@ -188,6 +188,7 @@ __smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
                kfree(td);
                return PTR_ERR(tsk);
        }
+       kthread_set_per_cpu(tsk, cpu);
        /*
         * Park the thread so that it could start right on the CPU
         * when it is available.
index 7404d38..87389b9 100644 (file)
@@ -498,7 +498,7 @@ out:
 static void sync_hw_clock(struct work_struct *work);
 static DECLARE_WORK(sync_work, sync_hw_clock);
 static struct hrtimer sync_hrtimer;
-#define SYNC_PERIOD_NS (11UL * 60 * NSEC_PER_SEC)
+#define SYNC_PERIOD_NS (11ULL * 60 * NSEC_PER_SEC)
 
 static enum hrtimer_restart sync_timer_callback(struct hrtimer *timer)
 {
@@ -512,7 +512,7 @@ static void sched_sync_hw_clock(unsigned long offset_nsec, bool retry)
        ktime_t exp = ktime_set(ktime_get_real_seconds(), 0);
 
        if (retry)
-               exp = ktime_add_ns(exp, 2 * NSEC_PER_SEC - offset_nsec);
+               exp = ktime_add_ns(exp, 2ULL * NSEC_PER_SEC - offset_nsec);
        else
                exp = ktime_add_ns(exp, SYNC_PERIOD_NS - offset_nsec);
 
index a45cedd..6aee576 100644 (file)
@@ -991,8 +991,7 @@ EXPORT_SYMBOL_GPL(ktime_get_seconds);
 /**
  * ktime_get_real_seconds - Get the seconds portion of CLOCK_REALTIME
  *
- * Returns the wall clock seconds since 1970. This replaces the
- * get_seconds() interface which is not y2038 safe on 32bit systems.
+ * Returns the wall clock seconds since 1970.
  *
  * For 64bit systems the fast access to tk->xtime_sec is preserved. On
  * 32bit systems the access must be protected with the sequence
index d5a1941..c1a62ae 100644 (file)
@@ -538,7 +538,7 @@ config KPROBE_EVENTS
 config KPROBE_EVENTS_ON_NOTRACE
        bool "Do NOT protect notrace function from kprobe events"
        depends on KPROBE_EVENTS
-       depends on KPROBES_ON_FTRACE
+       depends on DYNAMIC_FTRACE
        default n
        help
          This is only for the developers who want to debug ftrace itself
index 9c31f42..e6fba17 100644 (file)
@@ -434,7 +434,7 @@ static int disable_trace_kprobe(struct trace_event_call *call,
        return 0;
 }
 
-#if defined(CONFIG_KPROBES_ON_FTRACE) && \
+#if defined(CONFIG_DYNAMIC_FTRACE) && \
        !defined(CONFIG_KPROBE_EVENTS_ON_NOTRACE)
 static bool __within_notrace_func(unsigned long addr)
 {
index b5295a0..894bb88 100644 (file)
@@ -1848,12 +1848,6 @@ static void worker_attach_to_pool(struct worker *worker,
 {
        mutex_lock(&wq_pool_attach_mutex);
 
-       /*
-        * set_cpus_allowed_ptr() will fail if the cpumask doesn't have any
-        * online CPUs.  It'll be re-applied when any of the CPUs come up.
-        */
-       set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask);
-
        /*
         * The wq_pool_attach_mutex ensures %POOL_DISASSOCIATED remains
         * stable across this function.  See the comments above the flag
@@ -1861,6 +1855,11 @@ static void worker_attach_to_pool(struct worker *worker,
         */
        if (pool->flags & POOL_DISASSOCIATED)
                worker->flags |= WORKER_UNBOUND;
+       else
+               kthread_set_per_cpu(worker->task, pool->cpu);
+
+       if (worker->rescue_wq)
+               set_cpus_allowed_ptr(worker->task, pool->attrs->cpumask);
 
        list_add_tail(&worker->node, &pool->workers);
        worker->pool = pool;
@@ -1883,6 +1882,7 @@ static void worker_detach_from_pool(struct worker *worker)
 
        mutex_lock(&wq_pool_attach_mutex);
 
+       kthread_set_per_cpu(worker->task, -1);
        list_del(&worker->node);
        worker->pool = NULL;
 
@@ -3731,17 +3731,24 @@ static void pwq_adjust_max_active(struct pool_workqueue *pwq)
         * is updated and visible.
         */
        if (!freezable || !workqueue_freezing) {
+               bool kick = false;
+
                pwq->max_active = wq->saved_max_active;
 
                while (!list_empty(&pwq->delayed_works) &&
-                      pwq->nr_active < pwq->max_active)
+                      pwq->nr_active < pwq->max_active) {
                        pwq_activate_first_delayed(pwq);
+                       kick = true;
+               }
 
                /*
                 * Need to kick a worker after thawed or an unbound wq's
-                * max_active is bumped.  It's a slow path.  Do it always.
+                * max_active is bumped. In realtime scenarios, always kicking a
+                * worker will cause interference on the isolated cpu cores, so
+                * let's kick iff work items were activated.
                 */
-               wake_up_worker(pwq->pool);
+               if (kick)
+                       wake_up_worker(pwq->pool);
        } else {
                pwq->max_active = 0;
        }
@@ -4912,8 +4919,10 @@ static void unbind_workers(int cpu)
 
                raw_spin_unlock_irq(&pool->lock);
 
-               for_each_pool_worker(worker, pool)
-                       WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, cpu_active_mask) < 0);
+               for_each_pool_worker(worker, pool) {
+                       kthread_set_per_cpu(worker->task, -1);
+                       WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, cpu_possible_mask) < 0);
+               }
 
                mutex_unlock(&wq_pool_attach_mutex);
 
@@ -4965,9 +4974,11 @@ static void rebind_workers(struct worker_pool *pool)
         * of all workers first and then clear UNBOUND.  As we're called
         * from CPU_ONLINE, the following shouldn't fail.
         */
-       for_each_pool_worker(worker, pool)
+       for_each_pool_worker(worker, pool) {
+               kthread_set_per_cpu(worker->task, pool->cpu);
                WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task,
                                                  pool->attrs->cpumask) < 0);
+       }
 
        raw_spin_lock_irq(&pool->lock);
 
index e6e58b2..7937265 100644 (file)
@@ -295,14 +295,6 @@ config GDB_SCRIPTS
 
 endif # DEBUG_INFO
 
-config ENABLE_MUST_CHECK
-       bool "Enable __must_check logic"
-       default y
-       help
-         Enable the __must_check logic in the kernel build.  Disable this to
-         suppress the "warning: ignoring return value of 'foo', declared with
-         attribute warn_unused_result" messages.
-
 config FRAME_WARN
        int "Warn for stack frames larger than"
        range 0 8192
index 8b635fd..3a0b1c9 100644 (file)
@@ -123,6 +123,7 @@ config UBSAN_SIGNED_OVERFLOW
 config UBSAN_UNSIGNED_OVERFLOW
        bool "Perform checking for unsigned arithmetic overflow"
        depends on $(cc-option,-fsanitize=unsigned-integer-overflow)
+       depends on !X86_32 # avoid excessive stack usage on x86-32/clang
        help
          This option enables -fsanitize=unsigned-integer-overflow which checks
          for overflow of any arithmetic operations with unsigned integers. This
index afeff05..a6b160c 100644 (file)
@@ -36,7 +36,8 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
         flex_proportions.o ratelimit.o show_mem.o \
         is_single_threaded.o plist.o decompress.o kobject_uevent.o \
         earlycpio.o seq_buf.o siphash.o dec_and_lock.o \
-        nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o
+        nmi_backtrace.o nodemask.o win_minmax.o memcat_p.o \
+        buildid.o
 
 lib-$(CONFIG_PRINTK) += dump_stack.o
 lib-$(CONFIG_SMP) += cpumask.o
diff --git a/lib/buildid.c b/lib/buildid.c
new file mode 100644 (file)
index 0000000..6156997
--- /dev/null
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/buildid.h>
+#include <linux/elf.h>
+#include <linux/pagemap.h>
+
+#define BUILD_ID 3
+/*
+ * Parse build id from the note segment. This logic can be shared between
+ * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
+ * identical.
+ */
+static inline int parse_build_id(void *page_addr,
+                                unsigned char *build_id,
+                                __u32 *size,
+                                void *note_start,
+                                Elf32_Word note_size)
+{
+       Elf32_Word note_offs = 0, new_offs;
+
+       /* check for overflow */
+       if (note_start < page_addr || note_start + note_size < note_start)
+               return -EINVAL;
+
+       /* only supports note that fits in the first page */
+       if (note_start + note_size > page_addr + PAGE_SIZE)
+               return -EINVAL;
+
+       while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
+               Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
+
+               if (nhdr->n_type == BUILD_ID &&
+                   nhdr->n_namesz == sizeof("GNU") &&
+                   nhdr->n_descsz > 0 &&
+                   nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {
+                       memcpy(build_id,
+                              note_start + note_offs +
+                              ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
+                              nhdr->n_descsz);
+                       memset(build_id + nhdr->n_descsz, 0,
+                              BUILD_ID_SIZE_MAX - nhdr->n_descsz);
+                       if (size)
+                               *size = nhdr->n_descsz;
+                       return 0;
+               }
+               new_offs = note_offs + sizeof(Elf32_Nhdr) +
+                       ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
+               if (new_offs <= note_offs)  /* overflow */
+                       break;
+               note_offs = new_offs;
+       }
+       return -EINVAL;
+}
+
+/* Parse build ID from 32-bit ELF */
+static int get_build_id_32(void *page_addr, unsigned char *build_id,
+                          __u32 *size)
+{
+       Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
+       Elf32_Phdr *phdr;
+       int i;
+
+       /* only supports phdr that fits in one page */
+       if (ehdr->e_phnum >
+           (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
+               return -EINVAL;
+
+       phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
+
+       for (i = 0; i < ehdr->e_phnum; ++i) {
+               if (phdr[i].p_type == PT_NOTE &&
+                   !parse_build_id(page_addr, build_id, size,
+                                   page_addr + phdr[i].p_offset,
+                                   phdr[i].p_filesz))
+                       return 0;
+       }
+       return -EINVAL;
+}
+
+/* Parse build ID from 64-bit ELF */
+static int get_build_id_64(void *page_addr, unsigned char *build_id,
+                          __u32 *size)
+{
+       Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
+       Elf64_Phdr *phdr;
+       int i;
+
+       /* only supports phdr that fits in one page */
+       if (ehdr->e_phnum >
+           (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
+               return -EINVAL;
+
+       phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
+
+       for (i = 0; i < ehdr->e_phnum; ++i) {
+               if (phdr[i].p_type == PT_NOTE &&
+                   !parse_build_id(page_addr, build_id, size,
+                                   page_addr + phdr[i].p_offset,
+                                   phdr[i].p_filesz))
+                       return 0;
+       }
+       return -EINVAL;
+}
+
+/*
+ * Parse build ID of ELF file mapped to vma
+ * @vma:      vma object
+ * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
+ * @size:     returns actual build id size in case of success
+ *
+ * Returns 0 on success, otherwise error (< 0).
+ */
+int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
+                  __u32 *size)
+{
+       Elf32_Ehdr *ehdr;
+       struct page *page;
+       void *page_addr;
+       int ret;
+
+       /* only works for page backed storage  */
+       if (!vma->vm_file)
+               return -EINVAL;
+
+       page = find_get_page(vma->vm_file->f_mapping, 0);
+       if (!page)
+               return -EFAULT; /* page not mapped */
+
+       ret = -EINVAL;
+       page_addr = kmap_atomic(page);
+       ehdr = (Elf32_Ehdr *)page_addr;
+
+       /* compare magic x7f "ELF" */
+       if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
+               goto out;
+
+       /* only support executable file and shared object file */
+       if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+               goto out;
+
+       if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
+               ret = get_build_id_32(page_addr, build_id, size);
+       else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
+               ret = get_build_id_64(page_addr, build_id, size);
+out:
+       kunmap_atomic(page_addr);
+       put_page(page);
+       return ret;
+}
index 1955d62..5baedc5 100644 (file)
@@ -774,8 +774,8 @@ static const struct font_data fontdata_ter16x32 = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc,
        0x7f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 95 */
-       0x00, 0x00, 0x1c, 0x00, 0x0e, 0x00, 0x07, 0x00,
-       0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0e, 0x00,
+       0x07, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1169,7 +1169,7 @@ static const struct font_data fontdata_ter16x32 = {
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-       0x7f, 0xf8, 0x7f, 0xfc, 0x03, 0x9e, 0x03, 0x8e,
+       0x7e, 0xf8, 0x7f, 0xfc, 0x03, 0x9e, 0x03, 0x8e,
        0x03, 0x8e, 0x3f, 0x8e, 0x7f, 0xfe, 0xf3, 0xfe,
        0xe3, 0x80, 0xe3, 0x80, 0xe3, 0x80, 0xf3, 0xce,
        0x7f, 0xfe, 0x3e, 0xfc, 0x00, 0x00, 0x00, 0x00,
index 7f1244b..dab97bb 100644 (file)
@@ -81,14 +81,14 @@ static int clear_bits_ll(unsigned long *addr, unsigned long mask_to_clear)
  * users set the same bit, one user will return remain bits, otherwise
  * return 0.
  */
-static int bitmap_set_ll(unsigned long *map, int start, int nr)
+static int bitmap_set_ll(unsigned long *map, unsigned long start, unsigned long nr)
 {
        unsigned long *p = map + BIT_WORD(start);
-       const int size = start + nr;
+       const unsigned long size = start + nr;
        int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
        unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
 
-       while (nr - bits_to_set >= 0) {
+       while (nr >= bits_to_set) {
                if (set_bits_ll(p, mask_to_set))
                        return nr;
                nr -= bits_to_set;
@@ -116,14 +116,15 @@ static int bitmap_set_ll(unsigned long *map, int start, int nr)
  * users clear the same bit, one user will return remain bits,
  * otherwise return 0.
  */
-static int bitmap_clear_ll(unsigned long *map, int start, int nr)
+static unsigned long
+bitmap_clear_ll(unsigned long *map, unsigned long start, unsigned long nr)
 {
        unsigned long *p = map + BIT_WORD(start);
-       const int size = start + nr;
+       const unsigned long size = start + nr;
        int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
        unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
 
-       while (nr - bits_to_clear >= 0) {
+       while (nr >= bits_to_clear) {
                if (clear_bits_ll(p, mask_to_clear))
                        return nr;
                nr -= bits_to_clear;
@@ -183,8 +184,8 @@ int gen_pool_add_owner(struct gen_pool *pool, unsigned long virt, phys_addr_t ph
                 size_t size, int nid, void *owner)
 {
        struct gen_pool_chunk *chunk;
-       int nbits = size >> pool->min_alloc_order;
-       int nbytes = sizeof(struct gen_pool_chunk) +
+       unsigned long nbits = size >> pool->min_alloc_order;
+       unsigned long nbytes = sizeof(struct gen_pool_chunk) +
                                BITS_TO_LONGS(nbits) * sizeof(long);
 
        chunk = vzalloc_node(nbytes, nid);
@@ -242,7 +243,7 @@ void gen_pool_destroy(struct gen_pool *pool)
        struct list_head *_chunk, *_next_chunk;
        struct gen_pool_chunk *chunk;
        int order = pool->min_alloc_order;
-       int bit, end_bit;
+       unsigned long bit, end_bit;
 
        list_for_each_safe(_chunk, _next_chunk, &pool->chunks) {
                chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
@@ -278,7 +279,7 @@ unsigned long gen_pool_alloc_algo_owner(struct gen_pool *pool, size_t size,
        struct gen_pool_chunk *chunk;
        unsigned long addr = 0;
        int order = pool->min_alloc_order;
-       int nbits, start_bit, end_bit, remain;
+       unsigned long nbits, start_bit, end_bit, remain;
 
 #ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
        BUG_ON(in_nmi());
@@ -487,7 +488,7 @@ void gen_pool_free_owner(struct gen_pool *pool, unsigned long addr, size_t size,
 {
        struct gen_pool_chunk *chunk;
        int order = pool->min_alloc_order;
-       int start_bit, nbits, remain;
+       unsigned long start_bit, nbits, remain;
 
 #ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
        BUG_ON(in_nmi());
@@ -755,7 +756,7 @@ unsigned long gen_pool_best_fit(unsigned long *map, unsigned long size,
        index = bitmap_find_next_zero_area(map, size, start, nr, 0);
 
        while (index < size) {
-               int next_bit = find_next_bit(map, size, index + nr);
+               unsigned long next_bit = find_next_bit(map, size, index + nr);
                if ((next_bit - index) < len) {
                        len = next_bit - index;
                        start_bit = index;
index 1635111..a21e6a5 100644 (file)
@@ -1658,7 +1658,7 @@ static int copy_compat_iovec_from_user(struct iovec *iov,
                (const struct compat_iovec __user *)uvec;
        int ret = -EFAULT, i;
 
-       if (!user_access_begin(uvec, nr_segs * sizeof(*uvec)))
+       if (!user_access_begin(uiov, nr_segs * sizeof(*uiov)))
                return -EFAULT;
 
        for (i = 0; i < nr_segs; i++) {
index b4c0df6..c770570 100644 (file)
@@ -48,7 +48,7 @@ endif
 endif
 
 quiet_cmd_unroll = UNROLL  $@
-      cmd_unroll = $(AWK) -f$(srctree)/$(src)/unroll.awk -vN=$* < $< > $@
+      cmd_unroll = $(AWK) -v N=$* -f $(srctree)/$(src)/unroll.awk < $< > $@
 
 targets += int1.c int2.c int4.c int8.c int16.c int32.c
 $(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE
index ca7d635..49ec9e8 100644 (file)
@@ -4295,13 +4295,13 @@ static struct bpf_test tests[] = {
                { { 0, 0xffffffff } },
                .stack_depth = 40,
        },
-       /* BPF_STX | BPF_XADD | BPF_W/DW */
+       /* BPF_STX | BPF_ATOMIC | BPF_W/DW */
        {
                "STX_XADD_W: Test: 0x12 + 0x10 = 0x22",
                .u.insns_int = {
                        BPF_ALU32_IMM(BPF_MOV, R0, 0x12),
                        BPF_ST_MEM(BPF_W, R10, -40, 0x10),
-                       BPF_STX_XADD(BPF_W, R10, R0, -40),
+                       BPF_ATOMIC_OP(BPF_W, BPF_ADD, R10, R0, -40),
                        BPF_LDX_MEM(BPF_W, R0, R10, -40),
                        BPF_EXIT_INSN(),
                },
@@ -4316,7 +4316,7 @@ static struct bpf_test tests[] = {
                        BPF_ALU64_REG(BPF_MOV, R1, R10),
                        BPF_ALU32_IMM(BPF_MOV, R0, 0x12),
                        BPF_ST_MEM(BPF_W, R10, -40, 0x10),
-                       BPF_STX_XADD(BPF_W, R10, R0, -40),
+                       BPF_ATOMIC_OP(BPF_W, BPF_ADD, R10, R0, -40),
                        BPF_ALU64_REG(BPF_MOV, R0, R10),
                        BPF_ALU64_REG(BPF_SUB, R0, R1),
                        BPF_EXIT_INSN(),
@@ -4331,7 +4331,7 @@ static struct bpf_test tests[] = {
                .u.insns_int = {
                        BPF_ALU32_IMM(BPF_MOV, R0, 0x12),
                        BPF_ST_MEM(BPF_W, R10, -40, 0x10),
-                       BPF_STX_XADD(BPF_W, R10, R0, -40),
+                       BPF_ATOMIC_OP(BPF_W, BPF_ADD, R10, R0, -40),
                        BPF_EXIT_INSN(),
                },
                INTERNAL,
@@ -4352,7 +4352,7 @@ static struct bpf_test tests[] = {
                .u.insns_int = {
                        BPF_ALU32_IMM(BPF_MOV, R0, 0x12),
                        BPF_ST_MEM(BPF_DW, R10, -40, 0x10),
-                       BPF_STX_XADD(BPF_DW, R10, R0, -40),
+                       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, R10, R0, -40),
                        BPF_LDX_MEM(BPF_DW, R0, R10, -40),
                        BPF_EXIT_INSN(),
                },
@@ -4367,7 +4367,7 @@ static struct bpf_test tests[] = {
                        BPF_ALU64_REG(BPF_MOV, R1, R10),
                        BPF_ALU32_IMM(BPF_MOV, R0, 0x12),
                        BPF_ST_MEM(BPF_DW, R10, -40, 0x10),
-                       BPF_STX_XADD(BPF_DW, R10, R0, -40),
+                       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, R10, R0, -40),
                        BPF_ALU64_REG(BPF_MOV, R0, R10),
                        BPF_ALU64_REG(BPF_SUB, R0, R1),
                        BPF_EXIT_INSN(),
@@ -4382,7 +4382,7 @@ static struct bpf_test tests[] = {
                .u.insns_int = {
                        BPF_ALU32_IMM(BPF_MOV, R0, 0x12),
                        BPF_ST_MEM(BPF_DW, R10, -40, 0x10),
-                       BPF_STX_XADD(BPF_DW, R10, R0, -40),
+                       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, R10, R0, -40),
                        BPF_EXIT_INSN(),
                },
                INTERNAL,
index 8e4d5af..66e1c96 100644 (file)
@@ -8,4 +8,4 @@
 
 obj-$(CONFIG_ZLIB_DFLTCC) += zlib_dfltcc.o
 
-zlib_dfltcc-objs := dfltcc.o dfltcc_deflate.o dfltcc_inflate.o dfltcc_syms.o
+zlib_dfltcc-objs := dfltcc.o dfltcc_deflate.o dfltcc_inflate.o
index c30de43..782f76e 100644 (file)
@@ -1,7 +1,8 @@
 // SPDX-License-Identifier: Zlib
 /* dfltcc.c - SystemZ DEFLATE CONVERSION CALL support. */
 
-#include <linux/zutil.h>
+#include <linux/export.h>
+#include <linux/module.h>
 #include "dfltcc_util.h"
 #include "dfltcc.h"
 
@@ -53,3 +54,6 @@ void dfltcc_reset(
     dfltcc_state->dht_threshold = DFLTCC_DHT_MIN_SAMPLE_SIZE;
     dfltcc_state->param.ribm = DFLTCC_RIBM;
 }
+EXPORT_SYMBOL(dfltcc_reset);
+
+MODULE_LICENSE("GPL");
index 00c1851..6c946e8 100644 (file)
@@ -4,6 +4,7 @@
 #include "dfltcc_util.h"
 #include "dfltcc.h"
 #include <asm/setup.h>
+#include <linux/export.h>
 #include <linux/zutil.h>
 
 /*
@@ -34,6 +35,7 @@ int dfltcc_can_deflate(
 
     return 1;
 }
+EXPORT_SYMBOL(dfltcc_can_deflate);
 
 static void dfltcc_gdht(
     z_streamp strm
@@ -277,3 +279,4 @@ again:
         goto again; /* deflate() must use all input or all output */
     return 1;
 }
+EXPORT_SYMBOL(dfltcc_deflate);
index db10701..fb60b5a 100644 (file)
@@ -125,7 +125,7 @@ dfltcc_inflate_action dfltcc_inflate(
     param->ho = (state->write - state->whave) & ((1 << HB_BITS) - 1);
     if (param->hl)
         param->nt = 0; /* Honor history for the first block */
-    param->cv = state->flags ? REVERSE(state->check) : state->check;
+    param->cv = state->check;
 
     /* Inflate */
     do {
@@ -138,7 +138,7 @@ dfltcc_inflate_action dfltcc_inflate(
     state->bits = param->sbb;
     state->whave = param->hl;
     state->write = (param->ho + param->hl) & ((1 << HB_BITS) - 1);
-    state->check = state->flags ? REVERSE(param->cv) : param->cv;
+    state->check = param->cv;
     if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) {
         /* Report an error if stream is corrupted */
         state->mode = BAD;
diff --git a/lib/zlib_dfltcc/dfltcc_syms.c b/lib/zlib_dfltcc/dfltcc_syms.c
deleted file mode 100644 (file)
index 6f23481..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * linux/lib/zlib_dfltcc/dfltcc_syms.c
- *
- * Exported symbols for the s390 zlib dfltcc support.
- *
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/zlib.h>
-#include "dfltcc.h"
-
-EXPORT_SYMBOL(dfltcc_can_deflate);
-EXPORT_SYMBOL(dfltcc_deflate);
-EXPORT_SYMBOL(dfltcc_reset);
-MODULE_LICENSE("GPL");
index c3a9ea7..874b732 100644 (file)
@@ -473,6 +473,11 @@ static inline void *arch_kmap_local_high_get(struct page *page)
 }
 #endif
 
+#ifndef arch_kmap_local_set_pte
+#define arch_kmap_local_set_pte(mm, vaddr, ptep, ptev) \
+       set_pte_at(mm, vaddr, ptep, ptev)
+#endif
+
 /* Unmap a local mapping which was obtained by kmap_high_get() */
 static inline bool kmap_high_unmap_local(unsigned long vaddr)
 {
@@ -515,7 +520,7 @@ void *__kmap_local_pfn_prot(unsigned long pfn, pgprot_t prot)
        vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
        BUG_ON(!pte_none(*(kmap_pte - idx)));
        pteval = pfn_pte(pfn, prot);
-       set_pte_at(&init_mm, vaddr, kmap_pte - idx, pteval);
+       arch_kmap_local_set_pte(&init_mm, vaddr, kmap_pte - idx, pteval);
        arch_kmap_local_post_map(vaddr, pteval);
        current->kmap_ctrl.pteval[kmap_local_idx()] = pteval;
        preempt_enable();
index cbf32d2..18f6ee3 100644 (file)
@@ -4105,10 +4105,30 @@ retry_avoidcopy:
                 * may get SIGKILLed if it later faults.
                 */
                if (outside_reserve) {
+                       struct address_space *mapping = vma->vm_file->f_mapping;
+                       pgoff_t idx;
+                       u32 hash;
+
                        put_page(old_page);
                        BUG_ON(huge_pte_none(pte));
+                       /*
+                        * Drop hugetlb_fault_mutex and i_mmap_rwsem before
+                        * unmapping.  unmapping needs to hold i_mmap_rwsem
+                        * in write mode.  Dropping i_mmap_rwsem in read mode
+                        * here is OK as COW mappings do not interact with
+                        * PMD sharing.
+                        *
+                        * Reacquire both after unmap operation.
+                        */
+                       idx = vma_hugecache_offset(h, vma, haddr);
+                       hash = hugetlb_fault_mutex_hash(mapping, idx);
+                       mutex_unlock(&hugetlb_fault_mutex_table[hash]);
+                       i_mmap_unlock_read(mapping);
+
                        unmap_ref_private(mm, vma, old_page, haddr);
-                       BUG_ON(huge_pte_none(pte));
+
+                       i_mmap_lock_read(mapping);
+                       mutex_lock(&hugetlb_fault_mutex_table[hash]);
                        spin_lock(ptl);
                        ptep = huge_pte_offset(mm, haddr, huge_page_size(h));
                        if (likely(ptep &&
@@ -4351,7 +4371,7 @@ retry:
                 * So we need to block hugepage fault by PG_hwpoison bit check.
                 */
                if (unlikely(PageHWPoison(page))) {
-                       ret = VM_FAULT_HWPOISON |
+                       ret = VM_FAULT_HWPOISON_LARGE |
                                VM_FAULT_SET_HINDEX(hstate_index(h));
                        goto backout_unlocked;
                }
index 1dd5a0f..5106b84 100644 (file)
@@ -337,6 +337,8 @@ void kasan_record_aux_stack(void *addr)
        cache = page->slab_cache;
        object = nearest_obj(cache, page, addr);
        alloc_meta = kasan_get_alloc_meta(cache, object);
+       if (!alloc_meta)
+               return;
 
        alloc_meta->aux_stack[1] = alloc_meta->aux_stack[0];
        alloc_meta->aux_stack[0] = kasan_save_stack(GFP_NOWAIT);
index 55bd6f0..e529428 100644 (file)
 
 #include "kasan.h"
 
-enum kasan_arg_mode {
-       KASAN_ARG_MODE_DEFAULT,
-       KASAN_ARG_MODE_OFF,
-       KASAN_ARG_MODE_PROD,
-       KASAN_ARG_MODE_FULL,
+enum kasan_arg {
+       KASAN_ARG_DEFAULT,
+       KASAN_ARG_OFF,
+       KASAN_ARG_ON,
 };
 
 enum kasan_arg_stacktrace {
@@ -38,7 +37,7 @@ enum kasan_arg_fault {
        KASAN_ARG_FAULT_PANIC,
 };
 
-static enum kasan_arg_mode kasan_arg_mode __ro_after_init;
+static enum kasan_arg kasan_arg __ro_after_init;
 static enum kasan_arg_stacktrace kasan_arg_stacktrace __ro_after_init;
 static enum kasan_arg_fault kasan_arg_fault __ro_after_init;
 
@@ -52,26 +51,24 @@ DEFINE_STATIC_KEY_FALSE(kasan_flag_stacktrace);
 /* Whether panic or disable tag checking on fault. */
 bool kasan_flag_panic __ro_after_init;
 
-/* kasan.mode=off/prod/full */
-static int __init early_kasan_mode(char *arg)
+/* kasan=off/on */
+static int __init early_kasan_flag(char *arg)
 {
        if (!arg)
                return -EINVAL;
 
        if (!strcmp(arg, "off"))
-               kasan_arg_mode = KASAN_ARG_MODE_OFF;
-       else if (!strcmp(arg, "prod"))
-               kasan_arg_mode = KASAN_ARG_MODE_PROD;
-       else if (!strcmp(arg, "full"))
-               kasan_arg_mode = KASAN_ARG_MODE_FULL;
+               kasan_arg = KASAN_ARG_OFF;
+       else if (!strcmp(arg, "on"))
+               kasan_arg = KASAN_ARG_ON;
        else
                return -EINVAL;
 
        return 0;
 }
-early_param("kasan.mode", early_kasan_mode);
+early_param("kasan", early_kasan_flag);
 
-/* kasan.stack=off/on */
+/* kasan.stacktrace=off/on */
 static int __init early_kasan_flag_stacktrace(char *arg)
 {
        if (!arg)
@@ -113,8 +110,8 @@ void kasan_init_hw_tags_cpu(void)
         * as this function is only called for MTE-capable hardware.
         */
 
-       /* If KASAN is disabled, do nothing. */
-       if (kasan_arg_mode == KASAN_ARG_MODE_OFF)
+       /* If KASAN is disabled via command line, don't initialize it. */
+       if (kasan_arg == KASAN_ARG_OFF)
                return;
 
        hw_init_tags(KASAN_TAG_MAX);
@@ -124,43 +121,28 @@ void kasan_init_hw_tags_cpu(void)
 /* kasan_init_hw_tags() is called once on boot CPU. */
 void __init kasan_init_hw_tags(void)
 {
-       /* If hardware doesn't support MTE, do nothing. */
+       /* If hardware doesn't support MTE, don't initialize KASAN. */
        if (!system_supports_mte())
                return;
 
-       /* Choose KASAN mode if kasan boot parameter is not provided. */
-       if (kasan_arg_mode == KASAN_ARG_MODE_DEFAULT) {
-               if (IS_ENABLED(CONFIG_DEBUG_KERNEL))
-                       kasan_arg_mode = KASAN_ARG_MODE_FULL;
-               else
-                       kasan_arg_mode = KASAN_ARG_MODE_PROD;
-       }
-
-       /* Preset parameter values based on the mode. */
-       switch (kasan_arg_mode) {
-       case KASAN_ARG_MODE_DEFAULT:
-               /* Shouldn't happen as per the check above. */
-               WARN_ON(1);
-               return;
-       case KASAN_ARG_MODE_OFF:
-               /* If KASAN is disabled, do nothing. */
+       /* If KASAN is disabled via command line, don't initialize it. */
+       if (kasan_arg == KASAN_ARG_OFF)
                return;
-       case KASAN_ARG_MODE_PROD:
-               static_branch_enable(&kasan_flag_enabled);
-               break;
-       case KASAN_ARG_MODE_FULL:
-               static_branch_enable(&kasan_flag_enabled);
-               static_branch_enable(&kasan_flag_stacktrace);
-               break;
-       }
 
-       /* Now, optionally override the presets. */
+       /* Enable KASAN. */
+       static_branch_enable(&kasan_flag_enabled);
 
        switch (kasan_arg_stacktrace) {
        case KASAN_ARG_STACKTRACE_DEFAULT:
+               /*
+                * Default to enabling stack trace collection for
+                * debug kernels.
+                */
+               if (IS_ENABLED(CONFIG_DEBUG_KERNEL))
+                       static_branch_enable(&kasan_flag_stacktrace);
                break;
        case KASAN_ARG_STACKTRACE_OFF:
-               static_branch_disable(&kasan_flag_stacktrace);
+               /* Do nothing, kasan_flag_stacktrace keeps its default value. */
                break;
        case KASAN_ARG_STACKTRACE_ON:
                static_branch_enable(&kasan_flag_stacktrace);
@@ -169,11 +151,16 @@ void __init kasan_init_hw_tags(void)
 
        switch (kasan_arg_fault) {
        case KASAN_ARG_FAULT_DEFAULT:
+               /*
+                * Default to no panic on report.
+                * Do nothing, kasan_flag_panic keeps its default value.
+                */
                break;
        case KASAN_ARG_FAULT_REPORT:
-               kasan_flag_panic = false;
+               /* Do nothing, kasan_flag_panic keeps its default value. */
                break;
        case KASAN_ARG_FAULT_PANIC:
+               /* Enable panic on report. */
                kasan_flag_panic = true;
                break;
        }
index bc0ad20..c4605ac 100644 (file)
@@ -64,7 +64,8 @@ static inline bool kasan_pmd_table(pud_t pud)
        return false;
 }
 #endif
-pte_t kasan_early_shadow_pte[PTRS_PER_PTE] __page_aligned_bss;
+pte_t kasan_early_shadow_pte[PTRS_PER_PTE + PTE_HWTABLE_PTRS]
+       __page_aligned_bss;
 
 static inline bool kasan_pte_table(pmd_t pmd)
 {
@@ -372,9 +373,10 @@ static void kasan_remove_pmd_table(pmd_t *pmd, unsigned long addr,
 
                if (kasan_pte_table(*pmd)) {
                        if (IS_ALIGNED(addr, PMD_SIZE) &&
-                           IS_ALIGNED(next, PMD_SIZE))
+                           IS_ALIGNED(next, PMD_SIZE)) {
                                pmd_clear(pmd);
-                       continue;
+                               continue;
+                       }
                }
                pte = pte_offset_kernel(pmd, addr);
                kasan_remove_pte_table(pte, addr, next);
@@ -397,9 +399,10 @@ static void kasan_remove_pud_table(pud_t *pud, unsigned long addr,
 
                if (kasan_pmd_table(*pud)) {
                        if (IS_ALIGNED(addr, PUD_SIZE) &&
-                           IS_ALIGNED(next, PUD_SIZE))
+                           IS_ALIGNED(next, PUD_SIZE)) {
                                pud_clear(pud);
-                       continue;
+                               continue;
+                       }
                }
                pmd = pmd_offset(pud, addr);
                pmd_base = pmd_offset(pud, 0);
@@ -423,9 +426,10 @@ static void kasan_remove_p4d_table(p4d_t *p4d, unsigned long addr,
 
                if (kasan_pud_table(*p4d)) {
                        if (IS_ALIGNED(addr, P4D_SIZE) &&
-                           IS_ALIGNED(next, P4D_SIZE))
+                           IS_ALIGNED(next, P4D_SIZE)) {
                                p4d_clear(p4d);
-                       continue;
+                               continue;
+                       }
                }
                pud = pud_offset(p4d, addr);
                kasan_remove_pud_table(pud, addr, next);
@@ -456,9 +460,10 @@ void kasan_remove_zero_shadow(void *start, unsigned long size)
 
                if (kasan_p4d_table(*pgd)) {
                        if (IS_ALIGNED(addr, PGDIR_SIZE) &&
-                           IS_ALIGNED(next, PGDIR_SIZE))
+                           IS_ALIGNED(next, PGDIR_SIZE)) {
                                pgd_clear(pgd);
-                       continue;
+                               continue;
+                       }
                }
 
                p4d = p4d_offset(pgd, addr);
@@ -481,7 +486,6 @@ int kasan_add_zero_shadow(void *start, unsigned long size)
 
        ret = kasan_populate_early_shadow(shadow_start, shadow_end);
        if (ret)
-               kasan_remove_zero_shadow(shadow_start,
-                                       size >> KASAN_SHADOW_SCALE_SHIFT);
+               kasan_remove_zero_shadow(start, size);
        return ret;
 }
index d24bcfa..1eaaec1 100644 (file)
@@ -1427,7 +1427,7 @@ phys_addr_t __init memblock_phys_alloc_range(phys_addr_t size,
 }
 
 /**
- * memblock_phys_alloc_try_nid - allocate a memory block from specified MUMA node
+ * memblock_phys_alloc_try_nid - allocate a memory block from specified NUMA node
  * @size: size of memory block to be allocated in bytes
  * @align: alignment of the region and block's size
  * @nid: nid of the free area to find, %NUMA_NO_NODE for any node
index 605f671..e2de77b 100644 (file)
@@ -3115,9 +3115,7 @@ void __memcg_kmem_uncharge(struct mem_cgroup *memcg, unsigned int nr_pages)
        if (!cgroup_subsys_on_dfl(memory_cgrp_subsys))
                page_counter_uncharge(&memcg->kmem, nr_pages);
 
-       page_counter_uncharge(&memcg->memory, nr_pages);
-       if (do_memsw_account())
-               page_counter_uncharge(&memcg->memsw, nr_pages);
+       refill_stock(memcg, nr_pages);
 }
 
 /**
index 5a38e9e..e948163 100644 (file)
@@ -1885,6 +1885,12 @@ static int soft_offline_free_page(struct page *page)
        return rc;
 }
 
+static void put_ref_page(struct page *page)
+{
+       if (page)
+               put_page(page);
+}
+
 /**
  * soft_offline_page - Soft offline a page.
  * @pfn: pfn to soft-offline
@@ -1910,20 +1916,26 @@ static int soft_offline_free_page(struct page *page)
 int soft_offline_page(unsigned long pfn, int flags)
 {
        int ret;
-       struct page *page;
        bool try_again = true;
+       struct page *page, *ref_page = NULL;
+
+       WARN_ON_ONCE(!pfn_valid(pfn) && (flags & MF_COUNT_INCREASED));
 
        if (!pfn_valid(pfn))
                return -ENXIO;
+       if (flags & MF_COUNT_INCREASED)
+               ref_page = pfn_to_page(pfn);
+
        /* Only online pages can be soft-offlined (esp., not ZONE_DEVICE). */
        page = pfn_to_online_page(pfn);
-       if (!page)
+       if (!page) {
+               put_ref_page(ref_page);
                return -EIO;
+       }
 
        if (PageHWPoison(page)) {
                pr_info("%s: %#lx page already poisoned\n", __func__, pfn);
-               if (flags & MF_COUNT_INCREASED)
-                       put_page(page);
+               put_ref_page(ref_page);
                return 0;
        }
 
@@ -1940,7 +1952,7 @@ retry:
                        goto retry;
                }
        } else if (ret == -EIO) {
-               pr_info("%s: %#lx: unknown page type: %lx (%pGP)\n",
+               pr_info("%s: %#lx: unknown page type: %lx (%pGp)\n",
                         __func__, pfn, page->flags, &page->flags);
        }
 
index 7d60876..feff48e 100644 (file)
@@ -2892,11 +2892,13 @@ static vm_fault_t wp_page_copy(struct vm_fault *vmf)
                entry = mk_pte(new_page, vma->vm_page_prot);
                entry = pte_sw_mkyoung(entry);
                entry = maybe_mkwrite(pte_mkdirty(entry), vma);
+
                /*
                 * Clear the pte entry and flush it first, before updating the
-                * pte with the new entry. This will avoid a race condition
-                * seen in the presence of one thread doing SMC and another
-                * thread doing COW.
+                * pte with the new entry, to keep TLBs on different CPUs in
+                * sync. This code used to set the new PTE then flush TLBs, but
+                * that left a window where the new PTE could be loaded into
+                * some TLBs while the old PTE remains in others.
                 */
                ptep_clear_flush_notify(vma, vmf->address, vmf->pte);
                page_add_new_anon_rmap(new_page, vma, vmf->address, false);
index af41fb9..f9d57b9 100644 (file)
@@ -713,7 +713,7 @@ void __ref move_pfn_range_to_zone(struct zone *zone, unsigned long start_pfn,
         * expects the zone spans the pfn range. All the pages in the range
         * are reserved so nobody should be touching them so we should be safe
         */
-       memmap_init_zone(nr_pages, nid, zone_idx(zone), start_pfn,
+       memmap_init_zone(nr_pages, nid, zone_idx(zone), start_pfn, 0,
                         MEMINIT_HOTPLUG, altmap, migratetype);
 
        set_zone_contiguous(zone);
index 8cf96bd..2c3a865 100644 (file)
@@ -1111,7 +1111,7 @@ int do_migrate_pages(struct mm_struct *mm, const nodemask_t *from,
                     const nodemask_t *to, int flags)
 {
        int busy = 0;
-       int err;
+       int err = 0;
        nodemask_t tmp;
 
        migrate_prep();
index ee5e612..c0efe92 100644 (file)
@@ -402,6 +402,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
        struct zone *oldzone, *newzone;
        int dirty;
        int expected_count = expected_page_refs(mapping, page) + extra_count;
+       int nr = thp_nr_pages(page);
 
        if (!mapping) {
                /* Anonymous page without mapping */
@@ -437,7 +438,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
         */
        newpage->index = page->index;
        newpage->mapping = page->mapping;
-       page_ref_add(newpage, thp_nr_pages(page)); /* add cache reference */
+       page_ref_add(newpage, nr); /* add cache reference */
        if (PageSwapBacked(page)) {
                __SetPageSwapBacked(newpage);
                if (PageSwapCache(page)) {
@@ -459,7 +460,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
        if (PageTransHuge(page)) {
                int i;
 
-               for (i = 1; i < HPAGE_PMD_NR; i++) {
+               for (i = 1; i < nr; i++) {
                        xas_next(&xas);
                        xas_store(&xas, newpage);
                }
@@ -470,7 +471,7 @@ int migrate_page_move_mapping(struct address_space *mapping,
         * to one less reference.
         * We know this isn't the last reference.
         */
-       page_ref_unfreeze(page, expected_count - thp_nr_pages(page));
+       page_ref_unfreeze(page, expected_count - nr);
 
        xas_unlock(&xas);
        /* Leave irq disabled to prevent preemption while updating stats */
@@ -493,17 +494,17 @@ int migrate_page_move_mapping(struct address_space *mapping,
                old_lruvec = mem_cgroup_lruvec(memcg, oldzone->zone_pgdat);
                new_lruvec = mem_cgroup_lruvec(memcg, newzone->zone_pgdat);
 
-               __dec_lruvec_state(old_lruvec, NR_FILE_PAGES);
-               __inc_lruvec_state(new_lruvec, NR_FILE_PAGES);
+               __mod_lruvec_state(old_lruvec, NR_FILE_PAGES, -nr);
+               __mod_lruvec_state(new_lruvec, NR_FILE_PAGES, nr);
                if (PageSwapBacked(page) && !PageSwapCache(page)) {
-                       __dec_lruvec_state(old_lruvec, NR_SHMEM);
-                       __inc_lruvec_state(new_lruvec, NR_SHMEM);
+                       __mod_lruvec_state(old_lruvec, NR_SHMEM, -nr);
+                       __mod_lruvec_state(new_lruvec, NR_SHMEM, nr);
                }
                if (dirty && mapping_can_writeback(mapping)) {
-                       __dec_node_state(oldzone->zone_pgdat, NR_FILE_DIRTY);
-                       __dec_zone_state(oldzone, NR_ZONE_WRITE_PENDING);
-                       __inc_node_state(newzone->zone_pgdat, NR_FILE_DIRTY);
-                       __inc_zone_state(newzone, NR_ZONE_WRITE_PENDING);
+                       __mod_lruvec_state(old_lruvec, NR_FILE_DIRTY, -nr);
+                       __mod_zone_page_state(oldzone, NR_ZONE_WRITE_PENDING, -nr);
+                       __mod_lruvec_state(new_lruvec, NR_FILE_DIRTY, nr);
+                       __mod_zone_page_state(newzone, NR_ZONE_WRITE_PENDING, nr);
                }
        }
        local_irq_enable();
index c5590af..f554320 100644 (file)
@@ -358,7 +358,9 @@ static unsigned long get_extent(enum pgt_entry entry, unsigned long old_addr,
 
        next = (old_addr + size) & mask;
        /* even if next overflowed, extent below will be ok */
-       extent = (next > old_end) ? old_end - old_addr : next - old_addr;
+       extent = next - old_addr;
+       if (extent > old_end - old_addr)
+               extent = old_end - old_addr;
        next = (new_addr + size) & mask;
        if (extent > next - new_addr)
                extent = next - new_addr;
index 5860424..eb34d20 100644 (file)
@@ -2826,7 +2826,7 @@ EXPORT_SYMBOL(__test_set_page_writeback);
  */
 void wait_on_page_writeback(struct page *page)
 {
-       if (PageWriteback(page)) {
+       while (PageWriteback(page)) {
                trace_wait_on_page_writeback(page, page_mapping(page));
                wait_on_page_bit(page, PG_writeback);
        }
index 7a2c89b..519a60d 100644 (file)
@@ -423,6 +423,8 @@ defer_init(int nid, unsigned long pfn, unsigned long end_pfn)
        if (end_pfn < pgdat_end_pfn(NODE_DATA(nid)))
                return false;
 
+       if (NODE_DATA(nid)->first_deferred_pfn != ULONG_MAX)
+               return true;
        /*
         * We start only with one section of pages, more pages are added as
         * needed until the rest of deferred pages are initialized.
@@ -1205,8 +1207,10 @@ static void kernel_init_free_pages(struct page *page, int numpages)
        /* s390's use of memset() could override KASAN redzones. */
        kasan_disable_current();
        for (i = 0; i < numpages; i++) {
+               u8 tag = page_kasan_tag(page + i);
                page_kasan_tag_reset(page + i);
                clear_highpage(page + i);
+               page_kasan_tag_set(page + i, tag);
        }
        kasan_enable_current();
 }
@@ -2860,20 +2864,20 @@ __rmqueue(struct zone *zone, unsigned int order, int migratetype,
 {
        struct page *page;
 
-#ifdef CONFIG_CMA
-       /*
-        * Balance movable allocations between regular and CMA areas by
-        * allocating from CMA when over half of the zone's free memory
-        * is in the CMA area.
-        */
-       if (alloc_flags & ALLOC_CMA &&
-           zone_page_state(zone, NR_FREE_CMA_PAGES) >
-           zone_page_state(zone, NR_FREE_PAGES) / 2) {
-               page = __rmqueue_cma_fallback(zone, order);
-               if (page)
-                       return page;
+       if (IS_ENABLED(CONFIG_CMA)) {
+               /*
+                * Balance movable allocations between regular and CMA areas by
+                * allocating from CMA when over half of the zone's free memory
+                * is in the CMA area.
+                */
+               if (alloc_flags & ALLOC_CMA &&
+                   zone_page_state(zone, NR_FREE_CMA_PAGES) >
+                   zone_page_state(zone, NR_FREE_PAGES) / 2) {
+                       page = __rmqueue_cma_fallback(zone, order);
+                       if (page)
+                               goto out;
+               }
        }
-#endif
 retry:
        page = __rmqueue_smallest(zone, order, migratetype);
        if (unlikely(!page)) {
@@ -2884,8 +2888,9 @@ retry:
                                                                alloc_flags))
                        goto retry;
        }
-
-       trace_mm_page_alloc_zone_locked(page, order, migratetype);
+out:
+       if (page)
+               trace_mm_page_alloc_zone_locked(page, order, migratetype);
        return page;
 }
 
@@ -6116,7 +6121,7 @@ overlap_memmap_init(unsigned long zone, unsigned long *pfn)
  * zone stats (e.g., nr_isolate_pageblock) are touched.
  */
 void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
-               unsigned long start_pfn,
+               unsigned long start_pfn, unsigned long zone_end_pfn,
                enum meminit_context context,
                struct vmem_altmap *altmap, int migratetype)
 {
@@ -6152,7 +6157,7 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
                if (context == MEMINIT_EARLY) {
                        if (overlap_memmap_init(zone, &pfn))
                                continue;
-                       if (defer_init(nid, pfn, end_pfn))
+                       if (defer_init(nid, pfn, zone_end_pfn))
                                break;
                }
 
@@ -6266,7 +6271,7 @@ void __meminit __weak memmap_init(unsigned long size, int nid,
 
                if (end_pfn > start_pfn) {
                        size = end_pfn - start_pfn;
-                       memmap_init_zone(size, nid, zone, start_pfn,
+                       memmap_init_zone(size, nid, zone, start_pfn, range_end_pfn,
                                         MEMINIT_EARLY, NULL, MIGRATE_MOVABLE);
                }
        }
index 4bcc119..f5fee9c 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/mm.h>
 #include <linux/uio.h>
 #include <linux/sched.h>
+#include <linux/compat.h>
 #include <linux/sched/mm.h>
 #include <linux/highmem.h>
 #include <linux/ptrace.h>
index 0c8b43a..7ecbbbe 100644 (file)
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1619,9 +1619,6 @@ static inline struct page *alloc_slab_page(struct kmem_cache *s,
        else
                page = __alloc_pages_node(node, flags, order);
 
-       if (page)
-               account_slab_page(page, order, s);
-
        return page;
 }
 
@@ -1774,6 +1771,8 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
 
        page->objects = oo_objects(oo);
 
+       account_slab_page(page, oo_order(oo), s);
+
        page->slab_cache = s;
        __SetPageSlab(page);
        if (page_is_pfmemalloc(page))
@@ -1974,7 +1973,7 @@ static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
 
                t = acquire_slab(s, n, page, object == NULL, &objects);
                if (!t)
-                       break;
+                       continue; /* cmpxchg raced */
 
                available += objects;
                if (!object) {
@@ -2792,7 +2791,8 @@ static __always_inline void maybe_wipe_obj_freeptr(struct kmem_cache *s,
                                                   void *obj)
 {
        if (unlikely(slab_want_init_on_free(s)) && obj)
-               memset((void *)((char *)obj + s->offset), 0, sizeof(void *));
+               memset((void *)((char *)kasan_reset_tag(obj) + s->offset),
+                       0, sizeof(void *));
 }
 
 /*
@@ -2884,7 +2884,7 @@ redo:
                stat(s, ALLOC_FASTPATH);
        }
 
-       maybe_wipe_obj_freeptr(s, kasan_reset_tag(object));
+       maybe_wipe_obj_freeptr(s, object);
 
        if (unlikely(slab_want_init_on_alloc(gfpflags, s)) && object)
                memset(kasan_reset_tag(object), 0, s->object_size);
@@ -3330,7 +3330,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
                int j;
 
                for (j = 0; j < i; j++)
-                       memset(p[j], 0, s->object_size);
+                       memset(kasan_reset_tag(p[j]), 0, s->object_size);
        }
 
        /* memcg and kmem_cache debug support */
@@ -5625,10 +5625,8 @@ static int sysfs_slab_add(struct kmem_cache *s)
 
        s->kobj.kset = kset;
        err = kobject_init_and_add(&s->kobj, &slab_ktype, NULL, "%s", name);
-       if (err) {
-               kobject_put(&s->kobj);
+       if (err)
                goto out;
-       }
 
        err = sysfs_create_group(&s->kobj, &slab_attr_group);
        if (err)
index 4d88fe5..e6f352b 100644 (file)
@@ -2420,8 +2420,10 @@ void *vmap(struct page **pages, unsigned int count,
                return NULL;
        }
 
-       if (flags & VM_MAP_PUT_PAGES)
+       if (flags & VM_MAP_PUT_PAGES) {
                area->pages = pages;
+               area->nr_pages = count;
+       }
        return area->addr;
 }
 EXPORT_SYMBOL(vmap);
index 257cba7..b1b574a 100644 (file)
@@ -1238,6 +1238,8 @@ static unsigned int shrink_page_list(struct list_head *page_list,
                        if (!PageSwapCache(page)) {
                                if (!(sc->gfp_mask & __GFP_IO))
                                        goto keep_locked;
+                               if (page_maybe_dma_pinned(page))
+                                       goto keep_locked;
                                if (PageTransHuge(page)) {
                                        /* cannot split THP, skip it */
                                        if (!can_split_huge_page(page, NULL))
index f292e02..8b64411 100644 (file)
@@ -284,8 +284,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
        return 0;
 
 out_free_newdev:
-       if (new_dev->reg_state == NETREG_UNINITIALIZED)
-               free_netdev(new_dev);
+       free_netdev(new_dev);
        return err;
 }
 
index ec8408d..dc1a197 100644 (file)
@@ -510,9 +510,17 @@ static void vlan_dev_set_lockdep_class(struct net_device *dev)
        netdev_for_each_tx_queue(dev, vlan_dev_set_lockdep_one, NULL);
 }
 
+static __be16 vlan_parse_protocol(const struct sk_buff *skb)
+{
+       struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
+
+       return __vlan_get_protocol(skb, veth->h_vlan_proto, NULL);
+}
+
 static const struct header_ops vlan_header_ops = {
        .create  = vlan_dev_hard_header,
        .parse   = eth_header_parse,
+       .parse_protocol = vlan_parse_protocol,
 };
 
 static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev,
@@ -532,6 +540,7 @@ static int vlan_passthru_hard_header(struct sk_buff *skb, struct net_device *dev
 static const struct header_ops vlan_passthru_header_ops = {
        .create  = vlan_passthru_hard_header,
        .parse   = eth_header_parse,
+       .parse_protocol = vlan_parse_protocol,
 };
 
 static struct device_type vlan_type = {
index 3d11fec..64468c4 100644 (file)
@@ -4,7 +4,6 @@
 #
 
 menuconfig NET_9P
-       depends on NET
        tristate "Plan 9 Resource Sharing Support (9P2000)"
        help
          If you say Y here, you will get experimental support for
index d96b0aa..9ca9572 100644 (file)
@@ -6,20 +6,19 @@
 # Rewritten to use lists instead of if-statements.
 #
 
-obj-$(CONFIG_NET)              := devres.o socket.o core/
+obj-y                          := devres.o socket.o core/
 
-tmp-$(CONFIG_COMPAT)           := compat.o
-obj-$(CONFIG_NET)              += $(tmp-y)
+obj-$(CONFIG_COMPAT)           += compat.o
 
 # LLC has to be linked before the files in net/802/
 obj-$(CONFIG_LLC)              += llc/
-obj-$(CONFIG_NET)              += ethernet/ 802/ sched/ netlink/ bpf/ ethtool/
+obj-y                          += ethernet/ 802/ sched/ netlink/ bpf/ ethtool/
 obj-$(CONFIG_NETFILTER)                += netfilter/
 obj-$(CONFIG_INET)             += ipv4/
 obj-$(CONFIG_TLS)              += tls/
 obj-$(CONFIG_XFRM)             += xfrm/
 obj-$(CONFIG_UNIX_SCM)         += unix/
-obj-$(CONFIG_NET)              += ipv6/
+obj-y                          += ipv6/
 obj-$(CONFIG_BPFILTER)         += bpfilter/
 obj-$(CONFIG_PACKET)           += packet/
 obj-$(CONFIG_NET_KEY)          += key/
@@ -56,16 +55,12 @@ obj-$(CONFIG_SMC)           += smc/
 obj-$(CONFIG_RFKILL)           += rfkill/
 obj-$(CONFIG_NET_9P)           += 9p/
 obj-$(CONFIG_CAIF)             += caif/
-ifneq ($(CONFIG_DCB),)
-obj-y                          += dcb/
-endif
+obj-$(CONFIG_DCB)              += dcb/
 obj-$(CONFIG_6LOWPAN)          += 6lowpan/
 obj-$(CONFIG_IEEE802154)       += ieee802154/
 obj-$(CONFIG_MAC802154)                += mac802154/
 
-ifeq ($(CONFIG_NET),y)
 obj-$(CONFIG_SYSCTL)           += sysctl_net.o
-endif
 obj-$(CONFIG_DNS_RESOLVER)     += dns_resolver/
 obj-$(CONFIG_CEPH_LIB)         += ceph/
 obj-$(CONFIG_BATMAN_ADV)       += batman-adv/
@@ -77,12 +72,8 @@ obj-$(CONFIG_VSOCKETS)       += vmw_vsock/
 obj-$(CONFIG_MPLS)             += mpls/
 obj-$(CONFIG_NET_NSH)          += nsh/
 obj-$(CONFIG_HSR)              += hsr/
-ifneq ($(CONFIG_NET_SWITCHDEV),)
-obj-y                          += switchdev/
-endif
-ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
-obj-y                          += l3mdev/
-endif
+obj-$(CONFIG_NET_SWITCHDEV)    += switchdev/
+obj-$(CONFIG_NET_L3_MASTER_DEV)        += l3mdev/
 obj-$(CONFIG_QRTR)             += qrtr/
 obj-$(CONFIG_NET_NCSI)         += ncsi/
 obj-$(CONFIG_XDP_SOCKETS)      += xdp/
index 579b66d..3e4f17d 100644 (file)
@@ -101,9 +101,11 @@ static inline struct pppoatm_vcc *chan_to_pvcc(const struct ppp_channel *chan)
  * doesn't want to be called in interrupt context, so we do it from
  * a tasklet
  */
-static void pppoatm_wakeup_sender(unsigned long arg)
+static void pppoatm_wakeup_sender(struct tasklet_struct *t)
 {
-       ppp_output_wakeup((struct ppp_channel *) arg);
+       struct pppoatm_vcc *pvcc = from_tasklet(pvcc, t, wakeup_tasklet);
+
+       ppp_output_wakeup(&pvcc->chan);
 }
 
 static void pppoatm_release_cb(struct atm_vcc *atmvcc)
@@ -389,11 +391,7 @@ static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
        struct atm_backend_ppp be;
        struct pppoatm_vcc *pvcc;
        int err;
-       /*
-        * Each PPPoATM instance has its own tasklet - this is just a
-        * prototypical one used to initialize them
-        */
-       static const DECLARE_TASKLET_OLD(tasklet_proto, pppoatm_wakeup_sender);
+
        if (copy_from_user(&be, arg, sizeof be))
                return -EFAULT;
        if (be.encaps != PPPOATM_ENCAPS_AUTODETECT &&
@@ -415,8 +413,7 @@ static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
        pvcc->chan.ops = &pppoatm_ops;
        pvcc->chan.mtu = atmvcc->qos.txtp.max_sdu - PPP_HDRLEN -
            (be.encaps == e_vc ? 0 : LLC_LEN);
-       pvcc->wakeup_tasklet = tasklet_proto;
-       pvcc->wakeup_tasklet.data = (unsigned long) &pvcc->chan;
+       tasklet_setup(&pvcc->wakeup_tasklet, pppoatm_wakeup_sender);
        err = ppp_register_channel(&pvcc->chan);
        if (err != 0) {
                kfree(pvcc);
index 993afd5..43ae3dc 100644 (file)
@@ -9,7 +9,6 @@
 
 config BATMAN_ADV
        tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
-       depends on NET
        select LIBCRC32C
        help
          B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
index 64e669a..400c513 100644 (file)
@@ -5,7 +5,7 @@
 
 menuconfig BT
        tristate "Bluetooth subsystem support"
-       depends on NET && !S390
+       depends on !S390
        depends on RFKILL || !RFKILL
        select CRC16
        select CRYPTO
index c1c30a9..58bcb8c 100644 (file)
@@ -272,7 +272,8 @@ int bpf_prog_test_run_raw_tp(struct bpf_prog *prog,
            kattr->test.repeat)
                return -EINVAL;
 
-       if (ctx_size_in < prog->aux->max_ctx_offset)
+       if (ctx_size_in < prog->aux->max_ctx_offset ||
+           ctx_size_in > MAX_BPF_FUNC_ARGS * sizeof(u64))
                return -EINVAL;
 
        if ((kattr->test.flags & BPF_F_TEST_RUN_ON_CPU) == 0 && cpu != 0)
@@ -636,14 +637,11 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr,
        if (IS_ERR(data))
                return PTR_ERR(data);
 
-       xdp.data_hard_start = data;
-       xdp.data = data + headroom;
-       xdp.data_meta = xdp.data;
-       xdp.data_end = xdp.data + size;
-       xdp.frame_sz = headroom + max_data_sz + tailroom;
-
        rxqueue = __netif_get_rx_queue(current->nsproxy->net_ns->loopback_dev, 0);
-       xdp.rxq = &rxqueue->xdp_rxq;
+       xdp_init_buff(&xdp, headroom + max_data_sz + tailroom,
+                     &rxqueue->xdp_rxq);
+       xdp_prepare_buff(&xdp, data, headroom, size, true);
+
        bpf_prog_change_xdp(NULL, prog);
        ret = bpf_test_run(prog, &xdp, repeat, &retval, &duration, true);
        if (ret)
index 8ad0233..3d4a214 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menuconfig BPFILTER
        bool "BPF based packet filtering framework (BPFILTER)"
-       depends on NET && BPF && INET
+       depends on BPF && INET
        select USERMODE_DRIVER
        help
          This builds experimental bpfilter framework that is aiming to
index 4702702..7fb9a02 100644 (file)
@@ -18,7 +18,7 @@ br_netfilter-y := br_netfilter_hooks.o
 br_netfilter-$(subst m,y,$(CONFIG_IPV6)) += br_netfilter_ipv6.o
 obj-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
 
-bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
+bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o br_multicast_eht.o
 
 bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o br_vlan_tunnel.o br_vlan_options.o
 
index 1b169f8..ef743f9 100644 (file)
@@ -122,7 +122,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
                break;
 
        case NETDEV_PRE_TYPE_CHANGE:
-               /* Forbid underlaying device to change its type. */
+               /* Forbid underlying device to change its type. */
                return NOTIFY_BAD;
 
        case NETDEV_RESEND_IGMP:
index 32ac834..b749023 100644 (file)
@@ -602,6 +602,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
                        /* fastpath: update of existing entry */
                        if (unlikely(source != fdb->dst &&
                                     !test_bit(BR_FDB_STICKY, &fdb->flags))) {
+                               br_switchdev_fdb_notify(fdb, RTM_DELNEIGH);
                                fdb->dst = source;
                                fdb_modified = true;
                                /* Take over HW learned entry */
index e28ffad..6e9b049 100644 (file)
@@ -39,8 +39,7 @@ int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb
        br_drop_fake_rtable(skb);
 
        if (skb->ip_summed == CHECKSUM_PARTIAL &&
-           (skb->protocol == htons(ETH_P_8021Q) ||
-            skb->protocol == htons(ETH_P_8021AD))) {
+           eth_type_vlan(skb->protocol)) {
                int depth;
 
                if (!__vlan_get_protocol(skb, skb->protocol, &depth))
index 8ca1f1b..222285d 100644 (file)
@@ -40,7 +40,7 @@ static int br_pass_frame_up(struct sk_buff *skb)
 
        vg = br_vlan_group_rcu(br);
        /* Bridge is just like any other port.  Make sure the
-        * packet is allowed except in promisc modue when someone
+        * packet is allowed except in promisc mode when someone
         * may be running packet capture.
         */
        if (!(brdev->flags & IFF_PROMISC) &&
index cec2c4e..fc0a988 100644 (file)
@@ -825,7 +825,7 @@ int br_mrp_start_in_test(struct net_bridge *br,
        return 0;
 }
 
-/* Determin if the frame type is a ring frame */
+/* Determine if the frame type is a ring frame */
 static bool br_mrp_ring_frame(struct sk_buff *skb)
 {
        const struct br_mrp_tlv_hdr *hdr;
@@ -845,7 +845,7 @@ static bool br_mrp_ring_frame(struct sk_buff *skb)
        return false;
 }
 
-/* Determin if the frame type is an interconnect frame */
+/* Determine if the frame type is an interconnect frame */
 static bool br_mrp_in_frame(struct sk_buff *skb)
 {
        const struct br_mrp_tlv_hdr *hdr;
@@ -894,7 +894,7 @@ static void br_mrp_mrm_process(struct br_mrp *mrp, struct net_bridge_port *port,
                br_mrp_ring_port_open(port->dev, false);
 }
 
-/* Determin if the test hdr has a better priority than the node */
+/* Determine if the test hdr has a better priority than the node */
 static bool br_mrp_test_better_than_own(struct br_mrp *mrp,
                                        struct net_bridge *br,
                                        const struct br_mrp_ring_test_hdr *hdr)
index 257ac4e..6f672eb 100644 (file)
@@ -33,6 +33,7 @@
 #endif
 
 #include "br_private.h"
+#include "br_private_mcast_eht.h"
 
 static const struct rhashtable_params br_mdb_rht_params = {
        .head_offset = offsetof(struct net_bridge_mdb_entry, rhnode),
@@ -441,7 +442,8 @@ static void br_multicast_fwd_src_add(struct net_bridge_group_src *src)
        br_multicast_sg_add_exclude_ports(star_mp, sg);
 }
 
-static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src)
+static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src,
+                                       bool fastleave)
 {
        struct net_bridge_port_group *p, *pg = src->pg;
        struct net_bridge_port_group __rcu **pp;
@@ -466,6 +468,8 @@ static void br_multicast_fwd_src_remove(struct net_bridge_group_src *src)
                    (p->flags & MDB_PG_FLAGS_PERMANENT))
                        break;
 
+               if (fastleave)
+                       p->flags |= MDB_PG_FLAGS_FAST_LEAVE;
                br_multicast_del_pg(mp, p, pp);
                break;
        }
@@ -559,11 +563,12 @@ static void br_multicast_destroy_group_src(struct net_bridge_mcast_gc *gc)
        kfree_rcu(src, rcu);
 }
 
-static void br_multicast_del_group_src(struct net_bridge_group_src *src)
+void br_multicast_del_group_src(struct net_bridge_group_src *src,
+                               bool fastleave)
 {
        struct net_bridge *br = src->pg->key.port->br;
 
-       br_multicast_fwd_src_remove(src);
+       br_multicast_fwd_src_remove(src, fastleave);
        hlist_del_init_rcu(&src->node);
        src->pg->src_ents--;
        hlist_add_head(&src->mcast_gc.gc_node, &br->mcast_gc_list);
@@ -593,8 +598,9 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
 
        rcu_assign_pointer(*pp, pg->next);
        hlist_del_init(&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);
+               br_multicast_del_group_src(ent, false);
        br_mdb_notify(br->dev, mp, pg, RTM_DELMDB);
        if (!br_multicast_is_star_g(&mp->addr)) {
                rhashtable_remove_fast(&br->sg_port_tbl, &pg->rhnode,
@@ -651,7 +657,7 @@ static void br_multicast_port_group_expired(struct timer_list *t)
        pg->filter_mode = MCAST_INCLUDE;
        hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
                if (!timer_pending(&src_ent->timer)) {
-                       br_multicast_del_group_src(src_ent);
+                       br_multicast_del_group_src(src_ent, false);
                        changed = true;
                }
        }
@@ -1078,7 +1084,7 @@ static void br_multicast_group_src_expired(struct timer_list *t)
 
        pg = src->pg;
        if (pg->filter_mode == MCAST_INCLUDE) {
-               br_multicast_del_group_src(src);
+               br_multicast_del_group_src(src, false);
                if (!hlist_empty(&pg->src_list))
                        goto out;
                br_multicast_find_del_pg(br, pg);
@@ -1090,7 +1096,7 @@ out:
        spin_unlock(&br->multicast_lock);
 }
 
-static struct net_bridge_group_src *
+struct net_bridge_group_src *
 br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip)
 {
        struct net_bridge_group_src *ent;
@@ -1172,6 +1178,8 @@ struct net_bridge_port_group *br_multicast_new_port_group(
        p->flags = flags;
        p->filter_mode = filter_mode;
        p->rt_protocol = rt_protocol;
+       p->eht_host_tree = RB_ROOT;
+       p->eht_set_tree = RB_ROOT;
        p->mcast_gc.destroy = br_multicast_destroy_port_group;
        INIT_HLIST_HEAD(&p->src_list);
 
@@ -1292,7 +1300,7 @@ static int br_multicast_add_group(struct net_bridge *br,
        pg = __br_multicast_add_group(br, port, group, src, filter_mode,
                                      igmpv2_mldv1, false);
        /* NULL is considered valid for host joined groups */
-       err = IS_ERR(pg) ? PTR_ERR(pg) : 0;
+       err = PTR_ERR_OR_ZERO(pg);
        spin_unlock(&br->multicast_lock);
 
        return err;
@@ -1600,6 +1608,7 @@ static void br_mc_disabled_update(struct net_device *dev, bool value)
 int br_multicast_add_port(struct net_bridge_port *port)
 {
        port->multicast_router = MDB_RTR_TYPE_TEMP_QUERY;
+       port->multicast_eht_hosts_limit = BR_MCAST_DEFAULT_EHT_HOSTS_LIMIT;
 
        timer_setup(&port->multicast_router_timer,
                    br_multicast_router_expired, 0);
@@ -1700,7 +1709,7 @@ static int __grp_src_delete_marked(struct net_bridge_port_group *pg)
 
        hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
                if (ent->flags & BR_SGRP_F_DELETE) {
-                       br_multicast_del_group_src(ent);
+                       br_multicast_del_group_src(ent, false);
                        deleted++;
                }
 
@@ -1799,8 +1808,9 @@ static void __grp_send_query_and_rexmit(struct net_bridge_port_group *pg)
  * INCLUDE (A)    ALLOW (B)     INCLUDE (A+B)            (B)=GMI
  * EXCLUDE (X,Y)  ALLOW (A)     EXCLUDE (X+A,Y-A)        (A)=GMI
  */
-static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg,
-                                    void *srcs, u32 nsrcs, size_t src_size)
+static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg, void *h_addr,
+                                    void *srcs, u32 nsrcs, size_t addr_size,
+                                    int grec_type)
 {
        struct net_bridge *br = pg->key.port->br;
        struct net_bridge_group_src *ent;
@@ -1812,7 +1822,7 @@ static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (!ent) {
                        ent = br_multicast_new_group_src(pg, &src_ip);
@@ -1822,9 +1832,11 @@ static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg,
 
                if (ent)
                        __grp_src_mod_timer(ent, now + br_multicast_gmi(br));
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        return changed;
 }
 
@@ -1833,8 +1845,9 @@ static bool br_multicast_isinc_allow(struct net_bridge_port_group *pg,
  *                                                       Delete (A-B)
  *                                                       Group Timer=GMI
  */
-static void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
-                                void *srcs, u32 nsrcs, size_t src_size)
+static void __grp_src_isexc_incl(struct net_bridge_port_group *pg, void *h_addr,
+                                void *srcs, u32 nsrcs, size_t addr_size,
+                                int grec_type)
 {
        struct net_bridge_group_src *ent;
        struct br_ip src_ip;
@@ -1846,7 +1859,7 @@ static void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent)
                        ent->flags &= ~BR_SGRP_F_DELETE;
@@ -1854,9 +1867,10 @@ static void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
                        ent = br_multicast_new_group_src(pg, &src_ip);
                if (ent)
                        br_multicast_fwd_src_handle(ent);
-               srcs += src_size;
        }
 
+       br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type);
+
        __grp_src_delete_marked(pg);
 }
 
@@ -1866,8 +1880,9 @@ static void __grp_src_isexc_incl(struct net_bridge_port_group *pg,
  *                                                       Delete (Y-A)
  *                                                       Group Timer=GMI
  */
-static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg,
-                                void *srcs, u32 nsrcs, size_t src_size)
+static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg, void *h_addr,
+                                void *srcs, u32 nsrcs, size_t addr_size,
+                                int grec_type)
 {
        struct net_bridge *br = pg->key.port->br;
        struct net_bridge_group_src *ent;
@@ -1882,7 +1897,7 @@ static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent) {
                        ent->flags &= ~BR_SGRP_F_DELETE;
@@ -1894,29 +1909,34 @@ static bool __grp_src_isexc_excl(struct net_bridge_port_group *pg,
                                changed = true;
                        }
                }
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        if (__grp_src_delete_marked(pg))
                changed = true;
 
        return changed;
 }
 
-static bool br_multicast_isexc(struct net_bridge_port_group *pg,
-                              void *srcs, u32 nsrcs, size_t src_size)
+static bool br_multicast_isexc(struct net_bridge_port_group *pg, void *h_addr,
+                              void *srcs, u32 nsrcs, size_t addr_size,
+                              int grec_type)
 {
        struct net_bridge *br = pg->key.port->br;
        bool changed = false;
 
        switch (pg->filter_mode) {
        case MCAST_INCLUDE:
-               __grp_src_isexc_incl(pg, srcs, nsrcs, src_size);
+               __grp_src_isexc_incl(pg, h_addr, srcs, nsrcs, addr_size,
+                                    grec_type);
                br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
                changed = true;
                break;
        case MCAST_EXCLUDE:
-               changed = __grp_src_isexc_excl(pg, srcs, nsrcs, src_size);
+               changed = __grp_src_isexc_excl(pg, h_addr, srcs, nsrcs, addr_size,
+                                              grec_type);
                break;
        }
 
@@ -1930,8 +1950,9 @@ static bool br_multicast_isexc(struct net_bridge_port_group *pg,
  * INCLUDE (A)    TO_IN (B)     INCLUDE (A+B)            (B)=GMI
  *                                                       Send Q(G,A-B)
  */
-static bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
-                               void *srcs, u32 nsrcs, size_t src_size)
+static bool __grp_src_toin_incl(struct net_bridge_port_group *pg, void *h_addr,
+                               void *srcs, u32 nsrcs, size_t addr_size,
+                               int grec_type)
 {
        struct net_bridge *br = pg->key.port->br;
        u32 src_idx, to_send = pg->src_ents;
@@ -1946,7 +1967,7 @@ static bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent) {
                        ent->flags &= ~BR_SGRP_F_SEND;
@@ -1958,9 +1979,11 @@ static bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
                }
                if (ent)
                        __grp_src_mod_timer(ent, now + br_multicast_gmi(br));
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        if (to_send)
                __grp_src_query_marked_and_rexmit(pg);
 
@@ -1972,8 +1995,9 @@ static bool __grp_src_toin_incl(struct net_bridge_port_group *pg,
  *                                                       Send Q(G,X-A)
  *                                                       Send Q(G)
  */
-static bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
-                               void *srcs, u32 nsrcs, size_t src_size)
+static bool __grp_src_toin_excl(struct net_bridge_port_group *pg, void *h_addr,
+                               void *srcs, u32 nsrcs, size_t addr_size,
+                               int grec_type)
 {
        struct net_bridge *br = pg->key.port->br;
        u32 src_idx, to_send = pg->src_ents;
@@ -1989,7 +2013,7 @@ static bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent) {
                        if (timer_pending(&ent->timer)) {
@@ -2003,9 +2027,11 @@ static bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
                }
                if (ent)
                        __grp_src_mod_timer(ent, now + br_multicast_gmi(br));
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        if (to_send)
                __grp_src_query_marked_and_rexmit(pg);
 
@@ -2014,20 +2040,32 @@ static bool __grp_src_toin_excl(struct net_bridge_port_group *pg,
        return changed;
 }
 
-static bool br_multicast_toin(struct net_bridge_port_group *pg,
-                             void *srcs, u32 nsrcs, size_t src_size)
+static bool br_multicast_toin(struct net_bridge_port_group *pg, void *h_addr,
+                             void *srcs, u32 nsrcs, size_t addr_size,
+                             int grec_type)
 {
        bool changed = false;
 
        switch (pg->filter_mode) {
        case MCAST_INCLUDE:
-               changed = __grp_src_toin_incl(pg, srcs, nsrcs, src_size);
+               changed = __grp_src_toin_incl(pg, h_addr, srcs, nsrcs, addr_size,
+                                             grec_type);
                break;
        case MCAST_EXCLUDE:
-               changed = __grp_src_toin_excl(pg, srcs, nsrcs, src_size);
+               changed = __grp_src_toin_excl(pg, h_addr, srcs, nsrcs, addr_size,
+                                             grec_type);
                break;
        }
 
+       if (br_multicast_eht_should_del_pg(pg)) {
+               pg->flags |= MDB_PG_FLAGS_FAST_LEAVE;
+               br_multicast_find_del_pg(pg->key.port->br, pg);
+               /* a notification has already been sent and we shouldn't
+                * access pg after the delete so we have to return false
+                */
+               changed = false;
+       }
+
        return changed;
 }
 
@@ -2037,8 +2075,9 @@ static bool br_multicast_toin(struct net_bridge_port_group *pg,
  *                                                       Send Q(G,A*B)
  *                                                       Group Timer=GMI
  */
-static void __grp_src_toex_incl(struct net_bridge_port_group *pg,
-                               void *srcs, u32 nsrcs, size_t src_size)
+static void __grp_src_toex_incl(struct net_bridge_port_group *pg, void *h_addr,
+                               void *srcs, u32 nsrcs, size_t addr_size,
+                               int grec_type)
 {
        struct net_bridge_group_src *ent;
        u32 src_idx, to_send = 0;
@@ -2050,7 +2089,7 @@ static void __grp_src_toex_incl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent) {
                        ent->flags = (ent->flags & ~BR_SGRP_F_DELETE) |
@@ -2061,9 +2100,10 @@ static void __grp_src_toex_incl(struct net_bridge_port_group *pg,
                }
                if (ent)
                        br_multicast_fwd_src_handle(ent);
-               srcs += src_size;
        }
 
+       br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type);
+
        __grp_src_delete_marked(pg);
        if (to_send)
                __grp_src_query_marked_and_rexmit(pg);
@@ -2076,8 +2116,9 @@ static void __grp_src_toex_incl(struct net_bridge_port_group *pg,
  *                                                       Send Q(G,A-Y)
  *                                                       Group Timer=GMI
  */
-static bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
-                               void *srcs, u32 nsrcs, size_t src_size)
+static bool __grp_src_toex_excl(struct net_bridge_port_group *pg, void *h_addr,
+                               void *srcs, u32 nsrcs, size_t addr_size,
+                               int grec_type)
 {
        struct net_bridge_group_src *ent;
        u32 src_idx, to_send = 0;
@@ -2090,7 +2131,7 @@ static bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent) {
                        ent->flags &= ~BR_SGRP_F_DELETE;
@@ -2105,9 +2146,11 @@ static bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
                        ent->flags |= BR_SGRP_F_SEND;
                        to_send++;
                }
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        if (__grp_src_delete_marked(pg))
                changed = true;
        if (to_send)
@@ -2116,20 +2159,23 @@ static bool __grp_src_toex_excl(struct net_bridge_port_group *pg,
        return changed;
 }
 
-static bool br_multicast_toex(struct net_bridge_port_group *pg,
-                             void *srcs, u32 nsrcs, size_t src_size)
+static bool br_multicast_toex(struct net_bridge_port_group *pg, void *h_addr,
+                             void *srcs, u32 nsrcs, size_t addr_size,
+                             int grec_type)
 {
        struct net_bridge *br = pg->key.port->br;
        bool changed = false;
 
        switch (pg->filter_mode) {
        case MCAST_INCLUDE:
-               __grp_src_toex_incl(pg, srcs, nsrcs, src_size);
+               __grp_src_toex_incl(pg, h_addr, srcs, nsrcs, addr_size,
+                                   grec_type);
                br_multicast_star_g_handle_mode(pg, MCAST_EXCLUDE);
                changed = true;
                break;
        case MCAST_EXCLUDE:
-               changed = __grp_src_toex_excl(pg, srcs, nsrcs, src_size);
+               changed = __grp_src_toex_excl(pg, h_addr, srcs, nsrcs, addr_size,
+                                             grec_type);
                break;
        }
 
@@ -2142,11 +2188,12 @@ static bool br_multicast_toex(struct net_bridge_port_group *pg,
 /* State          Msg type      New state                Actions
  * INCLUDE (A)    BLOCK (B)     INCLUDE (A)              Send Q(G,A*B)
  */
-static void __grp_src_block_incl(struct net_bridge_port_group *pg,
-                                void *srcs, u32 nsrcs, size_t src_size)
+static bool __grp_src_block_incl(struct net_bridge_port_group *pg, void *h_addr,
+                                void *srcs, u32 nsrcs, size_t addr_size, int grec_type)
 {
        struct net_bridge_group_src *ent;
        u32 src_idx, to_send = 0;
+       bool changed = false;
        struct br_ip src_ip;
 
        hlist_for_each_entry(ent, &pg->src_list, node)
@@ -2155,28 +2202,29 @@ static void __grp_src_block_incl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (ent) {
                        ent->flags |= BR_SGRP_F_SEND;
                        to_send++;
                }
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        if (to_send)
                __grp_src_query_marked_and_rexmit(pg);
 
-       if (pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list))
-               br_multicast_find_del_pg(pg->key.port->br, pg);
+       return changed;
 }
 
 /* State          Msg type      New state                Actions
  * EXCLUDE (X,Y)  BLOCK (A)     EXCLUDE (X+(A-Y),Y)      (A-X-Y)=Group Timer
  *                                                       Send Q(G,A-Y)
  */
-static bool __grp_src_block_excl(struct net_bridge_port_group *pg,
-                                void *srcs, u32 nsrcs, size_t src_size)
+static bool __grp_src_block_excl(struct net_bridge_port_group *pg, void *h_addr,
+                                void *srcs, u32 nsrcs, size_t addr_size, int grec_type)
 {
        struct net_bridge_group_src *ent;
        u32 src_idx, to_send = 0;
@@ -2189,7 +2237,7 @@ static bool __grp_src_block_excl(struct net_bridge_port_group *pg,
        memset(&src_ip, 0, sizeof(src_ip));
        src_ip.proto = pg->key.addr.proto;
        for (src_idx = 0; src_idx < nsrcs; src_idx++) {
-               memcpy(&src_ip.src, srcs, src_size);
+               memcpy(&src_ip.src, srcs + (src_idx * addr_size), addr_size);
                ent = br_multicast_find_group_src(pg, &src_ip);
                if (!ent) {
                        ent = br_multicast_new_group_src(pg, &src_ip);
@@ -2202,29 +2250,44 @@ static bool __grp_src_block_excl(struct net_bridge_port_group *pg,
                        ent->flags |= BR_SGRP_F_SEND;
                        to_send++;
                }
-               srcs += src_size;
        }
 
+       if (br_multicast_eht_handle(pg, h_addr, srcs, nsrcs, addr_size, grec_type))
+               changed = true;
+
        if (to_send)
                __grp_src_query_marked_and_rexmit(pg);
 
        return changed;
 }
 
-static bool br_multicast_block(struct net_bridge_port_group *pg,
-                              void *srcs, u32 nsrcs, size_t src_size)
+static bool br_multicast_block(struct net_bridge_port_group *pg, void *h_addr,
+                              void *srcs, u32 nsrcs, size_t addr_size, int grec_type)
 {
        bool changed = false;
 
        switch (pg->filter_mode) {
        case MCAST_INCLUDE:
-               __grp_src_block_incl(pg, srcs, nsrcs, src_size);
+               changed = __grp_src_block_incl(pg, h_addr, srcs, nsrcs, addr_size,
+                                              grec_type);
                break;
        case MCAST_EXCLUDE:
-               changed = __grp_src_block_excl(pg, srcs, nsrcs, src_size);
+               changed = __grp_src_block_excl(pg, h_addr, srcs, nsrcs, addr_size,
+                                              grec_type);
                break;
        }
 
+       if ((pg->filter_mode == MCAST_INCLUDE && hlist_empty(&pg->src_list)) ||
+           br_multicast_eht_should_del_pg(pg)) {
+               if (br_multicast_eht_should_del_pg(pg))
+                       pg->flags |= MDB_PG_FLAGS_FAST_LEAVE;
+               br_multicast_find_del_pg(pg->key.port->br, pg);
+               /* a notification has already been sent and we shouldn't
+                * access pg after the delete so we have to return false
+                */
+               changed = false;
+       }
+
        return changed;
 }
 
@@ -2257,8 +2320,8 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
        struct igmpv3_report *ih;
        struct igmpv3_grec *grec;
        int i, len, num, type;
+       __be32 group, *h_addr;
        bool changed = false;
-       __be32 group;
        int err = 0;
        u16 nsrcs;
 
@@ -2318,32 +2381,33 @@ static int br_ip4_multicast_igmp3_report(struct net_bridge *br,
                pg = br_multicast_find_port(mdst, port, src);
                if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
                        goto unlock_continue;
-               /* reload grec */
+               /* reload grec and host addr */
                grec = (void *)(skb->data + len - sizeof(*grec) - (nsrcs * 4));
+               h_addr = &ip_hdr(skb)->saddr;
                switch (type) {
                case IGMPV3_ALLOW_NEW_SOURCES:
-                       changed = br_multicast_isinc_allow(pg, grec->grec_src,
-                                                          nsrcs, sizeof(__be32));
+                       changed = br_multicast_isinc_allow(pg, h_addr, grec->grec_src,
+                                                          nsrcs, sizeof(__be32), type);
                        break;
                case IGMPV3_MODE_IS_INCLUDE:
-                       changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs,
-                                                          sizeof(__be32));
+                       changed = br_multicast_isinc_allow(pg, h_addr, grec->grec_src,
+                                                          nsrcs, sizeof(__be32), type);
                        break;
                case IGMPV3_MODE_IS_EXCLUDE:
-                       changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
-                                                    sizeof(__be32));
+                       changed = br_multicast_isexc(pg, h_addr, grec->grec_src,
+                                                    nsrcs, sizeof(__be32), type);
                        break;
                case IGMPV3_CHANGE_TO_INCLUDE:
-                       changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
-                                                   sizeof(__be32));
+                       changed = br_multicast_toin(pg, h_addr, grec->grec_src,
+                                                   nsrcs, sizeof(__be32), type);
                        break;
                case IGMPV3_CHANGE_TO_EXCLUDE:
-                       changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
-                                                   sizeof(__be32));
+                       changed = br_multicast_toex(pg, h_addr, grec->grec_src,
+                                                   nsrcs, sizeof(__be32), type);
                        break;
                case IGMPV3_BLOCK_OLD_SOURCES:
-                       changed = br_multicast_block(pg, grec->grec_src, nsrcs,
-                                                    sizeof(__be32));
+                       changed = br_multicast_block(pg, h_addr, grec->grec_src,
+                                                    nsrcs, sizeof(__be32), type);
                        break;
                }
                if (changed)
@@ -2367,6 +2431,7 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
        unsigned int nsrcs_offset;
        const unsigned char *src;
        struct icmp6hdr *icmp6h;
+       struct in6_addr *h_addr;
        struct mld2_grec *grec;
        unsigned int grec_len;
        bool changed = false;
@@ -2445,31 +2510,43 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br,
                pg = br_multicast_find_port(mdst, port, src);
                if (!pg || (pg->flags & MDB_PG_FLAGS_PERMANENT))
                        goto unlock_continue;
+               h_addr = &ipv6_hdr(skb)->saddr;
                switch (grec->grec_type) {
                case MLD2_ALLOW_NEW_SOURCES:
-                       changed = br_multicast_isinc_allow(pg, grec->grec_src,
-                                                          nsrcs,
-                                                          sizeof(struct in6_addr));
+                       changed = br_multicast_isinc_allow(pg, h_addr,
+                                                          grec->grec_src, nsrcs,
+                                                          sizeof(struct in6_addr),
+                                                          grec->grec_type);
                        break;
                case MLD2_MODE_IS_INCLUDE:
-                       changed = br_multicast_isinc_allow(pg, grec->grec_src, nsrcs,
-                                                          sizeof(struct in6_addr));
+                       changed = br_multicast_isinc_allow(pg, h_addr,
+                                                          grec->grec_src, nsrcs,
+                                                          sizeof(struct in6_addr),
+                                                          grec->grec_type);
                        break;
                case MLD2_MODE_IS_EXCLUDE:
-                       changed = br_multicast_isexc(pg, grec->grec_src, nsrcs,
-                                                    sizeof(struct in6_addr));
+                       changed = br_multicast_isexc(pg, h_addr,
+                                                    grec->grec_src, nsrcs,
+                                                    sizeof(struct in6_addr),
+                                                    grec->grec_type);
                        break;
                case MLD2_CHANGE_TO_INCLUDE:
-                       changed = br_multicast_toin(pg, grec->grec_src, nsrcs,
-                                                   sizeof(struct in6_addr));
+                       changed = br_multicast_toin(pg, h_addr,
+                                                   grec->grec_src, nsrcs,
+                                                   sizeof(struct in6_addr),
+                                                   grec->grec_type);
                        break;
                case MLD2_CHANGE_TO_EXCLUDE:
-                       changed = br_multicast_toex(pg, grec->grec_src, nsrcs,
-                                                   sizeof(struct in6_addr));
+                       changed = br_multicast_toex(pg, h_addr,
+                                                   grec->grec_src, nsrcs,
+                                                   sizeof(struct in6_addr),
+                                                   grec->grec_type);
                        break;
                case MLD2_BLOCK_OLD_SOURCES:
-                       changed = br_multicast_block(pg, grec->grec_src, nsrcs,
-                                                    sizeof(struct in6_addr));
+                       changed = br_multicast_block(pg, h_addr,
+                                                    grec->grec_src, nsrcs,
+                                                    sizeof(struct in6_addr),
+                                                    grec->grec_type);
                        break;
                }
                if (changed)
diff --git a/net/bridge/br_multicast_eht.c b/net/bridge/br_multicast_eht.c
new file mode 100644 (file)
index 0000000..fea38b9
--- /dev/null
@@ -0,0 +1,878 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (c) 2020, Nikolay Aleksandrov <nikolay@nvidia.com>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/if_ether.h>
+#include <linux/igmp.h>
+#include <linux/in.h>
+#include <linux/jhash.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter_bridge.h>
+#include <linux/random.h>
+#include <linux/rculist.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/inetdevice.h>
+#include <linux/mroute.h>
+#include <net/ip.h>
+#include <net/switchdev.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/icmpv6.h>
+#include <net/ipv6.h>
+#include <net/mld.h>
+#include <net/ip6_checksum.h>
+#include <net/addrconf.h>
+#endif
+
+#include "br_private.h"
+#include "br_private_mcast_eht.h"
+
+static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg,
+                                          union net_bridge_eht_addr *src_addr,
+                                          union net_bridge_eht_addr *h_addr);
+static void br_multicast_create_eht_set_entry(struct net_bridge_port_group *pg,
+                                             union net_bridge_eht_addr *src_addr,
+                                             union net_bridge_eht_addr *h_addr,
+                                             int filter_mode,
+                                             bool allow_zero_src);
+
+static struct net_bridge_group_eht_host *
+br_multicast_eht_host_lookup(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr)
+{
+       struct rb_node *node = pg->eht_host_tree.rb_node;
+
+       while (node) {
+               struct net_bridge_group_eht_host *this;
+               int result;
+
+               this = rb_entry(node, struct net_bridge_group_eht_host,
+                               rb_node);
+               result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else
+                       return this;
+       }
+
+       return NULL;
+}
+
+static int br_multicast_eht_host_filter_mode(struct net_bridge_port_group *pg,
+                                            union net_bridge_eht_addr *h_addr)
+{
+       struct net_bridge_group_eht_host *eht_host;
+
+       eht_host = br_multicast_eht_host_lookup(pg, h_addr);
+       if (!eht_host)
+               return MCAST_INCLUDE;
+
+       return eht_host->filter_mode;
+}
+
+static struct net_bridge_group_eht_set_entry *
+br_multicast_eht_set_entry_lookup(struct net_bridge_group_eht_set *eht_set,
+                                 union net_bridge_eht_addr *h_addr)
+{
+       struct rb_node *node = eht_set->entry_tree.rb_node;
+
+       while (node) {
+               struct net_bridge_group_eht_set_entry *this;
+               int result;
+
+               this = rb_entry(node, struct net_bridge_group_eht_set_entry,
+                               rb_node);
+               result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else
+                       return this;
+       }
+
+       return NULL;
+}
+
+static struct net_bridge_group_eht_set *
+br_multicast_eht_set_lookup(struct net_bridge_port_group *pg,
+                           union net_bridge_eht_addr *src_addr)
+{
+       struct rb_node *node = pg->eht_set_tree.rb_node;
+
+       while (node) {
+               struct net_bridge_group_eht_set *this;
+               int result;
+
+               this = rb_entry(node, struct net_bridge_group_eht_set,
+                               rb_node);
+               result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
+               if (result < 0)
+                       node = node->rb_left;
+               else if (result > 0)
+                       node = node->rb_right;
+               else
+                       return this;
+       }
+
+       return NULL;
+}
+
+static void __eht_destroy_host(struct net_bridge_group_eht_host *eht_host)
+{
+       WARN_ON(!hlist_empty(&eht_host->set_entries));
+
+       br_multicast_eht_hosts_dec(eht_host->pg);
+
+       rb_erase(&eht_host->rb_node, &eht_host->pg->eht_host_tree);
+       RB_CLEAR_NODE(&eht_host->rb_node);
+       kfree(eht_host);
+}
+
+static void br_multicast_destroy_eht_set_entry(struct net_bridge_mcast_gc *gc)
+{
+       struct net_bridge_group_eht_set_entry *set_h;
+
+       set_h = container_of(gc, struct net_bridge_group_eht_set_entry, mcast_gc);
+       WARN_ON(!RB_EMPTY_NODE(&set_h->rb_node));
+
+       del_timer_sync(&set_h->timer);
+       kfree(set_h);
+}
+
+static void br_multicast_destroy_eht_set(struct net_bridge_mcast_gc *gc)
+{
+       struct net_bridge_group_eht_set *eht_set;
+
+       eht_set = container_of(gc, struct net_bridge_group_eht_set, mcast_gc);
+       WARN_ON(!RB_EMPTY_NODE(&eht_set->rb_node));
+       WARN_ON(!RB_EMPTY_ROOT(&eht_set->entry_tree));
+
+       del_timer_sync(&eht_set->timer);
+       kfree(eht_set);
+}
+
+static void __eht_del_set_entry(struct net_bridge_group_eht_set_entry *set_h)
+{
+       struct net_bridge_group_eht_host *eht_host = set_h->h_parent;
+       union net_bridge_eht_addr zero_addr;
+
+       rb_erase(&set_h->rb_node, &set_h->eht_set->entry_tree);
+       RB_CLEAR_NODE(&set_h->rb_node);
+       hlist_del_init(&set_h->host_list);
+       memset(&zero_addr, 0, sizeof(zero_addr));
+       if (memcmp(&set_h->h_addr, &zero_addr, sizeof(zero_addr)))
+               eht_host->num_entries--;
+       hlist_add_head(&set_h->mcast_gc.gc_node, &set_h->br->mcast_gc_list);
+       queue_work(system_long_wq, &set_h->br->mcast_gc_work);
+
+       if (hlist_empty(&eht_host->set_entries))
+               __eht_destroy_host(eht_host);
+}
+
+static void br_multicast_del_eht_set(struct net_bridge_group_eht_set *eht_set)
+{
+       struct net_bridge_group_eht_set_entry *set_h;
+       struct rb_node *node;
+
+       while ((node = rb_first(&eht_set->entry_tree))) {
+               set_h = rb_entry(node, struct net_bridge_group_eht_set_entry,
+                                rb_node);
+               __eht_del_set_entry(set_h);
+       }
+
+       rb_erase(&eht_set->rb_node, &eht_set->pg->eht_set_tree);
+       RB_CLEAR_NODE(&eht_set->rb_node);
+       hlist_add_head(&eht_set->mcast_gc.gc_node, &eht_set->br->mcast_gc_list);
+       queue_work(system_long_wq, &eht_set->br->mcast_gc_work);
+}
+
+void br_multicast_eht_clean_sets(struct net_bridge_port_group *pg)
+{
+       struct net_bridge_group_eht_set *eht_set;
+       struct rb_node *node;
+
+       while ((node = rb_first(&pg->eht_set_tree))) {
+               eht_set = rb_entry(node, struct net_bridge_group_eht_set,
+                                  rb_node);
+               br_multicast_del_eht_set(eht_set);
+       }
+}
+
+static void br_multicast_eht_set_entry_expired(struct timer_list *t)
+{
+       struct net_bridge_group_eht_set_entry *set_h = from_timer(set_h, t, timer);
+       struct net_bridge *br = set_h->br;
+
+       spin_lock(&br->multicast_lock);
+       if (RB_EMPTY_NODE(&set_h->rb_node) || timer_pending(&set_h->timer))
+               goto out;
+
+       br_multicast_del_eht_set_entry(set_h->eht_set->pg,
+                                      &set_h->eht_set->src_addr,
+                                      &set_h->h_addr);
+out:
+       spin_unlock(&br->multicast_lock);
+}
+
+static void br_multicast_eht_set_expired(struct timer_list *t)
+{
+       struct net_bridge_group_eht_set *eht_set = from_timer(eht_set, t,
+                                                             timer);
+       struct net_bridge *br = eht_set->br;
+
+       spin_lock(&br->multicast_lock);
+       if (RB_EMPTY_NODE(&eht_set->rb_node) || timer_pending(&eht_set->timer))
+               goto out;
+
+       br_multicast_del_eht_set(eht_set);
+out:
+       spin_unlock(&br->multicast_lock);
+}
+
+static struct net_bridge_group_eht_host *
+__eht_lookup_create_host(struct net_bridge_port_group *pg,
+                        union net_bridge_eht_addr *h_addr,
+                        unsigned char filter_mode)
+{
+       struct rb_node **link = &pg->eht_host_tree.rb_node, *parent = NULL;
+       struct net_bridge_group_eht_host *eht_host;
+
+       while (*link) {
+               struct net_bridge_group_eht_host *this;
+               int result;
+
+               this = rb_entry(*link, struct net_bridge_group_eht_host,
+                               rb_node);
+               result = memcmp(h_addr, &this->h_addr, sizeof(*h_addr));
+               parent = *link;
+               if (result < 0)
+                       link = &((*link)->rb_left);
+               else if (result > 0)
+                       link = &((*link)->rb_right);
+               else
+                       return this;
+       }
+
+       if (br_multicast_eht_hosts_over_limit(pg))
+               return NULL;
+
+       eht_host = kzalloc(sizeof(*eht_host), GFP_ATOMIC);
+       if (!eht_host)
+               return NULL;
+
+       memcpy(&eht_host->h_addr, h_addr, sizeof(*h_addr));
+       INIT_HLIST_HEAD(&eht_host->set_entries);
+       eht_host->pg = pg;
+       eht_host->filter_mode = filter_mode;
+
+       rb_link_node(&eht_host->rb_node, parent, link);
+       rb_insert_color(&eht_host->rb_node, &pg->eht_host_tree);
+
+       br_multicast_eht_hosts_inc(pg);
+
+       return eht_host;
+}
+
+static struct net_bridge_group_eht_set_entry *
+__eht_lookup_create_set_entry(struct net_bridge *br,
+                             struct net_bridge_group_eht_set *eht_set,
+                             struct net_bridge_group_eht_host *eht_host,
+                             bool allow_zero_src)
+{
+       struct rb_node **link = &eht_set->entry_tree.rb_node, *parent = NULL;
+       struct net_bridge_group_eht_set_entry *set_h;
+
+       while (*link) {
+               struct net_bridge_group_eht_set_entry *this;
+               int result;
+
+               this = rb_entry(*link, struct net_bridge_group_eht_set_entry,
+                               rb_node);
+               result = memcmp(&eht_host->h_addr, &this->h_addr,
+                               sizeof(union net_bridge_eht_addr));
+               parent = *link;
+               if (result < 0)
+                       link = &((*link)->rb_left);
+               else if (result > 0)
+                       link = &((*link)->rb_right);
+               else
+                       return this;
+       }
+
+       /* always allow auto-created zero entry */
+       if (!allow_zero_src && eht_host->num_entries >= PG_SRC_ENT_LIMIT)
+               return NULL;
+
+       set_h = kzalloc(sizeof(*set_h), GFP_ATOMIC);
+       if (!set_h)
+               return NULL;
+
+       memcpy(&set_h->h_addr, &eht_host->h_addr,
+              sizeof(union net_bridge_eht_addr));
+       set_h->mcast_gc.destroy = br_multicast_destroy_eht_set_entry;
+       set_h->eht_set = eht_set;
+       set_h->h_parent = eht_host;
+       set_h->br = br;
+       timer_setup(&set_h->timer, br_multicast_eht_set_entry_expired, 0);
+
+       hlist_add_head(&set_h->host_list, &eht_host->set_entries);
+       rb_link_node(&set_h->rb_node, parent, link);
+       rb_insert_color(&set_h->rb_node, &eht_set->entry_tree);
+       /* we must not count the auto-created zero entry otherwise we won't be
+        * able to track the full list of PG_SRC_ENT_LIMIT entries
+        */
+       if (!allow_zero_src)
+               eht_host->num_entries++;
+
+       return set_h;
+}
+
+static struct net_bridge_group_eht_set *
+__eht_lookup_create_set(struct net_bridge_port_group *pg,
+                       union net_bridge_eht_addr *src_addr)
+{
+       struct rb_node **link = &pg->eht_set_tree.rb_node, *parent = NULL;
+       struct net_bridge_group_eht_set *eht_set;
+
+       while (*link) {
+               struct net_bridge_group_eht_set *this;
+               int result;
+
+               this = rb_entry(*link, struct net_bridge_group_eht_set,
+                               rb_node);
+               result = memcmp(src_addr, &this->src_addr, sizeof(*src_addr));
+               parent = *link;
+               if (result < 0)
+                       link = &((*link)->rb_left);
+               else if (result > 0)
+                       link = &((*link)->rb_right);
+               else
+                       return this;
+       }
+
+       eht_set = kzalloc(sizeof(*eht_set), GFP_ATOMIC);
+       if (!eht_set)
+               return NULL;
+
+       memcpy(&eht_set->src_addr, src_addr, sizeof(*src_addr));
+       eht_set->mcast_gc.destroy = br_multicast_destroy_eht_set;
+       eht_set->pg = pg;
+       eht_set->br = pg->key.port->br;
+       eht_set->entry_tree = RB_ROOT;
+       timer_setup(&eht_set->timer, br_multicast_eht_set_expired, 0);
+
+       rb_link_node(&eht_set->rb_node, parent, link);
+       rb_insert_color(&eht_set->rb_node, &pg->eht_set_tree);
+
+       return eht_set;
+}
+
+static void br_multicast_ip_src_to_eht_addr(const struct br_ip *src,
+                                           union net_bridge_eht_addr *dest)
+{
+       switch (src->proto) {
+       case htons(ETH_P_IP):
+               dest->ip4 = src->src.ip4;
+               break;
+#if IS_ENABLED(CONFIG_IPV6)
+       case htons(ETH_P_IPV6):
+               memcpy(&dest->ip6, &src->src.ip6, sizeof(struct in6_addr));
+               break;
+#endif
+       }
+}
+
+static void br_eht_convert_host_filter_mode(struct net_bridge_port_group *pg,
+                                           union net_bridge_eht_addr *h_addr,
+                                           int filter_mode)
+{
+       struct net_bridge_group_eht_host *eht_host;
+       union net_bridge_eht_addr zero_addr;
+
+       eht_host = br_multicast_eht_host_lookup(pg, h_addr);
+       if (eht_host)
+               eht_host->filter_mode = filter_mode;
+
+       memset(&zero_addr, 0, sizeof(zero_addr));
+       switch (filter_mode) {
+       case MCAST_INCLUDE:
+               br_multicast_del_eht_set_entry(pg, &zero_addr, h_addr);
+               break;
+       case MCAST_EXCLUDE:
+               br_multicast_create_eht_set_entry(pg, &zero_addr, h_addr,
+                                                 MCAST_EXCLUDE,
+                                                 true);
+               break;
+       }
+}
+
+static void br_multicast_create_eht_set_entry(struct net_bridge_port_group *pg,
+                                             union net_bridge_eht_addr *src_addr,
+                                             union net_bridge_eht_addr *h_addr,
+                                             int filter_mode,
+                                             bool allow_zero_src)
+{
+       struct net_bridge_group_eht_set_entry *set_h;
+       struct net_bridge_group_eht_host *eht_host;
+       struct net_bridge *br = pg->key.port->br;
+       struct net_bridge_group_eht_set *eht_set;
+       union net_bridge_eht_addr zero_addr;
+
+       memset(&zero_addr, 0, sizeof(zero_addr));
+       if (!allow_zero_src && !memcmp(src_addr, &zero_addr, sizeof(zero_addr)))
+               return;
+
+       eht_set = __eht_lookup_create_set(pg, src_addr);
+       if (!eht_set)
+               return;
+
+       eht_host = __eht_lookup_create_host(pg, h_addr, filter_mode);
+       if (!eht_host)
+               goto fail_host;
+
+       set_h = __eht_lookup_create_set_entry(br, eht_set, eht_host,
+                                             allow_zero_src);
+       if (!set_h)
+               goto fail_set_entry;
+
+       mod_timer(&set_h->timer, jiffies + br_multicast_gmi(br));
+       mod_timer(&eht_set->timer, jiffies + br_multicast_gmi(br));
+
+       return;
+
+fail_set_entry:
+       if (hlist_empty(&eht_host->set_entries))
+               __eht_destroy_host(eht_host);
+fail_host:
+       if (RB_EMPTY_ROOT(&eht_set->entry_tree))
+               br_multicast_del_eht_set(eht_set);
+}
+
+static bool br_multicast_del_eht_set_entry(struct net_bridge_port_group *pg,
+                                          union net_bridge_eht_addr *src_addr,
+                                          union net_bridge_eht_addr *h_addr)
+{
+       struct net_bridge_group_eht_set_entry *set_h;
+       struct net_bridge_group_eht_set *eht_set;
+       bool set_deleted = false;
+
+       eht_set = br_multicast_eht_set_lookup(pg, src_addr);
+       if (!eht_set)
+               goto out;
+
+       set_h = br_multicast_eht_set_entry_lookup(eht_set, h_addr);
+       if (!set_h)
+               goto out;
+
+       __eht_del_set_entry(set_h);
+
+       if (RB_EMPTY_ROOT(&eht_set->entry_tree)) {
+               br_multicast_del_eht_set(eht_set);
+               set_deleted = true;
+       }
+
+out:
+       return set_deleted;
+}
+
+static void br_multicast_del_eht_host(struct net_bridge_port_group *pg,
+                                     union net_bridge_eht_addr *h_addr)
+{
+       struct net_bridge_group_eht_set_entry *set_h;
+       struct net_bridge_group_eht_host *eht_host;
+       struct hlist_node *tmp;
+
+       eht_host = br_multicast_eht_host_lookup(pg, h_addr);
+       if (!eht_host)
+               return;
+
+       hlist_for_each_entry_safe(set_h, tmp, &eht_host->set_entries, host_list)
+               br_multicast_del_eht_set_entry(set_h->eht_set->pg,
+                                              &set_h->eht_set->src_addr,
+                                              &set_h->h_addr);
+}
+
+static void __eht_allow_incl(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            size_t addr_size)
+{
+       union net_bridge_eht_addr eht_src_addr;
+       u32 src_idx;
+
+       memset(&eht_src_addr, 0, sizeof(eht_src_addr));
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
+               br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
+                                                 MCAST_INCLUDE,
+                                                 false);
+       }
+}
+
+static bool __eht_allow_excl(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            size_t addr_size)
+{
+       bool changed = false, host_excl = false;
+       union net_bridge_eht_addr eht_src_addr;
+       struct net_bridge_group_src *src_ent;
+       struct br_ip src_ip;
+       u32 src_idx;
+
+       host_excl = !!(br_multicast_eht_host_filter_mode(pg, h_addr) == MCAST_EXCLUDE);
+       memset(&eht_src_addr, 0, sizeof(eht_src_addr));
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
+               if (!host_excl) {
+                       br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
+                                                         MCAST_INCLUDE,
+                                                         false);
+               } else {
+                       if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr,
+                                                           h_addr))
+                               continue;
+                       memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
+                       src_ent = br_multicast_find_group_src(pg, &src_ip);
+                       if (!src_ent)
+                               continue;
+                       br_multicast_del_group_src(src_ent, true);
+                       changed = true;
+               }
+       }
+
+       return changed;
+}
+
+static bool br_multicast_eht_allow(struct net_bridge_port_group *pg,
+                                  union net_bridge_eht_addr *h_addr,
+                                  void *srcs,
+                                  u32 nsrcs,
+                                  size_t addr_size)
+{
+       bool changed = false;
+
+       switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
+       case MCAST_INCLUDE:
+               __eht_allow_incl(pg, h_addr, srcs, nsrcs, addr_size);
+               break;
+       case MCAST_EXCLUDE:
+               changed = __eht_allow_excl(pg, h_addr, srcs, nsrcs, addr_size);
+               break;
+       }
+
+       return changed;
+}
+
+static bool __eht_block_incl(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            size_t addr_size)
+{
+       union net_bridge_eht_addr eht_src_addr;
+       struct net_bridge_group_src *src_ent;
+       bool changed = false;
+       struct br_ip src_ip;
+       u32 src_idx;
+
+       memset(&eht_src_addr, 0, sizeof(eht_src_addr));
+       memset(&src_ip, 0, sizeof(src_ip));
+       src_ip.proto = pg->key.addr.proto;
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
+               if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr, h_addr))
+                       continue;
+               memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
+               src_ent = br_multicast_find_group_src(pg, &src_ip);
+               if (!src_ent)
+                       continue;
+               br_multicast_del_group_src(src_ent, true);
+               changed = true;
+       }
+
+       return changed;
+}
+
+static bool __eht_block_excl(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            size_t addr_size)
+{
+       bool changed = false, host_excl = false;
+       union net_bridge_eht_addr eht_src_addr;
+       struct net_bridge_group_src *src_ent;
+       struct br_ip src_ip;
+       u32 src_idx;
+
+       host_excl = !!(br_multicast_eht_host_filter_mode(pg, h_addr) == MCAST_EXCLUDE);
+       memset(&eht_src_addr, 0, sizeof(eht_src_addr));
+       memset(&src_ip, 0, sizeof(src_ip));
+       src_ip.proto = pg->key.addr.proto;
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
+               if (host_excl) {
+                       br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
+                                                         MCAST_EXCLUDE,
+                                                         false);
+               } else {
+                       if (!br_multicast_del_eht_set_entry(pg, &eht_src_addr,
+                                                           h_addr))
+                               continue;
+                       memcpy(&src_ip, srcs + (src_idx * addr_size), addr_size);
+                       src_ent = br_multicast_find_group_src(pg, &src_ip);
+                       if (!src_ent)
+                               continue;
+                       br_multicast_del_group_src(src_ent, true);
+                       changed = true;
+               }
+       }
+
+       return changed;
+}
+
+static bool br_multicast_eht_block(struct net_bridge_port_group *pg,
+                                  union net_bridge_eht_addr *h_addr,
+                                  void *srcs,
+                                  u32 nsrcs,
+                                  size_t addr_size)
+{
+       bool changed = false;
+
+       switch (br_multicast_eht_host_filter_mode(pg, h_addr)) {
+       case MCAST_INCLUDE:
+               changed = __eht_block_incl(pg, h_addr, srcs, nsrcs, addr_size);
+               break;
+       case MCAST_EXCLUDE:
+               changed = __eht_block_excl(pg, h_addr, srcs, nsrcs, addr_size);
+               break;
+       }
+
+       return changed;
+}
+
+/* flush_entries is true when changing mode */
+static bool __eht_inc_exc(struct net_bridge_port_group *pg,
+                         union net_bridge_eht_addr *h_addr,
+                         void *srcs,
+                         u32 nsrcs,
+                         size_t addr_size,
+                         unsigned char filter_mode,
+                         bool to_report)
+{
+       bool changed = false, flush_entries = to_report;
+       union net_bridge_eht_addr eht_src_addr;
+       u32 src_idx;
+
+       if (br_multicast_eht_host_filter_mode(pg, h_addr) != filter_mode)
+               flush_entries = true;
+
+       memset(&eht_src_addr, 0, sizeof(eht_src_addr));
+       /* if we're changing mode del host and its entries */
+       if (flush_entries)
+               br_multicast_del_eht_host(pg, h_addr);
+       for (src_idx = 0; src_idx < nsrcs; src_idx++) {
+               memcpy(&eht_src_addr, srcs + (src_idx * addr_size), addr_size);
+               br_multicast_create_eht_set_entry(pg, &eht_src_addr, h_addr,
+                                                 filter_mode, false);
+       }
+       /* we can be missing sets only if we've deleted some entries */
+       if (flush_entries) {
+               struct net_bridge *br = pg->key.port->br;
+               struct net_bridge_group_eht_set *eht_set;
+               struct net_bridge_group_src *src_ent;
+               struct hlist_node *tmp;
+
+               hlist_for_each_entry_safe(src_ent, tmp, &pg->src_list, node) {
+                       br_multicast_ip_src_to_eht_addr(&src_ent->addr,
+                                                       &eht_src_addr);
+                       if (!br_multicast_eht_set_lookup(pg, &eht_src_addr)) {
+                               br_multicast_del_group_src(src_ent, true);
+                               changed = true;
+                               continue;
+                       }
+                       /* this is an optimization for TO_INCLUDE where we lower
+                        * the set's timeout to LMQT to catch timeout hosts:
+                        * - host A (timing out): set entries X, Y
+                        * - host B: set entry Z (new from current TO_INCLUDE)
+                        *           sends BLOCK Z after LMQT but host A's EHT
+                        *           entries still exist (unless lowered to LMQT
+                        *           so they can timeout with the S,Gs)
+                        * => we wait another LMQT, when we can just delete the
+                        *    group immediately
+                        */
+                       if (!(src_ent->flags & BR_SGRP_F_SEND) ||
+                           filter_mode != MCAST_INCLUDE ||
+                           !to_report)
+                               continue;
+                       eht_set = br_multicast_eht_set_lookup(pg,
+                                                             &eht_src_addr);
+                       if (!eht_set)
+                               continue;
+                       mod_timer(&eht_set->timer, jiffies + br_multicast_lmqt(br));
+               }
+       }
+
+       return changed;
+}
+
+static bool br_multicast_eht_inc(struct net_bridge_port_group *pg,
+                                union net_bridge_eht_addr *h_addr,
+                                void *srcs,
+                                u32 nsrcs,
+                                size_t addr_size,
+                                bool to_report)
+{
+       bool changed;
+
+       changed = __eht_inc_exc(pg, h_addr, srcs, nsrcs, addr_size,
+                               MCAST_INCLUDE, to_report);
+       br_eht_convert_host_filter_mode(pg, h_addr, MCAST_INCLUDE);
+
+       return changed;
+}
+
+static bool br_multicast_eht_exc(struct net_bridge_port_group *pg,
+                                union net_bridge_eht_addr *h_addr,
+                                void *srcs,
+                                u32 nsrcs,
+                                size_t addr_size,
+                                bool to_report)
+{
+       bool changed;
+
+       changed = __eht_inc_exc(pg, h_addr, srcs, nsrcs, addr_size,
+                               MCAST_EXCLUDE, to_report);
+       br_eht_convert_host_filter_mode(pg, h_addr, MCAST_EXCLUDE);
+
+       return changed;
+}
+
+static bool __eht_ip4_handle(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            int grec_type)
+{
+       bool changed = false, to_report = false;
+
+       switch (grec_type) {
+       case IGMPV3_ALLOW_NEW_SOURCES:
+               br_multicast_eht_allow(pg, h_addr, srcs, nsrcs, sizeof(__be32));
+               break;
+       case IGMPV3_BLOCK_OLD_SOURCES:
+               changed = br_multicast_eht_block(pg, h_addr, srcs, nsrcs,
+                                                sizeof(__be32));
+               break;
+       case IGMPV3_CHANGE_TO_INCLUDE:
+               to_report = true;
+               fallthrough;
+       case IGMPV3_MODE_IS_INCLUDE:
+               changed = br_multicast_eht_inc(pg, h_addr, srcs, nsrcs,
+                                              sizeof(__be32), to_report);
+               break;
+       case IGMPV3_CHANGE_TO_EXCLUDE:
+               to_report = true;
+               fallthrough;
+       case IGMPV3_MODE_IS_EXCLUDE:
+               changed = br_multicast_eht_exc(pg, h_addr, srcs, nsrcs,
+                                              sizeof(__be32), to_report);
+               break;
+       }
+
+       return changed;
+}
+
+#if IS_ENABLED(CONFIG_IPV6)
+static bool __eht_ip6_handle(struct net_bridge_port_group *pg,
+                            union net_bridge_eht_addr *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            int grec_type)
+{
+       bool changed = false, to_report = false;
+
+       switch (grec_type) {
+       case MLD2_ALLOW_NEW_SOURCES:
+               br_multicast_eht_allow(pg, h_addr, srcs, nsrcs,
+                                      sizeof(struct in6_addr));
+               break;
+       case MLD2_BLOCK_OLD_SOURCES:
+               changed = br_multicast_eht_block(pg, h_addr, srcs, nsrcs,
+                                                sizeof(struct in6_addr));
+               break;
+       case MLD2_CHANGE_TO_INCLUDE:
+               to_report = true;
+               fallthrough;
+       case MLD2_MODE_IS_INCLUDE:
+               changed = br_multicast_eht_inc(pg, h_addr, srcs, nsrcs,
+                                              sizeof(struct in6_addr),
+                                              to_report);
+               break;
+       case MLD2_CHANGE_TO_EXCLUDE:
+               to_report = true;
+               fallthrough;
+       case MLD2_MODE_IS_EXCLUDE:
+               changed = br_multicast_eht_exc(pg, h_addr, srcs, nsrcs,
+                                              sizeof(struct in6_addr),
+                                              to_report);
+               break;
+       }
+
+       return changed;
+}
+#endif
+
+/* true means an entry was deleted */
+bool br_multicast_eht_handle(struct net_bridge_port_group *pg,
+                            void *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            size_t addr_size,
+                            int grec_type)
+{
+       bool eht_enabled = !!(pg->key.port->flags & BR_MULTICAST_FAST_LEAVE);
+       union net_bridge_eht_addr eht_host_addr;
+       bool changed = false;
+
+       if (!eht_enabled)
+               goto out;
+
+       memset(&eht_host_addr, 0, sizeof(eht_host_addr));
+       memcpy(&eht_host_addr, h_addr, addr_size);
+       if (addr_size == sizeof(__be32))
+               changed = __eht_ip4_handle(pg, &eht_host_addr, srcs, nsrcs,
+                                          grec_type);
+#if IS_ENABLED(CONFIG_IPV6)
+       else
+               changed = __eht_ip6_handle(pg, &eht_host_addr, srcs, nsrcs,
+                                          grec_type);
+#endif
+
+out:
+       return changed;
+}
+
+int br_multicast_eht_set_hosts_limit(struct net_bridge_port *p,
+                                    u32 eht_hosts_limit)
+{
+       struct net_bridge *br = p->br;
+
+       if (!eht_hosts_limit)
+               return -EINVAL;
+
+       spin_lock_bh(&br->multicast_lock);
+       p->multicast_eht_hosts_limit = eht_hosts_limit;
+       spin_unlock_bh(&br->multicast_lock);
+
+       return 0;
+}
index 49700ce..bd3962d 100644 (file)
@@ -18,6 +18,7 @@
 #include "br_private_stp.h"
 #include "br_private_cfm.h"
 #include "br_private_tunnel.h"
+#include "br_private_mcast_eht.h"
 
 static int __get_num_vlan_infos(struct net_bridge_vlan_group *vg,
                                u32 filter_mask)
@@ -199,6 +200,8 @@ static inline size_t br_port_info_size(void)
                + nla_total_size(sizeof(u16))   /* IFLA_BRPORT_GROUP_FWD_MASK */
                + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_MRP_RING_OPEN */
                + nla_total_size(sizeof(u8))    /* IFLA_BRPORT_MRP_IN_OPEN */
+               + nla_total_size(sizeof(u32))   /* IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT */
+               + nla_total_size(sizeof(u32))   /* IFLA_BRPORT_MCAST_EHT_HOSTS_CNT */
                + 0;
 }
 
@@ -283,7 +286,11 @@ static int br_port_fill_attrs(struct sk_buff *skb,
 
 #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
        if (nla_put_u8(skb, IFLA_BRPORT_MULTICAST_ROUTER,
-                      p->multicast_router))
+                      p->multicast_router) ||
+           nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
+                       p->multicast_eht_hosts_limit) ||
+           nla_put_u32(skb, IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
+                       p->multicast_eht_hosts_cnt))
                return -EMSGSIZE;
 #endif
 
@@ -820,6 +827,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
        [IFLA_BRPORT_NEIGH_SUPPRESS] = { .type = NLA_U8 },
        [IFLA_BRPORT_ISOLATED]  = { .type = NLA_U8 },
        [IFLA_BRPORT_BACKUP_PORT] = { .type = NLA_U32 },
+       [IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT] = { .type = NLA_U32 },
 };
 
 /* Change the state of the port and notify spanning tree */
@@ -955,6 +963,15 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
                if (err)
                        return err;
        }
+
+       if (tb[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT]) {
+               u32 hlimit;
+
+               hlimit = nla_get_u32(tb[IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT]);
+               err = br_multicast_eht_set_hosts_limit(p, hlimit);
+               if (err)
+                       return err;
+       }
 #endif
 
        if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
@@ -1096,15 +1113,9 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[],
                return 0;
 
 #ifdef CONFIG_BRIDGE_VLAN_FILTERING
-       if (data[IFLA_BR_VLAN_PROTOCOL]) {
-               switch (nla_get_be16(data[IFLA_BR_VLAN_PROTOCOL])) {
-               case htons(ETH_P_8021Q):
-               case htons(ETH_P_8021AD):
-                       break;
-               default:
-                       return -EPROTONOSUPPORT;
-               }
-       }
+       if (data[IFLA_BR_VLAN_PROTOCOL] &&
+           !eth_type_vlan(nla_get_be16(data[IFLA_BR_VLAN_PROTOCOL])))
+               return -EPROTONOSUPPORT;
 
        if (data[IFLA_BR_VLAN_DEFAULT_PVID]) {
                __u16 defpvid = nla_get_u16(data[IFLA_BR_VLAN_DEFAULT_PVID]);
index d62c6e1..d242ba6 100644 (file)
@@ -252,6 +252,8 @@ struct net_bridge_port_group {
        struct timer_list               timer;
        struct timer_list               rexmit_timer;
        struct hlist_node               mglist;
+       struct rb_root                  eht_set_tree;
+       struct rb_root                  eht_host_tree;
 
        struct rhash_head               rhnode;
        struct net_bridge_mcast_gc      mcast_gc;
@@ -308,6 +310,8 @@ struct net_bridge_port {
 #if IS_ENABLED(CONFIG_IPV6)
        struct bridge_mcast_own_query   ip6_own_query;
 #endif /* IS_ENABLED(CONFIG_IPV6) */
+       u32                             multicast_eht_hosts_limit;
+       u32                             multicast_eht_hosts_cnt;
        unsigned char                   multicast_router;
        struct bridge_mcast_stats       __percpu *mcast_stats;
        struct timer_list               multicast_router_timer;
@@ -846,6 +850,10 @@ void br_multicast_star_g_handle_mode(struct net_bridge_port_group *pg,
                                     u8 filter_mode);
 void br_multicast_sg_add_exclude_ports(struct net_bridge_mdb_entry *star_mp,
                                       struct net_bridge_port_group *sg);
+struct net_bridge_group_src *
+br_multicast_find_group_src(struct net_bridge_port_group *pg, struct br_ip *ip);
+void br_multicast_del_group_src(struct net_bridge_group_src *src,
+                               bool fastleave);
 
 static inline bool br_group_is_l2(const struct br_ip *group)
 {
diff --git a/net/bridge/br_private_mcast_eht.h b/net/bridge/br_private_mcast_eht.h
new file mode 100644 (file)
index 0000000..f89049f
--- /dev/null
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (c) 2020, Nikolay Aleksandrov <nikolay@nvidia.com>
+ */
+#ifndef _BR_PRIVATE_MCAST_EHT_H_
+#define _BR_PRIVATE_MCAST_EHT_H_
+
+#define BR_MCAST_DEFAULT_EHT_HOSTS_LIMIT 512
+
+union net_bridge_eht_addr {
+       __be32                          ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+       struct in6_addr                 ip6;
+#endif
+};
+
+/* single host's list of set entries and filter_mode */
+struct net_bridge_group_eht_host {
+       struct rb_node                  rb_node;
+
+       union net_bridge_eht_addr       h_addr;
+       struct hlist_head               set_entries;
+       unsigned int                    num_entries;
+       unsigned char                   filter_mode;
+       struct net_bridge_port_group    *pg;
+};
+
+/* (host, src entry) added to a per-src set and host's list */
+struct net_bridge_group_eht_set_entry {
+       struct rb_node                  rb_node;
+       struct hlist_node               host_list;
+
+       union net_bridge_eht_addr       h_addr;
+       struct timer_list               timer;
+       struct net_bridge               *br;
+       struct net_bridge_group_eht_set *eht_set;
+       struct net_bridge_group_eht_host *h_parent;
+       struct net_bridge_mcast_gc      mcast_gc;
+};
+
+/* per-src set */
+struct net_bridge_group_eht_set {
+       struct rb_node                  rb_node;
+
+       union net_bridge_eht_addr       src_addr;
+       struct rb_root                  entry_tree;
+       struct timer_list               timer;
+       struct net_bridge_port_group    *pg;
+       struct net_bridge               *br;
+       struct net_bridge_mcast_gc      mcast_gc;
+};
+
+#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+void br_multicast_eht_clean_sets(struct net_bridge_port_group *pg);
+bool br_multicast_eht_handle(struct net_bridge_port_group *pg,
+                            void *h_addr,
+                            void *srcs,
+                            u32 nsrcs,
+                            size_t addr_size,
+                            int grec_type);
+int br_multicast_eht_set_hosts_limit(struct net_bridge_port *p,
+                                    u32 eht_hosts_limit);
+
+static inline bool
+br_multicast_eht_should_del_pg(const struct net_bridge_port_group *pg)
+{
+       return !!((pg->key.port->flags & BR_MULTICAST_FAST_LEAVE) &&
+                 RB_EMPTY_ROOT(&pg->eht_host_tree));
+}
+
+static inline bool
+br_multicast_eht_hosts_over_limit(const struct net_bridge_port_group *pg)
+{
+       const struct net_bridge_port *p = pg->key.port;
+
+       return !!(p->multicast_eht_hosts_cnt >= p->multicast_eht_hosts_limit);
+}
+
+static inline void br_multicast_eht_hosts_inc(struct net_bridge_port_group *pg)
+{
+       struct net_bridge_port *p = pg->key.port;
+
+       p->multicast_eht_hosts_cnt++;
+}
+
+static inline void br_multicast_eht_hosts_dec(struct net_bridge_port_group *pg)
+{
+       struct net_bridge_port *p = pg->key.port;
+
+       p->multicast_eht_hosts_cnt--;
+}
+#endif /* CONFIG_BRIDGE_IGMP_SNOOPING */
+
+#endif /* _BR_PRIVATE_MCAST_EHT_H_ */
index 1883118..32a48e5 100644 (file)
@@ -88,4 +88,33 @@ int br_mrp_switchdev_send_in_test(struct net_bridge *br, struct br_mrp *mrp,
 int br_mrp_ring_port_open(struct net_device *dev, u8 loc);
 int br_mrp_in_port_open(struct net_device *dev, u8 loc);
 
+/* MRP protocol data units */
+struct br_mrp_tlv_hdr {
+       __u8 type;
+       __u8 length;
+};
+
+struct br_mrp_common_hdr {
+       __be16 seq_id;
+       __u8 domain[MRP_DOMAIN_UUID_LENGTH];
+};
+
+struct br_mrp_ring_test_hdr {
+       __be16 prio;
+       __u8 sa[ETH_ALEN];
+       __be16 port_role;
+       __be16 state;
+       __be16 transitions;
+       __be32 timestamp;
+} __attribute__((__packed__));
+
+struct br_mrp_in_test_hdr {
+       __be16 id;
+       __u8 sa[ETH_ALEN];
+       __be16 port_role;
+       __be16 state;
+       __be16 transitions;
+       __be32 timestamp;
+} __attribute__((__packed__));
+
 #endif /* _BR_PRIVATE_MRP_H */
index 3e88be7..a3a5745 100644 (file)
@@ -601,8 +601,8 @@ int __set_ageing_time(struct net_device *dev, unsigned long t)
 /* Set time interval that dynamic forwarding entries live
  * For pure software bridge, allow values outside the 802.1
  * standard specification for special cases:
- *  0 - entry never ages (all permanant)
- *  1 - entry disappears (no persistance)
+ *  0 - entry never ages (all permanent)
+ *  1 - entry disappears (no persistence)
  *
  * Offloaded switch entries maybe more restrictive
  */
index 015209b..a9c23ef 100644 (file)
@@ -153,8 +153,7 @@ int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
                .obj.orig_dev = dev,
                .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
                .flags = flags,
-               .vid_begin = vid,
-               .vid_end = vid,
+               .vid = vid,
        };
 
        return switchdev_port_obj_add(dev, &v.obj, extack);
@@ -165,8 +164,7 @@ int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
        struct switchdev_obj_port_vlan v = {
                .obj.orig_dev = dev,
                .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
-               .vid_begin = vid,
-               .vid_end = vid,
+               .vid = vid,
        };
 
        return switchdev_port_obj_del(dev, &v.obj);
index 7a59cdd..b66305f 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/sched/signal.h>
 
 #include "br_private.h"
+#include "br_private_mcast_eht.h"
 
 struct brport_attribute {
        struct attribute        attr;
@@ -245,6 +246,29 @@ static int store_multicast_router(struct net_bridge_port *p,
 static BRPORT_ATTR(multicast_router, 0644, show_multicast_router,
                   store_multicast_router);
 
+static ssize_t show_multicast_eht_hosts_limit(struct net_bridge_port *p,
+                                             char *buf)
+{
+       return sprintf(buf, "%u\n", p->multicast_eht_hosts_limit);
+}
+
+static int store_multicast_eht_hosts_limit(struct net_bridge_port *p,
+                                          unsigned long v)
+{
+       return br_multicast_eht_set_hosts_limit(p, v);
+}
+static BRPORT_ATTR(multicast_eht_hosts_limit, 0644,
+                  show_multicast_eht_hosts_limit,
+                  store_multicast_eht_hosts_limit);
+
+static ssize_t show_multicast_eht_hosts_cnt(struct net_bridge_port *p,
+                                           char *buf)
+{
+       return sprintf(buf, "%u\n", p->multicast_eht_hosts_cnt);
+}
+static BRPORT_ATTR(multicast_eht_hosts_cnt, 0444, show_multicast_eht_hosts_cnt,
+                  NULL);
+
 BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE);
 BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST);
 #endif
@@ -274,6 +298,8 @@ static const struct brport_attribute *brport_attrs[] = {
        &brport_attr_multicast_router,
        &brport_attr_multicast_fast_leave,
        &brport_attr_multicast_to_unicast,
+       &brport_attr_multicast_eht_hosts_limit,
+       &brport_attr_multicast_eht_hosts_cnt,
 #endif
        &brport_attr_proxyarp,
        &brport_attr_proxyarp_wifi,
index 701cad6..bb29097 100644 (file)
@@ -917,7 +917,7 @@ err_filt:
 
 int br_vlan_set_proto(struct net_bridge *br, unsigned long val)
 {
-       if (val != ETH_P_8021Q && val != ETH_P_8021AD)
+       if (!eth_type_vlan(htons(val)))
                return -EPROTONOSUPPORT;
 
        return __br_vlan_set_proto(br, htons(val));
index 7c9958d..a9ac5ff 100644 (file)
@@ -4,7 +4,6 @@
 #
 
 menuconfig CAN
-       depends on NET
        tristate "CAN bus subsystem support"
        help
          Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
index 8598d9d..ba41248 100644 (file)
@@ -225,7 +225,7 @@ static void mod_store_ccdlc(struct canfd_frame *cf)
        if (ccf->len <= CAN_MAX_DLEN)
                return;
 
-       /* potentially broken values are catched in can_can_gw_rcv() */
+       /* potentially broken values are caught in can_can_gw_rcv() */
        if (ccf->len > CAN_MAX_RAW_DLC)
                return;
 
index 7839c3b..3ef7f78 100644 (file)
@@ -1155,6 +1155,7 @@ static int isotp_getname(struct socket *sock, struct sockaddr *uaddr, int peer)
        if (peer)
                return -EOPNOTSUPP;
 
+       memset(addr, 0, sizeof(*addr));
        addr->can_family = AF_CAN;
        addr->can_ifindex = so->ifindex;
        addr->can_addr.tp.rx_id = so->rxid;
index 6ec8aa1..37b47a3 100644 (file)
@@ -665,10 +665,18 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
                if (ro->count > 0) {
                        int fsize = ro->count * sizeof(struct can_filter);
 
-                       if (len > fsize)
-                               len = fsize;
-                       if (copy_to_user(optval, ro->filter, len))
-                               err = -EFAULT;
+                       /* user space buffer to small for filter list? */
+                       if (len < fsize) {
+                               /* return -ERANGE and needed space in optlen */
+                               err = -ERANGE;
+                               if (put_user(fsize, optlen))
+                                       err = -EFAULT;
+                       } else {
+                               if (len > fsize)
+                                       len = fsize;
+                               if (copy_to_user(optval, ro->filter, len))
+                                       err = -EFAULT;
+                       }
                } else {
                        len = 0;
                }
index 9815cfe..ca44c32 100644 (file)
@@ -569,6 +569,34 @@ e_range:
        return -ERANGE;
 }
 
+static int decode_con_secret(void **p, void *end, u8 *con_secret,
+                            int *con_secret_len)
+{
+       int len;
+
+       ceph_decode_32_safe(p, end, len, bad);
+       ceph_decode_need(p, end, len, bad);
+
+       dout("%s len %d\n", __func__, len);
+       if (con_secret) {
+               if (len > CEPH_MAX_CON_SECRET_LEN) {
+                       pr_err("connection secret too big %d\n", len);
+                       goto bad_memzero;
+               }
+               memcpy(con_secret, *p, len);
+               *con_secret_len = len;
+       }
+       memzero_explicit(*p, len);
+       *p += len;
+       return 0;
+
+bad_memzero:
+       memzero_explicit(*p, len);
+bad:
+       pr_err("failed to decode connection secret\n");
+       return -EINVAL;
+}
+
 static int handle_auth_session_key(struct ceph_auth_client *ac,
                                   void **p, void *end,
                                   u8 *session_key, int *session_key_len,
@@ -612,17 +640,9 @@ static int handle_auth_session_key(struct ceph_auth_client *ac,
                dout("%s decrypted %d bytes\n", __func__, ret);
                dend = dp + ret;
 
-               ceph_decode_32_safe(&dp, dend, len, e_inval);
-               if (len > CEPH_MAX_CON_SECRET_LEN) {
-                       pr_err("connection secret too big %d\n", len);
-                       return -EINVAL;
-               }
-
-               dout("%s connection secret len %d\n", __func__, len);
-               if (con_secret) {
-                       memcpy(con_secret, dp, len);
-                       *con_secret_len = len;
-               }
+               ret = decode_con_secret(&dp, dend, con_secret, con_secret_len);
+               if (ret)
+                       return ret;
        }
 
        /* service tickets */
@@ -828,7 +848,6 @@ static int decrypt_authorizer_reply(struct ceph_crypto_key *secret,
 {
        void *dp, *dend;
        u8 struct_v;
-       int len;
        int ret;
 
        dp = *p + ceph_x_encrypt_offset();
@@ -843,17 +862,9 @@ static int decrypt_authorizer_reply(struct ceph_crypto_key *secret,
        ceph_decode_64_safe(&dp, dend, *nonce_plus_one, e_inval);
        dout("%s nonce_plus_one %llu\n", __func__, *nonce_plus_one);
        if (struct_v >= 2) {
-               ceph_decode_32_safe(&dp, dend, len, e_inval);
-               if (len > CEPH_MAX_CON_SECRET_LEN) {
-                       pr_err("connection secret too big %d\n", len);
-                       return -EINVAL;
-               }
-
-               dout("%s connection secret len %d\n", __func__, len);
-               if (con_secret) {
-                       memcpy(con_secret, dp, len);
-                       *con_secret_len = len;
-               }
+               ret = decode_con_secret(&dp, dend, con_secret, con_secret_len);
+               if (ret)
+                       return ret;
        }
 
        return 0;
index 4f75df4..92d89b3 100644 (file)
@@ -96,6 +96,7 @@ int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end)
        key->len = ceph_decode_16(p);
        ceph_decode_need(p, end, key->len, bad);
        ret = set_secret(key, *p);
+       memzero_explicit(*p, key->len);
        *p += key->len;
        return ret;
 
@@ -134,7 +135,7 @@ int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey)
 void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
 {
        if (key) {
-               kfree(key->key);
+               kfree_sensitive(key->key);
                key->key = NULL;
                if (key->tfm) {
                        crypto_free_sync_skcipher(key->tfm);
index 04f653b..2cb5ffd 100644 (file)
@@ -1100,7 +1100,7 @@ static int read_partial_message(struct ceph_connection *con)
                if (ret < 0)
                        return ret;
 
-               BUG_ON(!con->in_msg ^ skip);
+               BUG_ON((!con->in_msg) ^ skip);
                if (skip) {
                        /* skip this message */
                        dout("alloc_msg said skip message\n");
index c1ebb2a..cc40ce4 100644 (file)
@@ -689,11 +689,10 @@ static int verify_epilogue_crcs(struct ceph_connection *con, u32 front_crc,
 }
 
 static int setup_crypto(struct ceph_connection *con,
-                       u8 *session_key, int session_key_len,
-                       u8 *con_secret, int con_secret_len)
+                       const u8 *session_key, int session_key_len,
+                       const u8 *con_secret, int con_secret_len)
 {
        unsigned int noio_flag;
-       void *p;
        int ret;
 
        dout("%s con %p con_mode %d session_key_len %d con_secret_len %d\n",
@@ -751,15 +750,14 @@ static int setup_crypto(struct ceph_connection *con,
                return ret;
        }
 
-       p = con_secret;
-       WARN_ON((unsigned long)p & crypto_aead_alignmask(con->v2.gcm_tfm));
-       ret = crypto_aead_setkey(con->v2.gcm_tfm, p, CEPH_GCM_KEY_LEN);
+       WARN_ON((unsigned long)con_secret &
+               crypto_aead_alignmask(con->v2.gcm_tfm));
+       ret = crypto_aead_setkey(con->v2.gcm_tfm, con_secret, CEPH_GCM_KEY_LEN);
        if (ret) {
                pr_err("failed to set gcm key: %d\n", ret);
                return ret;
        }
 
-       p += CEPH_GCM_KEY_LEN;
        WARN_ON(crypto_aead_ivsize(con->v2.gcm_tfm) != CEPH_GCM_IV_LEN);
        ret = crypto_aead_setauthsize(con->v2.gcm_tfm, CEPH_GCM_TAG_LEN);
        if (ret) {
@@ -777,8 +775,11 @@ static int setup_crypto(struct ceph_connection *con,
        aead_request_set_callback(con->v2.gcm_req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                                  crypto_req_done, &con->v2.gcm_wait);
 
-       memcpy(&con->v2.in_gcm_nonce, p, CEPH_GCM_IV_LEN);
-       memcpy(&con->v2.out_gcm_nonce, p + CEPH_GCM_IV_LEN, CEPH_GCM_IV_LEN);
+       memcpy(&con->v2.in_gcm_nonce, con_secret + CEPH_GCM_KEY_LEN,
+              CEPH_GCM_IV_LEN);
+       memcpy(&con->v2.out_gcm_nonce,
+              con_secret + CEPH_GCM_KEY_LEN + CEPH_GCM_IV_LEN,
+              CEPH_GCM_IV_LEN);
        return 0;  /* auth_x, secure mode */
 }
 
@@ -800,7 +801,7 @@ static int hmac_sha256(struct ceph_connection *con, const struct kvec *kvecs,
        desc->tfm = con->v2.hmac_tfm;
        ret = crypto_shash_init(desc);
        if (ret)
-               return ret;
+               goto out;
 
        for (i = 0; i < kvec_cnt; i++) {
                WARN_ON((unsigned long)kvecs[i].iov_base &
@@ -808,15 +809,14 @@ static int hmac_sha256(struct ceph_connection *con, const struct kvec *kvecs,
                ret = crypto_shash_update(desc, kvecs[i].iov_base,
                                          kvecs[i].iov_len);
                if (ret)
-                       return ret;
+                       goto out;
        }
 
        ret = crypto_shash_final(desc, hmac);
-       if (ret)
-               return ret;
 
+out:
        shash_desc_zero(desc);
-       return 0;  /* auth_x, both plain and secure modes */
+       return ret;  /* auth_x, both plain and secure modes */
 }
 
 static void gcm_inc_nonce(struct ceph_gcm_nonce *nonce)
@@ -1333,7 +1333,8 @@ static int prepare_auth_signature(struct ceph_connection *con)
        void *buf;
        int ret;
 
-       buf = alloc_conn_buf(con, head_onwire_len(SHA256_DIGEST_SIZE, false));
+       buf = alloc_conn_buf(con, head_onwire_len(SHA256_DIGEST_SIZE,
+                                                 con_secure(con)));
        if (!buf)
                return -ENOMEM;
 
@@ -2032,10 +2033,18 @@ bad:
        return -EINVAL;
 }
 
+/*
+ * Align session_key and con_secret to avoid GFP_ATOMIC allocation
+ * inside crypto_shash_setkey() and crypto_aead_setkey() called from
+ * setup_crypto().  __aligned(16) isn't guaranteed to work for stack
+ * objects, so do it by hand.
+ */
 static int process_auth_done(struct ceph_connection *con, void *p, void *end)
 {
-       u8 session_key[CEPH_KEY_LEN];
-       u8 con_secret[CEPH_MAX_CON_SECRET_LEN];
+       u8 session_key_buf[CEPH_KEY_LEN + 16];
+       u8 con_secret_buf[CEPH_MAX_CON_SECRET_LEN + 16];
+       u8 *session_key = PTR_ALIGN(&session_key_buf[0], 16);
+       u8 *con_secret = PTR_ALIGN(&con_secret_buf[0], 16);
        int session_key_len, con_secret_len;
        int payload_len;
        u64 global_id;
@@ -2063,27 +2072,32 @@ static int process_auth_done(struct ceph_connection *con, void *p, void *end)
        if (con->state != CEPH_CON_S_V2_AUTH) {
                dout("%s con %p state changed to %d\n", __func__, con,
                     con->state);
-               return -EAGAIN;
+               ret = -EAGAIN;
+               goto out;
        }
 
        dout("%s con %p handle_auth_done ret %d\n", __func__, con, ret);
        if (ret)
-               return ret;
+               goto out;
 
        ret = setup_crypto(con, session_key, session_key_len, con_secret,
                           con_secret_len);
        if (ret)
-               return ret;
+               goto out;
 
        reset_out_kvecs(con);
        ret = prepare_auth_signature(con);
        if (ret) {
                pr_err("prepare_auth_signature failed: %d\n", ret);
-               return ret;
+               goto out;
        }
 
        con->state = CEPH_CON_S_V2_AUTH_SIGNATURE;
-       return 0;
+
+out:
+       memzero_explicit(session_key_buf, sizeof(session_key_buf));
+       memzero_explicit(con_secret_buf, sizeof(con_secret_buf));
+       return ret;
 
 bad:
        pr_err("failed to decode auth_done\n");
@@ -3427,6 +3441,8 @@ void ceph_con_v2_reset_protocol(struct ceph_connection *con)
        }
 
        con->v2.con_mode = CEPH_CON_MODE_UNKNOWN;
+       memzero_explicit(&con->v2.in_gcm_nonce, CEPH_GCM_IV_LEN);
+       memzero_explicit(&con->v2.out_gcm_nonce, CEPH_GCM_IV_LEN);
 
        if (con->v2.hmac_tfm) {
                crypto_free_shash(con->v2.hmac_tfm);
index b9d54ed..195ceb8 100644 (file)
@@ -1433,7 +1433,7 @@ static int mon_handle_auth_bad_method(struct ceph_connection *con,
 /*
  * handle incoming message
  */
-static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
+static void mon_dispatch(struct ceph_connection *con, struct ceph_msg *msg)
 {
        struct ceph_mon_client *monc = con->private;
        int type = le16_to_cpu(msg->hdr.type);
@@ -1565,21 +1565,21 @@ static void mon_fault(struct ceph_connection *con)
  * will come from the messenger workqueue, which is drained prior to
  * mon_client destruction.
  */
-static struct ceph_connection *con_get(struct ceph_connection *con)
+static struct ceph_connection *mon_get_con(struct ceph_connection *con)
 {
        return con;
 }
 
-static void con_put(struct ceph_connection *con)
+static void mon_put_con(struct ceph_connection *con)
 {
 }
 
 static const struct ceph_connection_operations mon_con_ops = {
-       .get = con_get,
-       .put = con_put,
-       .dispatch = dispatch,
-       .fault = mon_fault,
+       .get = mon_get_con,
+       .put = mon_put_con,
        .alloc_msg = mon_alloc_msg,
+       .dispatch = mon_dispatch,
+       .fault = mon_fault,
        .get_auth_request = mon_get_auth_request,
        .handle_auth_reply_more = mon_handle_auth_reply_more,
        .handle_auth_done = mon_handle_auth_done,
index 61229c5..ff8624a 100644 (file)
@@ -5412,7 +5412,7 @@ void ceph_osdc_cleanup(void)
 /*
  * handle incoming message
  */
-static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
+static void osd_dispatch(struct ceph_connection *con, struct ceph_msg *msg)
 {
        struct ceph_osd *osd = con->private;
        struct ceph_osd_client *osdc = osd->o_osdc;
@@ -5534,9 +5534,9 @@ static struct ceph_msg *alloc_msg_with_page_vector(struct ceph_msg_header *hdr)
        return m;
 }
 
-static struct ceph_msg *alloc_msg(struct ceph_connection *con,
-                                 struct ceph_msg_header *hdr,
-                                 int *skip)
+static struct ceph_msg *osd_alloc_msg(struct ceph_connection *con,
+                                     struct ceph_msg_header *hdr,
+                                     int *skip)
 {
        struct ceph_osd *osd = con->private;
        int type = le16_to_cpu(hdr->type);
@@ -5560,7 +5560,7 @@ static struct ceph_msg *alloc_msg(struct ceph_connection *con,
 /*
  * Wrappers to refcount containing ceph_osd struct
  */
-static struct ceph_connection *get_osd_con(struct ceph_connection *con)
+static struct ceph_connection *osd_get_con(struct ceph_connection *con)
 {
        struct ceph_osd *osd = con->private;
        if (get_osd(osd))
@@ -5568,7 +5568,7 @@ static struct ceph_connection *get_osd_con(struct ceph_connection *con)
        return NULL;
 }
 
-static void put_osd_con(struct ceph_connection *con)
+static void osd_put_con(struct ceph_connection *con)
 {
        struct ceph_osd *osd = con->private;
        put_osd(osd);
@@ -5582,8 +5582,8 @@ static void put_osd_con(struct ceph_connection *con)
  * Note: returned pointer is the address of a structure that's
  * managed separately.  Caller must *not* attempt to free it.
  */
-static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
-                                       int *proto, int force_new)
+static struct ceph_auth_handshake *
+osd_get_authorizer(struct ceph_connection *con, int *proto, int force_new)
 {
        struct ceph_osd *o = con->private;
        struct ceph_osd_client *osdc = o->o_osdc;
@@ -5599,7 +5599,7 @@ static struct ceph_auth_handshake *get_authorizer(struct ceph_connection *con,
        return auth;
 }
 
-static int add_authorizer_challenge(struct ceph_connection *con,
+static int osd_add_authorizer_challenge(struct ceph_connection *con,
                                    void *challenge_buf, int challenge_buf_len)
 {
        struct ceph_osd *o = con->private;
@@ -5610,7 +5610,7 @@ static int add_authorizer_challenge(struct ceph_connection *con,
                                            challenge_buf, challenge_buf_len);
 }
 
-static int verify_authorizer_reply(struct ceph_connection *con)
+static int osd_verify_authorizer_reply(struct ceph_connection *con)
 {
        struct ceph_osd *o = con->private;
        struct ceph_osd_client *osdc = o->o_osdc;
@@ -5622,7 +5622,7 @@ static int verify_authorizer_reply(struct ceph_connection *con)
                NULL, NULL, NULL, NULL);
 }
 
-static int invalidate_authorizer(struct ceph_connection *con)
+static int osd_invalidate_authorizer(struct ceph_connection *con)
 {
        struct ceph_osd *o = con->private;
        struct ceph_osd_client *osdc = o->o_osdc;
@@ -5731,18 +5731,18 @@ static int osd_check_message_signature(struct ceph_msg *msg)
 }
 
 static const struct ceph_connection_operations osd_con_ops = {
-       .get = get_osd_con,
-       .put = put_osd_con,
-       .dispatch = dispatch,
-       .get_authorizer = get_authorizer,
-       .add_authorizer_challenge = add_authorizer_challenge,
-       .verify_authorizer_reply = verify_authorizer_reply,
-       .invalidate_authorizer = invalidate_authorizer,
-       .alloc_msg = alloc_msg,
+       .get = osd_get_con,
+       .put = osd_put_con,
+       .alloc_msg = osd_alloc_msg,
+       .dispatch = osd_dispatch,
+       .fault = osd_fault,
        .reencode_message = osd_reencode_message,
+       .get_authorizer = osd_get_authorizer,
+       .add_authorizer_challenge = osd_add_authorizer_challenge,
+       .verify_authorizer_reply = osd_verify_authorizer_reply,
+       .invalidate_authorizer = osd_invalidate_authorizer,
        .sign_message = osd_sign_message,
        .check_message_signature = osd_check_message_signature,
-       .fault = osd_fault,
        .get_auth_request = osd_get_auth_request,
        .handle_auth_reply_more = osd_handle_auth_reply_more,
        .handle_auth_done = osd_handle_auth_done,
index 8fa7392..6df3f1b 100644 (file)
@@ -3617,7 +3617,7 @@ static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
 int skb_csum_hwoffload_help(struct sk_buff *skb,
                            const netdev_features_t features)
 {
-       if (unlikely(skb->csum_not_inet))
+       if (unlikely(skb_csum_is_sctp(skb)))
                return !!(features & NETIF_F_SCTP_CRC) ? 0 :
                        skb_crc32c_csum_help(skb);
 
@@ -3878,6 +3878,7 @@ sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev)
 
        /* qdisc_skb_cb(skb)->pkt_len was already set by the caller. */
        qdisc_skb_cb(skb)->mru = 0;
+       qdisc_skb_cb(skb)->post_ct = false;
        mini_qdisc_bstats_cpu_update(miniq, skb);
 
        switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) {
@@ -4083,7 +4084,7 @@ static int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
        skb_reset_mac_header(skb);
 
        if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_SCHED_TSTAMP))
-               __skb_tstamp_tx(skb, NULL, skb->sk, SCM_TSTAMP_SCHED);
+               __skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SCHED);
 
        /* Disable soft irqs for various locks below. Also
         * stops preemption for RCU.
@@ -4603,14 +4604,14 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb,
                                     struct xdp_buff *xdp,
                                     struct bpf_prog *xdp_prog)
 {
+       void *orig_data, *orig_data_end, *hard_start;
        struct netdev_rx_queue *rxqueue;
-       void *orig_data, *orig_data_end;
        u32 metalen, act = XDP_DROP;
+       u32 mac_len, frame_sz;
        __be16 orig_eth_type;
        struct ethhdr *eth;
        bool orig_bcast;
-       int hlen, off;
-       u32 mac_len;
+       int off;
 
        /* Reinjected packets coming from act_mirred or similar should
         * not get XDP generic processing.
@@ -4642,15 +4643,16 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb,
         * header.
         */
        mac_len = skb->data - skb_mac_header(skb);
-       hlen = skb_headlen(skb) + mac_len;
-       xdp->data = skb->data - mac_len;
-       xdp->data_meta = xdp->data;
-       xdp->data_end = xdp->data + hlen;
-       xdp->data_hard_start = skb->data - skb_headroom(skb);
+       hard_start = skb->data - skb_headroom(skb);
 
        /* SKB "head" area always have tailroom for skb_shared_info */
-       xdp->frame_sz  = (void *)skb_end_pointer(skb) - xdp->data_hard_start;
-       xdp->frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+       frame_sz = (void *)skb_end_pointer(skb) - hard_start;
+       frame_sz += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+       rxqueue = netif_get_rxqueue(skb);
+       xdp_init_buff(xdp, frame_sz, &rxqueue->xdp_rxq);
+       xdp_prepare_buff(xdp, hard_start, skb_headroom(skb) - mac_len,
+                        skb_headlen(skb) + mac_len, true);
 
        orig_data_end = xdp->data_end;
        orig_data = xdp->data;
@@ -4658,9 +4660,6 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb,
        orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
        orig_eth_type = eth->h_proto;
 
-       rxqueue = netif_get_rxqueue(skb);
-       xdp->rxq = &rxqueue->xdp_rxq;
-
        act = bpf_prog_run_xdp(xdp_prog, xdp);
 
        /* check if bpf_xdp_adjust_head was used */
@@ -4962,6 +4961,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret,
 
        qdisc_skb_cb(skb)->pkt_len = skb->len;
        qdisc_skb_cb(skb)->mru = 0;
+       qdisc_skb_cb(skb)->post_ct = false;
        skb->tc_at_ingress = 1;
        mini_qdisc_bstats_cpu_update(miniq, skb);
 
@@ -5151,8 +5151,7 @@ another_round:
                skb_reset_mac_len(skb);
        }
 
-       if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
-           skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
+       if (eth_type_vlan(skb->protocol)) {
                skb = skb_vlan_untag(skb);
                if (unlikely(!skb))
                        goto out;
@@ -5236,8 +5235,7 @@ check_vlan_id:
                         * find vlan device.
                         */
                        skb->pkt_type = PACKET_OTHERHOST;
-               } else if (skb->protocol == cpu_to_be16(ETH_P_8021Q) ||
-                          skb->protocol == cpu_to_be16(ETH_P_8021AD)) {
+               } else if (eth_type_vlan(skb->protocol)) {
                        /* Outer header is 802.1P with vlan 0, inner header is
                         * 802.1Q or 802.1AD and vlan_do_receive() above could
                         * not find vlan dev for vlan id 0.
@@ -5713,7 +5711,7 @@ static void flush_all_backlogs(void)
        }
 
        /* we can have in flight packet[s] on the cpus we are not flushing,
-        * synchronize_net() in rollback_registered_many() will take care of
+        * synchronize_net() in unregister_netdevice_many() will take care of
         * them
         */
        for_each_cpu(cpu, &flush_cpus)
@@ -6070,10 +6068,6 @@ static gro_result_t napi_skb_finish(struct napi_struct *napi,
                gro_normal_one(napi, skb);
                break;
 
-       case GRO_DROP:
-               kfree_skb(skb);
-               break;
-
        case GRO_MERGED_FREE:
                if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD)
                        napi_skb_free_stolen_head(skb);
@@ -6158,10 +6152,6 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi,
                        gro_normal_one(napi, skb);
                break;
 
-       case GRO_DROP:
-               napi_reuse_skb(napi, skb);
-               break;
-
        case GRO_MERGED_FREE:
                if (NAPI_GRO_CB(skb)->free == NAPI_GRO_FREE_STOLEN_HEAD)
                        napi_skb_free_stolen_head(skb);
@@ -6223,9 +6213,6 @@ gro_result_t napi_gro_frags(struct napi_struct *napi)
        gro_result_t ret;
        struct sk_buff *skb = napi_frags_skb(napi);
 
-       if (!skb)
-               return GRO_DROP;
-
        trace_napi_gro_frags_entry(skb);
 
        ret = napi_frags_finish(napi, skb, dev_gro_receive(napi, skb));
@@ -8120,6 +8107,39 @@ struct net_device *netdev_get_xmit_slave(struct net_device *dev,
 }
 EXPORT_SYMBOL(netdev_get_xmit_slave);
 
+static struct net_device *netdev_sk_get_lower_dev(struct net_device *dev,
+                                                 struct sock *sk)
+{
+       const struct net_device_ops *ops = dev->netdev_ops;
+
+       if (!ops->ndo_sk_get_lower_dev)
+               return NULL;
+       return ops->ndo_sk_get_lower_dev(dev, sk);
+}
+
+/**
+ * netdev_sk_get_lowest_dev - Get the lowest device in chain given device and socket
+ * @dev: device
+ * @sk: the socket
+ *
+ * %NULL is returned if no lower device is found.
+ */
+
+struct net_device *netdev_sk_get_lowest_dev(struct net_device *dev,
+                                           struct sock *sk)
+{
+       struct net_device *lower;
+
+       lower = netdev_sk_get_lower_dev(dev, sk);
+       while (lower) {
+               dev = lower;
+               lower = netdev_sk_get_lower_dev(dev, sk);
+       }
+
+       return dev;
+}
+EXPORT_SYMBOL(netdev_sk_get_lowest_dev);
+
 static void netdev_adjacent_add_links(struct net_device *dev)
 {
        struct netdev_adjacent *iter;
@@ -9441,106 +9461,6 @@ static void net_set_todo(struct net_device *dev)
        dev_net(dev)->dev_unreg_count++;
 }
 
-static void rollback_registered_many(struct list_head *head)
-{
-       struct net_device *dev, *tmp;
-       LIST_HEAD(close_head);
-
-       BUG_ON(dev_boot_phase);
-       ASSERT_RTNL();
-
-       list_for_each_entry_safe(dev, tmp, head, unreg_list) {
-               /* Some devices call without registering
-                * for initialization unwind. Remove those
-                * devices and proceed with the remaining.
-                */
-               if (dev->reg_state == NETREG_UNINITIALIZED) {
-                       pr_debug("unregister_netdevice: device %s/%p never was registered\n",
-                                dev->name, dev);
-
-                       WARN_ON(1);
-                       list_del(&dev->unreg_list);
-                       continue;
-               }
-               dev->dismantle = true;
-               BUG_ON(dev->reg_state != NETREG_REGISTERED);
-       }
-
-       /* If device is running, close it first. */
-       list_for_each_entry(dev, head, unreg_list)
-               list_add_tail(&dev->close_list, &close_head);
-       dev_close_many(&close_head, true);
-
-       list_for_each_entry(dev, head, unreg_list) {
-               /* And unlink it from device chain. */
-               unlist_netdevice(dev);
-
-               dev->reg_state = NETREG_UNREGISTERING;
-       }
-       flush_all_backlogs();
-
-       synchronize_net();
-
-       list_for_each_entry(dev, head, unreg_list) {
-               struct sk_buff *skb = NULL;
-
-               /* Shutdown queueing discipline. */
-               dev_shutdown(dev);
-
-               dev_xdp_uninstall(dev);
-
-               /* Notify protocols, that we are about to destroy
-                * this device. They should clean all the things.
-                */
-               call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
-
-               if (!dev->rtnl_link_ops ||
-                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
-                       skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0,
-                                                    GFP_KERNEL, NULL, 0);
-
-               /*
-                *      Flush the unicast and multicast chains
-                */
-               dev_uc_flush(dev);
-               dev_mc_flush(dev);
-
-               netdev_name_node_alt_flush(dev);
-               netdev_name_node_free(dev->name_node);
-
-               if (dev->netdev_ops->ndo_uninit)
-                       dev->netdev_ops->ndo_uninit(dev);
-
-               if (skb)
-                       rtmsg_ifinfo_send(skb, dev, GFP_KERNEL);
-
-               /* Notifier chain MUST detach us all upper devices. */
-               WARN_ON(netdev_has_any_upper_dev(dev));
-               WARN_ON(netdev_has_any_lower_dev(dev));
-
-               /* Remove entries from kobject tree */
-               netdev_unregister_kobject(dev);
-#ifdef CONFIG_XPS
-               /* Remove XPS queueing entries */
-               netif_reset_xps_queues_gt(dev, 0);
-#endif
-       }
-
-       synchronize_net();
-
-       list_for_each_entry(dev, head, unreg_list)
-               dev_put(dev);
-}
-
-static void rollback_registered(struct net_device *dev)
-{
-       LIST_HEAD(single);
-
-       list_add(&dev->unreg_list, &single);
-       rollback_registered_many(&single);
-       list_del(&single);
-}
-
 static netdev_features_t netdev_sync_upper_features(struct net_device *lower,
        struct net_device *upper, netdev_features_t features)
 {
@@ -9661,9 +9581,20 @@ static netdev_features_t netdev_fix_features(struct net_device *dev,
                }
        }
 
-       if ((features & NETIF_F_HW_TLS_TX) && !(features & NETIF_F_HW_CSUM)) {
-               netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n");
-               features &= ~NETIF_F_HW_TLS_TX;
+       if (features & NETIF_F_HW_TLS_TX) {
+               bool ip_csum = (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) ==
+                       (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
+               bool hw_csum = features & NETIF_F_HW_CSUM;
+
+               if (!ip_csum && !hw_csum) {
+                       netdev_dbg(dev, "Dropping TLS TX HW offload feature since no CSUM feature.\n");
+                       features &= ~NETIF_F_HW_TLS_TX;
+               }
+       }
+
+       if ((features & NETIF_F_HW_TLS_RX) && !(features & NETIF_F_RXCSUM)) {
+               netdev_dbg(dev, "Dropping TLS RX HW offload feature since no RXCSUM feature.\n");
+               features &= ~NETIF_F_HW_TLS_RX;
        }
 
        return features;
@@ -10002,7 +9933,7 @@ int register_netdevice(struct net_device *dev)
        dev->hw_features |= (NETIF_F_SOFT_FEATURES | NETIF_F_SOFT_FEATURES_OFF);
        dev->features |= NETIF_F_SOFT_FEATURES;
 
-       if (dev->netdev_ops->ndo_udp_tunnel_add) {
+       if (dev->udp_tunnel_nic_info) {
                dev->features |= NETIF_F_RX_UDP_TUNNEL_PORT;
                dev->hw_features |= NETIF_F_RX_UDP_TUNNEL_PORT;
        }
@@ -10077,17 +10008,10 @@ int register_netdevice(struct net_device *dev)
        ret = call_netdevice_notifiers(NETDEV_REGISTER, dev);
        ret = notifier_to_errno(ret);
        if (ret) {
-               rollback_registered(dev);
-               rcu_barrier();
-
-               dev->reg_state = NETREG_UNREGISTERED;
-               /* We should put the kobject that hold in
-                * netdev_unregister_kobject(), otherwise
-                * the net device cannot be freed when
-                * driver calls free_netdev(), because the
-                * kobject is being hold.
-                */
-               kobject_put(&dev->dev.kobj);
+               /* Expect explicit free_netdev() on failure */
+               dev->needs_free_netdev = false;
+               unregister_netdevice_queue(dev, NULL);
+               goto out;
        }
        /*
         *      Prevent userspace races by waiting until the network
@@ -10631,6 +10555,17 @@ void free_netdev(struct net_device *dev)
        struct napi_struct *p, *n;
 
        might_sleep();
+
+       /* When called immediately after register_netdevice() failed the unwind
+        * handling may still be dismantling the device. Handle that case by
+        * deferring the free.
+        */
+       if (dev->reg_state == NETREG_UNREGISTERING) {
+               ASSERT_RTNL();
+               dev->needs_free_netdev = true;
+               return;
+       }
+
        netif_free_tx_queues(dev);
        netif_free_rx_queues(dev);
 
@@ -10697,9 +10632,10 @@ void unregister_netdevice_queue(struct net_device *dev, struct list_head *head)
        if (head) {
                list_move_tail(&dev->unreg_list, head);
        } else {
-               rollback_registered(dev);
-               /* Finish processing unregister after unlock */
-               net_set_todo(dev);
+               LIST_HEAD(single);
+
+               list_add(&dev->unreg_list, &single);
+               unregister_netdevice_many(&single);
        }
 }
 EXPORT_SYMBOL(unregister_netdevice_queue);
@@ -10713,14 +10649,100 @@ EXPORT_SYMBOL(unregister_netdevice_queue);
  */
 void unregister_netdevice_many(struct list_head *head)
 {
-       struct net_device *dev;
+       struct net_device *dev, *tmp;
+       LIST_HEAD(close_head);
 
-       if (!list_empty(head)) {
-               rollback_registered_many(head);
-               list_for_each_entry(dev, head, unreg_list)
-                       net_set_todo(dev);
-               list_del(head);
+       BUG_ON(dev_boot_phase);
+       ASSERT_RTNL();
+
+       if (list_empty(head))
+               return;
+
+       list_for_each_entry_safe(dev, tmp, head, unreg_list) {
+               /* Some devices call without registering
+                * for initialization unwind. Remove those
+                * devices and proceed with the remaining.
+                */
+               if (dev->reg_state == NETREG_UNINITIALIZED) {
+                       pr_debug("unregister_netdevice: device %s/%p never was registered\n",
+                                dev->name, dev);
+
+                       WARN_ON(1);
+                       list_del(&dev->unreg_list);
+                       continue;
+               }
+               dev->dismantle = true;
+               BUG_ON(dev->reg_state != NETREG_REGISTERED);
        }
+
+       /* If device is running, close it first. */
+       list_for_each_entry(dev, head, unreg_list)
+               list_add_tail(&dev->close_list, &close_head);
+       dev_close_many(&close_head, true);
+
+       list_for_each_entry(dev, head, unreg_list) {
+               /* And unlink it from device chain. */
+               unlist_netdevice(dev);
+
+               dev->reg_state = NETREG_UNREGISTERING;
+       }
+       flush_all_backlogs();
+
+       synchronize_net();
+
+       list_for_each_entry(dev, head, unreg_list) {
+               struct sk_buff *skb = NULL;
+
+               /* Shutdown queueing discipline. */
+               dev_shutdown(dev);
+
+               dev_xdp_uninstall(dev);
+
+               /* Notify protocols, that we are about to destroy
+                * this device. They should clean all the things.
+                */
+               call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+
+               if (!dev->rtnl_link_ops ||
+                   dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+                       skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0,
+                                                    GFP_KERNEL, NULL, 0);
+
+               /*
+                *      Flush the unicast and multicast chains
+                */
+               dev_uc_flush(dev);
+               dev_mc_flush(dev);
+
+               netdev_name_node_alt_flush(dev);
+               netdev_name_node_free(dev->name_node);
+
+               if (dev->netdev_ops->ndo_uninit)
+                       dev->netdev_ops->ndo_uninit(dev);
+
+               if (skb)
+                       rtmsg_ifinfo_send(skb, dev, GFP_KERNEL);
+
+               /* Notifier chain MUST detach us all upper devices. */
+               WARN_ON(netdev_has_any_upper_dev(dev));
+               WARN_ON(netdev_has_any_lower_dev(dev));
+
+               /* Remove entries from kobject tree */
+               netdev_unregister_kobject(dev);
+#ifdef CONFIG_XPS
+               /* Remove XPS queueing entries */
+               netif_reset_xps_queues_gt(dev, 0);
+#endif
+       }
+
+       synchronize_net();
+
+       list_for_each_entry(dev, head, unreg_list) {
+               dev_put(dev);
+               net_set_todo(dev);
+       }
+
+       list_del(head);
 }
 EXPORT_SYMBOL(unregister_netdevice_many);
 
index ee828e4..737b61c 100644 (file)
@@ -87,6 +87,9 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_trap_report);
 
 static const struct nla_policy devlink_function_nl_policy[DEVLINK_PORT_FUNCTION_ATTR_MAX + 1] = {
        [DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR] = { .type = NLA_BINARY },
+       [DEVLINK_PORT_FN_ATTR_STATE] =
+               NLA_POLICY_RANGE(NLA_U8, DEVLINK_PORT_FN_STATE_INACTIVE,
+                                DEVLINK_PORT_FN_STATE_ACTIVE),
 };
 
 static LIST_HEAD(devlink_list);
@@ -690,6 +693,15 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
                if (nla_put_u8(msg, DEVLINK_ATTR_PORT_EXTERNAL, attrs->pci_vf.external))
                        return -EMSGSIZE;
                break;
+       case DEVLINK_PORT_FLAVOUR_PCI_SF:
+               if (nla_put_u32(msg, DEVLINK_ATTR_PORT_CONTROLLER_NUMBER,
+                               attrs->pci_sf.controller) ||
+                   nla_put_u16(msg, DEVLINK_ATTR_PORT_PCI_PF_NUMBER,
+                               attrs->pci_sf.pf) ||
+                   nla_put_u32(msg, DEVLINK_ATTR_PORT_PCI_SF_NUMBER,
+                               attrs->pci_sf.sf))
+                       return -EMSGSIZE;
+               break;
        case DEVLINK_PORT_FLAVOUR_PHYSICAL:
        case DEVLINK_PORT_FLAVOUR_CPU:
        case DEVLINK_PORT_FLAVOUR_DSA:
@@ -712,6 +724,83 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg,
        return 0;
 }
 
+static int
+devlink_port_fn_hw_addr_fill(struct devlink *devlink, const struct devlink_ops *ops,
+                            struct devlink_port *port, struct sk_buff *msg,
+                            struct netlink_ext_ack *extack, bool *msg_updated)
+{
+       u8 hw_addr[MAX_ADDR_LEN];
+       int hw_addr_len;
+       int err;
+
+       if (!ops->port_function_hw_addr_get)
+               return 0;
+
+       err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
+       if (err) {
+               if (err == -EOPNOTSUPP)
+                       return 0;
+               return err;
+       }
+       err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);
+       if (err)
+               return err;
+       *msg_updated = true;
+       return 0;
+}
+
+static bool
+devlink_port_fn_state_valid(enum devlink_port_fn_state state)
+{
+       return state == DEVLINK_PORT_FN_STATE_INACTIVE ||
+              state == DEVLINK_PORT_FN_STATE_ACTIVE;
+}
+
+static bool
+devlink_port_fn_opstate_valid(enum devlink_port_fn_opstate opstate)
+{
+       return opstate == DEVLINK_PORT_FN_OPSTATE_DETACHED ||
+              opstate == DEVLINK_PORT_FN_OPSTATE_ATTACHED;
+}
+
+static int
+devlink_port_fn_state_fill(struct devlink *devlink,
+                          const struct devlink_ops *ops,
+                          struct devlink_port *port, struct sk_buff *msg,
+                          struct netlink_ext_ack *extack,
+                          bool *msg_updated)
+{
+       enum devlink_port_fn_opstate opstate;
+       enum devlink_port_fn_state state;
+       int err;
+
+       if (!ops->port_fn_state_get)
+               return 0;
+
+       err = ops->port_fn_state_get(devlink, port, &state, &opstate, extack);
+       if (err) {
+               if (err == -EOPNOTSUPP)
+                       return 0;
+               return err;
+       }
+       if (!devlink_port_fn_state_valid(state)) {
+               WARN_ON_ONCE(1);
+               NL_SET_ERR_MSG_MOD(extack, "Invalid state read from driver");
+               return -EINVAL;
+       }
+       if (!devlink_port_fn_opstate_valid(opstate)) {
+               WARN_ON_ONCE(1);
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Invalid operational state read from driver");
+               return -EINVAL;
+       }
+       if (nla_put_u8(msg, DEVLINK_PORT_FN_ATTR_STATE, state) ||
+           nla_put_u8(msg, DEVLINK_PORT_FN_ATTR_OPSTATE, opstate))
+               return -EMSGSIZE;
+       *msg_updated = true;
+       return 0;
+}
+
 static int
 devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *port,
                                   struct netlink_ext_ack *extack)
@@ -719,36 +808,22 @@ devlink_nl_port_function_attrs_put(struct sk_buff *msg, struct devlink_port *por
        struct devlink *devlink = port->devlink;
        const struct devlink_ops *ops;
        struct nlattr *function_attr;
-       bool empty_nest = true;
-       int err = 0;
+       bool msg_updated = false;
+       int err;
 
        function_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PORT_FUNCTION);
        if (!function_attr)
                return -EMSGSIZE;
 
        ops = devlink->ops;
-       if (ops->port_function_hw_addr_get) {
-               int hw_addr_len;
-               u8 hw_addr[MAX_ADDR_LEN];
-
-               err = ops->port_function_hw_addr_get(devlink, port, hw_addr, &hw_addr_len, extack);
-               if (err == -EOPNOTSUPP) {
-                       /* Port function attributes are optional for a port. If port doesn't
-                        * support function attribute, returning -EOPNOTSUPP is not an error.
-                        */
-                       err = 0;
-                       goto out;
-               } else if (err) {
-                       goto out;
-               }
-               err = nla_put(msg, DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, hw_addr_len, hw_addr);
-               if (err)
-                       goto out;
-               empty_nest = false;
-       }
-
+       err = devlink_port_fn_hw_addr_fill(devlink, ops, port, msg,
+                                          extack, &msg_updated);
+       if (err)
+               goto out;
+       err = devlink_port_fn_state_fill(devlink, ops, port, msg, extack,
+                                        &msg_updated);
 out:
-       if (err || empty_nest)
+       if (err || !msg_updated)
                nla_nest_cancel(msg, function_attr);
        else
                nla_nest_end(msg, function_attr);
@@ -986,7 +1061,6 @@ devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *
        const struct devlink_ops *ops;
        const u8 *hw_addr;
        int hw_addr_len;
-       int err;
 
        hw_addr = nla_data(attr);
        hw_addr_len = nla_len(attr);
@@ -1011,12 +1085,25 @@ devlink_port_function_hw_addr_set(struct devlink *devlink, struct devlink_port *
                return -EOPNOTSUPP;
        }
 
-       err = ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
-       if (err)
-               return err;
+       return ops->port_function_hw_addr_set(devlink, port, hw_addr, hw_addr_len, extack);
+}
 
-       devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
-       return 0;
+static int devlink_port_fn_state_set(struct devlink *devlink,
+                                    struct devlink_port *port,
+                                    const struct nlattr *attr,
+                                    struct netlink_ext_ack *extack)
+{
+       enum devlink_port_fn_state state;
+       const struct devlink_ops *ops;
+
+       state = nla_get_u8(attr);
+       ops = devlink->ops;
+       if (!ops->port_fn_state_set) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Function does not support state setting");
+               return -EOPNOTSUPP;
+       }
+       return ops->port_fn_state_set(devlink, port, state, extack);
 }
 
 static int
@@ -1034,9 +1121,21 @@ devlink_port_function_set(struct devlink *devlink, struct devlink_port *port,
        }
 
        attr = tb[DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR];
-       if (attr)
+       if (attr) {
                err = devlink_port_function_hw_addr_set(devlink, port, attr, extack);
+               if (err)
+                       return err;
+       }
+       /* Keep this as the last function attribute set, so that when
+        * multiple port function attributes are set along with state,
+        * Those can be applied first before activating the state.
+        */
+       attr = tb[DEVLINK_PORT_FN_ATTR_STATE];
+       if (attr)
+               err = devlink_port_fn_state_set(devlink, port, attr, extack);
 
+       if (!err)
+               devlink_port_notify(port, DEVLINK_CMD_PORT_NEW);
        return err;
 }
 
@@ -1136,6 +1235,111 @@ static int devlink_nl_cmd_port_unsplit_doit(struct sk_buff *skb,
        return devlink_port_unsplit(devlink, port_index, info->extack);
 }
 
+static int devlink_port_new_notifiy(struct devlink *devlink,
+                                   unsigned int port_index,
+                                   struct genl_info *info)
+{
+       struct devlink_port *devlink_port;
+       struct sk_buff *msg;
+       int err;
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       mutex_lock(&devlink->lock);
+       devlink_port = devlink_port_get_by_index(devlink, port_index);
+       if (!devlink_port) {
+               err = -ENODEV;
+               goto out;
+       }
+
+       err = devlink_nl_port_fill(msg, devlink, devlink_port,
+                                  DEVLINK_CMD_NEW, info->snd_portid,
+                                  info->snd_seq, 0, NULL);
+       if (err)
+               goto out;
+
+       err = genlmsg_reply(msg, info);
+       mutex_unlock(&devlink->lock);
+       return err;
+
+out:
+       mutex_unlock(&devlink->lock);
+       nlmsg_free(msg);
+       return err;
+}
+
+static int devlink_nl_cmd_port_new_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct netlink_ext_ack *extack = info->extack;
+       struct devlink_port_new_attrs new_attrs = {};
+       struct devlink *devlink = info->user_ptr[0];
+       unsigned int new_port_index;
+       int err;
+
+       if (!devlink->ops->port_new || !devlink->ops->port_del)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[DEVLINK_ATTR_PORT_FLAVOUR] ||
+           !info->attrs[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]) {
+               NL_SET_ERR_MSG_MOD(extack, "Port flavour or PCI PF are not specified");
+               return -EINVAL;
+       }
+       new_attrs.flavour = nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_FLAVOUR]);
+       new_attrs.pfnum =
+               nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_PCI_PF_NUMBER]);
+
+       if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
+               /* Port index of the new port being created by driver. */
+               new_attrs.port_index =
+                       nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
+               new_attrs.port_index_valid = true;
+       }
+       if (info->attrs[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER]) {
+               new_attrs.controller =
+                       nla_get_u16(info->attrs[DEVLINK_ATTR_PORT_CONTROLLER_NUMBER]);
+               new_attrs.controller_valid = true;
+       }
+       if (new_attrs.flavour == DEVLINK_PORT_FLAVOUR_PCI_SF &&
+           info->attrs[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]) {
+               new_attrs.sfnum = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_PCI_SF_NUMBER]);
+               new_attrs.sfnum_valid = true;
+       }
+
+       err = devlink->ops->port_new(devlink, &new_attrs, extack,
+                                    &new_port_index);
+       if (err)
+               return err;
+
+       err = devlink_port_new_notifiy(devlink, new_port_index, info);
+       if (err && err != -ENODEV) {
+               /* Fail to send the response; destroy newly created port. */
+               devlink->ops->port_del(devlink, new_port_index, extack);
+       }
+       return err;
+}
+
+static int devlink_nl_cmd_port_del_doit(struct sk_buff *skb,
+                                       struct genl_info *info)
+{
+       struct netlink_ext_ack *extack = info->extack;
+       struct devlink *devlink = info->user_ptr[0];
+       unsigned int port_index;
+
+       if (!devlink->ops->port_del)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
+               NL_SET_ERR_MSG_MOD(extack, "Port index is not specified");
+               return -EINVAL;
+       }
+       port_index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
+
+       return devlink->ops->port_del(devlink, port_index, extack);
+}
+
 static int devlink_nl_sb_fill(struct sk_buff *msg, struct devlink *devlink,
                              struct devlink_sb *devlink_sb,
                              enum devlink_command cmd, u32 portid,
@@ -4146,7 +4350,7 @@ out:
 static int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
                                              struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_port *devlink_port = info->user_ptr[1];
        struct devlink_param_item *param_item;
        struct sk_buff *msg;
        int err;
@@ -4175,7 +4379,7 @@ static int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
 static int devlink_nl_cmd_port_param_set_doit(struct sk_buff *skb,
                                              struct genl_info *info)
 {
-       struct devlink_port *devlink_port = info->user_ptr[0];
+       struct devlink_port *devlink_port = info->user_ptr[1];
 
        return __devlink_nl_cmd_param_set_doit(devlink_port->devlink,
                                               devlink_port->index,
@@ -7594,6 +7798,10 @@ static const struct nla_policy devlink_nl_policy[DEVLINK_ATTR_MAX + 1] = {
        [DEVLINK_ATTR_RELOAD_ACTION] = NLA_POLICY_RANGE(NLA_U8, DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
                                                        DEVLINK_RELOAD_ACTION_MAX),
        [DEVLINK_ATTR_RELOAD_LIMITS] = NLA_POLICY_BITFIELD32(DEVLINK_RELOAD_LIMITS_VALID_MASK),
+       [DEVLINK_ATTR_PORT_FLAVOUR] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_PORT_PCI_PF_NUMBER] = { .type = NLA_U16 },
+       [DEVLINK_ATTR_PORT_PCI_SF_NUMBER] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_PORT_CONTROLLER_NUMBER] = { .type = NLA_U32 },
 };
 
 static const struct genl_small_ops devlink_nl_ops[] = {
@@ -7633,6 +7841,18 @@ static const struct genl_small_ops devlink_nl_ops[] = {
                .flags = GENL_ADMIN_PERM,
                .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
        },
+       {
+               .cmd = DEVLINK_CMD_PORT_NEW,
+               .doit = devlink_nl_cmd_port_new_doit,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
+       },
+       {
+               .cmd = DEVLINK_CMD_PORT_DEL,
+               .doit = devlink_nl_cmd_port_del_doit,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = DEVLINK_NL_FLAG_NO_LOCK,
+       },
        {
                .cmd = DEVLINK_CMD_SB_GET,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
@@ -8372,6 +8592,32 @@ void devlink_port_attrs_pci_vf_set(struct devlink_port *devlink_port, u32 contro
 }
 EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_vf_set);
 
+/**
+ *     devlink_port_attrs_pci_sf_set - Set PCI SF port attributes
+ *
+ *     @devlink_port: devlink port
+ *     @controller: associated controller number for the devlink port instance
+ *     @pf: associated PF for the devlink port instance
+ *     @sf: associated SF of a PF for the devlink port instance
+ */
+void devlink_port_attrs_pci_sf_set(struct devlink_port *devlink_port, u32 controller,
+                                  u16 pf, u32 sf)
+{
+       struct devlink_port_attrs *attrs = &devlink_port->attrs;
+       int ret;
+
+       if (WARN_ON(devlink_port->registered))
+               return;
+       ret = __devlink_port_attrs_set(devlink_port,
+                                      DEVLINK_PORT_FLAVOUR_PCI_SF);
+       if (ret)
+               return;
+       attrs->pci_sf.controller = controller;
+       attrs->pci_sf.pf = pf;
+       attrs->pci_sf.sf = sf;
+}
+EXPORT_SYMBOL_GPL(devlink_port_attrs_pci_sf_set);
+
 static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
                                             char *name, size_t len)
 {
@@ -8420,6 +8666,10 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port,
                n = snprintf(name, len, "pf%uvf%u",
                             attrs->pci_vf.pf, attrs->pci_vf.vf);
                break;
+       case DEVLINK_PORT_FLAVOUR_PCI_SF:
+               n = snprintf(name, len, "pf%usf%u", attrs->pci_sf.pf,
+                            attrs->pci_sf.sf);
+               break;
        }
 
        if (n >= len)
@@ -8617,6 +8867,10 @@ EXPORT_SYMBOL_GPL(devlink_dpipe_table_unregister);
  *     @resource_id: resource's id
  *     @parent_resource_id: resource's parent id
  *     @size_params: size parameters
+ *
+ *     Generic resources should reuse the same names across drivers.
+ *     Please see the generic resources list at:
+ *     Documentation/networking/devlink/devlink-resource.rst
  */
 int devlink_resource_register(struct devlink *devlink,
                              const char *resource_name,
@@ -9508,6 +9762,7 @@ static const struct devlink_trap devlink_trap_generic[] = {
        DEVLINK_TRAP(GTP_PARSING, DROP),
        DEVLINK_TRAP(ESP_PARSING, DROP),
        DEVLINK_TRAP(BLACKHOLE_NEXTHOP, DROP),
+       DEVLINK_TRAP(DMAC_FILTER, DROP),
 };
 
 #define DEVLINK_TRAP_GROUP(_id)                                                      \
index 255aeee..9ab94e9 100644 (file)
@@ -4770,6 +4770,10 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
                                ifindex = dev->ifindex;
                                dev_put(dev);
                        }
+                       fallthrough;
+               case SO_BINDTOIFINDEX:
+                       if (optname == SO_BINDTOIFINDEX)
+                               ifindex = val;
                        ret = sock_bindtoindex(sk, ifindex, false);
                        break;
                case SO_KEEPALIVE:
@@ -4932,8 +4936,25 @@ static int _bpf_getsockopt(struct sock *sk, int level, int optname,
 
        sock_owned_by_me(sk);
 
+       if (level == SOL_SOCKET) {
+               if (optlen != sizeof(int))
+                       goto err_clear;
+
+               switch (optname) {
+               case SO_MARK:
+                       *((int *)optval) = sk->sk_mark;
+                       break;
+               case SO_PRIORITY:
+                       *((int *)optval) = sk->sk_priority;
+                       break;
+               case SO_BINDTOIFINDEX:
+                       *((int *)optval) = sk->sk_bound_dev_if;
+                       break;
+               default:
+                       goto err_clear;
+               }
 #ifdef CONFIG_INET
-       if (level == SOL_TCP && sk->sk_prot->getsockopt == tcp_getsockopt) {
+       } else if (level == SOL_TCP && sk->sk_prot->getsockopt == tcp_getsockopt) {
                struct inet_connection_sock *icsk;
                struct tcp_sock *tp;
 
@@ -4986,12 +5007,12 @@ static int _bpf_getsockopt(struct sock *sk, int level, int optname,
                default:
                        goto err_clear;
                }
+#endif
 #endif
        } else {
                goto err_clear;
        }
        return 0;
-#endif
 err_clear:
        memset(optval, 0, optlen);
        return -EINVAL;
index 6f1adba..c565c7a 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/if_ether.h>
 #include <linux/mpls.h>
 #include <linux/tcp.h>
+#include <linux/ptp_classify.h>
 #include <net/flow_dissector.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <uapi/linux/batadv_packet.h>
@@ -236,9 +237,8 @@ skb_flow_dissect_set_enc_addr_type(enum flow_dissector_key_id type,
 void
 skb_flow_dissect_ct(const struct sk_buff *skb,
                    struct flow_dissector *flow_dissector,
-                   void *target_container,
-                   u16 *ctinfo_map,
-                   size_t mapsize)
+                   void *target_container, u16 *ctinfo_map,
+                   size_t mapsize, bool post_ct)
 {
 #if IS_ENABLED(CONFIG_NF_CONNTRACK)
        struct flow_dissector_key_ct *key;
@@ -250,13 +250,19 @@ skb_flow_dissect_ct(const struct sk_buff *skb,
                return;
 
        ct = nf_ct_get(skb, &ctinfo);
-       if (!ct)
+       if (!ct && !post_ct)
                return;
 
        key = skb_flow_dissector_target(flow_dissector,
                                        FLOW_DISSECTOR_KEY_CT,
                                        target_container);
 
+       if (!ct) {
+               key->ct_state = TCA_FLOWER_KEY_CT_FLAGS_TRACKED |
+                               TCA_FLOWER_KEY_CT_FLAGS_INVALID;
+               return;
+       }
+
        if (ctinfo < mapsize)
                key->ct_state = ctinfo_map[ctinfo];
 #if IS_ENABLED(CONFIG_NF_CONNTRACK_ZONES)
@@ -1251,6 +1257,21 @@ proto_again:
                                                  &proto, &nhoff, hlen, flags);
                break;
 
+       case htons(ETH_P_1588): {
+               struct ptp_header *hdr, _hdr;
+
+               hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data,
+                                          hlen, &_hdr);
+               if (!hdr) {
+                       fdret = FLOW_DISSECT_RET_OUT_BAD;
+                       break;
+               }
+
+               nhoff += ntohs(hdr->message_length);
+               fdret = FLOW_DISSECT_RET_OUT_GOOD;
+               break;
+       }
+
        default:
                fdret = FLOW_DISSECT_RET_OUT_BAD;
                break;
index 80dbf2f..8e582e2 100644 (file)
@@ -80,11 +80,11 @@ static void est_timer(struct timer_list *t)
        u64 rate, brate;
 
        est_fetch_counters(est, &b);
-       brate = (b.bytes - est->last_bytes) << (10 - est->ewma_log - est->intvl_log);
-       brate -= (est->avbps >> est->ewma_log);
+       brate = (b.bytes - est->last_bytes) << (10 - est->intvl_log);
+       brate = (brate >> est->ewma_log) - (est->avbps >> est->ewma_log);
 
-       rate = (b.packets - est->last_packets) << (10 - est->ewma_log - est->intvl_log);
-       rate -= (est->avpps >> est->ewma_log);
+       rate = (b.packets - est->last_packets) << (10 - est->intvl_log);
+       rate = (rate >> est->ewma_log) - (est->avpps >> est->ewma_log);
 
        write_seqcount_begin(&est->seq);
        est->avbps += brate;
@@ -143,6 +143,9 @@ int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
        if (parm->interval < -2 || parm->interval > 3)
                return -EINVAL;
 
+       if (parm->ewma_log == 0 || parm->ewma_log >= 31)
+               return -EINVAL;
+
        est = kzalloc(sizeof(*est), GFP_KERNEL);
        if (!est)
                return -ENOBUFS;
index 9500d28..ff07358 100644 (file)
@@ -41,7 +41,6 @@
 
 #include <trace/events/neigh.h>
 
-#define DEBUG
 #define NEIGH_DEBUG 1
 #define neigh_dbg(level, fmt, ...)             \
 do {                                           \
@@ -1569,10 +1568,8 @@ static void neigh_proxy_process(struct timer_list *t)
 void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
                    struct sk_buff *skb)
 {
-       unsigned long now = jiffies;
-
-       unsigned long sched_next = now + (prandom_u32() %
-                                         NEIGH_VAR(p, PROXY_DELAY));
+       unsigned long sched_next = jiffies +
+                       prandom_u32_max(NEIGH_VAR(p, PROXY_DELAY));
 
        if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) {
                kfree_skb(skb);
index 999b70c..daf502c 100644 (file)
@@ -1317,8 +1317,8 @@ static const struct attribute_group dql_group = {
 static ssize_t xps_cpus_show(struct netdev_queue *queue,
                             char *buf)
 {
+       int cpu, len, ret, num_tc = 1, tc = 0;
        struct net_device *dev = queue->dev;
-       int cpu, len, num_tc = 1, tc = 0;
        struct xps_dev_maps *dev_maps;
        cpumask_var_t mask;
        unsigned long index;
@@ -1328,22 +1328,31 @@ static ssize_t xps_cpus_show(struct netdev_queue *queue,
 
        index = get_netdev_queue_index(queue);
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        if (dev->num_tc) {
                /* Do not allow XPS on subordinate device directly */
                num_tc = dev->num_tc;
-               if (num_tc < 0)
-                       return -EINVAL;
+               if (num_tc < 0) {
+                       ret = -EINVAL;
+                       goto err_rtnl_unlock;
+               }
 
                /* If queue belongs to subordinate dev use its map */
                dev = netdev_get_tx_queue(dev, index)->sb_dev ? : dev;
 
                tc = netdev_txq_to_tc(dev, index);
-               if (tc < 0)
-                       return -EINVAL;
+               if (tc < 0) {
+                       ret = -EINVAL;
+                       goto err_rtnl_unlock;
+               }
        }
 
-       if (!zalloc_cpumask_var(&mask, GFP_KERNEL))
-               return -ENOMEM;
+       if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) {
+               ret = -ENOMEM;
+               goto err_rtnl_unlock;
+       }
 
        rcu_read_lock();
        dev_maps = rcu_dereference(dev->xps_cpus_map);
@@ -1366,9 +1375,15 @@ static ssize_t xps_cpus_show(struct netdev_queue *queue,
        }
        rcu_read_unlock();
 
+       rtnl_unlock();
+
        len = snprintf(buf, PAGE_SIZE, "%*pb\n", cpumask_pr_args(mask));
        free_cpumask_var(mask);
        return len < PAGE_SIZE ? len : -EINVAL;
+
+err_rtnl_unlock:
+       rtnl_unlock();
+       return ret;
 }
 
 static ssize_t xps_cpus_store(struct netdev_queue *queue,
@@ -1396,7 +1411,13 @@ static ssize_t xps_cpus_store(struct netdev_queue *queue,
                return err;
        }
 
+       if (!rtnl_trylock()) {
+               free_cpumask_var(mask);
+               return restart_syscall();
+       }
+
        err = netif_set_xps_queue(dev, mask, index);
+       rtnl_unlock();
 
        free_cpumask_var(mask);
 
@@ -1408,22 +1429,29 @@ static struct netdev_queue_attribute xps_cpus_attribute __ro_after_init
 
 static ssize_t xps_rxqs_show(struct netdev_queue *queue, char *buf)
 {
+       int j, len, ret, num_tc = 1, tc = 0;
        struct net_device *dev = queue->dev;
        struct xps_dev_maps *dev_maps;
        unsigned long *mask, index;
-       int j, len, num_tc = 1, tc = 0;
 
        index = get_netdev_queue_index(queue);
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        if (dev->num_tc) {
                num_tc = dev->num_tc;
                tc = netdev_txq_to_tc(dev, index);
-               if (tc < 0)
-                       return -EINVAL;
+               if (tc < 0) {
+                       ret = -EINVAL;
+                       goto err_rtnl_unlock;
+               }
        }
        mask = bitmap_zalloc(dev->num_rx_queues, GFP_KERNEL);
-       if (!mask)
-               return -ENOMEM;
+       if (!mask) {
+               ret = -ENOMEM;
+               goto err_rtnl_unlock;
+       }
 
        rcu_read_lock();
        dev_maps = rcu_dereference(dev->xps_rxqs_map);
@@ -1449,10 +1477,16 @@ static ssize_t xps_rxqs_show(struct netdev_queue *queue, char *buf)
 out_no_maps:
        rcu_read_unlock();
 
+       rtnl_unlock();
+
        len = bitmap_print_to_pagebuf(false, buf, mask, dev->num_rx_queues);
        bitmap_free(mask);
 
        return len < PAGE_SIZE ? len : -EINVAL;
+
+err_rtnl_unlock:
+       rtnl_unlock();
+       return ret;
 }
 
 static ssize_t xps_rxqs_store(struct netdev_queue *queue, const char *buf,
@@ -1478,10 +1512,17 @@ static ssize_t xps_rxqs_store(struct netdev_queue *queue, const char *buf,
                return err;
        }
 
+       if (!rtnl_trylock()) {
+               bitmap_free(mask);
+               return restart_syscall();
+       }
+
        cpus_read_lock();
        err = __netif_set_xps_queue(dev, mask, index, true);
        cpus_read_unlock();
 
+       rtnl_unlock();
+
        bitmap_free(mask);
        return err ? : len;
 }
index 1059786..3fba429 100644 (file)
@@ -3464,7 +3464,7 @@ static int pktgen_thread_worker(void *arg)
        struct pktgen_dev *pkt_dev = NULL;
        int cpu = t->cpu;
 
-       BUG_ON(smp_processor_id() != cpu);
+       WARN_ON(smp_processor_id() != cpu);
 
        init_waitqueue_head(&t->queue);
        complete(&t->start_done);
index bb0596c..c313aaf 100644 (file)
@@ -55,7 +55,7 @@
 #include <net/net_namespace.h>
 
 #define RTNL_MAX_TYPE          50
-#define RTNL_SLAVE_MAX_TYPE    36
+#define RTNL_SLAVE_MAX_TYPE    40
 
 struct rtnl_link {
        rtnl_doit_func          doit;
@@ -3439,26 +3439,15 @@ replay:
 
        dev->ifindex = ifm->ifi_index;
 
-       if (ops->newlink) {
+       if (ops->newlink)
                err = ops->newlink(link_net ? : net, dev, tb, data, extack);
-               /* Drivers should call free_netdev() in ->destructor
-                * and unregister it on failure after registration
-                * so that device could be finally freed in rtnl_unlock.
-                */
-               if (err < 0) {
-                       /* If device is not registered at all, free it now */
-                       if (dev->reg_state == NETREG_UNINITIALIZED ||
-                           dev->reg_state == NETREG_UNREGISTERED)
-                               free_netdev(dev);
-                       goto out;
-               }
-       } else {
+       else
                err = register_netdevice(dev);
-               if (err < 0) {
-                       free_netdev(dev);
-                       goto out;
-               }
+       if (err < 0) {
+               free_netdev(dev);
+               goto out;
        }
+
        err = rtnl_configure_link(dev, ifm);
        if (err < 0)
                goto out_unregister;
index f62cae3..2af12f7 100644 (file)
@@ -437,7 +437,11 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int len,
 
        len += NET_SKB_PAD;
 
-       if ((len > SKB_WITH_OVERHEAD(PAGE_SIZE)) ||
+       /* If requested length is either too small or too big,
+        * we use kmalloc() for skb->head allocation.
+        */
+       if (len <= SKB_WITH_OVERHEAD(1024) ||
+           len > SKB_WITH_OVERHEAD(PAGE_SIZE) ||
            (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) {
                skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE);
                if (!skb)
@@ -501,13 +505,17 @@ EXPORT_SYMBOL(__netdev_alloc_skb);
 struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len,
                                 gfp_t gfp_mask)
 {
-       struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache);
+       struct napi_alloc_cache *nc;
        struct sk_buff *skb;
        void *data;
 
        len += NET_SKB_PAD + NET_IP_ALIGN;
 
-       if ((len > SKB_WITH_OVERHEAD(PAGE_SIZE)) ||
+       /* If requested length is either too small or too big,
+        * we use kmalloc() for skb->head allocation.
+        */
+       if (len <= SKB_WITH_OVERHEAD(1024) ||
+           len > SKB_WITH_OVERHEAD(PAGE_SIZE) ||
            (gfp_mask & (__GFP_DIRECT_RECLAIM | GFP_DMA))) {
                skb = __alloc_skb(len, gfp_mask, SKB_ALLOC_RX, NUMA_NO_NODE);
                if (!skb)
@@ -515,6 +523,7 @@ struct sk_buff *__napi_alloc_skb(struct napi_struct *napi, unsigned int len,
                goto skb_success;
        }
 
+       nc = this_cpu_ptr(&napi_alloc_cache);
        len += SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
        len = SKB_DATA_ALIGN(len);
 
@@ -605,13 +614,14 @@ static void skb_release_data(struct sk_buff *skb)
                              &shinfo->dataref))
                return;
 
+       skb_zcopy_clear(skb, true);
+
        for (i = 0; i < shinfo->nr_frags; i++)
                __skb_frag_unref(&shinfo->frags[i]);
 
        if (shinfo->frag_list)
                kfree_skb_list(shinfo->frag_list);
 
-       skb_zcopy_clear(skb, true);
        skb_free_head(skb);
 }
 
@@ -1093,7 +1103,7 @@ void mm_unaccount_pinned_pages(struct mmpin *mmp)
 }
 EXPORT_SYMBOL_GPL(mm_unaccount_pinned_pages);
 
-struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size)
+struct ubuf_info *msg_zerocopy_alloc(struct sock *sk, size_t size)
 {
        struct ubuf_info *uarg;
        struct sk_buff *skb;
@@ -1113,25 +1123,26 @@ struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size)
                return NULL;
        }
 
-       uarg->callback = sock_zerocopy_callback;
+       uarg->callback = msg_zerocopy_callback;
        uarg->id = ((u32)atomic_inc_return(&sk->sk_zckey)) - 1;
        uarg->len = 1;
        uarg->bytelen = size;
        uarg->zerocopy = 1;
+       uarg->flags = SKBFL_ZEROCOPY_FRAG;
        refcount_set(&uarg->refcnt, 1);
        sock_hold(sk);
 
        return uarg;
 }
-EXPORT_SYMBOL_GPL(sock_zerocopy_alloc);
+EXPORT_SYMBOL_GPL(msg_zerocopy_alloc);
 
 static inline struct sk_buff *skb_from_uarg(struct ubuf_info *uarg)
 {
        return container_of((void *)uarg, struct sk_buff, cb);
 }
 
-struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size,
-                                       struct ubuf_info *uarg)
+struct ubuf_info *msg_zerocopy_realloc(struct sock *sk, size_t size,
+                                      struct ubuf_info *uarg)
 {
        if (uarg) {
                const u32 byte_limit = 1 << 19;         /* limit to a few TSO */
@@ -1163,16 +1174,16 @@ struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size,
 
                        /* no extra ref when appending to datagram (MSG_MORE) */
                        if (sk->sk_type == SOCK_STREAM)
-                               sock_zerocopy_get(uarg);
+                               net_zcopy_get(uarg);
 
                        return uarg;
                }
        }
 
 new_alloc:
-       return sock_zerocopy_alloc(sk, size);
+       return msg_zerocopy_alloc(sk, size);
 }
-EXPORT_SYMBOL_GPL(sock_zerocopy_realloc);
+EXPORT_SYMBOL_GPL(msg_zerocopy_realloc);
 
 static bool skb_zerocopy_notify_extend(struct sk_buff *skb, u32 lo, u16 len)
 {
@@ -1194,7 +1205,7 @@ static bool skb_zerocopy_notify_extend(struct sk_buff *skb, u32 lo, u16 len)
        return true;
 }
 
-void sock_zerocopy_callback(struct ubuf_info *uarg, bool success)
+static void __msg_zerocopy_callback(struct ubuf_info *uarg)
 {
        struct sk_buff *tail, *skb = skb_from_uarg(uarg);
        struct sock_exterr_skb *serr;
@@ -1222,7 +1233,7 @@ void sock_zerocopy_callback(struct ubuf_info *uarg, bool success)
        serr->ee.ee_origin = SO_EE_ORIGIN_ZEROCOPY;
        serr->ee.ee_data = hi;
        serr->ee.ee_info = lo;
-       if (!success)
+       if (!uarg->zerocopy)
                serr->ee.ee_code |= SO_EE_CODE_ZEROCOPY_COPIED;
 
        q = &sk->sk_error_queue;
@@ -1241,32 +1252,28 @@ release:
        consume_skb(skb);
        sock_put(sk);
 }
-EXPORT_SYMBOL_GPL(sock_zerocopy_callback);
 
-void sock_zerocopy_put(struct ubuf_info *uarg)
+void msg_zerocopy_callback(struct sk_buff *skb, struct ubuf_info *uarg,
+                          bool success)
 {
-       if (uarg && refcount_dec_and_test(&uarg->refcnt)) {
-               if (uarg->callback)
-                       uarg->callback(uarg, uarg->zerocopy);
-               else
-                       consume_skb(skb_from_uarg(uarg));
-       }
+       uarg->zerocopy = uarg->zerocopy & success;
+
+       if (refcount_dec_and_test(&uarg->refcnt))
+               __msg_zerocopy_callback(uarg);
 }
-EXPORT_SYMBOL_GPL(sock_zerocopy_put);
+EXPORT_SYMBOL_GPL(msg_zerocopy_callback);
 
-void sock_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref)
+void msg_zerocopy_put_abort(struct ubuf_info *uarg, bool have_uref)
 {
-       if (uarg) {
-               struct sock *sk = skb_from_uarg(uarg)->sk;
+       struct sock *sk = skb_from_uarg(uarg)->sk;
 
-               atomic_dec(&sk->sk_zckey);
-               uarg->len--;
+       atomic_dec(&sk->sk_zckey);
+       uarg->len--;
 
-               if (have_uref)
-                       sock_zerocopy_put(uarg);
-       }
+       if (have_uref)
+               msg_zerocopy_callback(NULL, uarg, true);
 }
-EXPORT_SYMBOL_GPL(sock_zerocopy_put_abort);
+EXPORT_SYMBOL_GPL(msg_zerocopy_put_abort);
 
 int skb_zerocopy_iter_dgram(struct sk_buff *skb, struct msghdr *msg, int len)
 {
@@ -1330,7 +1337,7 @@ static int skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig,
  *     @skb: the skb to modify
  *     @gfp_mask: allocation priority
  *
- *     This must be called on SKBTX_DEV_ZEROCOPY skb.
+ *     This must be called on skb with SKBFL_ZEROCOPY_ENABLE.
  *     It will copy all frags into kernel and drop the reference
  *     to userspace pages.
  *
@@ -3267,8 +3274,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len)
 {
        int pos = skb_headlen(skb);
 
-       skb_shinfo(skb1)->tx_flags |= skb_shinfo(skb)->tx_flags &
-                                     SKBTX_SHARED_FRAG;
+       skb_shinfo(skb1)->flags |= skb_shinfo(skb)->flags & SKBFL_SHARED_FRAG;
        skb_zerocopy_clone(skb1, skb, 0);
        if (len < pos)  /* Split line is inside header. */
                skb_split_inside_header(skb, skb1, len, pos);
@@ -3442,6 +3448,7 @@ void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from,
        st->root_skb = st->cur_skb = skb;
        st->frag_idx = st->stepped_offset = 0;
        st->frag_data = NULL;
+       st->frag_off = 0;
 }
 EXPORT_SYMBOL(skb_prepare_seq_read);
 
@@ -3496,14 +3503,27 @@ next_skb:
                st->stepped_offset += skb_headlen(st->cur_skb);
 
        while (st->frag_idx < skb_shinfo(st->cur_skb)->nr_frags) {
+               unsigned int pg_idx, pg_off, pg_sz;
+
                frag = &skb_shinfo(st->cur_skb)->frags[st->frag_idx];
-               block_limit = skb_frag_size(frag) + st->stepped_offset;
 
+               pg_idx = 0;
+               pg_off = skb_frag_off(frag);
+               pg_sz = skb_frag_size(frag);
+
+               if (skb_frag_must_loop(skb_frag_page(frag))) {
+                       pg_idx = (pg_off + st->frag_off) >> PAGE_SHIFT;
+                       pg_off = offset_in_page(pg_off + st->frag_off);
+                       pg_sz = min_t(unsigned int, pg_sz - st->frag_off,
+                                                   PAGE_SIZE - pg_off);
+               }
+
+               block_limit = pg_sz + st->stepped_offset;
                if (abs_offset < block_limit) {
                        if (!st->frag_data)
-                               st->frag_data = kmap_atomic(skb_frag_page(frag));
+                               st->frag_data = kmap_atomic(skb_frag_page(frag) + pg_idx);
 
-                       *data = (u8 *) st->frag_data + skb_frag_off(frag) +
+                       *data = (u8 *)st->frag_data + pg_off +
                                (abs_offset - st->stepped_offset);
 
                        return block_limit - abs_offset;
@@ -3514,8 +3534,12 @@ next_skb:
                        st->frag_data = NULL;
                }
 
-               st->frag_idx++;
-               st->stepped_offset += skb_frag_size(frag);
+               st->stepped_offset += pg_sz;
+               st->frag_off += pg_sz;
+               if (st->frag_off == skb_frag_size(frag)) {
+                       st->frag_off = 0;
+                       st->frag_idx++;
+               }
        }
 
        if (st->frag_data) {
@@ -3655,7 +3679,8 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
        unsigned int delta_truesize = 0;
        unsigned int delta_len = 0;
        struct sk_buff *tail = NULL;
-       struct sk_buff *nskb;
+       struct sk_buff *nskb, *tmp;
+       int err;
 
        skb_push(skb, -skb_network_offset(skb) + offset);
 
@@ -3665,11 +3690,28 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
                nskb = list_skb;
                list_skb = list_skb->next;
 
+               err = 0;
+               if (skb_shared(nskb)) {
+                       tmp = skb_clone(nskb, GFP_ATOMIC);
+                       if (tmp) {
+                               consume_skb(nskb);
+                               nskb = tmp;
+                               err = skb_unclone(nskb, GFP_ATOMIC);
+                       } else {
+                               err = -ENOMEM;
+                       }
+               }
+
                if (!tail)
                        skb->next = nskb;
                else
                        tail->next = nskb;
 
+               if (unlikely(err)) {
+                       nskb->next = list_skb;
+                       goto err_linearize;
+               }
+
                tail = nskb;
 
                delta_len += nskb->len;
@@ -3856,12 +3898,8 @@ normal:
                }
 
                hsize = skb_headlen(head_skb) - offset;
-               if (hsize < 0)
-                       hsize = 0;
-               if (hsize > len || !sg)
-                       hsize = len;
 
-               if (!hsize && i >= nfrags && skb_headlen(list_skb) &&
+               if (hsize <= 0 && i >= nfrags && skb_headlen(list_skb) &&
                    (skb_headlen(list_skb) == len || sg)) {
                        BUG_ON(skb_headlen(list_skb) > len);
 
@@ -3904,6 +3942,11 @@ normal:
                        skb_release_head_state(nskb);
                        __skb_push(nskb, doffset);
                } else {
+                       if (hsize < 0)
+                               hsize = 0;
+                       if (hsize > len || !sg)
+                               hsize = len;
+
                        nskb = __alloc_skb(hsize + doffset + headroom,
                                           GFP_ATOMIC, skb_alloc_rx_flag(head_skb),
                                           NUMA_NO_NODE);
@@ -3957,8 +4000,8 @@ normal:
                skb_copy_from_linear_data_offset(head_skb, offset,
                                                 skb_put(nskb, hsize), hsize);
 
-               skb_shinfo(nskb)->tx_flags |= skb_shinfo(head_skb)->tx_flags &
-                                             SKBTX_SHARED_FRAG;
+               skb_shinfo(nskb)->flags |= skb_shinfo(head_skb)->flags &
+                                          SKBFL_SHARED_FRAG;
 
                if (skb_orphan_frags(frag_skb, GFP_ATOMIC) ||
                    skb_zerocopy_clone(nskb, frag_skb, GFP_ATOMIC))
@@ -4678,6 +4721,7 @@ err:
 EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
 
 void __skb_tstamp_tx(struct sk_buff *orig_skb,
+                    const struct sk_buff *ack_skb,
                     struct skb_shared_hwtstamps *hwtstamps,
                     struct sock *sk, int tstype)
 {
@@ -4700,7 +4744,8 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
                if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS) &&
                    sk->sk_protocol == IPPROTO_TCP &&
                    sk->sk_type == SOCK_STREAM) {
-                       skb = tcp_get_timestamping_opt_stats(sk, orig_skb);
+                       skb = tcp_get_timestamping_opt_stats(sk, orig_skb,
+                                                            ack_skb);
                        opt_stats = true;
                } else
 #endif
@@ -4729,7 +4774,7 @@ EXPORT_SYMBOL_GPL(__skb_tstamp_tx);
 void skb_tstamp_tx(struct sk_buff *orig_skb,
                   struct skb_shared_hwtstamps *hwtstamps)
 {
-       return __skb_tstamp_tx(orig_skb, hwtstamps, orig_skb->sk,
+       return __skb_tstamp_tx(orig_skb, NULL, hwtstamps, orig_skb->sk,
                               SCM_TSTAMP_SND);
 }
 EXPORT_SYMBOL_GPL(skb_tstamp_tx);
index bbcd4b9..4600e58 100644 (file)
@@ -1876,123 +1876,120 @@ static void sk_init_common(struct sock *sk)
 struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
 {
        struct proto *prot = READ_ONCE(sk->sk_prot);
-       struct sock *newsk;
+       struct sk_filter *filter;
        bool is_charged = true;
+       struct sock *newsk;
 
        newsk = sk_prot_alloc(prot, priority, sk->sk_family);
-       if (newsk != NULL) {
-               struct sk_filter *filter;
+       if (!newsk)
+               goto out;
 
-               sock_copy(newsk, sk);
+       sock_copy(newsk, sk);
 
-               newsk->sk_prot_creator = prot;
+       newsk->sk_prot_creator = prot;
 
-               /* SANITY */
-               if (likely(newsk->sk_net_refcnt))
-                       get_net(sock_net(newsk));
-               sk_node_init(&newsk->sk_node);
-               sock_lock_init(newsk);
-               bh_lock_sock(newsk);
-               newsk->sk_backlog.head  = newsk->sk_backlog.tail = NULL;
-               newsk->sk_backlog.len = 0;
+       /* SANITY */
+       if (likely(newsk->sk_net_refcnt))
+               get_net(sock_net(newsk));
+       sk_node_init(&newsk->sk_node);
+       sock_lock_init(newsk);
+       bh_lock_sock(newsk);
+       newsk->sk_backlog.head  = newsk->sk_backlog.tail = NULL;
+       newsk->sk_backlog.len = 0;
 
-               atomic_set(&newsk->sk_rmem_alloc, 0);
-               /*
-                * sk_wmem_alloc set to one (see sk_free() and sock_wfree())
-                */
-               refcount_set(&newsk->sk_wmem_alloc, 1);
-               atomic_set(&newsk->sk_omem_alloc, 0);
-               sk_init_common(newsk);
+       atomic_set(&newsk->sk_rmem_alloc, 0);
 
-               newsk->sk_dst_cache     = NULL;
-               newsk->sk_dst_pending_confirm = 0;
-               newsk->sk_wmem_queued   = 0;
-               newsk->sk_forward_alloc = 0;
-               atomic_set(&newsk->sk_drops, 0);
-               newsk->sk_send_head     = NULL;
-               newsk->sk_userlocks     = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
-               atomic_set(&newsk->sk_zckey, 0);
+       /* sk_wmem_alloc set to one (see sk_free() and sock_wfree()) */
+       refcount_set(&newsk->sk_wmem_alloc, 1);
 
-               sock_reset_flag(newsk, SOCK_DONE);
+       atomic_set(&newsk->sk_omem_alloc, 0);
+       sk_init_common(newsk);
 
-               /* sk->sk_memcg will be populated at accept() time */
-               newsk->sk_memcg = NULL;
+       newsk->sk_dst_cache     = NULL;
+       newsk->sk_dst_pending_confirm = 0;
+       newsk->sk_wmem_queued   = 0;
+       newsk->sk_forward_alloc = 0;
+       atomic_set(&newsk->sk_drops, 0);
+       newsk->sk_send_head     = NULL;
+       newsk->sk_userlocks     = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK;
+       atomic_set(&newsk->sk_zckey, 0);
 
-               cgroup_sk_clone(&newsk->sk_cgrp_data);
+       sock_reset_flag(newsk, SOCK_DONE);
 
-               rcu_read_lock();
-               filter = rcu_dereference(sk->sk_filter);
-               if (filter != NULL)
-                       /* though it's an empty new sock, the charging may fail
-                        * if sysctl_optmem_max was changed between creation of
-                        * original socket and cloning
-                        */
-                       is_charged = sk_filter_charge(newsk, filter);
-               RCU_INIT_POINTER(newsk->sk_filter, filter);
-               rcu_read_unlock();
+       /* sk->sk_memcg will be populated at accept() time */
+       newsk->sk_memcg = NULL;
 
-               if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
-                       /* We need to make sure that we don't uncharge the new
-                        * socket if we couldn't charge it in the first place
-                        * as otherwise we uncharge the parent's filter.
-                        */
-                       if (!is_charged)
-                               RCU_INIT_POINTER(newsk->sk_filter, NULL);
-                       sk_free_unlock_clone(newsk);
-                       newsk = NULL;
-                       goto out;
-               }
-               RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
+       cgroup_sk_clone(&newsk->sk_cgrp_data);
 
-               if (bpf_sk_storage_clone(sk, newsk)) {
-                       sk_free_unlock_clone(newsk);
-                       newsk = NULL;
-                       goto out;
-               }
+       rcu_read_lock();
+       filter = rcu_dereference(sk->sk_filter);
+       if (filter != NULL)
+               /* though it's an empty new sock, the charging may fail
+                * if sysctl_optmem_max was changed between creation of
+                * original socket and cloning
+                */
+               is_charged = sk_filter_charge(newsk, filter);
+       RCU_INIT_POINTER(newsk->sk_filter, filter);
+       rcu_read_unlock();
 
-               /* Clear sk_user_data if parent had the pointer tagged
-                * as not suitable for copying when cloning.
+       if (unlikely(!is_charged || xfrm_sk_clone_policy(newsk, sk))) {
+               /* We need to make sure that we don't uncharge the new
+                * socket if we couldn't charge it in the first place
+                * as otherwise we uncharge the parent's filter.
                 */
-               if (sk_user_data_is_nocopy(newsk))
-                       newsk->sk_user_data = NULL;
+               if (!is_charged)
+                       RCU_INIT_POINTER(newsk->sk_filter, NULL);
+               sk_free_unlock_clone(newsk);
+               newsk = NULL;
+               goto out;
+       }
+       RCU_INIT_POINTER(newsk->sk_reuseport_cb, NULL);
 
-               newsk->sk_err      = 0;
-               newsk->sk_err_soft = 0;
-               newsk->sk_priority = 0;
-               newsk->sk_incoming_cpu = raw_smp_processor_id();
-               if (likely(newsk->sk_net_refcnt))
-                       sock_inuse_add(sock_net(newsk), 1);
+       if (bpf_sk_storage_clone(sk, newsk)) {
+               sk_free_unlock_clone(newsk);
+               newsk = NULL;
+               goto out;
+       }
 
-               /*
-                * Before updating sk_refcnt, we must commit prior changes to memory
-                * (Documentation/RCU/rculist_nulls.rst for details)
-                */
-               smp_wmb();
-               refcount_set(&newsk->sk_refcnt, 2);
+       /* Clear sk_user_data if parent had the pointer tagged
+        * as not suitable for copying when cloning.
+        */
+       if (sk_user_data_is_nocopy(newsk))
+               newsk->sk_user_data = NULL;
 
-               /*
-                * Increment the counter in the same struct proto as the master
-                * sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that
-                * is the same as sk->sk_prot->socks, as this field was copied
-                * with memcpy).
-                *
-                * This _changes_ the previous behaviour, where
-                * tcp_create_openreq_child always was incrementing the
-                * equivalent to tcp_prot->socks (inet_sock_nr), so this have
-                * to be taken into account in all callers. -acme
-                */
-               sk_refcnt_debug_inc(newsk);
-               sk_set_socket(newsk, NULL);
-               sk_tx_queue_clear(newsk);
-               RCU_INIT_POINTER(newsk->sk_wq, NULL);
+       newsk->sk_err      = 0;
+       newsk->sk_err_soft = 0;
+       newsk->sk_priority = 0;
+       newsk->sk_incoming_cpu = raw_smp_processor_id();
+       if (likely(newsk->sk_net_refcnt))
+               sock_inuse_add(sock_net(newsk), 1);
 
-               if (newsk->sk_prot->sockets_allocated)
-                       sk_sockets_allocated_inc(newsk);
+       /* Before updating sk_refcnt, we must commit prior changes to memory
+        * (Documentation/RCU/rculist_nulls.rst for details)
+        */
+       smp_wmb();
+       refcount_set(&newsk->sk_refcnt, 2);
 
-               if (sock_needs_netstamp(sk) &&
-                   newsk->sk_flags & SK_FLAGS_TIMESTAMP)
-                       net_enable_timestamp();
-       }
+       /* Increment the counter in the same struct proto as the master
+        * sock (sk_refcnt_debug_inc uses newsk->sk_prot->socks, that
+        * is the same as sk->sk_prot->socks, as this field was copied
+        * with memcpy).
+        *
+        * This _changes_ the previous behaviour, where
+        * tcp_create_openreq_child always was incrementing the
+        * equivalent to tcp_prot->socks (inet_sock_nr), so this have
+        * to be taken into account in all callers. -acme
+        */
+       sk_refcnt_debug_inc(newsk);
+       sk_set_socket(newsk, NULL);
+       sk_tx_queue_clear(newsk);
+       RCU_INIT_POINTER(newsk->sk_wq, NULL);
+
+       if (newsk->sk_prot->sockets_allocated)
+               sk_sockets_allocated_inc(newsk);
+
+       if (sock_needs_netstamp(sk) && newsk->sk_flags & SK_FLAGS_TIMESTAMP)
+               net_enable_timestamp();
 out:
        return newsk;
 }
index 64b5ec1..d758fb8 100644 (file)
@@ -602,7 +602,7 @@ int sock_map_update_elem_sys(struct bpf_map *map, void *key, void *value,
                ret = sock_hash_update_common(map, key, sk, flags);
        sock_map_sk_release(sk);
 out:
-       fput(sock->file);
+       sockfd_put(sock);
        return ret;
 }
 
index bbdd3c7..b065f0a 100644 (file)
@@ -293,7 +293,7 @@ select_by_hash:
                        i = j = reciprocal_scale(hash, socks);
                        while (reuse->socks[i]->sk_state == TCP_ESTABLISHED) {
                                i++;
-                               if (i >= reuse->num_socks)
+                               if (i >= socks)
                                        i = 0;
                                if (i == j)
                                        goto out;
index d86d8d1..4567de5 100644 (file)
@@ -309,7 +309,6 @@ proc_dolongvec_minmax_bpf_restricted(struct ctl_table *table, int write,
 #endif
 
 static struct ctl_table net_core_table[] = {
-#ifdef CONFIG_NET
        {
                .procname       = "wmem_max",
                .data           = &sysctl_wmem_max,
@@ -507,7 +506,6 @@ static struct ctl_table net_core_table[] = {
                .proc_handler   = set_default_qdisc
        },
 #endif
-#endif /* CONFIG_NET */
        {
                .procname       = "netdev_budget",
                .data           = &netdev_budget,
index 3016e5a..2c0fa16 100644 (file)
@@ -1,2 +1,2 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_DCB) += dcbnl.o dcbevent.o
+obj-y += dcbnl.o dcbevent.o
index 084e159..653e3bc 100644 (file)
@@ -1765,6 +1765,8 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh,
        fn = &reply_funcs[dcb->cmd];
        if (!fn->cb)
                return -EOPNOTSUPP;
+       if (fn->type == RTM_SETDCB && !netlink_capable(skb, CAP_NET_ADMIN))
+               return -EPERM;
 
        if (!tb[DCB_ATTR_IFNAME])
                return -EINVAL;
index 4cac31d..2193ae5 100644 (file)
@@ -1035,7 +1035,7 @@ source_ok:
                        fld.saddr = dnet_select_source(dev_out, 0,
                                                       RT_SCOPE_HOST);
                        if (!fld.daddr)
-                               goto out;
+                               goto done;
                }
                fld.flowidn_oif = LOOPBACK_IFINDEX;
                res.type = RTN_LOCAL;
index 255df9b..155b061 100644 (file)
@@ -4,7 +4,7 @@
 #
 config DNS_RESOLVER
        tristate "DNS Resolver support"
-       depends on NET && KEYS
+       depends on KEYS
        help
          Saying Y here will include support for the DNS Resolver key type
          which can be used to make upcalls to perform DNS lookups in
index dfecd7b..2d226a5 100644 (file)
@@ -139,4 +139,10 @@ config NET_DSA_TAG_TRAILER
          Say Y or M if you want to enable support for tagging frames at
          with a trailed. e.g. Marvell 88E6060.
 
+config NET_DSA_TAG_XRS700X
+       tristate "Tag driver for XRS700x switches"
+       help
+         Say Y or M if you want to enable support for tagging frames for
+         Arrow SpeedChips XRS700x switches that use a single byte tag trailer.
+
 endif
index 0fb2b75..92cea21 100644 (file)
@@ -18,3 +18,4 @@ obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
 obj-$(CONFIG_NET_DSA_TAG_QCA) += tag_qca.o
 obj-$(CONFIG_NET_DSA_TAG_SJA1105) += tag_sja1105.o
 obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
+obj-$(CONFIG_NET_DSA_TAG_XRS700X) += tag_xrs700x.o
index a1b1dc8..f4ce3c5 100644 (file)
@@ -219,11 +219,21 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev,
        }
 
        skb = nskb;
-       p = netdev_priv(skb->dev);
        skb_push(skb, ETH_HLEN);
        skb->pkt_type = PACKET_HOST;
        skb->protocol = eth_type_trans(skb, skb->dev);
 
+       if (unlikely(!dsa_slave_dev_check(skb->dev))) {
+               /* Packet is to be injected directly on an upper
+                * device, e.g. a team/bond, so skip all DSA-port
+                * specific actions.
+                */
+               netif_rx(skb);
+               return 0;
+       }
+
+       p = netdev_priv(skb->dev);
+
        if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
                nskb = dsa_untag_bridge_pvid(skb);
                if (!nskb) {
@@ -309,28 +319,6 @@ bool dsa_schedule_work(struct work_struct *work)
        return queue_work(dsa_owq, work);
 }
 
-static ATOMIC_NOTIFIER_HEAD(dsa_notif_chain);
-
-int register_dsa_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_register(&dsa_notif_chain, nb);
-}
-EXPORT_SYMBOL_GPL(register_dsa_notifier);
-
-int unregister_dsa_notifier(struct notifier_block *nb)
-{
-       return atomic_notifier_chain_unregister(&dsa_notif_chain, nb);
-}
-EXPORT_SYMBOL_GPL(unregister_dsa_notifier);
-
-int call_dsa_notifiers(unsigned long val, struct net_device *dev,
-                      struct dsa_notifier_info *info)
-{
-       info->dev = dev;
-       return atomic_notifier_call_chain(&dsa_notif_chain, val, info);
-}
-EXPORT_SYMBOL_GPL(call_dsa_notifiers);
-
 int dsa_devlink_param_get(struct devlink *dl, u32 id,
                          struct devlink_param_gset_ctx *ctx)
 {
index 183003e..cc13549 100644 (file)
 static DEFINE_MUTEX(dsa2_mutex);
 LIST_HEAD(dsa_tree_list);
 
+/**
+ * dsa_lag_map() - Map LAG netdev to a linear LAG ID
+ * @dst: Tree in which to record the mapping.
+ * @lag: Netdev that is to be mapped to an ID.
+ *
+ * dsa_lag_id/dsa_lag_dev can then be used to translate between the
+ * two spaces. The size of the mapping space is determined by the
+ * driver by setting ds->num_lag_ids. It is perfectly legal to leave
+ * it unset if it is not needed, in which case these functions become
+ * no-ops.
+ */
+void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag)
+{
+       unsigned int id;
+
+       if (dsa_lag_id(dst, lag) >= 0)
+               /* Already mapped */
+               return;
+
+       for (id = 0; id < dst->lags_len; id++) {
+               if (!dsa_lag_dev(dst, id)) {
+                       dst->lags[id] = lag;
+                       return;
+               }
+       }
+
+       /* No IDs left, which is OK. Some drivers do not need it. The
+        * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id
+        * returns an error for this device when joining the LAG. The
+        * driver can then return -EOPNOTSUPP back to DSA, which will
+        * fall back to a software LAG.
+        */
+}
+
+/**
+ * dsa_lag_unmap() - Remove a LAG ID mapping
+ * @dst: Tree in which the mapping is recorded.
+ * @lag: Netdev that was mapped.
+ *
+ * As there may be multiple users of the mapping, it is only removed
+ * if there are no other references to it.
+ */
+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag)
+{
+       struct dsa_port *dp;
+       unsigned int id;
+
+       dsa_lag_foreach_port(dp, dst, lag)
+               /* There are remaining users of this mapping */
+               return;
+
+       dsa_lags_foreach_id(id, dst) {
+               if (dsa_lag_dev(dst, id) == lag) {
+                       dst->lags[id] = NULL;
+                       break;
+               }
+       }
+}
+
 struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
 {
        struct dsa_switch_tree *dst;
@@ -353,9 +412,13 @@ static int dsa_port_devlink_setup(struct dsa_port *dp)
 
 static void dsa_port_teardown(struct dsa_port *dp)
 {
+       struct devlink_port *dlp = &dp->devlink_port;
+
        if (!dp->setup)
                return;
 
+       devlink_port_type_clear(dlp);
+
        switch (dp->type) {
        case DSA_PORT_TYPE_UNUSED:
                break;
@@ -400,8 +463,165 @@ static int dsa_devlink_info_get(struct devlink *dl,
        return -EOPNOTSUPP;
 }
 
+static int dsa_devlink_sb_pool_get(struct devlink *dl,
+                                  unsigned int sb_index, u16 pool_index,
+                                  struct devlink_sb_pool_info *pool_info)
+{
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+
+       if (!ds->ops->devlink_sb_pool_get)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_pool_get(ds, sb_index, pool_index,
+                                           pool_info);
+}
+
+static int dsa_devlink_sb_pool_set(struct devlink *dl, unsigned int sb_index,
+                                  u16 pool_index, u32 size,
+                                  enum devlink_sb_threshold_type threshold_type,
+                                  struct netlink_ext_ack *extack)
+{
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+
+       if (!ds->ops->devlink_sb_pool_set)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_pool_set(ds, sb_index, pool_index, size,
+                                           threshold_type, extack);
+}
+
+static int dsa_devlink_sb_port_pool_get(struct devlink_port *dlp,
+                                       unsigned int sb_index, u16 pool_index,
+                                       u32 *p_threshold)
+{
+       struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
+       int port = dsa_devlink_port_to_port(dlp);
+
+       if (!ds->ops->devlink_sb_port_pool_get)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_port_pool_get(ds, port, sb_index,
+                                                pool_index, p_threshold);
+}
+
+static int dsa_devlink_sb_port_pool_set(struct devlink_port *dlp,
+                                       unsigned int sb_index, u16 pool_index,
+                                       u32 threshold,
+                                       struct netlink_ext_ack *extack)
+{
+       struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
+       int port = dsa_devlink_port_to_port(dlp);
+
+       if (!ds->ops->devlink_sb_port_pool_set)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_port_pool_set(ds, port, sb_index,
+                                                pool_index, threshold, extack);
+}
+
+static int
+dsa_devlink_sb_tc_pool_bind_get(struct devlink_port *dlp,
+                               unsigned int sb_index, u16 tc_index,
+                               enum devlink_sb_pool_type pool_type,
+                               u16 *p_pool_index, u32 *p_threshold)
+{
+       struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
+       int port = dsa_devlink_port_to_port(dlp);
+
+       if (!ds->ops->devlink_sb_tc_pool_bind_get)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_tc_pool_bind_get(ds, port, sb_index,
+                                                   tc_index, pool_type,
+                                                   p_pool_index, p_threshold);
+}
+
+static int
+dsa_devlink_sb_tc_pool_bind_set(struct devlink_port *dlp,
+                               unsigned int sb_index, u16 tc_index,
+                               enum devlink_sb_pool_type pool_type,
+                               u16 pool_index, u32 threshold,
+                               struct netlink_ext_ack *extack)
+{
+       struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
+       int port = dsa_devlink_port_to_port(dlp);
+
+       if (!ds->ops->devlink_sb_tc_pool_bind_set)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_tc_pool_bind_set(ds, port, sb_index,
+                                                   tc_index, pool_type,
+                                                   pool_index, threshold,
+                                                   extack);
+}
+
+static int dsa_devlink_sb_occ_snapshot(struct devlink *dl,
+                                      unsigned int sb_index)
+{
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+
+       if (!ds->ops->devlink_sb_occ_snapshot)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_occ_snapshot(ds, sb_index);
+}
+
+static int dsa_devlink_sb_occ_max_clear(struct devlink *dl,
+                                       unsigned int sb_index)
+{
+       struct dsa_switch *ds = dsa_devlink_to_ds(dl);
+
+       if (!ds->ops->devlink_sb_occ_max_clear)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_occ_max_clear(ds, sb_index);
+}
+
+static int dsa_devlink_sb_occ_port_pool_get(struct devlink_port *dlp,
+                                           unsigned int sb_index,
+                                           u16 pool_index, u32 *p_cur,
+                                           u32 *p_max)
+{
+       struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
+       int port = dsa_devlink_port_to_port(dlp);
+
+       if (!ds->ops->devlink_sb_occ_port_pool_get)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_occ_port_pool_get(ds, port, sb_index,
+                                                    pool_index, p_cur, p_max);
+}
+
+static int
+dsa_devlink_sb_occ_tc_port_bind_get(struct devlink_port *dlp,
+                                   unsigned int sb_index, u16 tc_index,
+                                   enum devlink_sb_pool_type pool_type,
+                                   u32 *p_cur, u32 *p_max)
+{
+       struct dsa_switch *ds = dsa_devlink_port_to_ds(dlp);
+       int port = dsa_devlink_port_to_port(dlp);
+
+       if (!ds->ops->devlink_sb_occ_tc_port_bind_get)
+               return -EOPNOTSUPP;
+
+       return ds->ops->devlink_sb_occ_tc_port_bind_get(ds, port,
+                                                       sb_index, tc_index,
+                                                       pool_type, p_cur,
+                                                       p_max);
+}
+
 static const struct devlink_ops dsa_devlink_ops = {
-       .info_get = dsa_devlink_info_get,
+       .info_get                       = dsa_devlink_info_get,
+       .sb_pool_get                    = dsa_devlink_sb_pool_get,
+       .sb_pool_set                    = dsa_devlink_sb_pool_set,
+       .sb_port_pool_get               = dsa_devlink_sb_port_pool_get,
+       .sb_port_pool_set               = dsa_devlink_sb_port_pool_set,
+       .sb_tc_pool_bind_get            = dsa_devlink_sb_tc_pool_bind_get,
+       .sb_tc_pool_bind_set            = dsa_devlink_sb_tc_pool_bind_set,
+       .sb_occ_snapshot                = dsa_devlink_sb_occ_snapshot,
+       .sb_occ_max_clear               = dsa_devlink_sb_occ_max_clear,
+       .sb_occ_port_pool_get           = dsa_devlink_sb_occ_port_pool_get,
+       .sb_occ_tc_port_bind_get        = dsa_devlink_sb_occ_tc_port_bind_get,
 };
 
 static int dsa_switch_setup(struct dsa_switch *ds)
@@ -448,6 +668,8 @@ static int dsa_switch_setup(struct dsa_switch *ds)
        if (err)
                goto unregister_devlink_ports;
 
+       ds->configure_vlan_while_not_filtering = true;
+
        err = ds->ops->setup(ds);
        if (err < 0)
                goto unregister_notifier;
@@ -578,6 +800,32 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst)
                        dsa_master_teardown(dp->master);
 }
 
+static int dsa_tree_setup_lags(struct dsa_switch_tree *dst)
+{
+       unsigned int len = 0;
+       struct dsa_port *dp;
+
+       list_for_each_entry(dp, &dst->ports, list) {
+               if (dp->ds->num_lag_ids > len)
+                       len = dp->ds->num_lag_ids;
+       }
+
+       if (!len)
+               return 0;
+
+       dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL);
+       if (!dst->lags)
+               return -ENOMEM;
+
+       dst->lags_len = len;
+       return 0;
+}
+
+static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst)
+{
+       kfree(dst->lags);
+}
+
 static int dsa_tree_setup(struct dsa_switch_tree *dst)
 {
        bool complete;
@@ -605,12 +853,18 @@ static int dsa_tree_setup(struct dsa_switch_tree *dst)
        if (err)
                goto teardown_switches;
 
+       err = dsa_tree_setup_lags(dst);
+       if (err)
+               goto teardown_master;
+
        dst->setup = true;
 
        pr_info("DSA: tree %d setup\n", dst->index);
 
        return 0;
 
+teardown_master:
+       dsa_tree_teardown_master(dst);
 teardown_switches:
        dsa_tree_teardown_switches(dst);
 teardown_default_cpu:
@@ -626,6 +880,8 @@ static void dsa_tree_teardown(struct dsa_switch_tree *dst)
        if (!dst->setup)
                return;
 
+       dsa_tree_teardown_lags(dst);
+
        dsa_tree_teardown_master(dst);
 
        dsa_tree_teardown_switches(dst);
@@ -783,6 +1039,8 @@ static int dsa_switch_parse_ports_of(struct dsa_switch *ds,
                        goto out_put_node;
 
                if (reg >= ds->num_ports) {
+                       dev_err(ds->dev, "port %pOF index %u exceeds num_ports (%zu)\n",
+                               port, reg, ds->num_ports);
                        err = -EINVAL;
                        goto out_put_node;
                }
index 7c96aae..2ce46bb 100644 (file)
@@ -20,6 +20,9 @@ enum {
        DSA_NOTIFIER_BRIDGE_LEAVE,
        DSA_NOTIFIER_FDB_ADD,
        DSA_NOTIFIER_FDB_DEL,
+       DSA_NOTIFIER_LAG_CHANGE,
+       DSA_NOTIFIER_LAG_JOIN,
+       DSA_NOTIFIER_LAG_LEAVE,
        DSA_NOTIFIER_MDB_ADD,
        DSA_NOTIFIER_MDB_DEL,
        DSA_NOTIFIER_VLAN_ADD,
@@ -29,7 +32,6 @@ enum {
 
 /* DSA_NOTIFIER_AGEING_TIME */
 struct dsa_notifier_ageing_time_info {
-       struct switchdev_trans *trans;
        unsigned int ageing_time;
 };
 
@@ -52,15 +54,22 @@ struct dsa_notifier_fdb_info {
 /* DSA_NOTIFIER_MDB_* */
 struct dsa_notifier_mdb_info {
        const struct switchdev_obj_port_mdb *mdb;
-       struct switchdev_trans *trans;
        int sw_index;
        int port;
 };
 
+/* DSA_NOTIFIER_LAG_* */
+struct dsa_notifier_lag_info {
+       struct net_device *lag;
+       int sw_index;
+       int port;
+
+       struct netdev_lag_upper_info *info;
+};
+
 /* DSA_NOTIFIER_VLAN_* */
 struct dsa_notifier_vlan_info {
        const struct switchdev_obj_port_vlan *vlan;
-       struct switchdev_trans *trans;
        int sw_index;
        int port;
 };
@@ -73,6 +82,18 @@ struct dsa_notifier_mtu_info {
        int mtu;
 };
 
+struct dsa_switchdev_event_work {
+       struct dsa_switch *ds;
+       int port;
+       struct work_struct work;
+       unsigned long event;
+       /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and
+        * SWITCHDEV_FDB_DEL_TO_DEVICE
+        */
+       unsigned char addr[ETH_ALEN];
+       u16 vid;
+};
+
 struct dsa_slave_priv {
        /* Copy of CPU port xmit for faster access in slave transmit hot path */
        struct sk_buff *        (*xmit)(struct sk_buff *skb,
@@ -98,15 +119,6 @@ void dsa_tag_driver_put(const struct dsa_device_ops *ops);
 bool dsa_schedule_work(struct work_struct *work);
 const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops);
 
-int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
-                      struct net_device *dev,
-                      const unsigned char *addr, u16 vid,
-                      u16 flags,
-                      struct netlink_ext_ack *extack);
-int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
-                      struct net_device *dev,
-                      const unsigned char *addr, u16 vid);
-
 /* master.c */
 int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp);
 void dsa_master_teardown(struct net_device *dev);
@@ -127,19 +139,21 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev,
 }
 
 /* port.c */
-int dsa_port_set_state(struct dsa_port *dp, u8 state,
-                      struct switchdev_trans *trans);
+int dsa_port_set_state(struct dsa_port *dp, u8 state);
 int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy);
 int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy);
 void dsa_port_disable_rt(struct dsa_port *dp);
 void dsa_port_disable(struct dsa_port *dp);
 int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br);
 void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br);
-int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
-                           struct switchdev_trans *trans);
+int dsa_port_lag_change(struct dsa_port *dp,
+                       struct netdev_lag_lower_state_info *linfo);
+int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
+                     struct netdev_lag_upper_info *uinfo);
+void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev);
+int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering);
 bool dsa_port_skip_vlan_configuration(struct dsa_port *dp);
-int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
-                        struct switchdev_trans *trans);
+int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock);
 int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
                        bool propagate_upstream);
 int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
@@ -148,31 +162,41 @@ int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
                     u16 vid);
 int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data);
 int dsa_port_mdb_add(const struct dsa_port *dp,
-                    const struct switchdev_obj_port_mdb *mdb,
-                    struct switchdev_trans *trans);
+                    const struct switchdev_obj_port_mdb *mdb);
 int dsa_port_mdb_del(const struct dsa_port *dp,
                     const struct switchdev_obj_port_mdb *mdb);
-int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
-                             struct switchdev_trans *trans);
-int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
-                         struct switchdev_trans *trans);
-int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
-                    struct switchdev_trans *trans);
+int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags);
+int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags);
+int dsa_port_mrouter(struct dsa_port *dp, bool mrouter);
 int dsa_port_vlan_add(struct dsa_port *dp,
-                     const struct switchdev_obj_port_vlan *vlan,
-                     struct switchdev_trans *trans);
+                     const struct switchdev_obj_port_vlan *vlan);
 int dsa_port_vlan_del(struct dsa_port *dp,
                      const struct switchdev_obj_port_vlan *vlan);
 int dsa_port_link_register_of(struct dsa_port *dp);
 void dsa_port_link_unregister_of(struct dsa_port *dp);
 extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
 
+static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
+                                           struct net_device *dev)
+{
+       /* Switchdev offloading can be configured on: */
+
+       if (dev == dp->slave)
+               /* DSA ports directly connected to a bridge. */
+               return true;
+
+       if (dp->lag_dev == dev)
+               /* DSA ports connected to a bridge via a LAG */
+               return true;
+
+       return false;
+}
+
 /* slave.c */
 extern const struct dsa_device_ops notag_netdev_ops;
 void dsa_slave_mii_bus_init(struct dsa_switch *ds);
 int dsa_slave_create(struct dsa_port *dp);
 void dsa_slave_destroy(struct net_device *slave_dev);
-bool dsa_slave_dev_check(const struct net_device *dev);
 int dsa_slave_suspend(struct net_device *slave_dev);
 int dsa_slave_resume(struct net_device *slave_dev);
 int dsa_slave_register_notifier(void);
@@ -257,6 +281,9 @@ int dsa_switch_register_notifier(struct dsa_switch *ds);
 void dsa_switch_unregister_notifier(struct dsa_switch *ds);
 
 /* dsa2.c */
+void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag);
+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag);
+
 extern struct list_head dsa_tree_list;
 
 #endif
index 5a0f6fe..cb3a5cf 100644 (file)
@@ -309,8 +309,18 @@ static struct lock_class_key dsa_master_addr_list_lock_key;
 int dsa_master_setup(struct net_device *dev, struct dsa_port *cpu_dp)
 {
        int mtu = ETH_DATA_LEN + cpu_dp->tag_ops->overhead;
+       struct dsa_switch *ds = cpu_dp->ds;
+       struct device_link *consumer_link;
        int ret;
 
+       /* The DSA master must use SET_NETDEV_DEV for this to work. */
+       consumer_link = device_link_add(ds->dev, dev->dev.parent,
+                                       DL_FLAG_AUTOREMOVE_CONSUMER);
+       if (!consumer_link)
+               netdev_err(dev,
+                          "Failed to create a device link to DSA switch %s\n",
+                          dev_name(ds->dev));
+
        rtnl_lock();
        ret = dev_set_mtu(dev, mtu);
        rtnl_unlock();
index 73569c9..f5b0f72 100644 (file)
@@ -40,17 +40,15 @@ static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
        return notifier_to_errno(err);
 }
 
-int dsa_port_set_state(struct dsa_port *dp, u8 state,
-                      struct switchdev_trans *trans)
+int dsa_port_set_state(struct dsa_port *dp, u8 state)
 {
        struct dsa_switch *ds = dp->ds;
        int port = dp->index;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return ds->ops->port_stp_state_set ? 0 : -EOPNOTSUPP;
+       if (!ds->ops->port_stp_state_set)
+               return -EOPNOTSUPP;
 
-       if (ds->ops->port_stp_state_set)
-               ds->ops->port_stp_state_set(ds, port, state);
+       ds->ops->port_stp_state_set(ds, port, state);
 
        if (ds->ops->port_fast_age) {
                /* Fast age FDB entries or flush appropriate forwarding database
@@ -75,7 +73,7 @@ static void dsa_port_set_state_now(struct dsa_port *dp, u8 state)
 {
        int err;
 
-       err = dsa_port_set_state(dp, state, NULL);
+       err = dsa_port_set_state(dp, state);
        if (err)
                pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
 }
@@ -145,7 +143,7 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
        int err;
 
        /* Set the flooding mode before joining the port in the switch */
-       err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL);
+       err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD);
        if (err)
                return err;
 
@@ -158,7 +156,7 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
 
        /* The bridging is rolled back on error */
        if (err) {
-               dsa_port_bridge_flags(dp, 0, NULL);
+               dsa_port_bridge_flags(dp, 0);
                dp->bridge_dev = NULL;
        }
 
@@ -185,7 +183,7 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
                pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
 
        /* Port is leaving the bridge, disable flooding */
-       dsa_port_bridge_flags(dp, 0, NULL);
+       dsa_port_bridge_flags(dp, 0);
 
        /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
         * so allow it to be in BR_STATE_FORWARDING to be kept functional
@@ -193,6 +191,85 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
        dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
 }
 
+int dsa_port_lag_change(struct dsa_port *dp,
+                       struct netdev_lag_lower_state_info *linfo)
+{
+       struct dsa_notifier_lag_info info = {
+               .sw_index = dp->ds->index,
+               .port = dp->index,
+       };
+       bool tx_enabled;
+
+       if (!dp->lag_dev)
+               return 0;
+
+       /* On statically configured aggregates (e.g. loadbalance
+        * without LACP) ports will always be tx_enabled, even if the
+        * link is down. Thus we require both link_up and tx_enabled
+        * in order to include it in the tx set.
+        */
+       tx_enabled = linfo->link_up && linfo->tx_enabled;
+
+       if (tx_enabled == dp->lag_tx_enabled)
+               return 0;
+
+       dp->lag_tx_enabled = tx_enabled;
+
+       return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info);
+}
+
+int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag,
+                     struct netdev_lag_upper_info *uinfo)
+{
+       struct dsa_notifier_lag_info info = {
+               .sw_index = dp->ds->index,
+               .port = dp->index,
+               .lag = lag,
+               .info = uinfo,
+       };
+       int err;
+
+       dsa_lag_map(dp->ds->dst, lag);
+       dp->lag_dev = lag;
+
+       err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info);
+       if (err) {
+               dp->lag_dev = NULL;
+               dsa_lag_unmap(dp->ds->dst, lag);
+       }
+
+       return err;
+}
+
+void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
+{
+       struct dsa_notifier_lag_info info = {
+               .sw_index = dp->ds->index,
+               .port = dp->index,
+               .lag = lag,
+       };
+       int err;
+
+       if (!dp->lag_dev)
+               return;
+
+       /* Port might have been part of a LAG that in turn was
+        * attached to a bridge.
+        */
+       if (dp->bridge_dev)
+               dsa_port_bridge_leave(dp, dp->bridge_dev);
+
+       dp->lag_tx_enabled = false;
+       dp->lag_dev = NULL;
+
+       err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
+       if (err)
+               pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n",
+                      err);
+
+       dsa_lag_unmap(dp->ds->dst, lag);
+}
+
 /* Must be called under rcu_read_lock() */
 static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
                                              bool vlan_filtering)
@@ -259,43 +336,36 @@ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
        return true;
 }
 
-int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
-                           struct switchdev_trans *trans)
+int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering)
 {
        struct dsa_switch *ds = dp->ds;
+       bool apply;
        int err;
 
-       if (switchdev_trans_ph_prepare(trans)) {
-               bool apply;
-
-               if (!ds->ops->port_vlan_filtering)
-                       return -EOPNOTSUPP;
+       if (!ds->ops->port_vlan_filtering)
+               return -EOPNOTSUPP;
 
-               /* We are called from dsa_slave_switchdev_blocking_event(),
-                * which is not under rcu_read_lock(), unlike
-                * dsa_slave_switchdev_event().
-                */
-               rcu_read_lock();
-               apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering);
-               rcu_read_unlock();
-               if (!apply)
-                       return -EINVAL;
-       }
+       /* We are called from dsa_slave_switchdev_blocking_event(),
+        * which is not under rcu_read_lock(), unlike
+        * dsa_slave_switchdev_event().
+        */
+       rcu_read_lock();
+       apply = dsa_port_can_apply_vlan_filtering(dp, vlan_filtering);
+       rcu_read_unlock();
+       if (!apply)
+               return -EINVAL;
 
        if (dsa_port_is_vlan_filtering(dp) == vlan_filtering)
                return 0;
 
-       err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering,
-                                          trans);
+       err = ds->ops->port_vlan_filtering(ds, dp->index, vlan_filtering);
        if (err)
                return err;
 
-       if (switchdev_trans_ph_commit(trans)) {
-               if (ds->vlan_filtering_is_global)
-                       ds->vlan_filtering = vlan_filtering;
-               else
-                       dp->vlan_filtering = vlan_filtering;
-       }
+       if (ds->vlan_filtering_is_global)
+               ds->vlan_filtering = vlan_filtering;
+       else
+               dp->vlan_filtering = vlan_filtering;
 
        return 0;
 }
@@ -314,26 +384,25 @@ bool dsa_port_skip_vlan_configuration(struct dsa_port *dp)
                !br_vlan_enabled(dp->bridge_dev));
 }
 
-int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
-                        struct switchdev_trans *trans)
+int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
 {
        unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock);
        unsigned int ageing_time = jiffies_to_msecs(ageing_jiffies);
-       struct dsa_notifier_ageing_time_info info = {
-               .ageing_time = ageing_time,
-               .trans = trans,
-       };
+       struct dsa_notifier_ageing_time_info info;
+       int err;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
+       info.ageing_time = ageing_time;
+
+       err = dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
+       if (err)
+               return err;
 
        dp->ageing_time = ageing_time;
 
-       return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
+       return 0;
 }
 
-int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
-                             struct switchdev_trans *trans)
+int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags)
 {
        struct dsa_switch *ds = dp->ds;
 
@@ -344,16 +413,12 @@ int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
        return 0;
 }
 
-int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
-                         struct switchdev_trans *trans)
+int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags)
 {
        struct dsa_switch *ds = dp->ds;
        int port = dp->index;
        int err = 0;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return 0;
-
        if (ds->ops->port_egress_floods)
                err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
                                                  flags & BR_MCAST_FLOOD);
@@ -361,14 +426,13 @@ int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
        return err;
 }
 
-int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
-                    struct switchdev_trans *trans)
+int dsa_port_mrouter(struct dsa_port *dp, bool mrouter)
 {
        struct dsa_switch *ds = dp->ds;
        int port = dp->index;
 
-       if (switchdev_trans_ph_prepare(trans))
-               return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP;
+       if (!ds->ops->port_egress_floods)
+               return -EOPNOTSUPP;
 
        return ds->ops->port_egress_floods(ds, port, true, mrouter);
 }
@@ -425,13 +489,11 @@ int dsa_port_fdb_dump(struct dsa_port *dp, dsa_fdb_dump_cb_t *cb, void *data)
 }
 
 int dsa_port_mdb_add(const struct dsa_port *dp,
-                    const struct switchdev_obj_port_mdb *mdb,
-                    struct switchdev_trans *trans)
+                    const struct switchdev_obj_port_mdb *mdb)
 {
        struct dsa_notifier_mdb_info info = {
                .sw_index = dp->ds->index,
                .port = dp->index,
-               .trans = trans,
                .mdb = mdb,
        };
 
@@ -451,13 +513,11 @@ int dsa_port_mdb_del(const struct dsa_port *dp,
 }
 
 int dsa_port_vlan_add(struct dsa_port *dp,
-                     const struct switchdev_obj_port_vlan *vlan,
-                     struct switchdev_trans *trans)
+                     const struct switchdev_obj_port_vlan *vlan)
 {
        struct dsa_notifier_vlan_info info = {
                .sw_index = dp->ds->index,
                .port = dp->index,
-               .trans = trans,
                .vlan = vlan,
        };
 
index 4a0498b..f2fb433 100644 (file)
@@ -268,32 +268,32 @@ static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 }
 
 static int dsa_slave_port_attr_set(struct net_device *dev,
-                                  const struct switchdev_attr *attr,
-                                  struct switchdev_trans *trans)
+                                  const struct switchdev_attr *attr)
 {
        struct dsa_port *dp = dsa_slave_to_port(dev);
        int ret;
 
+       if (!dsa_port_offloads_netdev(dp, attr->orig_dev))
+               return -EOPNOTSUPP;
+
        switch (attr->id) {
        case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-               ret = dsa_port_set_state(dp, attr->u.stp_state, trans);
+               ret = dsa_port_set_state(dp, attr->u.stp_state);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
-               ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
-                                             trans);
+               ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
-               ret = dsa_port_ageing_time(dp, attr->u.ageing_time, trans);
+               ret = dsa_port_ageing_time(dp, attr->u.ageing_time);
                break;
        case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
-               ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
-                                               trans);
+               ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-               ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans);
+               ret = dsa_port_bridge_flags(dp, attr->u.brport_flags);
                break;
        case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
-               ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans);
+               ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter);
                break;
        default:
                ret = -EOPNOTSUPP;
@@ -318,7 +318,7 @@ dsa_slave_vlan_check_for_8021q_uppers(struct net_device *slave,
                        continue;
 
                vid = vlan_dev_vlan_id(upper_dev);
-               if (vid >= vlan->vid_begin && vid <= vlan->vid_end)
+               if (vid == vlan->vid)
                        return -EBUSY;
        }
 
@@ -327,25 +327,27 @@ dsa_slave_vlan_check_for_8021q_uppers(struct net_device *slave,
 
 static int dsa_slave_vlan_add(struct net_device *dev,
                              const struct switchdev_obj *obj,
-                             struct switchdev_trans *trans)
+                             struct netlink_ext_ack *extack)
 {
        struct net_device *master = dsa_slave_to_master(dev);
        struct dsa_port *dp = dsa_slave_to_port(dev);
        struct switchdev_obj_port_vlan vlan;
-       int vid, err;
+       int err;
 
-       if (obj->orig_dev != dev)
+       if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
                return -EOPNOTSUPP;
 
-       if (dsa_port_skip_vlan_configuration(dp))
+       if (dsa_port_skip_vlan_configuration(dp)) {
+               NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN");
                return 0;
+       }
 
        vlan = *SWITCHDEV_OBJ_PORT_VLAN(obj);
 
        /* Deny adding a bridge VLAN when there is already an 802.1Q upper with
         * the same VID.
         */
-       if (trans->ph_prepare && br_vlan_enabled(dp->bridge_dev)) {
+       if (br_vlan_enabled(dp->bridge_dev)) {
                rcu_read_lock();
                err = dsa_slave_vlan_check_for_8021q_uppers(dev, &vlan);
                rcu_read_unlock();
@@ -353,7 +355,7 @@ static int dsa_slave_vlan_add(struct net_device *dev,
                        return err;
        }
 
-       err = dsa_port_vlan_add(dp, &vlan, trans);
+       err = dsa_port_vlan_add(dp, &vlan);
        if (err)
                return err;
 
@@ -363,47 +365,34 @@ static int dsa_slave_vlan_add(struct net_device *dev,
         */
        vlan.flags &= ~BRIDGE_VLAN_INFO_PVID;
 
-       err = dsa_port_vlan_add(dp->cpu_dp, &vlan, trans);
+       err = dsa_port_vlan_add(dp->cpu_dp, &vlan);
        if (err)
                return err;
 
-       for (vid = vlan.vid_begin; vid <= vlan.vid_end; vid++) {
-               err = vlan_vid_add(master, htons(ETH_P_8021Q), vid);
-               if (err)
-                       return err;
-       }
-
-       return 0;
+       return vlan_vid_add(master, htons(ETH_P_8021Q), vlan.vid);
 }
 
 static int dsa_slave_port_obj_add(struct net_device *dev,
                                  const struct switchdev_obj *obj,
-                                 struct switchdev_trans *trans,
                                  struct netlink_ext_ack *extack)
 {
        struct dsa_port *dp = dsa_slave_to_port(dev);
        int err;
 
-       /* For the prepare phase, ensure the full set of changes is feasable in
-        * one go in order to signal a failure properly. If an operation is not
-        * supported, return -EOPNOTSUPP.
-        */
-
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_MDB:
-               if (obj->orig_dev != dev)
+               if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
                        return -EOPNOTSUPP;
-               err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans);
+               err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
                break;
        case SWITCHDEV_OBJ_ID_HOST_MDB:
                /* DSA can directly translate this to a normal MDB add,
                 * but on the CPU port.
                 */
-               err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj),
-                                      trans);
+               err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
                break;
        case SWITCHDEV_OBJ_ID_PORT_VLAN:
-               err = dsa_slave_vlan_add(dev, obj, trans);
+               err = dsa_slave_vlan_add(dev, obj, extack);
                break;
        default:
                err = -EOPNOTSUPP;
@@ -419,9 +408,9 @@ static int dsa_slave_vlan_del(struct net_device *dev,
        struct net_device *master = dsa_slave_to_master(dev);
        struct dsa_port *dp = dsa_slave_to_port(dev);
        struct switchdev_obj_port_vlan *vlan;
-       int vid, err;
+       int err;
 
-       if (obj->orig_dev != dev)
+       if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
                return -EOPNOTSUPP;
 
        if (dsa_port_skip_vlan_configuration(dp))
@@ -436,8 +425,7 @@ static int dsa_slave_vlan_del(struct net_device *dev,
        if (err)
                return err;
 
-       for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
-               vlan_vid_del(master, htons(ETH_P_8021Q), vid);
+       vlan_vid_del(master, htons(ETH_P_8021Q), vlan->vid);
 
        return 0;
 }
@@ -450,7 +438,7 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
 
        switch (obj->id) {
        case SWITCHDEV_OBJ_ID_PORT_MDB:
-               if (obj->orig_dev != dev)
+               if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
                        return -EOPNOTSUPP;
                err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
                break;
@@ -1289,33 +1277,19 @@ static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto,
        struct dsa_port *dp = dsa_slave_to_port(dev);
        struct switchdev_obj_port_vlan vlan = {
                .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
-               .vid_begin = vid,
-               .vid_end = vid,
+               .vid = vid,
                /* This API only allows programming tagged, non-PVID VIDs */
                .flags = 0,
        };
-       struct switchdev_trans trans;
        int ret;
 
        /* User port... */
-       trans.ph_prepare = true;
-       ret = dsa_port_vlan_add(dp, &vlan, &trans);
-       if (ret)
-               return ret;
-
-       trans.ph_prepare = false;
-       ret = dsa_port_vlan_add(dp, &vlan, &trans);
+       ret = dsa_port_vlan_add(dp, &vlan);
        if (ret)
                return ret;
 
        /* And CPU port... */
-       trans.ph_prepare = true;
-       ret = dsa_port_vlan_add(dp->cpu_dp, &vlan, &trans);
-       if (ret)
-               return ret;
-
-       trans.ph_prepare = false;
-       ret = dsa_port_vlan_add(dp->cpu_dp, &vlan, &trans);
+       ret = dsa_port_vlan_add(dp->cpu_dp, &vlan);
        if (ret)
                return ret;
 
@@ -1328,8 +1302,7 @@ static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto,
        struct net_device *master = dsa_slave_to_master(dev);
        struct dsa_port *dp = dsa_slave_to_port(dev);
        struct switchdev_obj_port_vlan vlan = {
-               .vid_begin = vid,
-               .vid_end = vid,
+               .vid = vid,
                /* This API only allows programming tagged, non-PVID VIDs */
                .flags = 0,
        };
@@ -1575,20 +1548,20 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
 };
 
 /* legacy way, bypassing the bridge *****************************************/
-int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
-                      struct net_device *dev,
-                      const unsigned char *addr, u16 vid,
-                      u16 flags,
-                      struct netlink_ext_ack *extack)
+static int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                             struct net_device *dev,
+                             const unsigned char *addr, u16 vid,
+                             u16 flags,
+                             struct netlink_ext_ack *extack)
 {
        struct dsa_port *dp = dsa_slave_to_port(dev);
 
        return dsa_port_fdb_add(dp, addr, vid);
 }
 
-int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
-                      struct net_device *dev,
-                      const unsigned char *addr, u16 vid)
+static int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
+                             struct net_device *dev,
+                             const unsigned char *addr, u16 vid)
 {
        struct dsa_port *dp = dsa_slave_to_port(dev);
 
@@ -1602,6 +1575,18 @@ static struct devlink_port *dsa_slave_get_devlink_port(struct net_device *dev)
        return dp->ds->devlink ? &dp->devlink_port : NULL;
 }
 
+static void dsa_slave_get_stats64(struct net_device *dev,
+                                 struct rtnl_link_stats64 *s)
+{
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+       struct dsa_switch *ds = dp->ds;
+
+       if (ds->ops->get_stats64)
+               ds->ops->get_stats64(ds, dp->index, s);
+       else
+               dev_get_tstats64(dev, s);
+}
+
 static const struct net_device_ops dsa_slave_netdev_ops = {
        .ndo_open               = dsa_slave_open,
        .ndo_stop               = dsa_slave_close,
@@ -1621,7 +1606,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
 #endif
        .ndo_get_phys_port_name = dsa_slave_get_phys_port_name,
        .ndo_setup_tc           = dsa_slave_setup_tc,
-       .ndo_get_stats64        = dev_get_tstats64,
+       .ndo_get_stats64        = dsa_slave_get_stats64,
        .ndo_get_port_parent_id = dsa_slave_get_port_parent_id,
        .ndo_vlan_rx_add_vid    = dsa_slave_vlan_rx_add_vid,
        .ndo_vlan_rx_kill_vid   = dsa_slave_vlan_rx_kill_vid,
@@ -1764,20 +1749,6 @@ int dsa_slave_resume(struct net_device *slave_dev)
        return 0;
 }
 
-static void dsa_slave_notify(struct net_device *dev, unsigned long val)
-{
-       struct net_device *master = dsa_slave_to_master(dev);
-       struct dsa_port *dp = dsa_slave_to_port(dev);
-       struct dsa_notifier_register_info rinfo = {
-               .switch_number = dp->ds->index,
-               .port_number = dp->index,
-               .master = master,
-               .info.dev = dev,
-       };
-
-       call_dsa_notifiers(val, dev, &rinfo.info);
-}
-
 int dsa_slave_create(struct dsa_port *port)
 {
        const struct dsa_port *cpu_dp = port->cpu_dp;
@@ -1863,8 +1834,6 @@ int dsa_slave_create(struct dsa_port *port)
                goto out_gcells;
        }
 
-       dsa_slave_notify(slave_dev, DSA_PORT_REGISTER);
-
        rtnl_lock();
 
        ret = register_netdevice(slave_dev);
@@ -1913,7 +1882,6 @@ void dsa_slave_destroy(struct net_device *slave_dev)
        phylink_disconnect_phy(dp->pl);
        rtnl_unlock();
 
-       dsa_slave_notify(slave_dev, DSA_PORT_UNREGISTER);
        phylink_destroy(dp->pl);
        gro_cells_destroy(&p->gcells);
        free_percpu(slave_dev->tstats);
@@ -1924,6 +1892,7 @@ bool dsa_slave_dev_check(const struct net_device *dev)
 {
        return dev->netdev_ops == &dsa_slave_netdev_ops;
 }
+EXPORT_SYMBOL_GPL(dsa_slave_dev_check);
 
 static int dsa_slave_changeupper(struct net_device *dev,
                                 struct netdev_notifier_changeupper_info *info)
@@ -1941,6 +1910,46 @@ static int dsa_slave_changeupper(struct net_device *dev,
                        dsa_port_bridge_leave(dp, info->upper_dev);
                        err = NOTIFY_OK;
                }
+       } else if (netif_is_lag_master(info->upper_dev)) {
+               if (info->linking) {
+                       err = dsa_port_lag_join(dp, info->upper_dev,
+                                               info->upper_info);
+                       if (err == -EOPNOTSUPP) {
+                               NL_SET_ERR_MSG_MOD(info->info.extack,
+                                                  "Offloading not supported");
+                               err = 0;
+                       }
+                       err = notifier_from_errno(err);
+               } else {
+                       dsa_port_lag_leave(dp, info->upper_dev);
+                       err = NOTIFY_OK;
+               }
+       }
+
+       return err;
+}
+
+static int
+dsa_slave_lag_changeupper(struct net_device *dev,
+                         struct netdev_notifier_changeupper_info *info)
+{
+       struct net_device *lower;
+       struct list_head *iter;
+       int err = NOTIFY_DONE;
+       struct dsa_port *dp;
+
+       netdev_for_each_lower_dev(dev, lower, iter) {
+               if (!dsa_slave_dev_check(lower))
+                       continue;
+
+               dp = dsa_slave_to_port(lower);
+               if (!dp->lag_dev)
+                       /* Software LAG */
+                       continue;
+
+               err = dsa_slave_changeupper(lower, info);
+               if (notifier_to_errno(err))
+                       break;
        }
 
        return err;
@@ -2038,128 +2047,192 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
                break;
        }
        case NETDEV_CHANGEUPPER:
+               if (dsa_slave_dev_check(dev))
+                       return dsa_slave_changeupper(dev, ptr);
+
+               if (netif_is_lag_master(dev))
+                       return dsa_slave_lag_changeupper(dev, ptr);
+
+               break;
+       case NETDEV_CHANGELOWERSTATE: {
+               struct netdev_notifier_changelowerstate_info *info = ptr;
+               struct dsa_port *dp;
+               int err;
+
                if (!dsa_slave_dev_check(dev))
-                       return NOTIFY_DONE;
+                       break;
 
-               return dsa_slave_changeupper(dev, ptr);
+               dp = dsa_slave_to_port(dev);
+
+               err = dsa_port_lag_change(dp, info->lower_state_info);
+               return notifier_from_errno(err);
+       }
        }
 
        return NOTIFY_DONE;
 }
 
-struct dsa_switchdev_event_work {
-       struct work_struct work;
-       struct switchdev_notifier_fdb_info fdb_info;
-       struct net_device *dev;
-       unsigned long event;
-};
+static void
+dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work)
+{
+       struct dsa_switch *ds = switchdev_work->ds;
+       struct switchdev_notifier_fdb_info info;
+       struct dsa_port *dp;
+
+       if (!dsa_is_user_port(ds, switchdev_work->port))
+               return;
+
+       info.addr = switchdev_work->addr;
+       info.vid = switchdev_work->vid;
+       info.offloaded = true;
+       dp = dsa_to_port(ds, switchdev_work->port);
+       call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED,
+                                dp->slave, &info.info, NULL);
+}
 
 static void dsa_slave_switchdev_event_work(struct work_struct *work)
 {
        struct dsa_switchdev_event_work *switchdev_work =
                container_of(work, struct dsa_switchdev_event_work, work);
-       struct net_device *dev = switchdev_work->dev;
-       struct switchdev_notifier_fdb_info *fdb_info;
-       struct dsa_port *dp = dsa_slave_to_port(dev);
+       struct dsa_switch *ds = switchdev_work->ds;
+       struct dsa_port *dp;
        int err;
 
+       dp = dsa_to_port(ds, switchdev_work->port);
+
        rtnl_lock();
        switch (switchdev_work->event) {
        case SWITCHDEV_FDB_ADD_TO_DEVICE:
-               fdb_info = &switchdev_work->fdb_info;
-               if (!fdb_info->added_by_user)
-                       break;
-
-               err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid);
+               err = dsa_port_fdb_add(dp, switchdev_work->addr,
+                                      switchdev_work->vid);
                if (err) {
-                       netdev_dbg(dev, "fdb add failed err=%d\n", err);
+                       dev_err(ds->dev,
+                               "port %d failed to add %pM vid %d to fdb: %d\n",
+                               dp->index, switchdev_work->addr,
+                               switchdev_work->vid, err);
                        break;
                }
-               fdb_info->offloaded = true;
-               call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
-                                        &fdb_info->info, NULL);
+               dsa_fdb_offload_notify(switchdev_work);
                break;
 
        case SWITCHDEV_FDB_DEL_TO_DEVICE:
-               fdb_info = &switchdev_work->fdb_info;
-               if (!fdb_info->added_by_user)
-                       break;
-
-               err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid);
+               err = dsa_port_fdb_del(dp, switchdev_work->addr,
+                                      switchdev_work->vid);
                if (err) {
-                       netdev_dbg(dev, "fdb del failed err=%d\n", err);
-                       dev_close(dev);
+                       dev_err(ds->dev,
+                               "port %d failed to delete %pM vid %d from fdb: %d\n",
+                               dp->index, switchdev_work->addr,
+                               switchdev_work->vid, err);
                }
+
                break;
        }
        rtnl_unlock();
 
-       kfree(switchdev_work->fdb_info.addr);
        kfree(switchdev_work);
-       dev_put(dev);
+       if (dsa_is_user_port(ds, dp->index))
+               dev_put(dp->slave);
 }
 
-static int
-dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work *
-                                 switchdev_work,
-                                 const struct switchdev_notifier_fdb_info *
-                                 fdb_info)
-{
-       memcpy(&switchdev_work->fdb_info, fdb_info,
-              sizeof(switchdev_work->fdb_info));
-       switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
-       if (!switchdev_work->fdb_info.addr)
-               return -ENOMEM;
-       ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
-                       fdb_info->addr);
+static int dsa_lower_dev_walk(struct net_device *lower_dev,
+                             struct netdev_nested_priv *priv)
+{
+       if (dsa_slave_dev_check(lower_dev)) {
+               priv->data = (void *)netdev_priv(lower_dev);
+               return 1;
+       }
+
        return 0;
 }
 
+static struct dsa_slave_priv *dsa_slave_dev_lower_find(struct net_device *dev)
+{
+       struct netdev_nested_priv priv = {
+               .data = NULL,
+       };
+
+       netdev_walk_all_lower_dev_rcu(dev, dsa_lower_dev_walk, &priv);
+
+       return (struct dsa_slave_priv *)priv.data;
+}
+
 /* Called under rcu_read_lock() */
 static int dsa_slave_switchdev_event(struct notifier_block *unused,
                                     unsigned long event, void *ptr)
 {
        struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+       const struct switchdev_notifier_fdb_info *fdb_info;
        struct dsa_switchdev_event_work *switchdev_work;
+       struct dsa_port *dp;
        int err;
 
-       if (event == SWITCHDEV_PORT_ATTR_SET) {
+       switch (event) {
+       case SWITCHDEV_PORT_ATTR_SET:
                err = switchdev_handle_port_attr_set(dev, ptr,
                                                     dsa_slave_dev_check,
                                                     dsa_slave_port_attr_set);
                return notifier_from_errno(err);
-       }
+       case SWITCHDEV_FDB_ADD_TO_DEVICE:
+       case SWITCHDEV_FDB_DEL_TO_DEVICE:
+               fdb_info = ptr;
 
-       if (!dsa_slave_dev_check(dev))
-               return NOTIFY_DONE;
+               if (dsa_slave_dev_check(dev)) {
+                       if (!fdb_info->added_by_user)
+                               return NOTIFY_OK;
 
-       switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
-       if (!switchdev_work)
-               return NOTIFY_BAD;
+                       dp = dsa_slave_to_port(dev);
+               } else {
+                       /* Snoop addresses learnt on foreign interfaces
+                        * bridged with us, for switches that don't
+                        * automatically learn SA from CPU-injected traffic
+                        */
+                       struct net_device *br_dev;
+                       struct dsa_slave_priv *p;
 
-       INIT_WORK(&switchdev_work->work,
-                 dsa_slave_switchdev_event_work);
-       switchdev_work->dev = dev;
-       switchdev_work->event = event;
+                       br_dev = netdev_master_upper_dev_get_rcu(dev);
+                       if (!br_dev)
+                               return NOTIFY_DONE;
 
-       switch (event) {
-       case SWITCHDEV_FDB_ADD_TO_DEVICE:
-       case SWITCHDEV_FDB_DEL_TO_DEVICE:
-               if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr))
-                       goto err_fdb_work_init;
-               dev_hold(dev);
+                       if (!netif_is_bridge_master(br_dev))
+                               return NOTIFY_DONE;
+
+                       p = dsa_slave_dev_lower_find(br_dev);
+                       if (!p)
+                               return NOTIFY_DONE;
+
+                       dp = p->dp->cpu_dp;
+
+                       if (!dp->ds->assisted_learning_on_cpu_port)
+                               return NOTIFY_DONE;
+               }
+
+               if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del)
+                       return NOTIFY_DONE;
+
+               switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
+               if (!switchdev_work)
+                       return NOTIFY_BAD;
+
+               INIT_WORK(&switchdev_work->work,
+                         dsa_slave_switchdev_event_work);
+               switchdev_work->ds = dp->ds;
+               switchdev_work->port = dp->index;
+               switchdev_work->event = event;
+
+               ether_addr_copy(switchdev_work->addr,
+                               fdb_info->addr);
+               switchdev_work->vid = fdb_info->vid;
+
+               /* Hold a reference on the slave for dsa_fdb_offload_notify */
+               if (dsa_is_user_port(dp->ds, dp->index))
+                       dev_hold(dev);
+               dsa_schedule_work(&switchdev_work->work);
                break;
        default:
-               kfree(switchdev_work);
                return NOTIFY_DONE;
        }
 
-       dsa_schedule_work(&switchdev_work->work);
        return NOTIFY_OK;
-
-err_fdb_work_init:
-       kfree(switchdev_work);
-       return NOTIFY_BAD;
 }
 
 static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused,
index 3fb362b..cc0b25f 100644 (file)
@@ -33,15 +33,12 @@ static int dsa_switch_ageing_time(struct dsa_switch *ds,
                                  struct dsa_notifier_ageing_time_info *info)
 {
        unsigned int ageing_time = info->ageing_time;
-       struct switchdev_trans *trans = info->trans;
-
-       if (switchdev_trans_ph_prepare(trans)) {
-               if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
-                       return -ERANGE;
-               if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
-                       return -ERANGE;
-               return 0;
-       }
+
+       if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
+               return -ERANGE;
+
+       if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
+               return -ERANGE;
 
        /* Program the fastest ageing time in case of multiple bridges */
        ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
@@ -139,17 +136,8 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
                }
        }
        if (unset_vlan_filtering) {
-               struct switchdev_trans trans;
-
-               trans.ph_prepare = true;
                err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
-                                             false, &trans);
-               if (err && err != EOPNOTSUPP)
-                       return err;
-
-               trans.ph_prepare = false;
-               err = dsa_port_vlan_filtering(dsa_to_port(ds, info->port),
-                                             false, &trans);
+                                             false);
                if (err && err != EOPNOTSUPP)
                        return err;
        }
@@ -178,6 +166,47 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds,
        return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
 }
 
+static int dsa_switch_lag_change(struct dsa_switch *ds,
+                                struct dsa_notifier_lag_info *info)
+{
+       if (ds->index == info->sw_index && ds->ops->port_lag_change)
+               return ds->ops->port_lag_change(ds, info->port);
+
+       if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
+               return ds->ops->crosschip_lag_change(ds, info->sw_index,
+                                                    info->port);
+
+       return 0;
+}
+
+static int dsa_switch_lag_join(struct dsa_switch *ds,
+                              struct dsa_notifier_lag_info *info)
+{
+       if (ds->index == info->sw_index && ds->ops->port_lag_join)
+               return ds->ops->port_lag_join(ds, info->port, info->lag,
+                                             info->info);
+
+       if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
+               return ds->ops->crosschip_lag_join(ds, info->sw_index,
+                                                  info->port, info->lag,
+                                                  info->info);
+
+       return 0;
+}
+
+static int dsa_switch_lag_leave(struct dsa_switch *ds,
+                               struct dsa_notifier_lag_info *info)
+{
+       if (ds->index == info->sw_index && ds->ops->port_lag_leave)
+               return ds->ops->port_lag_leave(ds, info->port, info->lag);
+
+       if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
+               return ds->ops->crosschip_lag_leave(ds, info->sw_index,
+                                                   info->port, info->lag);
+
+       return 0;
+}
+
 static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
                                 struct dsa_notifier_mdb_info *info)
 {
@@ -190,41 +219,24 @@ static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
        return false;
 }
 
-static int dsa_switch_mdb_prepare(struct dsa_switch *ds,
-                                 struct dsa_notifier_mdb_info *info)
+static int dsa_switch_mdb_add(struct dsa_switch *ds,
+                             struct dsa_notifier_mdb_info *info)
 {
-       int port, err;
+       int err = 0;
+       int port;
 
-       if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
+       if (!ds->ops->port_mdb_add)
                return -EOPNOTSUPP;
 
        for (port = 0; port < ds->num_ports; port++) {
                if (dsa_switch_mdb_match(ds, port, info)) {
-                       err = ds->ops->port_mdb_prepare(ds, port, info->mdb);
+                       err = ds->ops->port_mdb_add(ds, port, info->mdb);
                        if (err)
-                               return err;
+                               break;
                }
        }
 
-       return 0;
-}
-
-static int dsa_switch_mdb_add(struct dsa_switch *ds,
-                             struct dsa_notifier_mdb_info *info)
-{
-       int port;
-
-       if (switchdev_trans_ph_prepare(info->trans))
-               return dsa_switch_mdb_prepare(ds, info);
-
-       if (!ds->ops->port_mdb_add)
-               return 0;
-
-       for (port = 0; port < ds->num_ports; port++)
-               if (dsa_switch_mdb_match(ds, port, info))
-                       ds->ops->port_mdb_add(ds, port, info->mdb);
-
-       return 0;
+       return err;
 }
 
 static int dsa_switch_mdb_del(struct dsa_switch *ds,
@@ -251,17 +263,17 @@ static bool dsa_switch_vlan_match(struct dsa_switch *ds, int port,
        return false;
 }
 
-static int dsa_switch_vlan_prepare(struct dsa_switch *ds,
-                                  struct dsa_notifier_vlan_info *info)
+static int dsa_switch_vlan_add(struct dsa_switch *ds,
+                              struct dsa_notifier_vlan_info *info)
 {
        int port, err;
 
-       if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
+       if (!ds->ops->port_vlan_add)
                return -EOPNOTSUPP;
 
        for (port = 0; port < ds->num_ports; port++) {
                if (dsa_switch_vlan_match(ds, port, info)) {
-                       err = ds->ops->port_vlan_prepare(ds, port, info->vlan);
+                       err = ds->ops->port_vlan_add(ds, port, info->vlan);
                        if (err)
                                return err;
                }
@@ -270,24 +282,6 @@ static int dsa_switch_vlan_prepare(struct dsa_switch *ds,
        return 0;
 }
 
-static int dsa_switch_vlan_add(struct dsa_switch *ds,
-                              struct dsa_notifier_vlan_info *info)
-{
-       int port;
-
-       if (switchdev_trans_ph_prepare(info->trans))
-               return dsa_switch_vlan_prepare(ds, info);
-
-       if (!ds->ops->port_vlan_add)
-               return 0;
-
-       for (port = 0; port < ds->num_ports; port++)
-               if (dsa_switch_vlan_match(ds, port, info))
-                       ds->ops->port_vlan_add(ds, port, info->vlan);
-
-       return 0;
-}
-
 static int dsa_switch_vlan_del(struct dsa_switch *ds,
                               struct dsa_notifier_vlan_info *info)
 {
@@ -325,6 +319,15 @@ static int dsa_switch_event(struct notifier_block *nb,
        case DSA_NOTIFIER_FDB_DEL:
                err = dsa_switch_fdb_del(ds, info);
                break;
+       case DSA_NOTIFIER_LAG_CHANGE:
+               err = dsa_switch_lag_change(ds, info);
+               break;
+       case DSA_NOTIFIER_LAG_JOIN:
+               err = dsa_switch_lag_join(ds, info);
+               break;
+       case DSA_NOTIFIER_LAG_LEAVE:
+               err = dsa_switch_lag_leave(ds, info);
+               break;
        case DSA_NOTIFIER_MDB_ADD:
                err = dsa_switch_mdb_add(ds, info);
                break;
@@ -345,10 +348,6 @@ static int dsa_switch_event(struct notifier_block *nb,
                break;
        }
 
-       /* Non-switchdev operations cannot be rolled back. If a DSA driver
-        * returns an error during the chained call, switch chips may be in an
-        * inconsistent state.
-        */
        if (err)
                dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
                        event, err);
index e934dac..e2577a7 100644 (file)
@@ -5,6 +5,7 @@
  * Copyright (C) 2014 Broadcom Corporation
  */
 
+#include <linux/dsa/brcm.h>
 #include <linux/etherdevice.h>
 #include <linux/list.h>
 #include <linux/slab.h>
index 112c7c6..7e7b7de 100644 (file)
@@ -163,6 +163,7 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
                                  u8 extra)
 {
        int source_device, source_port;
+       bool trunk = false;
        enum dsa_code code;
        enum dsa_cmd cmd;
        u8 *dsa_header;
@@ -174,6 +175,8 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
        switch (cmd) {
        case DSA_CMD_FORWARD:
                skb->offload_fwd_mark = 1;
+
+               trunk = !!(dsa_header[1] & 7);
                break;
 
        case DSA_CMD_TO_CPU:
@@ -216,7 +219,19 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev,
        source_device = dsa_header[0] & 0x1f;
        source_port = (dsa_header[1] >> 3) & 0x1f;
 
-       skb->dev = dsa_master_find_slave(dev, source_device, source_port);
+       if (trunk) {
+               struct dsa_port *cpu_dp = dev->dsa_ptr;
+
+               /* The exact source port is not available in the tag,
+                * so we inject the frame directly on the upper
+                * team/bond.
+                */
+               skb->dev = dsa_lag_dev(cpu_dp->dst, source_port);
+       } else {
+               skb->dev = dsa_master_find_slave(dev, source_device,
+                                                source_port);
+       }
+
        if (!skb->dev)
                return NULL;
 
diff --git a/net/dsa/tag_xrs700x.c b/net/dsa/tag_xrs700x.c
new file mode 100644 (file)
index 0000000..db0ed1a
--- /dev/null
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * XRS700x tag format handling
+ * Copyright (c) 2008-2009 Marvell Semiconductor
+ * Copyright (c) 2020 NovaTech LLC
+ */
+
+#include <linux/bitops.h>
+
+#include "dsa_priv.h"
+
+static struct sk_buff *xrs700x_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+       u8 *trailer;
+
+       trailer = skb_put(skb, 1);
+       trailer[0] = BIT(dp->index);
+
+       return skb;
+}
+
+static struct sk_buff *xrs700x_rcv(struct sk_buff *skb, struct net_device *dev,
+                                  struct packet_type *pt)
+{
+       int source_port;
+       u8 *trailer;
+
+       trailer = skb_tail_pointer(skb) - 1;
+
+       source_port = ffs((int)trailer[0]) - 1;
+
+       if (source_port < 0)
+               return NULL;
+
+       skb->dev = dsa_master_find_slave(dev, 0, source_port);
+       if (!skb->dev)
+               return NULL;
+
+       if (pskb_trim_rcsum(skb, skb->len - 1))
+               return NULL;
+
+       /* Frame is forwarded by hardware, don't forward in software. */
+       skb->offload_fwd_mark = 1;
+
+       return skb;
+}
+
+static const struct dsa_device_ops xrs700x_netdev_ops = {
+       .name   = "xrs700x",
+       .proto  = DSA_TAG_PROTO_XRS700X,
+       .xmit   = xrs700x_xmit,
+       .rcv    = xrs700x_rcv,
+       .overhead = 1,
+       .tail_tag = true,
+};
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_XRS700X);
+
+module_dsa_tag_driver(xrs700x_netdev_ops);
index 24036e3..1812201 100644 (file)
@@ -68,6 +68,7 @@ const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] = {
        [NETIF_F_HW_TLS_RX_BIT] =        "tls-hw-rx-offload",
        [NETIF_F_GRO_FRAGLIST_BIT] =     "rx-gro-list",
        [NETIF_F_HW_MACSEC_BIT] =        "macsec-hw-offload",
+       [NETIF_F_GRO_UDP_FWD_BIT] =      "rx-udp-gro-forwarding",
 };
 
 const char
index bcf6505..de36a5b 100644 (file)
@@ -4,7 +4,6 @@
 #
 
 menuconfig NET_IFE
-       depends on NET
        tristate "Inter-FE based on IETF ForCES InterFE LFB"
        default n
        help
index 8b07f3a..a3271ec 100644 (file)
@@ -443,7 +443,6 @@ static int esp_output_encap(struct xfrm_state *x, struct sk_buff *skb,
 int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp)
 {
        u8 *tail;
-       u8 *vaddr;
        int nfrags;
        int esph_offset;
        struct page *page;
@@ -485,14 +484,10 @@ int esp_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *
                        page = pfrag->page;
                        get_page(page);
 
-                       vaddr = kmap_atomic(page);
-
-                       tail = vaddr + pfrag->offset;
+                       tail = page_address(page) + pfrag->offset;
 
                        esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto);
 
-                       kunmap_atomic(vaddr);
-
                        nfrags = skb_shinfo(skb)->nr_frags;
 
                        __skb_fill_page_desc(skb, nfrags, page, pfrag->offset,
index cdf6ec5..84bb707 100644 (file)
@@ -292,7 +292,7 @@ __be32 fib_compute_spec_dst(struct sk_buff *skb)
                        .flowi4_iif = LOOPBACK_IFINDEX,
                        .flowi4_oif = l3mdev_master_ifindex_rcu(dev),
                        .daddr = ip_hdr(skb)->saddr,
-                       .flowi4_tos = RT_TOS(ip_hdr(skb)->tos),
+                       .flowi4_tos = ip_hdr(skb)->tos & IPTOS_RT_MASK,
                        .flowi4_scope = scope,
                        .flowi4_mark = vmark ? skb->mark : 0,
                };
index 66fdbfe..5d1e6fe 100644 (file)
@@ -128,7 +128,7 @@ int gre_parse_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
         * to 0 and sets the configured key in the
         * inner erspan header field
         */
-       if (greh->protocol == htons(ETH_P_ERSPAN) ||
+       if ((greh->protocol == htons(ETH_P_ERSPAN) && hdr_len != 4) ||
            greh->protocol == htons(ETH_P_ERSPAN2)) {
                struct erspan_base_hdr *ershdr;
 
index e0a2465..10bc49b 100644 (file)
@@ -15,10 +15,10 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                                       netdev_features_t features)
 {
        int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb);
-       bool need_csum, need_recompute_csum, gso_partial;
        struct sk_buff *segs = ERR_PTR(-EINVAL);
        u16 mac_offset = skb->mac_header;
        __be16 protocol = skb->protocol;
+       bool need_csum, gso_partial;
        u16 mac_len = skb->mac_len;
        int gre_offset, outer_hlen;
 
@@ -41,10 +41,11 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
        skb->protocol = skb->inner_protocol;
 
        need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM);
-       need_recompute_csum = skb->csum_not_inet;
        skb->encap_hdr_csum = need_csum;
 
        features &= skb->dev->hw_enc_features;
+       if (need_csum)
+               features &= ~NETIF_F_SCTP_CRC;
 
        /* segment inner packet. */
        segs = skb_mac_gso_segment(skb, features);
@@ -99,15 +100,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
                }
 
                *(pcsum + 1) = 0;
-               if (need_recompute_csum && !skb_is_gso(skb)) {
-                       __wsum csum;
-
-                       csum = skb_checksum(skb, gre_offset,
-                                           skb->len - gre_offset, 0);
-                       *pcsum = csum_fold(csum);
-               } else {
-                       *pcsum = gso_make_checksum(skb, 0);
-               }
+               *pcsum = gso_make_checksum(skb, 0);
        } while ((skb = skb->next));
 out:
        return segs;
index fd8b880..6bd7ca0 100644 (file)
@@ -851,6 +851,7 @@ struct sock *inet_csk_clone_lock(const struct sock *sk,
                newicsk->icsk_retransmits = 0;
                newicsk->icsk_backoff     = 0;
                newicsk->icsk_probes_out  = 0;
+               newicsk->icsk_probes_tstamp = 0;
 
                /* Deinitialize accept_queue to trap illegal accesses. */
                memset(&newicsk->icsk_accept_queue, 0, sizeof(newicsk->icsk_accept_queue));
index 89fff5f..959b94e 100644 (file)
@@ -302,7 +302,7 @@ static int __ip_finish_output(struct net *net, struct sock *sk, struct sk_buff *
        if (skb_is_gso(skb))
                return ip_finish_output_gso(net, sk, skb, mtu);
 
-       if (skb->len > mtu || (IPCB(skb)->flags & IPSKB_FRAG_PMTU))
+       if (skb->len > mtu || IPCB(skb)->frag_max_size)
                return ip_fragment(net, sk, skb, mtu, ip_finish_output2);
 
        return ip_finish_output2(net, sk, skb);
@@ -1018,7 +1018,7 @@ static int __ip_append_data(struct sock *sk,
                csummode = CHECKSUM_PARTIAL;
 
        if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
-               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               uarg = msg_zerocopy_realloc(sk, length, skb_zcopy(skb));
                if (!uarg)
                        return -ENOBUFS;
                extra_uref = !skb_zcopy(skb);   /* only ref on new uarg */
@@ -1230,8 +1230,7 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
-       if (uarg)
-               sock_zerocopy_put_abort(uarg, extra_uref);
+       net_zcopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
index ee65c92..64594aa 100644 (file)
@@ -759,8 +759,11 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                goto tx_error;
        }
 
-       if (tnl_update_pmtu(dev, skb, rt, tnl_params->frag_off, inner_iph,
-                           0, 0, false)) {
+       df = tnl_params->frag_off;
+       if (skb->protocol == htons(ETH_P_IP) && !tunnel->ignore_df)
+               df |= (inner_iph->frag_off & htons(IP_DF));
+
+       if (tnl_update_pmtu(dev, skb, rt, df, inner_iph, 0, 0, false)) {
                ip_rt_put(rt);
                goto tx_error;
        }
@@ -788,10 +791,6 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
                        ttl = ip4_dst_hoplimit(&rt->dst);
        }
 
-       df = tnl_params->frag_off;
-       if (skb->protocol == htons(ETH_P_IP) && !tunnel->ignore_df)
-               df |= (inner_iph->frag_off&htons(IP_DF));
-
        max_headroom = LL_RESERVED_SPACE(rt->dst.dev) + sizeof(struct iphdr)
                        + rt->dst.header_len + ip_encap_hlen(&tunnel->encap);
        if (max_headroom > dev->needed_headroom)
index 7ca338f..6b2dc7b 100644 (file)
@@ -222,7 +222,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
                .code                   = ICMP_FRAG_NEEDED,
                .checksum               = 0,
                .un.frag.__unused       = 0,
-               .un.frag.mtu            = ntohs(mtu),
+               .un.frag.mtu            = htons(mtu),
        };
        icmph->checksum = ip_compute_csum(icmph, len);
        skb_reset_transport_header(skb);
@@ -245,7 +245,7 @@ static int iptunnel_pmtud_build_icmp(struct sk_buff *skb, int mtu)
 
        skb->ip_summed = CHECKSUM_NONE;
 
-       eth_header(skb, skb->dev, htons(eh.h_proto), eh.h_source, eh.h_dest, 0);
+       eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0);
        skb_reset_mac_header(skb);
 
        return skb->len;
@@ -338,7 +338,7 @@ static int iptunnel_pmtud_build_icmpv6(struct sk_buff *skb, int mtu)
 
        skb->ip_summed = CHECKSUM_NONE;
 
-       eth_header(skb, skb->dev, htons(eh.h_proto), eh.h_source, eh.h_dest, 0);
+       eth_header(skb, skb->dev, ntohs(eh.h_proto), eh.h_source, eh.h_dest, 0);
        skb_reset_mac_header(skb);
 
        return skb->len;
@@ -583,8 +583,9 @@ static int ip_tun_parse_opts_erspan(struct nlattr *attr,
 static int ip_tun_parse_opts(struct nlattr *attr, struct ip_tunnel_info *info,
                             struct netlink_ext_ack *extack)
 {
-       int err, rem, opt_len, opts_len = 0, type = 0;
+       int err, rem, opt_len, opts_len = 0;
        struct nlattr *nla;
+       __be16 type = 0;
 
        if (!attr)
                return 0;
index 563b62b..c576a63 100644 (file)
@@ -1379,7 +1379,7 @@ static int compat_get_entries(struct net *net,
        xt_compat_lock(NFPROTO_ARP);
        t = xt_find_table_lock(net, NFPROTO_ARP, get.name);
        if (!IS_ERR(t)) {
-               const struct xt_table_info *private = t->private;
+               const struct xt_table_info *private = xt_table_get_private_protected(t);
                struct xt_table_info info;
 
                ret = compat_table_info(private, &info);
index 6e2851f..e8f6f9d 100644 (file)
@@ -1589,7 +1589,7 @@ compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr,
        xt_compat_lock(AF_INET);
        t = xt_find_table_lock(net, AF_INET, get.name);
        if (!IS_ERR(t)) {
-               const struct xt_table_info *private = t->private;
+               const struct xt_table_info *private = xt_table_get_private_protected(t);
                struct xt_table_info info;
                ret = compat_table_info(private, &info);
                if (!ret && get.size == info.size)
index cc23f1c..8cd3224 100644 (file)
@@ -76,7 +76,7 @@ static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
        flow.daddr = iph->saddr;
        flow.saddr = rpfilter_get_saddr(iph->daddr);
        flow.flowi4_mark = info->flags & XT_RPFILTER_VALID_MARK ? skb->mark : 0;
-       flow.flowi4_tos = RT_TOS(iph->tos);
+       flow.flowi4_tos = iph->tos & IPTOS_RT_MASK;
        flow.flowi4_scope = RT_SCOPE_UNIVERSE;
        flow.flowi4_oif = l3mdev_master_ifindex_rcu(xt_in(par));
 
index 5e1b22d..f1c6cbd 100644 (file)
@@ -22,7 +22,7 @@ static void remove_nexthop(struct net *net, struct nexthop *nh,
 #define NH_DEV_HASHBITS  8
 #define NH_DEV_HASHSIZE (1U << NH_DEV_HASHBITS)
 
-static const struct nla_policy rtm_nh_policy[NHA_MAX + 1] = {
+static const struct nla_policy rtm_nh_policy_new[] = {
        [NHA_ID]                = { .type = NLA_U32 },
        [NHA_GROUP]             = { .type = NLA_BINARY },
        [NHA_GROUP_TYPE]        = { .type = NLA_U16 },
@@ -31,6 +31,15 @@ static const struct nla_policy rtm_nh_policy[NHA_MAX + 1] = {
        [NHA_GATEWAY]           = { .type = NLA_BINARY },
        [NHA_ENCAP_TYPE]        = { .type = NLA_U16 },
        [NHA_ENCAP]             = { .type = NLA_NESTED },
+       [NHA_FDB]               = { .type = NLA_FLAG },
+};
+
+static const struct nla_policy rtm_nh_policy_get[] = {
+       [NHA_ID]                = { .type = NLA_U32 },
+};
+
+static const struct nla_policy rtm_nh_policy_dump[] = {
+       [NHA_OIF]               = { .type = NLA_U32 },
        [NHA_GROUPS]            = { .type = NLA_FLAG },
        [NHA_MASTER]            = { .type = NLA_U32 },
        [NHA_FDB]               = { .type = NLA_FLAG },
@@ -62,6 +71,7 @@ __nh_notifier_single_info_init(struct nh_notifier_single_info *nh_info,
 static int nh_notifier_single_info_init(struct nh_notifier_info *info,
                                        const struct nexthop *nh)
 {
+       info->type = NH_NOTIFIER_INFO_TYPE_SINGLE;
        info->nh = kzalloc(sizeof(*info->nh), GFP_KERNEL);
        if (!info->nh)
                return -ENOMEM;
@@ -76,13 +86,13 @@ static void nh_notifier_single_info_fini(struct nh_notifier_info *info)
        kfree(info->nh);
 }
 
-static int nh_notifier_grp_info_init(struct nh_notifier_info *info,
-                                    const struct nexthop *nh)
+static int nh_notifier_mp_info_init(struct nh_notifier_info *info,
+                                   struct nh_group *nhg)
 {
-       struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
        u16 num_nh = nhg->num_nh;
        int i;
 
+       info->type = NH_NOTIFIER_INFO_TYPE_GRP;
        info->nh_grp = kzalloc(struct_size(info->nh_grp, nh_entries, num_nh),
                               GFP_KERNEL);
        if (!info->nh_grp)
@@ -103,27 +113,41 @@ static int nh_notifier_grp_info_init(struct nh_notifier_info *info,
        return 0;
 }
 
-static void nh_notifier_grp_info_fini(struct nh_notifier_info *info)
+static int nh_notifier_grp_info_init(struct nh_notifier_info *info,
+                                    const struct nexthop *nh)
 {
-       kfree(info->nh_grp);
+       struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
+
+       if (nhg->mpath)
+               return nh_notifier_mp_info_init(info, nhg);
+       return -EINVAL;
+}
+
+static void nh_notifier_grp_info_fini(struct nh_notifier_info *info,
+                                     const struct nexthop *nh)
+{
+       struct nh_group *nhg = rtnl_dereference(nh->nh_grp);
+
+       if (nhg->mpath)
+               kfree(info->nh_grp);
 }
 
 static int nh_notifier_info_init(struct nh_notifier_info *info,
                                 const struct nexthop *nh)
 {
        info->id = nh->id;
-       info->is_grp = nh->is_group;
 
-       if (info->is_grp)
+       if (nh->is_group)
                return nh_notifier_grp_info_init(info, nh);
        else
                return nh_notifier_single_info_init(info, nh);
 }
 
-static void nh_notifier_info_fini(struct nh_notifier_info *info)
+static void nh_notifier_info_fini(struct nh_notifier_info *info,
+                                 const struct nexthop *nh)
 {
-       if (info->is_grp)
-               nh_notifier_grp_info_fini(info);
+       if (nh->is_group)
+               nh_notifier_grp_info_fini(info, nh);
        else
                nh_notifier_single_info_fini(info);
 }
@@ -152,7 +176,7 @@ static int call_nexthop_notifiers(struct net *net,
 
        err = blocking_notifier_call_chain(&net->nexthop.notifier_chain,
                                           event_type, &info);
-       nh_notifier_info_fini(&info);
+       nh_notifier_info_fini(&info, nh);
 
        return notifier_to_errno(err);
 }
@@ -173,7 +197,7 @@ static int call_nexthop_notifier(struct notifier_block *nb, struct net *net,
                return err;
 
        err = nb->notifier_call(nb, event_type, &info);
-       nh_notifier_info_fini(&info);
+       nh_notifier_info_fini(&info, nh);
 
        return notifier_to_errno(err);
 }
@@ -200,7 +224,7 @@ static void nexthop_devhash_add(struct net *net, struct nh_info *nhi)
        hlist_add_head(&nhi->dev_hash, head);
 }
 
-static void nexthop_free_mpath(struct nexthop *nh)
+static void nexthop_free_group(struct nexthop *nh)
 {
        struct nh_group *nhg;
        int i;
@@ -240,7 +264,7 @@ void nexthop_free_rcu(struct rcu_head *head)
        struct nexthop *nh = container_of(head, struct nexthop, rcu);
 
        if (nh->is_group)
-               nexthop_free_mpath(nh);
+               nexthop_free_group(nh);
        else
                nexthop_free_single(nh);
 
@@ -565,7 +589,8 @@ static int nh_check_attr_fdb_group(struct nexthop *nh, u8 *nh_family,
        return 0;
 }
 
-static int nh_check_attr_group(struct net *net, struct nlattr *tb[],
+static int nh_check_attr_group(struct net *net,
+                              struct nlattr *tb[], size_t tb_size,
                               struct netlink_ext_ack *extack)
 {
        unsigned int len = nla_len(tb[NHA_GROUP]);
@@ -624,10 +649,10 @@ static int nh_check_attr_group(struct net *net, struct nlattr *tb[],
                        return -EINVAL;
                }
        }
-       for (i = NHA_GROUP_TYPE + 1; i < __NHA_MAX; ++i) {
+       for (i = NHA_GROUP_TYPE + 1; i < tb_size; ++i) {
                if (!tb[i])
                        continue;
-               if (tb[NHA_FDB])
+               if (i == NHA_FDB)
                        continue;
                NL_SET_ERR_MSG(extack,
                               "No other attributes can be set in nexthop groups");
@@ -670,21 +695,16 @@ static bool ipv4_good_nh(const struct fib_nh *nh)
        return !!(state & NUD_VALID);
 }
 
-struct nexthop *nexthop_select_path(struct nexthop *nh, int hash)
+static struct nexthop *nexthop_select_path_mp(struct nh_group *nhg, int hash)
 {
        struct nexthop *rc = NULL;
-       struct nh_group *nhg;
        int i;
 
-       if (!nh->is_group)
-               return nh;
-
-       nhg = rcu_dereference(nh->nh_grp);
        for (i = 0; i < nhg->num_nh; ++i) {
                struct nh_grp_entry *nhge = &nhg->nh_entries[i];
                struct nh_info *nhi;
 
-               if (hash > atomic_read(&nhge->upper_bound))
+               if (hash > atomic_read(&nhge->mpath.upper_bound))
                        continue;
 
                nhi = rcu_dereference(nhge->nh->nh_info);
@@ -711,6 +731,21 @@ struct nexthop *nexthop_select_path(struct nexthop *nh, int hash)
 
        return rc;
 }
+
+struct nexthop *nexthop_select_path(struct nexthop *nh, int hash)
+{
+       struct nh_group *nhg;
+
+       if (!nh->is_group)
+               return nh;
+
+       nhg = rcu_dereference(nh->nh_grp);
+       if (nhg->mpath)
+               return nexthop_select_path_mp(nhg, hash);
+
+       /* Unreachable. */
+       return NULL;
+}
 EXPORT_SYMBOL_GPL(nexthop_select_path);
 
 int nexthop_for_each_fib6_nh(struct nexthop *nh,
@@ -904,7 +939,7 @@ static void nh_group_rebalance(struct nh_group *nhg)
 
                w += nhge->weight;
                upper_bound = DIV_ROUND_CLOSEST_ULL((u64)w << 31, total) - 1;
-               atomic_set(&nhge->upper_bound, upper_bound);
+               atomic_set(&nhge->mpath.upper_bound, upper_bound);
        }
 }
 
@@ -1446,10 +1481,13 @@ static struct nexthop *nexthop_create_group(struct net *net,
                nhg->nh_entries[i].nh_parent = nh;
        }
 
-       if (cfg->nh_grp_type == NEXTHOP_GRP_TYPE_MPATH) {
+       if (cfg->nh_grp_type == NEXTHOP_GRP_TYPE_MPATH)
                nhg->mpath = 1;
+
+       WARN_ON_ONCE(nhg->mpath != 1);
+
+       if (nhg->mpath)
                nh_group_rebalance(nhg);
-       }
 
        if (cfg->nh_fdb)
                nhg->fdb_nh = 1;
@@ -1459,8 +1497,10 @@ static struct nexthop *nexthop_create_group(struct net *net,
        return nh;
 
 out_no_nh:
-       for (; i >= 0; --i)
+       for (i--; i >= 0; --i) {
+               list_del(&nhg->nh_entries[i].nh_list);
                nexthop_put(nhg->nh_entries[i].nh);
+       }
 
        kfree(nhg->spare);
        kfree(nhg);
@@ -1641,11 +1681,12 @@ static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
                            struct netlink_ext_ack *extack)
 {
        struct nhmsg *nhm = nlmsg_data(nlh);
-       struct nlattr *tb[NHA_MAX + 1];
+       struct nlattr *tb[ARRAY_SIZE(rtm_nh_policy_new)];
        int err;
 
-       err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy,
-                         extack);
+       err = nlmsg_parse(nlh, sizeof(*nhm), tb,
+                         ARRAY_SIZE(rtm_nh_policy_new) - 1,
+                         rtm_nh_policy_new, extack);
        if (err < 0)
                return err;
 
@@ -1672,11 +1713,6 @@ static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
                goto out;
        }
 
-       if (tb[NHA_GROUPS] || tb[NHA_MASTER]) {
-               NL_SET_ERR_MSG(extack, "Invalid attributes in request");
-               goto out;
-       }
-
        memset(cfg, 0, sizeof(*cfg));
        cfg->nlflags = nlh->nlmsg_flags;
        cfg->nlinfo.portid = NETLINK_CB(skb).portid;
@@ -1718,7 +1754,7 @@ static int rtm_to_nh_config(struct net *net, struct sk_buff *skb,
                        NL_SET_ERR_MSG(extack, "Invalid group type");
                        goto out;
                }
-               err = nh_check_attr_group(net, tb, extack);
+               err = nh_check_attr_group(net, tb, ARRAY_SIZE(tb), extack);
 
                /* no other attributes should be set */
                goto out;
@@ -1836,49 +1872,44 @@ static int rtm_new_nexthop(struct sk_buff *skb, struct nlmsghdr *nlh,
        return err;
 }
 
-static int nh_valid_get_del_req(struct nlmsghdr *nlh, u32 *id,
-                               struct netlink_ext_ack *extack)
+static int __nh_valid_get_del_req(const struct nlmsghdr *nlh,
+                                 struct nlattr **tb, u32 *id,
+                                 struct netlink_ext_ack *extack)
 {
        struct nhmsg *nhm = nlmsg_data(nlh);
-       struct nlattr *tb[NHA_MAX + 1];
-       int err, i;
-
-       err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy,
-                         extack);
-       if (err < 0)
-               return err;
 
-       err = -EINVAL;
-       for (i = 0; i < __NHA_MAX; ++i) {
-               if (!tb[i])
-                       continue;
-
-               switch (i) {
-               case NHA_ID:
-                       break;
-               default:
-                       NL_SET_ERR_MSG_ATTR(extack, tb[i],
-                                           "Unexpected attribute in request");
-                       goto out;
-               }
-       }
        if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) {
                NL_SET_ERR_MSG(extack, "Invalid values in header");
-               goto out;
+               return -EINVAL;
        }
 
        if (!tb[NHA_ID]) {
                NL_SET_ERR_MSG(extack, "Nexthop id is missing");
-               goto out;
+               return -EINVAL;
        }
 
        *id = nla_get_u32(tb[NHA_ID]);
-       if (!(*id))
+       if (!(*id)) {
                NL_SET_ERR_MSG(extack, "Invalid nexthop id");
-       else
-               err = 0;
-out:
-       return err;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nh_valid_get_del_req(const struct nlmsghdr *nlh, u32 *id,
+                               struct netlink_ext_ack *extack)
+{
+       struct nlattr *tb[ARRAY_SIZE(rtm_nh_policy_get)];
+       int err;
+
+       err = nlmsg_parse(nlh, sizeof(struct nhmsg), tb,
+                         ARRAY_SIZE(rtm_nh_policy_get) - 1,
+                         rtm_nh_policy_get, extack);
+       if (err < 0)
+               return err;
+
+       return __nh_valid_get_del_req(nlh, tb, id, extack);
 }
 
 /* rtnl */
@@ -1947,16 +1978,23 @@ errout_free:
        goto out;
 }
 
-static bool nh_dump_filtered(struct nexthop *nh, int dev_idx, int master_idx,
-                            bool group_filter, u8 family)
+struct nh_dump_filter {
+       int dev_idx;
+       int master_idx;
+       bool group_filter;
+       bool fdb_filter;
+};
+
+static bool nh_dump_filtered(struct nexthop *nh,
+                            struct nh_dump_filter *filter, u8 family)
 {
        const struct net_device *dev;
        const struct nh_info *nhi;
 
-       if (group_filter && !nh->is_group)
+       if (filter->group_filter && !nh->is_group)
                return true;
 
-       if (!dev_idx && !master_idx && !family)
+       if (!filter->dev_idx && !filter->master_idx && !family)
                return false;
 
        if (nh->is_group)
@@ -1967,70 +2005,48 @@ static bool nh_dump_filtered(struct nexthop *nh, int dev_idx, int master_idx,
                return true;
 
        dev = nhi->fib_nhc.nhc_dev;
-       if (dev_idx && (!dev || dev->ifindex != dev_idx))
+       if (filter->dev_idx && (!dev || dev->ifindex != filter->dev_idx))
                return true;
 
-       if (master_idx) {
+       if (filter->master_idx) {
                struct net_device *master;
 
                if (!dev)
                        return true;
 
                master = netdev_master_upper_dev_get((struct net_device *)dev);
-               if (!master || master->ifindex != master_idx)
+               if (!master || master->ifindex != filter->master_idx)
                        return true;
        }
 
        return false;
 }
 
-static int nh_valid_dump_req(const struct nlmsghdr *nlh, int *dev_idx,
-                            int *master_idx, bool *group_filter,
-                            bool *fdb_filter, struct netlink_callback *cb)
+static int __nh_valid_dump_req(const struct nlmsghdr *nlh, struct nlattr **tb,
+                              struct nh_dump_filter *filter,
+                              struct netlink_ext_ack *extack)
 {
-       struct netlink_ext_ack *extack = cb->extack;
-       struct nlattr *tb[NHA_MAX + 1];
        struct nhmsg *nhm;
-       int err, i;
        u32 idx;
 
-       err = nlmsg_parse(nlh, sizeof(*nhm), tb, NHA_MAX, rtm_nh_policy,
-                         NULL);
-       if (err < 0)
-               return err;
-
-       for (i = 0; i <= NHA_MAX; ++i) {
-               if (!tb[i])
-                       continue;
-
-               switch (i) {
-               case NHA_OIF:
-                       idx = nla_get_u32(tb[i]);
-                       if (idx > INT_MAX) {
-                               NL_SET_ERR_MSG(extack, "Invalid device index");
-                               return -EINVAL;
-                       }
-                       *dev_idx = idx;
-                       break;
-               case NHA_MASTER:
-                       idx = nla_get_u32(tb[i]);
-                       if (idx > INT_MAX) {
-                               NL_SET_ERR_MSG(extack, "Invalid master device index");
-                               return -EINVAL;
-                       }
-                       *master_idx = idx;
-                       break;
-               case NHA_GROUPS:
-                       *group_filter = true;
-                       break;
-               case NHA_FDB:
-                       *fdb_filter = true;
-                       break;
-               default:
-                       NL_SET_ERR_MSG(extack, "Unsupported attribute in dump request");
+       if (tb[NHA_OIF]) {
+               idx = nla_get_u32(tb[NHA_OIF]);
+               if (idx > INT_MAX) {
+                       NL_SET_ERR_MSG(extack, "Invalid device index");
+                       return -EINVAL;
+               }
+               filter->dev_idx = idx;
+       }
+       if (tb[NHA_MASTER]) {
+               idx = nla_get_u32(tb[NHA_MASTER]);
+               if (idx > INT_MAX) {
+                       NL_SET_ERR_MSG(extack, "Invalid master device index");
                        return -EINVAL;
                }
+               filter->master_idx = idx;
        }
+       filter->group_filter = nla_get_flag(tb[NHA_GROUPS]);
+       filter->fdb_filter = nla_get_flag(tb[NHA_FDB]);
 
        nhm = nlmsg_data(nlh);
        if (nhm->nh_protocol || nhm->resvd || nhm->nh_scope || nhm->nh_flags) {
@@ -2041,24 +2057,49 @@ static int nh_valid_dump_req(const struct nlmsghdr *nlh, int *dev_idx,
        return 0;
 }
 
-/* rtnl */
-static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb)
+static int nh_valid_dump_req(const struct nlmsghdr *nlh,
+                            struct nh_dump_filter *filter,
+                            struct netlink_callback *cb)
 {
-       bool group_filter = false, fdb_filter = false;
-       struct nhmsg *nhm = nlmsg_data(cb->nlh);
-       int dev_filter_idx = 0, master_idx = 0;
-       struct net *net = sock_net(skb->sk);
-       struct rb_root *root = &net->nexthop.rb_root;
-       struct rb_node *node;
-       int idx = 0, s_idx;
+       struct nlattr *tb[ARRAY_SIZE(rtm_nh_policy_dump)];
        int err;
 
-       err = nh_valid_dump_req(cb->nlh, &dev_filter_idx, &master_idx,
-                               &group_filter, &fdb_filter, cb);
+       err = nlmsg_parse(nlh, sizeof(struct nhmsg), tb,
+                         ARRAY_SIZE(rtm_nh_policy_dump) - 1,
+                         rtm_nh_policy_dump, cb->extack);
        if (err < 0)
                return err;
 
-       s_idx = cb->args[0];
+       return __nh_valid_dump_req(nlh, tb, filter, cb->extack);
+}
+
+struct rtm_dump_nh_ctx {
+       u32 idx;
+};
+
+static struct rtm_dump_nh_ctx *
+rtm_dump_nh_ctx(struct netlink_callback *cb)
+{
+       struct rtm_dump_nh_ctx *ctx = (void *)cb->ctx;
+
+       BUILD_BUG_ON(sizeof(*ctx) > sizeof(cb->ctx));
+       return ctx;
+}
+
+static int rtm_dump_walk_nexthops(struct sk_buff *skb,
+                                 struct netlink_callback *cb,
+                                 struct rb_root *root,
+                                 struct rtm_dump_nh_ctx *ctx,
+                                 int (*nh_cb)(struct sk_buff *skb,
+                                              struct netlink_callback *cb,
+                                              struct nexthop *nh, void *data),
+                                 void *data)
+{
+       struct rb_node *node;
+       int idx = 0, s_idx;
+       int err;
+
+       s_idx = ctx->idx;
        for (node = rb_first(root); node; node = rb_next(node)) {
                struct nexthop *nh;
 
@@ -2066,30 +2107,58 @@ static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb)
                        goto cont;
 
                nh = rb_entry(node, struct nexthop, rb_node);
-               if (nh_dump_filtered(nh, dev_filter_idx, master_idx,
-                                    group_filter, nhm->nh_family))
-                       goto cont;
-
-               err = nh_fill_node(skb, nh, RTM_NEWNEXTHOP,
-                                  NETLINK_CB(cb->skb).portid,
-                                  cb->nlh->nlmsg_seq, NLM_F_MULTI);
-               if (err < 0) {
-                       if (likely(skb->len))
-                               goto out;
-
-                       goto out_err;
-               }
+               ctx->idx = idx;
+               err = nh_cb(skb, cb, nh, data);
+               if (err)
+                       return err;
 cont:
                idx++;
        }
 
+       ctx->idx = idx;
+       return 0;
+}
+
+static int rtm_dump_nexthop_cb(struct sk_buff *skb, struct netlink_callback *cb,
+                              struct nexthop *nh, void *data)
+{
+       struct nhmsg *nhm = nlmsg_data(cb->nlh);
+       struct nh_dump_filter *filter = data;
+
+       if (nh_dump_filtered(nh, filter, nhm->nh_family))
+               return 0;
+
+       return nh_fill_node(skb, nh, RTM_NEWNEXTHOP,
+                           NETLINK_CB(cb->skb).portid,
+                           cb->nlh->nlmsg_seq, NLM_F_MULTI);
+}
+
+/* rtnl */
+static int rtm_dump_nexthop(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct rtm_dump_nh_ctx *ctx = rtm_dump_nh_ctx(cb);
+       struct net *net = sock_net(skb->sk);
+       struct rb_root *root = &net->nexthop.rb_root;
+       struct nh_dump_filter filter = {};
+       int err;
+
+       err = nh_valid_dump_req(cb->nlh, &filter, cb);
+       if (err < 0)
+               return err;
+
+       err = rtm_dump_walk_nexthops(skb, cb, root, ctx,
+                                    &rtm_dump_nexthop_cb, &filter);
+       if (err < 0) {
+               if (likely(skb->len))
+                       goto out;
+               goto out_err;
+       }
+
 out:
        err = skb->len;
 out_err:
-       cb->args[0] = idx;
        cb->seq = net->nexthop.seq;
        nl_dump_check_consistent(cb, nlmsg_hdr(skb));
-
        return err;
 }
 
index ed42d21..e1a17c6 100644 (file)
 #include <asm/ioctls.h>
 #include <net/busy_poll.h>
 
+/* Track pending CMSGs. */
+enum {
+       TCP_CMSG_INQ = 1,
+       TCP_CMSG_TS = 2
+};
+
 struct percpu_counter tcp_orphan_count;
 EXPORT_SYMBOL_GPL(tcp_orphan_count);
 
@@ -1010,7 +1016,7 @@ new_segment:
        }
 
        if (!(flags & MSG_NO_SHARED_FRAGS))
-               skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
+               skb_shinfo(skb)->flags |= SKBFL_SHARED_FRAG;
 
        skb->len += copy;
        skb->data_len += copy;
@@ -1217,7 +1223,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
 
        if (flags & MSG_ZEROCOPY && size && sock_flag(sk, SOCK_ZEROCOPY)) {
                skb = tcp_write_queue_tail(sk);
-               uarg = sock_zerocopy_realloc(sk, size, skb_zcopy(skb));
+               uarg = msg_zerocopy_realloc(sk, size, skb_zcopy(skb));
                if (!uarg) {
                        err = -ENOBUFS;
                        goto out_err;
@@ -1429,7 +1435,7 @@ out:
                tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
        }
 out_nopush:
-       sock_zerocopy_put(uarg);
+       net_zcopy_put(uarg);
        return copied + copied_syn;
 
 do_error:
@@ -1440,7 +1446,7 @@ do_fault:
        if (copied + copied_syn)
                goto out;
 out_err:
-       sock_zerocopy_put_abort(uarg, true);
+       net_zcopy_put_abort(uarg, true);
        err = sk_stream_error(sk, flags, err);
        /* make sure we wake any epoll edge trigger waiter */
        if (unlikely(tcp_rtx_and_write_queues_empty(sk) && err == -EAGAIN)) {
@@ -1739,6 +1745,20 @@ int tcp_set_rcvlowat(struct sock *sk, int val)
 }
 EXPORT_SYMBOL(tcp_set_rcvlowat);
 
+static void tcp_update_recv_tstamps(struct sk_buff *skb,
+                                   struct scm_timestamping_internal *tss)
+{
+       if (skb->tstamp)
+               tss->ts[0] = ktime_to_timespec64(skb->tstamp);
+       else
+               tss->ts[0] = (struct timespec64) {0};
+
+       if (skb_hwtstamps(skb)->hwtstamp)
+               tss->ts[2] = ktime_to_timespec64(skb_hwtstamps(skb)->hwtstamp);
+       else
+               tss->ts[2] = (struct timespec64) {0};
+}
+
 #ifdef CONFIG_MMU
 static const struct vm_operations_struct tcp_vm_ops = {
 };
@@ -1842,13 +1862,13 @@ static int tcp_recvmsg_locked(struct sock *sk, struct msghdr *msg, size_t len,
                              struct scm_timestamping_internal *tss,
                              int *cmsg_flags);
 static int receive_fallback_to_copy(struct sock *sk,
-                                   struct tcp_zerocopy_receive *zc, int inq)
+                                   struct tcp_zerocopy_receive *zc, int inq,
+                                   struct scm_timestamping_internal *tss)
 {
        unsigned long copy_address = (unsigned long)zc->copybuf_address;
-       struct scm_timestamping_internal tss_unused;
-       int err, cmsg_flags_unused;
        struct msghdr msg = {};
        struct iovec iov;
+       int err;
 
        zc->length = 0;
        zc->recv_skip_hint = 0;
@@ -1862,7 +1882,7 @@ static int receive_fallback_to_copy(struct sock *sk,
                return err;
 
        err = tcp_recvmsg_locked(sk, &msg, inq, /*nonblock=*/1, /*flags=*/0,
-                                &tss_unused, &cmsg_flags_unused);
+                                tss, &zc->msg_flags);
        if (err < 0)
                return err;
 
@@ -1903,21 +1923,27 @@ static int tcp_copy_straggler_data(struct tcp_zerocopy_receive *zc,
        return (__s32)copylen;
 }
 
-static int tcp_zerocopy_handle_leftover_data(struct tcp_zerocopy_receive *zc,
-                                            struct sock *sk,
-                                            struct sk_buff *skb,
-                                            u32 *seq,
-                                            s32 copybuf_len)
+static int tcp_zc_handle_leftover(struct tcp_zerocopy_receive *zc,
+                                 struct sock *sk,
+                                 struct sk_buff *skb,
+                                 u32 *seq,
+                                 s32 copybuf_len,
+                                 struct scm_timestamping_internal *tss)
 {
        u32 offset, copylen = min_t(u32, copybuf_len, zc->recv_skip_hint);
 
        if (!copylen)
                return 0;
        /* skb is null if inq < PAGE_SIZE. */
-       if (skb)
+       if (skb) {
                offset = *seq - TCP_SKB_CB(skb)->seq;
-       else
+       } else {
                skb = tcp_recv_skb(sk, *seq, &offset);
+               if (TCP_SKB_CB(skb)->has_rxtstamp) {
+                       tcp_update_recv_tstamps(skb, tss);
+                       zc->msg_flags |= TCP_CMSG_TS;
+               }
+       }
 
        zc->copybuf_len = tcp_copy_straggler_data(zc, skb, copylen, &offset,
                                                  seq);
@@ -2004,9 +2030,37 @@ static int tcp_zerocopy_vm_insert_batch(struct vm_area_struct *vma,
                err);
 }
 
+static void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
+                              struct scm_timestamping_internal *tss);
+static void tcp_zc_finalize_rx_tstamp(struct sock *sk,
+                                     struct tcp_zerocopy_receive *zc,
+                                     struct scm_timestamping_internal *tss)
+{
+       unsigned long msg_control_addr;
+       struct msghdr cmsg_dummy;
+
+       msg_control_addr = (unsigned long)zc->msg_control;
+       cmsg_dummy.msg_control = (void *)msg_control_addr;
+       cmsg_dummy.msg_controllen =
+               (__kernel_size_t)zc->msg_controllen;
+       cmsg_dummy.msg_flags = in_compat_syscall()
+               ? MSG_CMSG_COMPAT : 0;
+       zc->msg_flags = 0;
+       if (zc->msg_control == msg_control_addr &&
+           zc->msg_controllen == cmsg_dummy.msg_controllen) {
+               tcp_recv_timestamp(&cmsg_dummy, sk, tss);
+               zc->msg_control = (__u64)
+                       ((uintptr_t)cmsg_dummy.msg_control);
+               zc->msg_controllen =
+                       (__u64)cmsg_dummy.msg_controllen;
+               zc->msg_flags = (__u32)cmsg_dummy.msg_flags;
+       }
+}
+
 #define TCP_ZEROCOPY_PAGE_BATCH_SIZE 32
 static int tcp_zerocopy_receive(struct sock *sk,
-                               struct tcp_zerocopy_receive *zc)
+                               struct tcp_zerocopy_receive *zc,
+                               struct scm_timestamping_internal *tss)
 {
        u32 length = 0, offset, vma_len, avail_len, copylen = 0;
        unsigned long address = (unsigned long)zc->address;
@@ -2023,6 +2077,7 @@ static int tcp_zerocopy_receive(struct sock *sk,
        int ret;
 
        zc->copybuf_len = 0;
+       zc->msg_flags = 0;
 
        if (address & (PAGE_SIZE - 1) || address != zc->address)
                return -EINVAL;
@@ -2033,7 +2088,7 @@ static int tcp_zerocopy_receive(struct sock *sk,
        sock_rps_record_flow(sk);
 
        if (inq && inq <= copybuf_len)
-               return receive_fallback_to_copy(sk, zc, inq);
+               return receive_fallback_to_copy(sk, zc, inq, tss);
 
        if (inq < PAGE_SIZE) {
                zc->length = 0;
@@ -2078,6 +2133,11 @@ static int tcp_zerocopy_receive(struct sock *sk,
                        } else {
                                skb = tcp_recv_skb(sk, seq, &offset);
                        }
+
+                       if (TCP_SKB_CB(skb)->has_rxtstamp) {
+                               tcp_update_recv_tstamps(skb, tss);
+                               zc->msg_flags |= TCP_CMSG_TS;
+                       }
                        zc->recv_skip_hint = skb->len - offset;
                        frags = skb_advance_to_frag(skb, offset, &offset_frag);
                        if (!frags || offset_frag)
@@ -2120,8 +2180,7 @@ out:
        mmap_read_unlock(current->mm);
        /* Try to copy straggler data. */
        if (!ret)
-               copylen = tcp_zerocopy_handle_leftover_data(zc, sk, skb, &seq,
-                                                           copybuf_len);
+               copylen = tcp_zc_handle_leftover(zc, sk, skb, &seq, copybuf_len, tss);
 
        if (length + copylen) {
                WRITE_ONCE(tp->copied_seq, seq);
@@ -2142,20 +2201,6 @@ out:
 }
 #endif
 
-static void tcp_update_recv_tstamps(struct sk_buff *skb,
-                                   struct scm_timestamping_internal *tss)
-{
-       if (skb->tstamp)
-               tss->ts[0] = ktime_to_timespec64(skb->tstamp);
-       else
-               tss->ts[0] = (struct timespec64) {0};
-
-       if (skb_hwtstamps(skb)->hwtstamp)
-               tss->ts[2] = ktime_to_timespec64(skb_hwtstamps(skb)->hwtstamp);
-       else
-               tss->ts[2] = (struct timespec64) {0};
-}
-
 /* Similar to __sock_recv_timestamp, but does not require an skb */
 static void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
                               struct scm_timestamping_internal *tss)
@@ -2272,7 +2317,7 @@ static int tcp_recvmsg_locked(struct sock *sk, struct msghdr *msg, size_t len,
                goto out;
 
        if (tp->recvmsg_inq)
-               *cmsg_flags = 1;
+               *cmsg_flags = TCP_CMSG_INQ;
        timeo = sock_rcvtimeo(sk, nonblock);
 
        /* Urgent data needs to be handled specially. */
@@ -2453,7 +2498,7 @@ skip_copy:
 
                if (TCP_SKB_CB(skb)->has_rxtstamp) {
                        tcp_update_recv_tstamps(skb, tss);
-                       *cmsg_flags |= 2;
+                       *cmsg_flags |= TCP_CMSG_TS;
                }
 
                if (used + offset < skb->len)
@@ -2513,9 +2558,9 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
        release_sock(sk);
 
        if (cmsg_flags && ret >= 0) {
-               if (cmsg_flags & 2)
+               if (cmsg_flags & TCP_CMSG_TS)
                        tcp_recv_timestamp(msg, sk, &tss);
-               if (cmsg_flags & 1) {
+               if (cmsg_flags & TCP_CMSG_INQ) {
                        inq = tcp_inq_hint(sk);
                        put_cmsg(msg, SOL_TCP, TCP_CM_INQ, sizeof(inq), &inq);
                }
@@ -2937,6 +2982,7 @@ int tcp_disconnect(struct sock *sk, int flags)
 
        icsk->icsk_backoff = 0;
        icsk->icsk_probes_out = 0;
+       icsk->icsk_probes_tstamp = 0;
        icsk->icsk_rto = TCP_TIMEOUT_INIT;
        icsk->icsk_rto_min = TCP_RTO_MIN;
        icsk->icsk_delack_max = TCP_DELACK_MAX;
@@ -3766,11 +3812,24 @@ static size_t tcp_opt_stats_get_size(void)
                nla_total_size(sizeof(u16)) + /* TCP_NLA_TIMEOUT_REHASH */
                nla_total_size(sizeof(u32)) + /* TCP_NLA_BYTES_NOTSENT */
                nla_total_size_64bit(sizeof(u64)) + /* TCP_NLA_EDT */
+               nla_total_size(sizeof(u8)) + /* TCP_NLA_TTL */
                0;
 }
 
+/* Returns TTL or hop limit of an incoming packet from skb. */
+static u8 tcp_skb_ttl_or_hop_limit(const struct sk_buff *skb)
+{
+       if (skb->protocol == htons(ETH_P_IP))
+               return ip_hdr(skb)->ttl;
+       else if (skb->protocol == htons(ETH_P_IPV6))
+               return ipv6_hdr(skb)->hop_limit;
+       else
+               return 0;
+}
+
 struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
-                                              const struct sk_buff *orig_skb)
+                                              const struct sk_buff *orig_skb,
+                                              const struct sk_buff *ack_skb)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *stats;
@@ -3826,6 +3885,9 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk,
                    max_t(int, 0, tp->write_seq - tp->snd_nxt));
        nla_put_u64_64bit(stats, TCP_NLA_EDT, orig_skb->skb_mstamp_ns,
                          TCP_NLA_PAD);
+       if (ack_skb)
+               nla_put_u8(stats, TCP_NLA_TTL,
+                          tcp_skb_ttl_or_hop_limit(ack_skb));
 
        return stats;
 }
@@ -4082,6 +4144,7 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
        }
 #ifdef CONFIG_MMU
        case TCP_ZEROCOPY_RECEIVE: {
+               struct scm_timestamping_internal tss;
                struct tcp_zerocopy_receive zc = {};
                int err;
 
@@ -4097,11 +4160,18 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
                if (copy_from_user(&zc, optval, len))
                        return -EFAULT;
                lock_sock(sk);
-               err = tcp_zerocopy_receive(sk, &zc);
+               err = tcp_zerocopy_receive(sk, &zc, &tss);
                release_sock(sk);
-               if (len >= offsetofend(struct tcp_zerocopy_receive, err))
-                       goto zerocopy_rcv_sk_err;
+               if (len >= offsetofend(struct tcp_zerocopy_receive, msg_flags))
+                       goto zerocopy_rcv_cmsg;
                switch (len) {
+               case offsetofend(struct tcp_zerocopy_receive, msg_flags):
+                       goto zerocopy_rcv_cmsg;
+               case offsetofend(struct tcp_zerocopy_receive, msg_controllen):
+               case offsetofend(struct tcp_zerocopy_receive, msg_control):
+               case offsetofend(struct tcp_zerocopy_receive, flags):
+               case offsetofend(struct tcp_zerocopy_receive, copybuf_len):
+               case offsetofend(struct tcp_zerocopy_receive, copybuf_address):
                case offsetofend(struct tcp_zerocopy_receive, err):
                        goto zerocopy_rcv_sk_err;
                case offsetofend(struct tcp_zerocopy_receive, inq):
@@ -4110,6 +4180,11 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
                default:
                        goto zerocopy_rcv_out;
                }
+zerocopy_rcv_cmsg:
+               if (zc.msg_flags & TCP_CMSG_TS)
+                       tcp_zc_finalize_rx_tstamp(sk, &zc, &tss);
+               else
+                       zc.msg_flags = 0;
 zerocopy_rcv_sk_err:
                if (!err)
                        zc.err = sock_error(sk);
index c7bf5b2..ffcbe46 100644 (file)
@@ -104,16 +104,7 @@ struct bictcp {
 
 static inline void bictcp_reset(struct bictcp *ca)
 {
-       ca->cnt = 0;
-       ca->last_max_cwnd = 0;
-       ca->last_cwnd = 0;
-       ca->last_time = 0;
-       ca->bic_origin_point = 0;
-       ca->bic_K = 0;
-       ca->delay_min = 0;
-       ca->epoch_start = 0;
-       ca->ack_cnt = 0;
-       ca->tcp_cwnd = 0;
+       memset(ca, 0, offsetof(struct bictcp, unused));
        ca->found = 0;
 }
 
index c7e16b0..a8f8f98 100644 (file)
@@ -2859,7 +2859,8 @@ static void tcp_identify_packet_loss(struct sock *sk, int *ack_flag)
        } else if (tcp_is_rack(sk)) {
                u32 prior_retrans = tp->retrans_out;
 
-               tcp_rack_mark_lost(sk);
+               if (tcp_rack_mark_lost(sk))
+                       *ack_flag &= ~FLAG_SET_XMIT_TIMER;
                if (prior_retrans > tp->retrans_out)
                        *ack_flag |= FLAG_LOST_RETRANS;
        }
@@ -3145,7 +3146,7 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb)
 }
 
 static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb,
-                          u32 prior_snd_una)
+                          const struct sk_buff *ack_skb, u32 prior_snd_una)
 {
        const struct skb_shared_info *shinfo;
 
@@ -3157,7 +3158,7 @@ static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb,
        if (!before(shinfo->tskey, prior_snd_una) &&
            before(shinfo->tskey, tcp_sk(sk)->snd_una)) {
                tcp_skb_tsorted_save(skb) {
-                       __skb_tstamp_tx(skb, NULL, sk, SCM_TSTAMP_ACK);
+                       __skb_tstamp_tx(skb, ack_skb, NULL, sk, SCM_TSTAMP_ACK);
                } tcp_skb_tsorted_restore(skb);
        }
 }
@@ -3166,8 +3167,8 @@ static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb,
  * is before the ack sequence we can discard it as it's confirmed to have
  * arrived at the other end.
  */
-static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack,
-                              u32 prior_snd_una,
+static int tcp_clean_rtx_queue(struct sock *sk, const struct sk_buff *ack_skb,
+                              u32 prior_fack, u32 prior_snd_una,
                               struct tcp_sacktag_state *sack, bool ece_ack)
 {
        const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -3256,7 +3257,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack,
                if (!fully_acked)
                        break;
 
-               tcp_ack_tstamp(sk, skb, prior_snd_una);
+               tcp_ack_tstamp(sk, skb, ack_skb, prior_snd_una);
 
                next = skb_rb_next(skb);
                if (unlikely(skb == tp->retransmit_skb_hint))
@@ -3274,7 +3275,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack,
                tp->snd_up = tp->snd_una;
 
        if (skb) {
-               tcp_ack_tstamp(sk, skb, prior_snd_una);
+               tcp_ack_tstamp(sk, skb, ack_skb, prior_snd_una);
                if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)
                        flag |= FLAG_SACK_RENEGING;
        }
@@ -3384,6 +3385,7 @@ static void tcp_ack_probe(struct sock *sk)
                return;
        if (!after(TCP_SKB_CB(head)->end_seq, tcp_wnd_end(tp))) {
                icsk->icsk_backoff = 0;
+               icsk->icsk_probes_tstamp = 0;
                inet_csk_clear_xmit_timer(sk, ICSK_TIME_PROBE0);
                /* Socket must be waked up by subsequent tcp_data_snd_check().
                 * This function is not for random using!
@@ -3391,8 +3393,8 @@ static void tcp_ack_probe(struct sock *sk)
        } else {
                unsigned long when = tcp_probe0_when(sk, TCP_RTO_MAX);
 
-               tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
-                                    when, TCP_RTO_MAX);
+               when = tcp_clamp_probe0_to_user_timeout(sk, when);
+               tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, when, TCP_RTO_MAX);
        }
 }
 
@@ -3808,16 +3810,13 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                goto no_queue;
 
        /* See if we can take anything off of the retransmit queue. */
-       flag |= tcp_clean_rtx_queue(sk, prior_fack, prior_snd_una, &sack_state,
-                                   flag & FLAG_ECE);
+       flag |= tcp_clean_rtx_queue(sk, skb, prior_fack, prior_snd_una,
+                                   &sack_state, flag & FLAG_ECE);
 
        tcp_rack_update_reo_wnd(sk, &rs);
 
        if (tp->tlp_high_seq)
                tcp_process_tlp_ack(sk, ack, flag);
-       /* If needed, reset TLP/RTO timer; RACK may later override this. */
-       if (flag & FLAG_SET_XMIT_TIMER)
-               tcp_set_xmit_timer(sk);
 
        if (tcp_ack_is_dubious(sk, flag)) {
                if (!(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP))) {
@@ -3830,6 +3829,10 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
                                      &rexmit);
        }
 
+       /* If needed, reset TLP/RTO timer when RACK doesn't set. */
+       if (flag & FLAG_SET_XMIT_TIMER)
+               tcp_set_xmit_timer(sk);
+
        if ((flag & FLAG_FORWARD_PROGRESS) || !(flag & FLAG_NOT_DUP))
                sk_dst_confirm(sk);
 
@@ -4396,10 +4399,9 @@ static void tcp_rcv_spurious_retrans(struct sock *sk, const struct sk_buff *skb)
         * The receiver remembers and reflects via DSACKs. Leverage the
         * DSACK state and change the txhash to re-route speculatively.
         */
-       if (TCP_SKB_CB(skb)->seq == tcp_sk(sk)->duplicate_sack[0].start_seq) {
-               sk_rethink_txhash(sk);
+       if (TCP_SKB_CB(skb)->seq == tcp_sk(sk)->duplicate_sack[0].start_seq &&
+           sk_rethink_txhash(sk))
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDUPLICATEDATAREHASH);
-       }
 }
 
 static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb)
index 58207c7..777306b 100644 (file)
@@ -1595,6 +1595,8 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
                tcp_move_syn(newtp, req);
                ireq->ireq_opt = NULL;
        } else {
+               newinet->inet_opt = NULL;
+
                if (!req_unhash && found_dup_sk) {
                        /* This code path should only be executed in the
                         * syncookie case only
@@ -1602,8 +1604,6 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
                        bh_unlock_sock(newsk);
                        sock_put(newsk);
                        newsk = NULL;
-               } else {
-                       newinet->inet_opt = NULL;
                }
        }
        return newsk;
@@ -1760,6 +1760,7 @@ int tcp_v4_early_demux(struct sk_buff *skb)
 bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
 {
        u32 limit = READ_ONCE(sk->sk_rcvbuf) + READ_ONCE(sk->sk_sndbuf);
+       u32 tail_gso_size, tail_gso_segs;
        struct skb_shared_info *shinfo;
        const struct tcphdr *th;
        struct tcphdr *thtail;
@@ -1767,6 +1768,7 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
        unsigned int hdrlen;
        bool fragstolen;
        u32 gso_segs;
+       u32 gso_size;
        int delta;
 
        /* In case all data was pulled from skb frags (in __pskb_pull_tail()),
@@ -1792,13 +1794,6 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
         */
        th = (const struct tcphdr *)skb->data;
        hdrlen = th->doff * 4;
-       shinfo = skb_shinfo(skb);
-
-       if (!shinfo->gso_size)
-               shinfo->gso_size = skb->len - hdrlen;
-
-       if (!shinfo->gso_segs)
-               shinfo->gso_segs = 1;
 
        tail = sk->sk_backlog.tail;
        if (!tail)
@@ -1821,6 +1816,15 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
                goto no_coalesce;
 
        __skb_pull(skb, hdrlen);
+
+       shinfo = skb_shinfo(skb);
+       gso_size = shinfo->gso_size ?: skb->len;
+       gso_segs = shinfo->gso_segs ?: 1;
+
+       shinfo = skb_shinfo(tail);
+       tail_gso_size = shinfo->gso_size ?: (tail->len - hdrlen);
+       tail_gso_segs = shinfo->gso_segs ?: 1;
+
        if (skb_try_coalesce(tail, skb, &fragstolen, &delta)) {
                TCP_SKB_CB(tail)->end_seq = TCP_SKB_CB(skb)->end_seq;
 
@@ -1847,11 +1851,8 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
                }
 
                /* Not as strict as GRO. We only need to carry mss max value */
-               skb_shinfo(tail)->gso_size = max(shinfo->gso_size,
-                                                skb_shinfo(tail)->gso_size);
-
-               gso_segs = skb_shinfo(tail)->gso_segs + shinfo->gso_segs;
-               skb_shinfo(tail)->gso_segs = min_t(u32, gso_segs, 0xFFFF);
+               shinfo->gso_size = max(gso_size, tail_gso_size);
+               shinfo->gso_segs = min_t(u32, gso_segs + tail_gso_segs, 0xFFFF);
 
                sk->sk_backlog.len += delta;
                __NET_INC_STATS(sock_net(sk),
index f322e79..fbf140a 100644 (file)
@@ -1319,7 +1319,6 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
        skb_orphan(skb);
        skb->sk = sk;
        skb->destructor = skb_is_tcp_pure_ack(skb) ? __sock_wfree : tcp_wfree;
-       skb_set_hash_from_sk(skb, sk);
        refcount_add(skb->truesize, &sk->sk_wmem_alloc);
 
        skb_set_dst_pending_confirm(skb, sk->sk_dst_pending_confirm);
@@ -1390,6 +1389,7 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
                              tcp_skb_pcount(skb));
 
        tp->segs_out += tcp_skb_pcount(skb);
+       skb_set_hash_from_sk(skb, sk);
        /* OK, its time to fill skb_shinfo(skb)->gso_{segs|size} */
        skb_shinfo(skb)->gso_segs = tcp_skb_pcount(skb);
        skb_shinfo(skb)->gso_size = tcp_skb_mss(skb);
@@ -4084,6 +4084,7 @@ void tcp_send_probe0(struct sock *sk)
                /* Cancel probe timer, if it is not required. */
                icsk->icsk_probes_out = 0;
                icsk->icsk_backoff = 0;
+               icsk->icsk_probes_tstamp = 0;
                return;
        }
 
@@ -4098,6 +4099,8 @@ void tcp_send_probe0(struct sock *sk)
                 */
                timeout = TCP_RESOURCE_PROBE_INTERVAL;
        }
+
+       timeout = tcp_clamp_probe0_to_user_timeout(sk, timeout);
        tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, timeout, TCP_RTO_MAX);
 }
 
index 177307a..6f1b4ac 100644 (file)
@@ -96,13 +96,13 @@ static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout)
        }
 }
 
-void tcp_rack_mark_lost(struct sock *sk)
+bool tcp_rack_mark_lost(struct sock *sk)
 {
        struct tcp_sock *tp = tcp_sk(sk);
        u32 timeout;
 
        if (!tp->rack.advanced)
-               return;
+               return false;
 
        /* Reset the advanced flag to avoid unnecessary queue scanning */
        tp->rack.advanced = 0;
@@ -112,6 +112,7 @@ void tcp_rack_mark_lost(struct sock *sk)
                inet_csk_reset_xmit_timer(sk, ICSK_TIME_REO_TIMEOUT,
                                          timeout, inet_csk(sk)->icsk_rto);
        }
+       return !!timeout;
 }
 
 /* Record the most recently (re)sent time among the (s)acked packets
index 6c62b9e..4ef0807 100644 (file)
@@ -40,6 +40,24 @@ static u32 tcp_clamp_rto_to_user_timeout(const struct sock *sk)
        return min_t(u32, icsk->icsk_rto, msecs_to_jiffies(remaining));
 }
 
+u32 tcp_clamp_probe0_to_user_timeout(const struct sock *sk, u32 when)
+{
+       struct inet_connection_sock *icsk = inet_csk(sk);
+       u32 remaining;
+       s32 elapsed;
+
+       if (!icsk->icsk_user_timeout || !icsk->icsk_probes_tstamp)
+               return when;
+
+       elapsed = tcp_jiffies32 - icsk->icsk_probes_tstamp;
+       if (unlikely(elapsed < 0))
+               elapsed = 0;
+       remaining = msecs_to_jiffies(icsk->icsk_user_timeout) - elapsed;
+       remaining = max_t(u32, remaining, TCP_TIMEOUT_MIN);
+
+       return min_t(u32, remaining, when);
+}
+
 /**
  *  tcp_write_err() - close socket and save error info
  *  @sk:  The socket the error has appeared on.
@@ -219,14 +237,8 @@ static int tcp_write_timeout(struct sock *sk)
        int retry_until;
 
        if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
-               if (icsk->icsk_retransmits) {
-                       dst_negative_advice(sk);
-               } else {
-                       sk_rethink_txhash(sk);
-                       tp->timeout_rehash++;
-                       __NET_INC_STATS(sock_net(sk),
-                                       LINUX_MIB_TCPTIMEOUTREHASH);
-               }
+               if (icsk->icsk_retransmits)
+                       __dst_negative_advice(sk);
                retry_until = icsk->icsk_syn_retries ? : net->ipv4.sysctl_tcp_syn_retries;
                expired = icsk->icsk_retransmits >= retry_until;
        } else {
@@ -234,12 +246,7 @@ static int tcp_write_timeout(struct sock *sk)
                        /* Black hole detection */
                        tcp_mtu_probing(icsk, sk);
 
-                       dst_negative_advice(sk);
-               } else {
-                       sk_rethink_txhash(sk);
-                       tp->timeout_rehash++;
-                       __NET_INC_STATS(sock_net(sk),
-                                       LINUX_MIB_TCPTIMEOUTREHASH);
+                       __dst_negative_advice(sk);
                }
 
                retry_until = net->ipv4.sysctl_tcp_retries2;
@@ -270,6 +277,11 @@ static int tcp_write_timeout(struct sock *sk)
                return 1;
        }
 
+       if (sk_rethink_txhash(sk)) {
+               tp->timeout_rehash++;
+               __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTIMEOUTREHASH);
+       }
+
        return 0;
 }
 
@@ -349,6 +361,7 @@ static void tcp_probe_timer(struct sock *sk)
 
        if (tp->packets_out || !skb) {
                icsk->icsk_probes_out = 0;
+               icsk->icsk_probes_tstamp = 0;
                return;
        }
 
@@ -360,13 +373,12 @@ static void tcp_probe_timer(struct sock *sk)
         * corresponding system limit. We also implement similar policy when
         * we use RTO to probe window in tcp_retransmit_timer().
         */
-       if (icsk->icsk_user_timeout) {
-               u32 elapsed = tcp_model_timeout(sk, icsk->icsk_probes_out,
-                                               tcp_probe0_base(sk));
-
-               if (elapsed >= icsk->icsk_user_timeout)
-                       goto abort;
-       }
+       if (!icsk->icsk_probes_tstamp)
+               icsk->icsk_probes_tstamp = tcp_jiffies32;
+       else if (icsk->icsk_user_timeout &&
+                (s32)(tcp_jiffies32 - icsk->icsk_probes_tstamp) >=
+                msecs_to_jiffies(icsk->icsk_user_timeout))
+               goto abort;
 
        max_probes = sock_net(sk)->ipv4.sysctl_tcp_retries2;
        if (sock_flag(sk, SOCK_DEAD)) {
index 7103b0a..69ea765 100644 (file)
@@ -2555,7 +2555,8 @@ int udp_v4_early_demux(struct sk_buff *skb)
                 */
                if (!inet_sk(sk)->inet_daddr && in_dev)
                        return ip_mc_validate_source(skb, iph->daddr,
-                                                    iph->saddr, iph->tos,
+                                                    iph->saddr,
+                                                    iph->tos & IPTOS_RT_MASK,
                                                     skb->dev, in_dev, &itag);
        }
        return 0;
index ff39e94..4124970 100644 (file)
@@ -68,8 +68,8 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
                                      (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM))));
 
        features &= skb->dev->hw_enc_features;
-       /* CRC checksum can't be handled by HW when it's a UDP tunneling packet. */
-       features &= ~NETIF_F_SCTP_CRC;
+       if (need_csum)
+               features &= ~NETIF_F_SCTP_CRC;
 
        /* The only checksum offload we care about from here on out is the
         * outer one so strip the existing checksum feature flags and
@@ -460,7 +460,8 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
        if (skb->dev->features & NETIF_F_GRO_FRAGLIST)
                NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled: 1;
 
-       if ((sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist) {
+       if ((!sk && (skb->dev->features & NETIF_F_GRO_UDP_FWD)) ||
+           (sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist) {
                pp = call_gro_receive(udp_gro_receive_segment, head, skb);
                return pp;
        }
index 3eecba0..b97e363 100644 (file)
@@ -90,15 +90,11 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock,
        struct sock *sk = sock->sk;
        struct udp_tunnel_info ti;
 
-       if (!dev->netdev_ops->ndo_udp_tunnel_add ||
-           !(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
-               return;
-
        ti.type = type;
        ti.sa_family = sk->sk_family;
        ti.port = inet_sk(sk)->inet_sport;
 
-       dev->netdev_ops->ndo_udp_tunnel_add(dev, &ti);
+       udp_tunnel_nic_add_port(dev, &ti);
 }
 EXPORT_SYMBOL_GPL(udp_tunnel_push_rx_port);
 
@@ -108,15 +104,11 @@ void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock,
        struct sock *sk = sock->sk;
        struct udp_tunnel_info ti;
 
-       if (!dev->netdev_ops->ndo_udp_tunnel_del ||
-           !(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
-               return;
-
        ti.type = type;
        ti.sa_family = sk->sk_family;
        ti.port = inet_sk(sk)->inet_sport;
 
-       dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti);
+       udp_tunnel_nic_del_port(dev, &ti);
 }
 EXPORT_SYMBOL_GPL(udp_tunnel_drop_rx_port);
 
@@ -134,11 +126,7 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type)
 
        rcu_read_lock();
        for_each_netdev_rcu(net, dev) {
-               if (!dev->netdev_ops->ndo_udp_tunnel_add)
-                       continue;
-               if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
-                       continue;
-               dev->netdev_ops->ndo_udp_tunnel_add(dev, &ti);
+               udp_tunnel_nic_add_port(dev, &ti);
        }
        rcu_read_unlock();
 }
@@ -158,11 +146,7 @@ void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type)
 
        rcu_read_lock();
        for_each_netdev_rcu(net, dev) {
-               if (!dev->netdev_ops->ndo_udp_tunnel_del)
-                       continue;
-               if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT))
-                       continue;
-               dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti);
+               udp_tunnel_nic_del_port(dev, &ti);
        }
        rcu_read_unlock();
 }
index eff2cac..f2337fb 100644 (file)
@@ -205,6 +205,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
        .max_desync_factor      = MAX_DESYNC_FACTOR,
        .max_addresses          = IPV6_MAX_ADDRESSES,
        .accept_ra_defrtr       = 1,
+       .ra_defrtr_metric       = IP6_RT_PRIO_USER,
        .accept_ra_from_local   = 0,
        .accept_ra_min_hop_limit= 1,
        .accept_ra_pinfo        = 1,
@@ -260,6 +261,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
        .max_desync_factor      = MAX_DESYNC_FACTOR,
        .max_addresses          = IPV6_MAX_ADDRESSES,
        .accept_ra_defrtr       = 1,
+       .ra_defrtr_metric       = IP6_RT_PRIO_USER,
        .accept_ra_from_local   = 0,
        .accept_ra_min_hop_limit= 1,
        .accept_ra_pinfo        = 1,
@@ -2467,8 +2469,9 @@ static void addrconf_add_mroute(struct net_device *dev)
                .fc_ifindex = dev->ifindex,
                .fc_dst_len = 8,
                .fc_flags = RTF_UP,
-               .fc_type = RTN_UNICAST,
+               .fc_type = RTN_MULTICAST,
                .fc_nlinfo.nl_net = dev_net(dev),
+               .fc_protocol = RTPROT_KERNEL,
        };
 
        ipv6_addr_set(&cfg.fc_dst, htonl(0xFF000000), 0, 0, 0);
@@ -5475,6 +5478,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
        array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
        array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
        array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr;
+       array[DEVCONF_RA_DEFRTR_METRIC] = cnf->ra_defrtr_metric;
        array[DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT] = cnf->accept_ra_min_hop_limit;
        array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo;
 #ifdef CONFIG_IPV6_ROUTER_PREF
@@ -6667,6 +6671,14 @@ static const struct ctl_table addrconf_sysctl[] = {
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
        },
+       {
+               .procname       = "ra_defrtr_metric",
+               .data           = &ipv6_devconf.ra_defrtr_metric,
+               .maxlen         = sizeof(u32),
+               .mode           = 0644,
+               .proc_handler   = proc_douintvec_minmax,
+               .extra1         = (void *)SYSCTL_ONE,
+       },
        {
                .procname       = "accept_ra_min_hop_limit",
                .data           = &ipv6_devconf.accept_ra_min_hop_limit,
index 52c2f06..2b804fc 100644 (file)
@@ -478,7 +478,6 @@ static int esp6_output_encap(struct xfrm_state *x, struct sk_buff *skb,
 int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp)
 {
        u8 *tail;
-       u8 *vaddr;
        int nfrags;
        int esph_offset;
        struct page *page;
@@ -519,14 +518,10 @@ int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info
                        page = pfrag->page;
                        get_page(page);
 
-                       vaddr = kmap_atomic(page);
-
-                       tail = vaddr + pfrag->offset;
+                       tail = page_address(page) + pfrag->offset;
 
                        esp_output_fill_trailer(tail, esp->tfclen, esp->plen, esp->proto);
 
-                       kunmap_atomic(vaddr);
-
                        nfrags = skb_shinfo(skb)->nr_frags;
 
                        __skb_fill_page_desc(skb, nfrags, page, pfrag->offset,
index 605cdd3..f43e275 100644 (file)
@@ -1025,6 +1025,8 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
 {
        struct fib6_table *table = rt->fib6_table;
 
+       /* Flush all cached dst in exception table */
+       rt6_flush_exceptions(rt);
        fib6_drop_pcpu_from(rt, table);
 
        if (rt->nh && !list_empty(&rt->nh_list))
@@ -1927,9 +1929,6 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn,
        net->ipv6.rt6_stats->fib_rt_entries--;
        net->ipv6.rt6_stats->fib_discarded_routes++;
 
-       /* Flush all cached dst in exception table */
-       rt6_flush_exceptions(rt);
-
        /* Reset round-robin state, if necessary */
        if (rcu_access_pointer(fn->rr_ptr) == rt)
                fn->rr_ptr = NULL;
index 749ad72..117cd95 100644 (file)
@@ -125,8 +125,43 @@ static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *
        return -EINVAL;
 }
 
+static int
+ip6_finish_output_gso_slowpath_drop(struct net *net, struct sock *sk,
+                                   struct sk_buff *skb, unsigned int mtu)
+{
+       struct sk_buff *segs, *nskb;
+       netdev_features_t features;
+       int ret = 0;
+
+       /* Please see corresponding comment in ip_finish_output_gso
+        * describing the cases where GSO segment length exceeds the
+        * egress MTU.
+        */
+       features = netif_skb_features(skb);
+       segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
+       if (IS_ERR_OR_NULL(segs)) {
+               kfree_skb(skb);
+               return -ENOMEM;
+       }
+
+       consume_skb(skb);
+
+       skb_list_walk_safe(segs, segs, nskb) {
+               int err;
+
+               skb_mark_not_on_list(segs);
+               err = ip6_fragment(net, sk, segs, ip6_finish_output2);
+               if (err && ret == 0)
+                       ret = err;
+       }
+
+       return ret;
+}
+
 static int __ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 {
+       unsigned int mtu;
+
 #if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
        /* Policy lookup after SNAT yielded a new policy */
        if (skb_dst(skb)->xfrm) {
@@ -135,7 +170,11 @@ static int __ip6_finish_output(struct net *net, struct sock *sk, struct sk_buff
        }
 #endif
 
-       if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
+       mtu = ip6_skb_dst_mtu(skb);
+       if (skb_is_gso(skb) && !skb_gso_validate_network_len(skb, mtu))
+               return ip6_finish_output_gso_slowpath_drop(net, sk, skb, mtu);
+
+       if ((skb->len > mtu && !skb_is_gso(skb)) ||
            dst_allfrag(skb_dst(skb)) ||
            (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size))
                return ip6_fragment(net, sk, skb, ip6_finish_output2);
@@ -1471,7 +1510,7 @@ emsgsize:
                csummode = CHECKSUM_PARTIAL;
 
        if (flags & MSG_ZEROCOPY && length && sock_flag(sk, SOCK_ZEROCOPY)) {
-               uarg = sock_zerocopy_realloc(sk, length, skb_zcopy(skb));
+               uarg = msg_zerocopy_realloc(sk, length, skb_zcopy(skb));
                if (!uarg)
                        return -ENOBUFS;
                extra_uref = !skb_zcopy(skb);   /* only ref on new uarg */
@@ -1715,8 +1754,7 @@ alloc_new_skb:
 error_efault:
        err = -EFAULT;
 error:
-       if (uarg)
-               sock_zerocopy_put_abort(uarg, extra_uref);
+       net_zcopy_put_abort(uarg, extra_uref);
        cork->length -= length;
        IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
        refcount_add(wmem_alloc_delta, &sk->sk_wmem_alloc);
index 7671747..c467c64 100644 (file)
@@ -1173,6 +1173,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
        struct neighbour *neigh = NULL;
        struct inet6_dev *in6_dev;
        struct fib6_info *rt = NULL;
+       u32 defrtr_usr_metric;
        struct net *net;
        int lifetime;
        struct ndisc_options ndopts;
@@ -1303,18 +1304,21 @@ static void ndisc_router_discovery(struct sk_buff *skb)
                        return;
                }
        }
-       if (rt && lifetime == 0) {
+       /* Set default route metric as specified by user */
+       defrtr_usr_metric = in6_dev->cnf.ra_defrtr_metric;
+       /* delete the route if lifetime is 0 or if metric needs change */
+       if (rt && (lifetime == 0 || rt->fib6_metric != defrtr_usr_metric)) {
                ip6_del_rt(net, rt, false);
                rt = NULL;
        }
 
-       ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, for dev: %s\n",
-                 rt, lifetime, skb->dev->name);
+       ND_PRINTK(3, info, "RA: rt: %p  lifetime: %d, metric: %d, for dev: %s\n",
+                 rt, lifetime, defrtr_usr_metric, skb->dev->name);
        if (!rt && lifetime) {
                ND_PRINTK(3, info, "RA: adding default router\n");
 
                rt = rt6_add_dflt_router(net, &ipv6_hdr(skb)->saddr,
-                                        skb->dev, pref);
+                                        skb->dev, pref, defrtr_usr_metric);
                if (!rt) {
                        ND_PRINTK(0, err,
                                  "RA: %s failed to add default route\n",
index c4f532f..0d453fa 100644 (file)
@@ -1598,7 +1598,7 @@ compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
        xt_compat_lock(AF_INET6);
        t = xt_find_table_lock(net, AF_INET6, get.name);
        if (!IS_ERR(t)) {
-               const struct xt_table_info *private = t->private;
+               const struct xt_table_info *private = xt_table_get_private_protected(t);
                struct xt_table_info info;
                ret = compat_table_info(private, &info);
                if (!ret && get.size == info.size)
index 188e114..41d8f80 100644 (file)
@@ -4252,11 +4252,12 @@ struct fib6_info *rt6_get_dflt_router(struct net *net,
 struct fib6_info *rt6_add_dflt_router(struct net *net,
                                     const struct in6_addr *gwaddr,
                                     struct net_device *dev,
-                                    unsigned int pref)
+                                    unsigned int pref,
+                                    u32 defrtr_usr_metric)
 {
        struct fib6_config cfg = {
                .fc_table       = l3mdev_fib_table(dev) ? : RT6_TABLE_DFLT,
-               .fc_metric      = IP6_RT_PRIO_USER,
+               .fc_metric      = defrtr_usr_metric,
                .fc_ifindex     = dev->ifindex,
                .fc_flags       = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT |
                                  RTF_UP | RTF_EXPIRES | RTF_PREF(pref),
index 2da0ee7..9363686 100644 (file)
@@ -1645,8 +1645,11 @@ static int ipip6_newlink(struct net *src_net, struct net_device *dev,
        }
 
 #ifdef CONFIG_IPV6_SIT_6RD
-       if (ipip6_netlink_6rd_parms(data, &ip6rd))
+       if (ipip6_netlink_6rd_parms(data, &ip6rd)) {
                err = ipip6_tunnel_update_6rd(nt, &ip6rd);
+               if (err < 0)
+                       unregister_netdevice_queue(dev, NULL);
+       }
 #endif
 
        return err;
index 882f028..6092d5c 100644 (file)
@@ -89,7 +89,7 @@ static struct sock *iucv_accept_dequeue(struct sock *parent,
 static void iucv_sock_kill(struct sock *sk);
 static void iucv_sock_close(struct sock *sk);
 
-static void afiucv_hs_callback_txnotify(struct sk_buff *, enum iucv_tx_notify);
+static void afiucv_hs_callback_txnotify(struct sock *sk, enum iucv_tx_notify);
 
 /* Call Back functions */
 static void iucv_callback_rx(struct iucv_path *, struct iucv_message *);
@@ -182,7 +182,7 @@ static inline int iucv_below_msglim(struct sock *sk)
        if (sk->sk_state != IUCV_CONNECTED)
                return 1;
        if (iucv->transport == AF_IUCV_TRANS_IUCV)
-               return (skb_queue_len(&iucv->send_skb_q) < iucv->path->msglim);
+               return (atomic_read(&iucv->skbs_in_xmit) < iucv->path->msglim);
        else
                return ((atomic_read(&iucv->msg_sent) < iucv->msglimit_peer) &&
                        (atomic_read(&iucv->pendings) <= 0));
@@ -211,7 +211,6 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
 {
        struct iucv_sock *iucv = iucv_sk(sock);
        struct af_iucv_trans_hdr *phs_hdr;
-       struct sk_buff *nskb;
        int err, confirm_recv = 0;
 
        phs_hdr = skb_push(skb, sizeof(*phs_hdr));
@@ -257,22 +256,16 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
                        err = -EMSGSIZE;
                        goto err_free;
                }
-               skb_trim(skb, skb->dev->mtu);
+               err = pskb_trim(skb, skb->dev->mtu);
+               if (err)
+                       goto err_free;
        }
        skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
 
-       __skb_header_release(skb);
-       nskb = skb_clone(skb, GFP_ATOMIC);
-       if (!nskb) {
-               err = -ENOMEM;
-               goto err_free;
-       }
-
-       skb_queue_tail(&iucv->send_skb_q, nskb);
+       atomic_inc(&iucv->skbs_in_xmit);
        err = dev_queue_xmit(skb);
        if (net_xmit_eval(err)) {
-               skb_unlink(nskb, &iucv->send_skb_q);
-               kfree_skb(nskb);
+               atomic_dec(&iucv->skbs_in_xmit);
        } else {
                atomic_sub(confirm_recv, &iucv->msg_recv);
                WARN_ON(atomic_read(&iucv->msg_recv) < 0);
@@ -424,7 +417,7 @@ static void iucv_sock_close(struct sock *sk)
                sk->sk_state = IUCV_CLOSING;
                sk->sk_state_change(sk);
 
-               if (!err && !skb_queue_empty(&iucv->send_skb_q)) {
+               if (!err && atomic_read(&iucv->skbs_in_xmit) > 0) {
                        if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
                                timeo = sk->sk_lingertime;
                        else
@@ -491,6 +484,7 @@ static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio,
        atomic_set(&iucv->pendings, 0);
        iucv->flags = 0;
        iucv->msglimit = 0;
+       atomic_set(&iucv->skbs_in_xmit, 0);
        atomic_set(&iucv->msg_sent, 0);
        atomic_set(&iucv->msg_recv, 0);
        iucv->path = NULL;
@@ -1004,7 +998,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
        if (iucv->transport == AF_IUCV_TRANS_HIPER) {
                headroom = sizeof(struct af_iucv_trans_hdr) +
                           LL_RESERVED_SPACE(iucv->hs_dev);
-               linear = len;
+               linear = min(len, PAGE_SIZE - headroom);
        } else {
                if (len < PAGE_SIZE) {
                        linear = len;
@@ -1055,6 +1049,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                }
        } else { /* Classic VM IUCV transport */
                skb_queue_tail(&iucv->send_skb_q, skb);
+               atomic_inc(&iucv->skbs_in_xmit);
 
                if (((iucv->path->flags & IUCV_IPRMDATA) & iucv->flags) &&
                    skb->len <= 7) {
@@ -1063,6 +1058,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                        /* on success: there is no message_complete callback */
                        /* for an IPRMDATA msg; remove skb from send queue   */
                        if (err == 0) {
+                               atomic_dec(&iucv->skbs_in_xmit);
                                skb_unlink(skb, &iucv->send_skb_q);
                                kfree_skb(skb);
                        }
@@ -1071,6 +1067,7 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                        /* IUCV_IPRMDATA path flag is set... sever path */
                        if (err == 0x15) {
                                pr_iucv->path_sever(iucv->path, NULL);
+                               atomic_dec(&iucv->skbs_in_xmit);
                                skb_unlink(skb, &iucv->send_skb_q);
                                err = -EPIPE;
                                goto fail;
@@ -1109,6 +1106,8 @@ static int iucv_sock_sendmsg(struct socket *sock, struct msghdr *msg,
                        } else {
                                err = -EPIPE;
                        }
+
+                       atomic_dec(&iucv->skbs_in_xmit);
                        skb_unlink(skb, &iucv->send_skb_q);
                        goto fail;
                }
@@ -1748,10 +1747,14 @@ static void iucv_callback_txdone(struct iucv_path *path,
 {
        struct sock *sk = path->private;
        struct sk_buff *this = NULL;
-       struct sk_buff_head *list = &iucv_sk(sk)->send_skb_q;
+       struct sk_buff_head *list;
        struct sk_buff *list_skb;
+       struct iucv_sock *iucv;
        unsigned long flags;
 
+       iucv = iucv_sk(sk);
+       list = &iucv->send_skb_q;
+
        bh_lock_sock(sk);
 
        spin_lock_irqsave(&list->lock, flags);
@@ -1761,8 +1764,11 @@ static void iucv_callback_txdone(struct iucv_path *path,
                        break;
                }
        }
-       if (this)
+       if (this) {
+               atomic_dec(&iucv->skbs_in_xmit);
                __skb_unlink(this, list);
+       }
+
        spin_unlock_irqrestore(&list->lock, flags);
 
        if (this) {
@@ -1772,7 +1778,7 @@ static void iucv_callback_txdone(struct iucv_path *path,
        }
 
        if (sk->sk_state == IUCV_CLOSING) {
-               if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
+               if (atomic_read(&iucv->skbs_in_xmit) == 0) {
                        sk->sk_state = IUCV_CLOSED;
                        sk->sk_state_change(sk);
                }
@@ -2036,7 +2042,6 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
        char nullstring[8];
 
        if (!pskb_may_pull(skb, sizeof(*trans_hdr))) {
-               WARN_ONCE(1, "AF_IUCV failed to receive skb, len=%u", skb->len);
                kfree_skb(skb);
                return NET_RX_SUCCESS;
        }
@@ -2132,73 +2137,40 @@ static int afiucv_hs_rcv(struct sk_buff *skb, struct net_device *dev,
  * afiucv_hs_callback_txnotify() - handle send notifcations from HiperSockets
  *                                 transport
  **/
-static void afiucv_hs_callback_txnotify(struct sk_buff *skb,
-                                       enum iucv_tx_notify n)
+static void afiucv_hs_callback_txnotify(struct sock *sk, enum iucv_tx_notify n)
 {
-       struct sock *isk = skb->sk;
-       struct sock *sk = NULL;
-       struct iucv_sock *iucv = NULL;
-       struct sk_buff_head *list;
-       struct sk_buff *list_skb;
-       struct sk_buff *nskb;
-       unsigned long flags;
-
-       read_lock_irqsave(&iucv_sk_list.lock, flags);
-       sk_for_each(sk, &iucv_sk_list.head)
-               if (sk == isk) {
-                       iucv = iucv_sk(sk);
-                       break;
-               }
-       read_unlock_irqrestore(&iucv_sk_list.lock, flags);
+       struct iucv_sock *iucv = iucv_sk(sk);
 
-       if (!iucv || sock_flag(sk, SOCK_ZAPPED))
+       if (sock_flag(sk, SOCK_ZAPPED))
                return;
 
-       list = &iucv->send_skb_q;
-       spin_lock_irqsave(&list->lock, flags);
-       skb_queue_walk_safe(list, list_skb, nskb) {
-               if (skb_shinfo(list_skb) == skb_shinfo(skb)) {
-                       switch (n) {
-                       case TX_NOTIFY_OK:
-                               __skb_unlink(list_skb, list);
-                               kfree_skb(list_skb);
-                               iucv_sock_wake_msglim(sk);
-                               break;
-                       case TX_NOTIFY_PENDING:
-                               atomic_inc(&iucv->pendings);
-                               break;
-                       case TX_NOTIFY_DELAYED_OK:
-                               __skb_unlink(list_skb, list);
-                               atomic_dec(&iucv->pendings);
-                               if (atomic_read(&iucv->pendings) <= 0)
-                                       iucv_sock_wake_msglim(sk);
-                               kfree_skb(list_skb);
-                               break;
-                       case TX_NOTIFY_UNREACHABLE:
-                       case TX_NOTIFY_DELAYED_UNREACHABLE:
-                       case TX_NOTIFY_TPQFULL: /* not yet used */
-                       case TX_NOTIFY_GENERALERROR:
-                       case TX_NOTIFY_DELAYED_GENERALERROR:
-                               __skb_unlink(list_skb, list);
-                               kfree_skb(list_skb);
-                               if (sk->sk_state == IUCV_CONNECTED) {
-                                       sk->sk_state = IUCV_DISCONN;
-                                       sk->sk_state_change(sk);
-                               }
-                               break;
-                       }
-                       break;
+       switch (n) {
+       case TX_NOTIFY_OK:
+               atomic_dec(&iucv->skbs_in_xmit);
+               iucv_sock_wake_msglim(sk);
+               break;
+       case TX_NOTIFY_PENDING:
+               atomic_inc(&iucv->pendings);
+               break;
+       case TX_NOTIFY_DELAYED_OK:
+               atomic_dec(&iucv->skbs_in_xmit);
+               if (atomic_dec_return(&iucv->pendings) <= 0)
+                       iucv_sock_wake_msglim(sk);
+               break;
+       default:
+               atomic_dec(&iucv->skbs_in_xmit);
+               if (sk->sk_state == IUCV_CONNECTED) {
+                       sk->sk_state = IUCV_DISCONN;
+                       sk->sk_state_change(sk);
                }
        }
-       spin_unlock_irqrestore(&list->lock, flags);
 
        if (sk->sk_state == IUCV_CLOSING) {
-               if (skb_queue_empty(&iucv_sk(sk)->send_skb_q)) {
+               if (atomic_read(&iucv->skbs_in_xmit) == 0) {
                        sk->sk_state = IUCV_CLOSED;
                        sk->sk_state_change(sk);
                }
        }
-
 }
 
 /*
index 56dad95..d0b56ff 100644 (file)
@@ -786,7 +786,7 @@ static ssize_t kcm_sendpage(struct socket *sock, struct page *page,
 
                if (skb_can_coalesce(skb, i, page, offset)) {
                        skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], size);
-                       skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
+                       skb_shinfo(skb)->flags |= SKBFL_SHARED_FRAG;
                        goto coalesced;
                }
 
@@ -834,7 +834,7 @@ static ssize_t kcm_sendpage(struct socket *sock, struct page *page,
 
        get_page(page);
        skb_fill_page_desc(skb, i, page, offset, size);
-       skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG;
+       skb_shinfo(skb)->flags |= SKBFL_SHARED_FRAG;
 
 coalesced:
        skb->len += size;
@@ -1496,7 +1496,7 @@ static int kcm_attach_ioctl(struct socket *sock, struct kcm_attach *info)
 
        return 0;
 out:
-       fput(csock->file);
+       sockfd_put(csock);
        return err;
 }
 
@@ -1644,7 +1644,7 @@ static int kcm_unattach_ioctl(struct socket *sock, struct kcm_unattach *info)
        spin_unlock_bh(&mux->lock);
 
 out:
-       fput(csock->file);
+       sockfd_put(csock);
        return err;
 }
 
index c12dbc5..ef9b4ac 100644 (file)
@@ -2902,7 +2902,7 @@ static int count_ah_combs(const struct xfrm_tmpl *t)
                        break;
                if (!aalg->pfkey_supported)
                        continue;
-               if (aalg_tmpl_set(t, aalg) && aalg->available)
+               if (aalg_tmpl_set(t, aalg))
                        sz += sizeof(struct sadb_comb);
        }
        return sz + sizeof(struct sadb_prop);
@@ -2920,7 +2920,7 @@ static int count_esp_combs(const struct xfrm_tmpl *t)
                if (!ealg->pfkey_supported)
                        continue;
 
-               if (!(ealg_tmpl_set(t, ealg) && ealg->available))
+               if (!(ealg_tmpl_set(t, ealg)))
                        continue;
 
                for (k = 1; ; k++) {
@@ -2931,7 +2931,7 @@ static int count_esp_combs(const struct xfrm_tmpl *t)
                        if (!aalg->pfkey_supported)
                                continue;
 
-                       if (aalg_tmpl_set(t, aalg) && aalg->available)
+                       if (aalg_tmpl_set(t, aalg))
                                sz += sizeof(struct sadb_comb);
                }
        }
index 59755a9..9e7da0a 100644 (file)
@@ -3,4 +3,4 @@
 # Makefile for the L3 device API
 #
 
-obj-$(CONFIG_NET_L3_MASTER_DEV) += l3mdev.o
+obj-y += l3mdev.o
index 213ea7a..0511bbe 100644 (file)
@@ -122,6 +122,8 @@ static struct lapb_cb *lapb_create_cb(void)
 
        timer_setup(&lapb->t1timer, NULL, 0);
        timer_setup(&lapb->t2timer, NULL, 0);
+       lapb->t1timer_stop = true;
+       lapb->t2timer_stop = true;
 
        lapb->t1      = LAPB_DEFAULT_T1;
        lapb->t2      = LAPB_DEFAULT_T2;
@@ -129,6 +131,8 @@ static struct lapb_cb *lapb_create_cb(void)
        lapb->mode    = LAPB_DEFAULT_MODE;
        lapb->window  = LAPB_DEFAULT_WINDOW;
        lapb->state   = LAPB_STATE_0;
+
+       spin_lock_init(&lapb->lock);
        refcount_set(&lapb->refcnt, 1);
 out:
        return lapb;
@@ -178,11 +182,23 @@ int lapb_unregister(struct net_device *dev)
                goto out;
        lapb_put(lapb);
 
+       /* Wait for other refs to "lapb" to drop */
+       while (refcount_read(&lapb->refcnt) > 2)
+               usleep_range(1, 10);
+
+       spin_lock_bh(&lapb->lock);
+
        lapb_stop_t1timer(lapb);
        lapb_stop_t2timer(lapb);
 
        lapb_clear_queues(lapb);
 
+       spin_unlock_bh(&lapb->lock);
+
+       /* Wait for running timers to stop */
+       del_timer_sync(&lapb->t1timer);
+       del_timer_sync(&lapb->t2timer);
+
        __lapb_remove_cb(lapb);
 
        lapb_put(lapb);
@@ -201,6 +217,8 @@ int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms)
        if (!lapb)
                goto out;
 
+       spin_lock_bh(&lapb->lock);
+
        parms->t1      = lapb->t1 / HZ;
        parms->t2      = lapb->t2 / HZ;
        parms->n2      = lapb->n2;
@@ -219,6 +237,7 @@ int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms)
        else
                parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ;
 
+       spin_unlock_bh(&lapb->lock);
        lapb_put(lapb);
        rc = LAPB_OK;
 out:
@@ -234,6 +253,8 @@ int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms)
        if (!lapb)
                goto out;
 
+       spin_lock_bh(&lapb->lock);
+
        rc = LAPB_INVALUE;
        if (parms->t1 < 1 || parms->t2 < 1 || parms->n2 < 1)
                goto out_put;
@@ -256,6 +277,7 @@ int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms)
 
        rc = LAPB_OK;
 out_put:
+       spin_unlock_bh(&lapb->lock);
        lapb_put(lapb);
 out:
        return rc;
@@ -270,6 +292,8 @@ int lapb_connect_request(struct net_device *dev)
        if (!lapb)
                goto out;
 
+       spin_lock_bh(&lapb->lock);
+
        rc = LAPB_OK;
        if (lapb->state == LAPB_STATE_1)
                goto out_put;
@@ -285,24 +309,18 @@ int lapb_connect_request(struct net_device *dev)
 
        rc = LAPB_OK;
 out_put:
+       spin_unlock_bh(&lapb->lock);
        lapb_put(lapb);
 out:
        return rc;
 }
 EXPORT_SYMBOL(lapb_connect_request);
 
-int lapb_disconnect_request(struct net_device *dev)
+static int __lapb_disconnect_request(struct lapb_cb *lapb)
 {
-       struct lapb_cb *lapb = lapb_devtostruct(dev);
-       int rc = LAPB_BADTOKEN;
-
-       if (!lapb)
-               goto out;
-
        switch (lapb->state) {
        case LAPB_STATE_0:
-               rc = LAPB_NOTCONNECTED;
-               goto out_put;
+               return LAPB_NOTCONNECTED;
 
        case LAPB_STATE_1:
                lapb_dbg(1, "(%p) S1 TX DISC(1)\n", lapb->dev);
@@ -310,12 +328,10 @@ int lapb_disconnect_request(struct net_device *dev)
                lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
                lapb->state = LAPB_STATE_0;
                lapb_start_t1timer(lapb);
-               rc = LAPB_NOTCONNECTED;
-               goto out_put;
+               return LAPB_NOTCONNECTED;
 
        case LAPB_STATE_2:
-               rc = LAPB_OK;
-               goto out_put;
+               return LAPB_OK;
        }
 
        lapb_clear_queues(lapb);
@@ -328,8 +344,22 @@ int lapb_disconnect_request(struct net_device *dev)
        lapb_dbg(1, "(%p) S3 DISC(1)\n", lapb->dev);
        lapb_dbg(0, "(%p) S3 -> S2\n", lapb->dev);
 
-       rc = LAPB_OK;
-out_put:
+       return LAPB_OK;
+}
+
+int lapb_disconnect_request(struct net_device *dev)
+{
+       struct lapb_cb *lapb = lapb_devtostruct(dev);
+       int rc = LAPB_BADTOKEN;
+
+       if (!lapb)
+               goto out;
+
+       spin_lock_bh(&lapb->lock);
+
+       rc = __lapb_disconnect_request(lapb);
+
+       spin_unlock_bh(&lapb->lock);
        lapb_put(lapb);
 out:
        return rc;
@@ -344,6 +374,8 @@ int lapb_data_request(struct net_device *dev, struct sk_buff *skb)
        if (!lapb)
                goto out;
 
+       spin_lock_bh(&lapb->lock);
+
        rc = LAPB_NOTCONNECTED;
        if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4)
                goto out_put;
@@ -352,6 +384,7 @@ int lapb_data_request(struct net_device *dev, struct sk_buff *skb)
        lapb_kick(lapb);
        rc = LAPB_OK;
 out_put:
+       spin_unlock_bh(&lapb->lock);
        lapb_put(lapb);
 out:
        return rc;
@@ -364,7 +397,9 @@ int lapb_data_received(struct net_device *dev, struct sk_buff *skb)
        int rc = LAPB_BADTOKEN;
 
        if (lapb) {
+               spin_lock_bh(&lapb->lock);
                lapb_data_input(lapb, skb);
+               spin_unlock_bh(&lapb->lock);
                lapb_put(lapb);
                rc = LAPB_OK;
        }
@@ -435,6 +470,8 @@ static int lapb_device_event(struct notifier_block *this, unsigned long event,
        if (!lapb)
                return NOTIFY_DONE;
 
+       spin_lock_bh(&lapb->lock);
+
        switch (event) {
        case NETDEV_UP:
                lapb_dbg(0, "(%p) Interface up: %s\n", dev, dev->name);
@@ -454,7 +491,7 @@ static int lapb_device_event(struct notifier_block *this, unsigned long event,
                break;
        case NETDEV_GOING_DOWN:
                if (netif_carrier_ok(dev))
-                       lapb_disconnect_request(dev);
+                       __lapb_disconnect_request(lapb);
                break;
        case NETDEV_DOWN:
                lapb_dbg(0, "(%p) Interface down: %s\n", dev, dev->name);
@@ -489,6 +526,8 @@ static int lapb_device_event(struct notifier_block *this, unsigned long event,
                break;
        }
 
+       spin_unlock_bh(&lapb->lock);
+       lapb_put(lapb);
        return NOTIFY_DONE;
 }
 
index baa247f..0230b27 100644 (file)
@@ -40,6 +40,7 @@ void lapb_start_t1timer(struct lapb_cb *lapb)
        lapb->t1timer.function = lapb_t1timer_expiry;
        lapb->t1timer.expires  = jiffies + lapb->t1;
 
+       lapb->t1timer_stop = false;
        add_timer(&lapb->t1timer);
 }
 
@@ -50,16 +51,19 @@ void lapb_start_t2timer(struct lapb_cb *lapb)
        lapb->t2timer.function = lapb_t2timer_expiry;
        lapb->t2timer.expires  = jiffies + lapb->t2;
 
+       lapb->t2timer_stop = false;
        add_timer(&lapb->t2timer);
 }
 
 void lapb_stop_t1timer(struct lapb_cb *lapb)
 {
+       lapb->t1timer_stop = true;
        del_timer(&lapb->t1timer);
 }
 
 void lapb_stop_t2timer(struct lapb_cb *lapb)
 {
+       lapb->t2timer_stop = true;
        del_timer(&lapb->t2timer);
 }
 
@@ -72,16 +76,31 @@ static void lapb_t2timer_expiry(struct timer_list *t)
 {
        struct lapb_cb *lapb = from_timer(lapb, t, t2timer);
 
+       spin_lock_bh(&lapb->lock);
+       if (timer_pending(&lapb->t2timer)) /* A new timer has been set up */
+               goto out;
+       if (lapb->t2timer_stop) /* The timer has been stopped */
+               goto out;
+
        if (lapb->condition & LAPB_ACK_PENDING_CONDITION) {
                lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
                lapb_timeout_response(lapb);
        }
+
+out:
+       spin_unlock_bh(&lapb->lock);
 }
 
 static void lapb_t1timer_expiry(struct timer_list *t)
 {
        struct lapb_cb *lapb = from_timer(lapb, t, t1timer);
 
+       spin_lock_bh(&lapb->lock);
+       if (timer_pending(&lapb->t1timer)) /* A new timer has been set up */
+               goto out;
+       if (lapb->t1timer_stop) /* The timer has been stopped */
+               goto out;
+
        switch (lapb->state) {
 
                /*
@@ -108,7 +127,7 @@ static void lapb_t1timer_expiry(struct timer_list *t)
                                lapb->state = LAPB_STATE_0;
                                lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
                                lapb_dbg(0, "(%p) S1 -> S0\n", lapb->dev);
-                               return;
+                               goto out;
                        } else {
                                lapb->n2count++;
                                if (lapb->mode & LAPB_EXTENDED) {
@@ -132,7 +151,7 @@ static void lapb_t1timer_expiry(struct timer_list *t)
                                lapb->state = LAPB_STATE_0;
                                lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT);
                                lapb_dbg(0, "(%p) S2 -> S0\n", lapb->dev);
-                               return;
+                               goto out;
                        } else {
                                lapb->n2count++;
                                lapb_dbg(1, "(%p) S2 TX DISC(1)\n", lapb->dev);
@@ -150,7 +169,7 @@ static void lapb_t1timer_expiry(struct timer_list *t)
                                lapb_stop_t2timer(lapb);
                                lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
                                lapb_dbg(0, "(%p) S3 -> S0\n", lapb->dev);
-                               return;
+                               goto out;
                        } else {
                                lapb->n2count++;
                                lapb_requeue_frames(lapb);
@@ -167,7 +186,7 @@ static void lapb_t1timer_expiry(struct timer_list *t)
                                lapb->state = LAPB_STATE_0;
                                lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
                                lapb_dbg(0, "(%p) S4 -> S0\n", lapb->dev);
-                               return;
+                               goto out;
                        } else {
                                lapb->n2count++;
                                lapb_transmit_frmr(lapb);
@@ -176,4 +195,7 @@ static void lapb_t1timer_expiry(struct timer_list *t)
        }
 
        lapb_start_t1timer(lapb);
+
+out:
+       spin_unlock_bh(&lapb->lock);
 }
index b0e646a..7f79f5e 100644 (file)
@@ -1,7 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 config LLC
        tristate
-       depends on NET
 
 config LLC2
        tristate "ANSI/IEEE 802.2 LLC type 2 Support"
index ad04c36..23d25e8 100644 (file)
@@ -56,11 +56,9 @@ mac80211-$(CONFIG_PM) += pm.o
 CFLAGS_trace.o := -I$(src)
 
 rc80211_minstrel-y := \
-       rc80211_minstrel.o \
        rc80211_minstrel_ht.o
 
 rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += \
-       rc80211_minstrel_debugfs.o \
        rc80211_minstrel_ht_debugfs.o
 
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
index 48f144f..5296898 100644 (file)
@@ -120,18 +120,17 @@ static ssize_t aqm_write(struct file *file,
 {
        struct ieee80211_local *local = file->private_data;
        char buf[100];
-       size_t len;
 
-       if (count > sizeof(buf))
+       if (count >= sizeof(buf))
                return -EINVAL;
 
        if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
-       buf[sizeof(buf) - 1] = '\0';
-       len = strlen(buf);
-       if (len > 0 && buf[len-1] == '\n')
-               buf[len-1] = 0;
+       if (count && buf[count - 1] == '\n')
+               buf[count - 1] = '\0';
+       else
+               buf[count] = '\0';
 
        if (sscanf(buf, "fq_limit %u", &local->fq.limit) == 1)
                return count;
@@ -177,18 +176,17 @@ static ssize_t airtime_flags_write(struct file *file,
 {
        struct ieee80211_local *local = file->private_data;
        char buf[16];
-       size_t len;
 
-       if (count > sizeof(buf))
+       if (count >= sizeof(buf))
                return -EINVAL;
 
        if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
-       buf[sizeof(buf) - 1] = 0;
-       len = strlen(buf);
-       if (len > 0 && buf[len - 1] == '\n')
-               buf[len - 1] = 0;
+       if (count && buf[count - 1] == '\n')
+               buf[count - 1] = '\0';
+       else
+               buf[count] = '\0';
 
        if (kstrtou16(buf, 0, &local->airtime_flags))
                return -EINVAL;
@@ -237,20 +235,19 @@ static ssize_t aql_txq_limit_write(struct file *file,
 {
        struct ieee80211_local *local = file->private_data;
        char buf[100];
-       size_t len;
        u32 ac, q_limit_low, q_limit_high, q_limit_low_old, q_limit_high_old;
        struct sta_info *sta;
 
-       if (count > sizeof(buf))
+       if (count >= sizeof(buf))
                return -EINVAL;
 
        if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
-       buf[sizeof(buf) - 1] = 0;
-       len = strlen(buf);
-       if (len > 0 && buf[len - 1] == '\n')
-               buf[len - 1] = 0;
+       if (count && buf[count - 1] == '\n')
+               buf[count - 1] = '\0';
+       else
+               buf[count] = '\0';
 
        if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
                return -EINVAL;
@@ -284,6 +281,56 @@ static const struct file_operations aql_txq_limit_ops = {
        .llseek = default_llseek,
 };
 
+static ssize_t aql_enable_read(struct file *file, char __user *user_buf,
+                              size_t count, loff_t *ppos)
+{
+       char buf[3];
+       int len;
+
+       len = scnprintf(buf, sizeof(buf), "%d\n",
+                       !static_key_false(&aql_disable.key));
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t aql_enable_write(struct file *file, const char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       bool aql_disabled = static_key_false(&aql_disable.key);
+       char buf[3];
+       size_t len;
+
+       if (count > sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(buf, user_buf, count))
+               return -EFAULT;
+
+       buf[sizeof(buf) - 1] = '\0';
+       len = strlen(buf);
+       if (len > 0 && buf[len - 1] == '\n')
+               buf[len - 1] = 0;
+
+       if (buf[0] == '0' && buf[1] == '\0') {
+               if (!aql_disabled)
+                       static_branch_inc(&aql_disable);
+       } else if (buf[0] == '1' && buf[1] == '\0') {
+               if (aql_disabled)
+                       static_branch_dec(&aql_disable);
+       } else {
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static const struct file_operations aql_enable_ops = {
+       .write = aql_enable_write,
+       .read = aql_enable_read,
+       .open = simple_open,
+       .llseek = default_llseek,
+};
+
 static ssize_t force_tx_status_read(struct file *file,
                                    char __user *user_buf,
                                    size_t count,
@@ -306,18 +353,17 @@ static ssize_t force_tx_status_write(struct file *file,
 {
        struct ieee80211_local *local = file->private_data;
        char buf[3];
-       size_t len;
 
-       if (count > sizeof(buf))
+       if (count >= sizeof(buf))
                return -EINVAL;
 
        if (copy_from_user(buf, user_buf, count))
                return -EFAULT;
 
-       buf[sizeof(buf) - 1] = '\0';
-       len = strlen(buf);
-       if (len > 0 && buf[len - 1] == '\n')
-               buf[len - 1] = 0;
+       if (count && buf[count - 1] == '\n')
+               buf[count - 1] = '\0';
+       else
+               buf[count] = '\0';
 
        if (buf[0] == '0' && buf[1] == '\0')
                local->force_tx_status = 0;
@@ -409,6 +455,7 @@ static const char *hw_flag_names[] = {
        FLAG(SUPPORTS_ONLY_HE_MULTI_BSSID),
        FLAG(AMPDU_KEYBORDER_SUPPORT),
        FLAG(SUPPORTS_TX_ENCAP_OFFLOAD),
+       FLAG(SUPPORTS_RX_DECAP_OFFLOAD),
 #undef FLAG
 };
 
@@ -572,6 +619,7 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_ADD(power);
        DEBUGFS_ADD(hw_conf);
        DEBUGFS_ADD_MODE(force_tx_status, 0600);
+       DEBUGFS_ADD_MODE(aql_enable, 0600);
 
        if (local->ops->wake_tx_queue)
                DEBUGFS_ADD_MODE(aqm, 0600);
index eb4bb79..5a27c61 100644 (file)
@@ -79,6 +79,7 @@ static const char * const sta_flag_names[] = {
        FLAG(MPSP_RECIPIENT),
        FLAG(PS_DELIVER),
        FLAG(USES_ENCRYPTION),
+       FLAG(DECAP_OFFLOAD),
 #undef FLAG
 };
 
index bcdfd19..604ca59 100644 (file)
@@ -1413,4 +1413,20 @@ static inline void drv_sta_set_4addr(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline void drv_sta_set_decap_offload(struct ieee80211_local *local,
+                                            struct ieee80211_sub_if_data *sdata,
+                                            struct ieee80211_sta *sta,
+                                            bool enabled)
+{
+       sdata = get_bss_sdata(sdata);
+       if (!check_sdata_in_driver(sdata))
+               return;
+
+       trace_drv_sta_set_decap_offload(local, sdata, sta, enabled);
+       if (local->ops->sta_set_decap_offload)
+               local->ops->sta_set_decap_offload(&local->hw, &sdata->vif, sta,
+                                                 enabled);
+       trace_drv_return_void(local);
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index cc26f23..0c0b970 100644 (file)
@@ -52,6 +52,57 @@ ieee80211_update_from_he_6ghz_capa(const struct ieee80211_he_6ghz_capa *he_6ghz_
        sta->sta.he_6ghz_capa = *he_6ghz_capa;
 }
 
+static void ieee80211_he_mcs_disable(__le16 *he_mcs)
+{
+       u32 i;
+
+       for (i = 0; i < 8; i++)
+               *he_mcs |= cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << i * 2);
+}
+
+static void ieee80211_he_mcs_intersection(__le16 *he_own_rx, __le16 *he_peer_rx,
+                                         __le16 *he_own_tx, __le16 *he_peer_tx)
+{
+       u32 i;
+       u16 own_rx, own_tx, peer_rx, peer_tx;
+
+       for (i = 0; i < 8; i++) {
+               own_rx = le16_to_cpu(*he_own_rx);
+               own_rx = (own_rx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
+
+               own_tx = le16_to_cpu(*he_own_tx);
+               own_tx = (own_tx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
+
+               peer_rx = le16_to_cpu(*he_peer_rx);
+               peer_rx = (peer_rx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
+
+               peer_tx = le16_to_cpu(*he_peer_tx);
+               peer_tx = (peer_tx >> i * 2) & IEEE80211_HE_MCS_NOT_SUPPORTED;
+
+               if (peer_tx != IEEE80211_HE_MCS_NOT_SUPPORTED) {
+                       if (own_rx == IEEE80211_HE_MCS_NOT_SUPPORTED)
+                               peer_tx = IEEE80211_HE_MCS_NOT_SUPPORTED;
+                       else if (own_rx < peer_tx)
+                               peer_tx = own_rx;
+               }
+
+               if (peer_rx != IEEE80211_HE_MCS_NOT_SUPPORTED) {
+                       if (own_tx == IEEE80211_HE_MCS_NOT_SUPPORTED)
+                               peer_rx = IEEE80211_HE_MCS_NOT_SUPPORTED;
+                       else if (own_tx < peer_rx)
+                               peer_rx = own_tx;
+               }
+
+               *he_peer_rx &=
+                       ~cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << i * 2);
+               *he_peer_rx |= cpu_to_le16(peer_rx << i * 2);
+
+               *he_peer_tx &=
+                       ~cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << i * 2);
+               *he_peer_tx |= cpu_to_le16(peer_tx << i * 2);
+       }
+}
+
 void
 ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
                                  struct ieee80211_supported_band *sband,
@@ -60,10 +111,12 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
                                  struct sta_info *sta)
 {
        struct ieee80211_sta_he_cap *he_cap = &sta->sta.he_cap;
+       struct ieee80211_sta_he_cap own_he_cap = sband->iftype_data->he_cap;
        struct ieee80211_he_cap_elem *he_cap_ie_elem = (void *)he_cap_ie;
        u8 he_ppe_size;
        u8 mcs_nss_size;
        u8 he_total_size;
+       bool own_160, peer_160, own_80p80, peer_80p80;
 
        memset(he_cap, 0, sizeof(*he_cap));
 
@@ -101,6 +154,45 @@ ieee80211_he_cap_ie_to_sta_he_cap(struct ieee80211_sub_if_data *sdata,
 
        if (sband->band == NL80211_BAND_6GHZ && he_6ghz_capa)
                ieee80211_update_from_he_6ghz_capa(he_6ghz_capa, sta);
+
+       ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80,
+                                     &he_cap->he_mcs_nss_supp.rx_mcs_80,
+                                     &own_he_cap.he_mcs_nss_supp.tx_mcs_80,
+                                     &he_cap->he_mcs_nss_supp.tx_mcs_80);
+
+       own_160 = own_he_cap.he_cap_elem.phy_cap_info[0] &
+                 IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+       peer_160 = he_cap->he_cap_elem.phy_cap_info[0] &
+                  IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+       if (peer_160 && own_160) {
+               ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_160,
+                                             &he_cap->he_mcs_nss_supp.rx_mcs_160,
+                                             &own_he_cap.he_mcs_nss_supp.tx_mcs_160,
+                                             &he_cap->he_mcs_nss_supp.tx_mcs_160);
+       } else if (peer_160 && !own_160) {
+               ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_160);
+               ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_160);
+               he_cap->he_cap_elem.phy_cap_info[0] &=
+                       ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+       }
+
+       own_80p80 = own_he_cap.he_cap_elem.phy_cap_info[0] &
+                   IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+       peer_80p80 = he_cap->he_cap_elem.phy_cap_info[0] &
+                    IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+
+       if (peer_80p80 && own_80p80) {
+               ieee80211_he_mcs_intersection(&own_he_cap.he_mcs_nss_supp.rx_mcs_80p80,
+                                             &he_cap->he_mcs_nss_supp.rx_mcs_80p80,
+                                             &own_he_cap.he_mcs_nss_supp.tx_mcs_80p80,
+                                             &he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+       } else if (peer_80p80 && !own_80p80) {
+               ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.rx_mcs_80p80);
+               ieee80211_he_mcs_disable(&he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+               he_cap->he_cap_elem.phy_cap_info[0] &=
+                       ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G;
+       }
 }
 
 void
index 8bf9c0e..ecda126 100644 (file)
@@ -848,7 +848,6 @@ enum txq_info_flags {
  */
 struct txq_info {
        struct fq_tin tin;
-       struct fq_flow def_flow;
        struct codel_vars def_cvars;
        struct codel_stats cstats;
        struct sk_buff_head frags;
@@ -1078,6 +1077,7 @@ enum queue_stop_reason {
        IEEE80211_QUEUE_STOP_REASON_FLUSH,
        IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
        IEEE80211_QUEUE_STOP_REASON_RESERVE_TID,
+       IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE,
 
        IEEE80211_QUEUE_STOP_REASONS,
 };
@@ -1143,6 +1143,8 @@ enum mac80211_scan_state {
        SCAN_ABORT,
 };
 
+DECLARE_STATIC_KEY_FALSE(aql_disable);
+
 struct ieee80211_local {
        /* embed the driver visible part.
         * don't cast (use the static inlines below), but we keep
index 3b9ec4e..b80c9b0 100644 (file)
@@ -357,11 +357,14 @@ static int ieee80211_open(struct net_device *dev)
        if (err)
                return err;
 
-       return ieee80211_do_open(&sdata->wdev, true);
+       wiphy_lock(sdata->local->hw.wiphy);
+       err = ieee80211_do_open(&sdata->wdev, true);
+       wiphy_unlock(sdata->local->hw.wiphy);
+
+       return err;
 }
 
-static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
-                             bool going_down)
+static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_down)
 {
        struct ieee80211_local *local = sdata->local;
        unsigned long flags;
@@ -637,7 +640,9 @@ static int ieee80211_stop(struct net_device *dev)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
+       wiphy_lock(sdata->local->hw.wiphy);
        ieee80211_do_stop(sdata, true);
+       wiphy_unlock(sdata->local->hw.wiphy);
 
        return 0;
 }
@@ -765,7 +770,7 @@ static const struct net_device_ops ieee80211_dataif_8023_ops = {
        .ndo_get_stats64        = ieee80211_get_stats64,
 };
 
-static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype)
+static bool ieee80211_iftype_supports_hdr_offload(enum nl80211_iftype iftype)
 {
        switch (iftype) {
        /* P2P GO and client are mapped to AP/STATION types */
@@ -785,7 +790,7 @@ static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdat
        flags = sdata->vif.offload_flags;
 
        if (ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) &&
-           ieee80211_iftype_supports_encap_offload(sdata->vif.type)) {
+           ieee80211_iftype_supports_hdr_offload(sdata->vif.type)) {
                flags |= IEEE80211_OFFLOAD_ENCAP_ENABLED;
 
                if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) &&
@@ -798,10 +803,21 @@ static bool ieee80211_set_sdata_offload_flags(struct ieee80211_sub_if_data *sdat
                flags &= ~IEEE80211_OFFLOAD_ENCAP_ENABLED;
        }
 
+       if (ieee80211_hw_check(&local->hw, SUPPORTS_RX_DECAP_OFFLOAD) &&
+           ieee80211_iftype_supports_hdr_offload(sdata->vif.type)) {
+               flags |= IEEE80211_OFFLOAD_DECAP_ENABLED;
+
+               if (local->monitors)
+                       flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED;
+       } else {
+               flags &= ~IEEE80211_OFFLOAD_DECAP_ENABLED;
+       }
+
        if (sdata->vif.offload_flags == flags)
                return false;
 
        sdata->vif.offload_flags = flags;
+       ieee80211_check_fast_rx_iface(sdata);
        return true;
 }
 
@@ -819,7 +835,7 @@ static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
        }
 
        if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
-           !ieee80211_iftype_supports_encap_offload(bss->vif.type))
+           !ieee80211_iftype_supports_hdr_offload(bss->vif.type))
                return;
 
        enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
@@ -1617,6 +1633,10 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
        if (ret)
                return ret;
 
+       ieee80211_stop_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE);
+       synchronize_net();
+
        ieee80211_do_stop(sdata, false);
 
        ieee80211_teardown_sdata(sdata);
@@ -1639,6 +1659,8 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
        err = ieee80211_do_open(&sdata->wdev, false);
        WARN(err, "type change: do_open returned %d", err);
 
+       ieee80211_wake_vif_queues(local, sdata,
+                                 IEEE80211_QUEUE_STOP_REASON_IFTYPE_CHANGE);
        return ret;
 }
 
@@ -1965,7 +1987,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
                ndev->min_mtu = 256;
                ndev->max_mtu = local->hw.max_mtu;
 
-               ret = register_netdevice(ndev);
+               ret = cfg80211_register_netdevice(ndev);
                if (ret) {
                        free_netdev(ndev);
                        return ret;
@@ -1995,10 +2017,9 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
 
        synchronize_rcu();
 
-       if (sdata->dev) {
-               unregister_netdevice(sdata->dev);
-       } else {
-               cfg80211_unregister_wdev(&sdata->wdev);
+       cfg80211_unregister_wdev(&sdata->wdev);
+
+       if (!sdata->dev) {
                ieee80211_teardown_sdata(sdata);
                kfree(sdata);
        }
@@ -2047,13 +2068,16 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
                        list_add(&sdata->list, &wdev_list);
        }
        mutex_unlock(&local->iflist_mtx);
+
        unregister_netdevice_many(&unreg_list);
 
+       wiphy_lock(local->hw.wiphy);
        list_for_each_entry_safe(sdata, tmp, &wdev_list, list) {
                list_del(&sdata->list);
                cfg80211_unregister_wdev(&sdata->wdev);
                kfree(sdata);
        }
+       wiphy_unlock(local->hw.wiphy);
 }
 
 static int netdev_notify(struct notifier_block *nb,
index a4817aa..56c068c 100644 (file)
@@ -887,7 +887,7 @@ void ieee80211_reenable_keys(struct ieee80211_sub_if_data *sdata)
        struct ieee80211_key *key;
        struct ieee80211_sub_if_data *vlan;
 
-       ASSERT_RTNL();
+       lockdep_assert_wiphy(sdata->local->hw.wiphy);
 
        mutex_lock(&sdata->local->key_mtx);
 
@@ -924,7 +924,7 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
        struct ieee80211_key *key, *tmp;
        struct ieee80211_sub_if_data *sdata;
 
-       ASSERT_RTNL();
+       lockdep_assert_wiphy(hw->wiphy);
 
        mutex_lock(&local->key_mtx);
        if (vif) {
index dee88ec..4f3f8bb 100644 (file)
@@ -261,7 +261,9 @@ static void ieee80211_restart_work(struct work_struct *work)
             "%s called with hardware scan in progress\n", __func__);
 
        flush_work(&local->radar_detected_work);
+       /* we might do interface manipulations, so need both */
        rtnl_lock();
+       wiphy_lock(local->hw.wiphy);
        list_for_each_entry(sdata, &local->interfaces, list) {
                /*
                 * XXX: there may be more work for other vif types and even
@@ -293,6 +295,7 @@ static void ieee80211_restart_work(struct work_struct *work)
        synchronize_net();
 
        ieee80211_reconfig(local);
+       wiphy_unlock(local->hw.wiphy);
        rtnl_unlock();
 }
 
@@ -1272,6 +1275,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        rate_control_add_debugfs(local);
 
        rtnl_lock();
+       wiphy_lock(hw->wiphy);
 
        /* add one default STA interface if supported */
        if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
@@ -1285,6 +1289,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                                   "Failed to add default virtual iface\n");
        }
 
+       wiphy_unlock(hw->wiphy);
        rtnl_unlock();
 
 #ifdef CONFIG_INET
index ae378a4..7809a90 100644 (file)
@@ -1,4 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0
+/*
+ * Portions
+ * Copyright (C) 2020-2021 Intel Corporation
+ */
 #include <net/mac80211.h>
 #include <net/rtnetlink.h>
 
@@ -11,7 +15,7 @@ static void ieee80211_sched_scan_cancel(struct ieee80211_local *local)
 {
        if (ieee80211_request_sched_scan_stop(local))
                return;
-       cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy, 0);
+       cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
 }
 
 int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
deleted file mode 100644 (file)
index b13b1da..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Based on minstrel.c:
- *   Copyright (C) 2005-2007 Derek Smithies <derek@indranet.co.nz>
- *   Sponsored by Indranet Technologies Ltd
- *
- * Based on sample.c:
- *   Copyright (c) 2005 John Bicket
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer,
- *      without modification.
- *   2. Redistributions in binary form must reproduce at minimum a disclaimer
- *      similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
- *      redistribution must be conditioned upon including a substantially
- *      similar Disclaimer requirement for further binary redistribution.
- *   3. Neither the names of the above-listed copyright holders nor the names
- *      of any contributors may be used to endorse or promote products derived
- *      from this software without specific prior written permission.
- *
- *   Alternatively, this software may be distributed under the terms of the
- *   GNU General Public License ("GPL") version 2 as published by the Free
- *   Software Foundation.
- *
- *   NO WARRANTY
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
- *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- *   THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
- *   OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- *   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- *   THE POSSIBILITY OF SUCH DAMAGES.
- */
-#include <linux/netdevice.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/debugfs.h>
-#include <linux/random.h>
-#include <linux/ieee80211.h>
-#include <linux/slab.h>
-#include <net/mac80211.h>
-#include "rate.h"
-#include "rc80211_minstrel.h"
-
-#define SAMPLE_TBL(_mi, _idx, _col) \
-               _mi->sample_table[(_idx * SAMPLE_COLUMNS) + _col]
-
-/* convert mac80211 rate index to local array index */
-static inline int
-rix_to_ndx(struct minstrel_sta_info *mi, int rix)
-{
-       int i = rix;
-       for (i = rix; i >= 0; i--)
-               if (mi->r[i].rix == rix)
-                       break;
-       return i;
-}
-
-/* return current EMWA throughput */
-int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg)
-{
-       int usecs;
-
-       usecs = mr->perfect_tx_time;
-       if (!usecs)
-               usecs = 1000000;
-
-       /* reset thr. below 10% success */
-       if (mr->stats.prob_avg < MINSTREL_FRAC(10, 100))
-               return 0;
-
-       if (prob_avg > MINSTREL_FRAC(90, 100))
-               return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs));
-       else
-               return MINSTREL_TRUNC(100000 * (prob_avg / usecs));
-}
-
-/* find & sort topmost throughput rates */
-static inline void
-minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list)
-{
-       int j;
-       struct minstrel_rate_stats *tmp_mrs;
-       struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats;
-
-       for (j = MAX_THR_RATES; j > 0; --j) {
-               tmp_mrs = &mi->r[tp_list[j - 1]].stats;
-               if (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_avg) <=
-                   minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_avg))
-                       break;
-       }
-
-       if (j < MAX_THR_RATES - 1)
-               memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1));
-       if (j < MAX_THR_RATES)
-               tp_list[j] = i;
-}
-
-static void
-minstrel_set_rate(struct minstrel_sta_info *mi, struct ieee80211_sta_rates *ratetbl,
-                 int offset, int idx)
-{
-       struct minstrel_rate *r = &mi->r[idx];
-
-       ratetbl->rate[offset].idx = r->rix;
-       ratetbl->rate[offset].count = r->adjusted_retry_count;
-       ratetbl->rate[offset].count_cts = r->retry_count_cts;
-       ratetbl->rate[offset].count_rts = r->stats.retry_count_rtscts;
-}
-
-static void
-minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
-{
-       struct ieee80211_sta_rates *ratetbl;
-       int i = 0;
-
-       ratetbl = kzalloc(sizeof(*ratetbl), GFP_ATOMIC);
-       if (!ratetbl)
-               return;
-
-       /* Start with max_tp_rate */
-       minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[0]);
-
-       if (mp->hw->max_rates >= 3) {
-               /* At least 3 tx rates supported, use max_tp_rate2 next */
-               minstrel_set_rate(mi, ratetbl, i++, mi->max_tp_rate[1]);
-       }
-
-       if (mp->hw->max_rates >= 2) {
-               /* At least 2 tx rates supported, use max_prob_rate next */
-               minstrel_set_rate(mi, ratetbl, i++, mi->max_prob_rate);
-       }
-
-       /* Use lowest rate last */
-       ratetbl->rate[i].idx = mi->lowest_rix;
-       ratetbl->rate[i].count = mp->max_retry;
-       ratetbl->rate[i].count_cts = mp->max_retry;
-       ratetbl->rate[i].count_rts = mp->max_retry;
-
-       rate_control_set_rates(mp->hw, mi->sta, ratetbl);
-}
-
-/*
-* Recalculate statistics and counters of a given rate
-*/
-void
-minstrel_calc_rate_stats(struct minstrel_priv *mp,
-                        struct minstrel_rate_stats *mrs)
-{
-       unsigned int cur_prob;
-
-       if (unlikely(mrs->attempts > 0)) {
-               mrs->sample_skipped = 0;
-               cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
-               if (mp->new_avg) {
-                       minstrel_filter_avg_add(&mrs->prob_avg,
-                                               &mrs->prob_avg_1, cur_prob);
-               } else if (unlikely(!mrs->att_hist)) {
-                       mrs->prob_avg = cur_prob;
-               } else {
-                       /*update exponential weighted moving avarage */
-                       mrs->prob_avg = minstrel_ewma(mrs->prob_avg,
-                                                     cur_prob,
-                                                     EWMA_LEVEL);
-               }
-               mrs->att_hist += mrs->attempts;
-               mrs->succ_hist += mrs->success;
-       } else {
-               mrs->sample_skipped++;
-       }
-
-       mrs->last_success = mrs->success;
-       mrs->last_attempts = mrs->attempts;
-       mrs->success = 0;
-       mrs->attempts = 0;
-}
-
-static void
-minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
-{
-       u8 tmp_tp_rate[MAX_THR_RATES];
-       u8 tmp_prob_rate = 0;
-       int i, tmp_cur_tp, tmp_prob_tp;
-
-       for (i = 0; i < MAX_THR_RATES; i++)
-           tmp_tp_rate[i] = 0;
-
-       for (i = 0; i < mi->n_rates; i++) {
-               struct minstrel_rate *mr = &mi->r[i];
-               struct minstrel_rate_stats *mrs = &mi->r[i].stats;
-               struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
-
-               /* Update statistics of success probability per rate */
-               minstrel_calc_rate_stats(mp, mrs);
-
-               /* Sample less often below the 10% chance of success.
-                * Sample less often above the 95% chance of success. */
-               if (mrs->prob_avg > MINSTREL_FRAC(95, 100) ||
-                   mrs->prob_avg < MINSTREL_FRAC(10, 100)) {
-                       mr->adjusted_retry_count = mrs->retry_count >> 1;
-                       if (mr->adjusted_retry_count > 2)
-                               mr->adjusted_retry_count = 2;
-                       mr->sample_limit = 4;
-               } else {
-                       mr->sample_limit = -1;
-                       mr->adjusted_retry_count = mrs->retry_count;
-               }
-               if (!mr->adjusted_retry_count)
-                       mr->adjusted_retry_count = 2;
-
-               minstrel_sort_best_tp_rates(mi, i, tmp_tp_rate);
-
-               /* To determine the most robust rate (max_prob_rate) used at
-                * 3rd mmr stage we distinct between two cases:
-                * (1) if any success probabilitiy >= 95%, out of those rates
-                * choose the maximum throughput rate as max_prob_rate
-                * (2) if all success probabilities < 95%, the rate with
-                * highest success probability is chosen as max_prob_rate */
-               if (mrs->prob_avg >= MINSTREL_FRAC(95, 100)) {
-                       tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_avg);
-                       tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate],
-                                                         tmp_mrs->prob_avg);
-                       if (tmp_cur_tp >= tmp_prob_tp)
-                               tmp_prob_rate = i;
-               } else {
-                       if (mrs->prob_avg >= tmp_mrs->prob_avg)
-                               tmp_prob_rate = i;
-               }
-       }
-
-       /* Assign the new rate set */
-       memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
-       mi->max_prob_rate = tmp_prob_rate;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /* use fixed index if set */
-       if (mp->fixed_rate_idx != -1) {
-               mi->max_tp_rate[0] = mp->fixed_rate_idx;
-               mi->max_tp_rate[1] = mp->fixed_rate_idx;
-               mi->max_prob_rate = mp->fixed_rate_idx;
-       }
-#endif
-
-       /* Reset update timer */
-       mi->last_stats_update = jiffies;
-
-       minstrel_update_rates(mp, mi);
-}
-
-static void
-minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
-                  void *priv_sta, struct ieee80211_tx_status *st)
-{
-       struct ieee80211_tx_info *info = st->info;
-       struct minstrel_priv *mp = priv;
-       struct minstrel_sta_info *mi = priv_sta;
-       struct ieee80211_tx_rate *ar = info->status.rates;
-       int i, ndx;
-       int success;
-
-       success = !!(info->flags & IEEE80211_TX_STAT_ACK);
-
-       for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
-               if (ar[i].idx < 0 || !ar[i].count)
-                       break;
-
-               ndx = rix_to_ndx(mi, ar[i].idx);
-               if (ndx < 0)
-                       continue;
-
-               mi->r[ndx].stats.attempts += ar[i].count;
-
-               if ((i != IEEE80211_TX_MAX_RATES - 1) && (ar[i + 1].idx < 0))
-                       mi->r[ndx].stats.success += success;
-       }
-
-       if (time_after(jiffies, mi->last_stats_update +
-                               mp->update_interval / (mp->new_avg ? 2 : 1)))
-               minstrel_update_stats(mp, mi);
-}
-
-
-static inline unsigned int
-minstrel_get_retry_count(struct minstrel_rate *mr,
-                        struct ieee80211_tx_info *info)
-{
-       u8 retry = mr->adjusted_retry_count;
-
-       if (info->control.use_rts)
-               retry = max_t(u8, 2, min(mr->stats.retry_count_rtscts, retry));
-       else if (info->control.use_cts_prot)
-               retry = max_t(u8, 2, min(mr->retry_count_cts, retry));
-       return retry;
-}
-
-
-static int
-minstrel_get_next_sample(struct minstrel_sta_info *mi)
-{
-       unsigned int sample_ndx;
-       sample_ndx = SAMPLE_TBL(mi, mi->sample_row, mi->sample_column);
-       mi->sample_row++;
-       if ((int) mi->sample_row >= mi->n_rates) {
-               mi->sample_row = 0;
-               mi->sample_column++;
-               if (mi->sample_column >= SAMPLE_COLUMNS)
-                       mi->sample_column = 0;
-       }
-       return sample_ndx;
-}
-
-static void
-minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
-                 void *priv_sta, struct ieee80211_tx_rate_control *txrc)
-{
-       struct sk_buff *skb = txrc->skb;
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       struct minstrel_sta_info *mi = priv_sta;
-       struct minstrel_priv *mp = priv;
-       struct ieee80211_tx_rate *rate = &info->control.rates[0];
-       struct minstrel_rate *msr, *mr;
-       unsigned int ndx;
-       bool mrr_capable;
-       bool prev_sample;
-       int delta;
-       int sampling_ratio;
-
-       /* check multi-rate-retry capabilities & adjust lookaround_rate */
-       mrr_capable = mp->has_mrr &&
-                     !txrc->rts &&
-                     !txrc->bss_conf->use_cts_prot;
-       if (mrr_capable)
-               sampling_ratio = mp->lookaround_rate_mrr;
-       else
-               sampling_ratio = mp->lookaround_rate;
-
-       /* increase sum packet counter */
-       mi->total_packets++;
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       if (mp->fixed_rate_idx != -1)
-               return;
-#endif
-
-       /* Don't use EAPOL frames for sampling on non-mrr hw */
-       if (mp->hw->max_rates == 1 &&
-           (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
-               return;
-
-       delta = (mi->total_packets * sampling_ratio / 100) -
-                       mi->sample_packets;
-
-       /* delta < 0: no sampling required */
-       prev_sample = mi->prev_sample;
-       mi->prev_sample = false;
-       if (delta < 0 || (!mrr_capable && prev_sample))
-               return;
-
-       if (mi->total_packets >= 10000) {
-               mi->sample_packets = 0;
-               mi->total_packets = 0;
-       } else if (delta > mi->n_rates * 2) {
-               /* With multi-rate retry, not every planned sample
-                * attempt actually gets used, due to the way the retry
-                * chain is set up - [max_tp,sample,prob,lowest] for
-                * sample_rate < max_tp.
-                *
-                * If there's too much sampling backlog and the link
-                * starts getting worse, minstrel would start bursting
-                * out lots of sampling frames, which would result
-                * in a large throughput loss. */
-               mi->sample_packets += (delta - mi->n_rates * 2);
-       }
-
-       /* get next random rate sample */
-       ndx = minstrel_get_next_sample(mi);
-       msr = &mi->r[ndx];
-       mr = &mi->r[mi->max_tp_rate[0]];
-
-       /* Decide if direct ( 1st mrr stage) or indirect (2nd mrr stage)
-        * rate sampling method should be used.
-        * Respect such rates that are not sampled for 20 interations.
-        */
-       if (msr->perfect_tx_time < mr->perfect_tx_time ||
-           msr->stats.sample_skipped >= 20) {
-               if (!msr->sample_limit)
-                       return;
-
-               mi->sample_packets++;
-               if (msr->sample_limit > 0)
-                       msr->sample_limit--;
-       }
-
-       /* If we're not using MRR and the sampling rate already
-        * has a probability of >95%, we shouldn't be attempting
-        * to use it, as this only wastes precious airtime */
-       if (!mrr_capable &&
-          (mi->r[ndx].stats.prob_avg > MINSTREL_FRAC(95, 100)))
-               return;
-
-       mi->prev_sample = true;
-
-       rate->idx = mi->r[ndx].rix;
-       rate->count = minstrel_get_retry_count(&mi->r[ndx], info);
-       info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE;
-}
-
-
-static void
-calc_rate_durations(enum nl80211_band band,
-                   struct minstrel_rate *d,
-                   struct ieee80211_rate *rate,
-                   struct cfg80211_chan_def *chandef)
-{
-       int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
-       int shift = ieee80211_chandef_get_shift(chandef);
-
-       d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
-                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
-                       shift);
-       d->ack_time = ieee80211_frame_duration(band, 10,
-                       DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
-                       shift);
-}
-
-static void
-init_sample_table(struct minstrel_sta_info *mi)
-{
-       unsigned int i, col, new_idx;
-       u8 rnd[8];
-
-       mi->sample_column = 0;
-       mi->sample_row = 0;
-       memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);
-
-       for (col = 0; col < SAMPLE_COLUMNS; col++) {
-               prandom_bytes(rnd, sizeof(rnd));
-               for (i = 0; i < mi->n_rates; i++) {
-                       new_idx = (i + rnd[i & 7]) % mi->n_rates;
-                       while (SAMPLE_TBL(mi, new_idx, col) != 0xff)
-                               new_idx = (new_idx + 1) % mi->n_rates;
-
-                       SAMPLE_TBL(mi, new_idx, col) = i;
-               }
-       }
-}
-
-static void
-minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
-                  struct cfg80211_chan_def *chandef,
-                  struct ieee80211_sta *sta, void *priv_sta)
-{
-       struct minstrel_sta_info *mi = priv_sta;
-       struct minstrel_priv *mp = priv;
-       struct ieee80211_rate *ctl_rate;
-       unsigned int i, n = 0;
-       unsigned int t_slot = 9; /* FIXME: get real slot time */
-       u32 rate_flags;
-
-       mi->sta = sta;
-       mi->lowest_rix = rate_lowest_index(sband, sta);
-       ctl_rate = &sband->bitrates[mi->lowest_rix];
-       mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
-                               ctl_rate->bitrate,
-                               !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
-                               ieee80211_chandef_get_shift(chandef));
-
-       rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
-       memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
-       mi->max_prob_rate = 0;
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               struct minstrel_rate *mr = &mi->r[n];
-               struct minstrel_rate_stats *mrs = &mi->r[n].stats;
-               unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
-               unsigned int tx_time_single;
-               unsigned int cw = mp->cw_min;
-               int shift;
-
-               if (!rate_supported(sta, sband->band, i))
-                       continue;
-               if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
-                       continue;
-
-               n++;
-               memset(mr, 0, sizeof(*mr));
-               memset(mrs, 0, sizeof(*mrs));
-
-               mr->rix = i;
-               shift = ieee80211_chandef_get_shift(chandef);
-               mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
-                                          (1 << shift) * 5);
-               calc_rate_durations(sband->band, mr, &sband->bitrates[i],
-                                   chandef);
-
-               /* calculate maximum number of retransmissions before
-                * fallback (based on maximum segment size) */
-               mr->sample_limit = -1;
-               mrs->retry_count = 1;
-               mr->retry_count_cts = 1;
-               mrs->retry_count_rtscts = 1;
-               tx_time = mr->perfect_tx_time + mi->sp_ack_dur;
-               do {
-                       /* add one retransmission */
-                       tx_time_single = mr->ack_time + mr->perfect_tx_time;
-
-                       /* contention window */
-                       tx_time_single += (t_slot * cw) >> 1;
-                       cw = min((cw << 1) | 1, mp->cw_max);
-
-                       tx_time += tx_time_single;
-                       tx_time_cts += tx_time_single + mi->sp_ack_dur;
-                       tx_time_rtscts += tx_time_single + 2 * mi->sp_ack_dur;
-                       if ((tx_time_cts < mp->segment_size) &&
-                               (mr->retry_count_cts < mp->max_retry))
-                               mr->retry_count_cts++;
-                       if ((tx_time_rtscts < mp->segment_size) &&
-                               (mrs->retry_count_rtscts < mp->max_retry))
-                               mrs->retry_count_rtscts++;
-               } while ((tx_time < mp->segment_size) &&
-                               (++mr->stats.retry_count < mp->max_retry));
-               mr->adjusted_retry_count = mrs->retry_count;
-               if (!(sband->bitrates[i].flags & IEEE80211_RATE_ERP_G))
-                       mr->retry_count_cts = mrs->retry_count;
-       }
-
-       for (i = n; i < sband->n_bitrates; i++) {
-               struct minstrel_rate *mr = &mi->r[i];
-               mr->rix = -1;
-       }
-
-       mi->n_rates = n;
-       mi->last_stats_update = jiffies;
-
-       init_sample_table(mi);
-       minstrel_update_rates(mp, mi);
-}
-
-static u32 minstrel_get_expected_throughput(void *priv_sta)
-{
-       struct minstrel_sta_info *mi = priv_sta;
-       struct minstrel_rate_stats *tmp_mrs;
-       int idx = mi->max_tp_rate[0];
-       int tmp_cur_tp;
-
-       /* convert pkt per sec in kbps (1200 is the average pkt size used for
-        * computing cur_tp
-        */
-       tmp_mrs = &mi->r[idx].stats;
-       tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_avg) * 10;
-       tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
-
-       return tmp_cur_tp;
-}
-
-const struct rate_control_ops mac80211_minstrel = {
-       .tx_status_ext = minstrel_tx_status,
-       .get_rate = minstrel_get_rate,
-       .rate_init = minstrel_rate_init,
-       .get_expected_throughput = minstrel_get_expected_throughput,
-};
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
deleted file mode 100644 (file)
index 86cd80b..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
- */
-
-#ifndef __RC_MINSTREL_H
-#define __RC_MINSTREL_H
-
-#define EWMA_LEVEL     96      /* ewma weighting factor [/EWMA_DIV] */
-#define EWMA_DIV       128
-#define SAMPLE_COLUMNS 10      /* number of columns in sample table */
-
-/* scaled fraction values */
-#define MINSTREL_SCALE  12
-#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
-#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
-
-/* number of highest throughput rates to consider*/
-#define MAX_THR_RATES 4
-
-/*
- * Coefficients for moving average with noise filter (period=16),
- * scaled by 10 bits
- *
- * a1 = exp(-pi * sqrt(2) / period)
- * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period)
- * coeff3 = -sqr(a1)
- * coeff1 = 1 - coeff2 - coeff3
- */
-#define MINSTREL_AVG_COEFF1            (MINSTREL_FRAC(1, 1) - \
-                                        MINSTREL_AVG_COEFF2 - \
-                                        MINSTREL_AVG_COEFF3)
-#define MINSTREL_AVG_COEFF2            0x00001499
-#define MINSTREL_AVG_COEFF3            -0x0000092e
-
-/*
- * Perform EWMA (Exponentially Weighted Moving Average) calculation
- */
-static inline int
-minstrel_ewma(int old, int new, int weight)
-{
-       int diff, incr;
-
-       diff = new - old;
-       incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
-
-       return old + incr;
-}
-
-static inline int minstrel_filter_avg_add(u16 *prev_1, u16 *prev_2, s32 in)
-{
-       s32 out_1 = *prev_1;
-       s32 out_2 = *prev_2;
-       s32 val;
-
-       if (!in)
-               in += 1;
-
-       if (!out_1) {
-               val = out_1 = in;
-               goto out;
-       }
-
-       val = MINSTREL_AVG_COEFF1 * in;
-       val += MINSTREL_AVG_COEFF2 * out_1;
-       val += MINSTREL_AVG_COEFF3 * out_2;
-       val >>= MINSTREL_SCALE;
-
-       if (val > 1 << MINSTREL_SCALE)
-               val = 1 << MINSTREL_SCALE;
-       if (val < 0)
-               val = 1;
-
-out:
-       *prev_2 = out_1;
-       *prev_1 = val;
-
-       return val;
-}
-
-struct minstrel_rate_stats {
-       /* current / last sampling period attempts/success counters */
-       u16 attempts, last_attempts;
-       u16 success, last_success;
-
-       /* total attempts/success counters */
-       u32 att_hist, succ_hist;
-
-       /* prob_avg - moving average of prob */
-       u16 prob_avg;
-       u16 prob_avg_1;
-
-       /* maximum retry counts */
-       u8 retry_count;
-       u8 retry_count_rtscts;
-
-       u8 sample_skipped;
-       bool retry_updated;
-};
-
-struct minstrel_rate {
-       int bitrate;
-
-       s8 rix;
-       u8 retry_count_cts;
-       u8 adjusted_retry_count;
-
-       unsigned int perfect_tx_time;
-       unsigned int ack_time;
-
-       int sample_limit;
-
-       struct minstrel_rate_stats stats;
-};
-
-struct minstrel_sta_info {
-       struct ieee80211_sta *sta;
-
-       unsigned long last_stats_update;
-       unsigned int sp_ack_dur;
-       unsigned int rate_avg;
-
-       unsigned int lowest_rix;
-
-       u8 max_tp_rate[MAX_THR_RATES];
-       u8 max_prob_rate;
-       unsigned int total_packets;
-       unsigned int sample_packets;
-
-       unsigned int sample_row;
-       unsigned int sample_column;
-
-       int n_rates;
-       struct minstrel_rate *r;
-       bool prev_sample;
-
-       /* sampling table */
-       u8 *sample_table;
-};
-
-struct minstrel_priv {
-       struct ieee80211_hw *hw;
-       bool has_mrr;
-       bool new_avg;
-       u32 sample_switch;
-       unsigned int cw_min;
-       unsigned int cw_max;
-       unsigned int max_retry;
-       unsigned int segment_size;
-       unsigned int update_interval;
-       unsigned int lookaround_rate;
-       unsigned int lookaround_rate_mrr;
-
-       u8 cck_rates[4];
-
-#ifdef CONFIG_MAC80211_DEBUGFS
-       /*
-        * enable fixed rate processing per RC
-        *   - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx
-        *   - write -1 to enable RC processing again
-        *   - setting will be applied on next update
-        */
-       u32 fixed_rate_idx;
-#endif
-};
-
-struct minstrel_debugfs_info {
-       size_t len;
-       char buf[];
-};
-
-extern const struct rate_control_ops mac80211_minstrel;
-void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
-
-/* Recalculate success probabilities and counters for a given rate using EWMA */
-void minstrel_calc_rate_stats(struct minstrel_priv *mp,
-                             struct minstrel_rate_stats *mrs);
-int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg);
-
-/* debugfs */
-int minstrel_stats_open(struct inode *inode, struct file *file);
-int minstrel_stats_csv_open(struct inode *inode, struct file *file);
-
-#endif
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
deleted file mode 100644 (file)
index 9b8e0da..0000000
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2008 Felix Fietkau <nbd@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Based on minstrel.c:
- *   Copyright (C) 2005-2007 Derek Smithies <derek@indranet.co.nz>
- *   Sponsored by Indranet Technologies Ltd
- *
- * Based on sample.c:
- *   Copyright (c) 2005 John Bicket
- *   All rights reserved.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer,
- *      without modification.
- *   2. Redistributions in binary form must reproduce at minimum a disclaimer
- *      similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
- *      redistribution must be conditioned upon including a substantially
- *      similar Disclaimer requirement for further binary redistribution.
- *   3. Neither the names of the above-listed copyright holders nor the names
- *      of any contributors may be used to endorse or promote products derived
- *      from this software without specific prior written permission.
- *
- *   Alternatively, this software may be distributed under the terms of the
- *   GNU General Public License ("GPL") version 2 as published by the Free
- *   Software Foundation.
- *
- *   NO WARRANTY
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
- *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- *   THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
- *   OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- *   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- *   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- *   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- *   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- *   THE POSSIBILITY OF SUCH DAMAGES.
- */
-#include <linux/netdevice.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/debugfs.h>
-#include <linux/ieee80211.h>
-#include <linux/slab.h>
-#include <linux/export.h>
-#include <net/mac80211.h>
-#include "rc80211_minstrel.h"
-
-int
-minstrel_stats_open(struct inode *inode, struct file *file)
-{
-       struct minstrel_sta_info *mi = inode->i_private;
-       struct minstrel_debugfs_info *ms;
-       unsigned int i, tp_max, tp_avg, eprob;
-       char *p;
-
-       ms = kmalloc(2048, GFP_KERNEL);
-       if (!ms)
-               return -ENOMEM;
-
-       file->private_data = ms;
-       p = ms->buf;
-       p += sprintf(p, "\n");
-       p += sprintf(p,
-                    "best   __________rate_________    ____statistics___    ____last_____    ______sum-of________\n");
-       p += sprintf(p,
-                    "rate  [name idx airtime max_tp]  [avg(tp) avg(prob)]  [retry|suc|att]  [#success | #attempts]\n");
-
-       for (i = 0; i < mi->n_rates; i++) {
-               struct minstrel_rate *mr = &mi->r[i];
-               struct minstrel_rate_stats *mrs = &mi->r[i].stats;
-
-               *(p++) = (i == mi->max_tp_rate[0]) ? 'A' : ' ';
-               *(p++) = (i == mi->max_tp_rate[1]) ? 'B' : ' ';
-               *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' ';
-               *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' ';
-               *(p++) = (i == mi->max_prob_rate) ? 'P' : ' ';
-
-               p += sprintf(p, " %3u%s ", mr->bitrate / 2,
-                               (mr->bitrate & 1 ? ".5" : "  "));
-               p += sprintf(p, "%3u  ", i);
-               p += sprintf(p, "%6u ", mr->perfect_tx_time);
-
-               tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
-               tp_avg = minstrel_get_tp_avg(mr, mrs->prob_avg);
-               eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
-
-               p += sprintf(p, "%4u.%1u    %4u.%1u     %3u.%1u"
-                               "     %3u   %3u %-3u   "
-                               "%9llu   %-9llu\n",
-                               tp_max / 10, tp_max % 10,
-                               tp_avg / 10, tp_avg % 10,
-                               eprob / 10, eprob % 10,
-                               mrs->retry_count,
-                               mrs->last_success,
-                               mrs->last_attempts,
-                               (unsigned long long)mrs->succ_hist,
-                               (unsigned long long)mrs->att_hist);
-       }
-       p += sprintf(p, "\nTotal packet count::    ideal %d      "
-                       "lookaround %d\n\n",
-                       mi->total_packets - mi->sample_packets,
-                       mi->sample_packets);
-       ms->len = p - ms->buf;
-
-       WARN_ON(ms->len + sizeof(*ms) > 2048);
-
-       return 0;
-}
-
-int
-minstrel_stats_csv_open(struct inode *inode, struct file *file)
-{
-       struct minstrel_sta_info *mi = inode->i_private;
-       struct minstrel_debugfs_info *ms;
-       unsigned int i, tp_max, tp_avg, eprob;
-       char *p;
-
-       ms = kmalloc(2048, GFP_KERNEL);
-       if (!ms)
-               return -ENOMEM;
-
-       file->private_data = ms;
-       p = ms->buf;
-
-       for (i = 0; i < mi->n_rates; i++) {
-               struct minstrel_rate *mr = &mi->r[i];
-               struct minstrel_rate_stats *mrs = &mi->r[i].stats;
-
-               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : ""));
-               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : ""));
-               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : ""));
-               p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : ""));
-               p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : ""));
-
-               p += sprintf(p, ",%u%s", mr->bitrate / 2,
-                               (mr->bitrate & 1 ? ".5," : ","));
-               p += sprintf(p, "%u,", i);
-               p += sprintf(p, "%u,",mr->perfect_tx_time);
-
-               tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
-               tp_avg = minstrel_get_tp_avg(mr, mrs->prob_avg);
-               eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
-
-               p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u,"
-                               "%llu,%llu,%d,%d\n",
-                               tp_max / 10, tp_max % 10,
-                               tp_avg / 10, tp_avg % 10,
-                               eprob / 10, eprob % 10,
-                               mrs->retry_count,
-                               mrs->last_success,
-                               mrs->last_attempts,
-                               (unsigned long long)mrs->succ_hist,
-                               (unsigned long long)mrs->att_hist,
-                               mi->total_packets - mi->sample_packets,
-                               mi->sample_packets);
-
-       }
-       ms->len = p - ms->buf;
-
-       WARN_ON(ms->len + sizeof(*ms) > 2048);
-
-       return 0;
-}
index b11a2af..bfa5500 100644 (file)
@@ -13,7 +13,6 @@
 #include <net/mac80211.h>
 #include "rate.h"
 #include "sta_info.h"
-#include "rc80211_minstrel.h"
 #include "rc80211_minstrel_ht.h"
 
 #define AVG_AMPDU_SIZE 16
        __VHT_GROUP(_streams, _sgi, _bw,                                \
                    VHT_GROUP_SHIFT(_streams, _sgi, _bw))
 
-#define CCK_DURATION(_bitrate, _short, _len)           \
+#define CCK_DURATION(_bitrate, _short)                 \
        (1000 * (10 /* SIFS */ +                        \
         (_short ? 72 + 24 : 144 + 48) +                \
-        (8 * (_len + 4) * 10) / (_bitrate)))
-
-#define CCK_ACK_DURATION(_bitrate, _short)                     \
-       (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) +   \
-        CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE))
+        (8 * (AVG_PKT_SIZE + 4) * 10) / (_bitrate)))
 
 #define CCK_DURATION_LIST(_short, _s)                  \
-       CCK_ACK_DURATION(10, _short) >> _s,             \
-       CCK_ACK_DURATION(20, _short) >> _s,             \
-       CCK_ACK_DURATION(55, _short) >> _s,             \
-       CCK_ACK_DURATION(110, _short) >> _s
+       CCK_DURATION(10, _short) >> _s,                 \
+       CCK_DURATION(20, _short) >> _s,                 \
+       CCK_DURATION(55, _short) >> _s,                 \
+       CCK_DURATION(110, _short) >> _s
 
 #define __CCK_GROUP(_s)                                        \
        [MINSTREL_CCK_GROUP] = {                        \
        }
 
 #define CCK_GROUP_SHIFT                                        \
-       GROUP_SHIFT(CCK_ACK_DURATION(10, false))
+       GROUP_SHIFT(CCK_DURATION(10, false))
 
 #define CCK_GROUP __CCK_GROUP(CCK_GROUP_SHIFT)
 
+#define OFDM_DURATION(_bitrate)                                \
+       (1000 * (16 /* SIFS + signal ext */ +           \
+        16 /* T_PREAMBLE */ +                          \
+        4 /* T_SIGNAL */ +                             \
+        4 * (((16 + 80 * (AVG_PKT_SIZE + 4) + 6) /     \
+             ((_bitrate) * 4)))))
+
+#define OFDM_DURATION_LIST(_s)                         \
+       OFDM_DURATION(60) >> _s,                        \
+       OFDM_DURATION(90) >> _s,                        \
+       OFDM_DURATION(120) >> _s,                       \
+       OFDM_DURATION(180) >> _s,                       \
+       OFDM_DURATION(240) >> _s,                       \
+       OFDM_DURATION(360) >> _s,                       \
+       OFDM_DURATION(480) >> _s,                       \
+       OFDM_DURATION(540) >> _s
+
+#define __OFDM_GROUP(_s)                               \
+       [MINSTREL_OFDM_GROUP] = {                       \
+               .streams = 1,                           \
+               .flags = 0,                             \
+               .shift = _s,                            \
+               .duration = {                           \
+                       OFDM_DURATION_LIST(_s),         \
+               }                                       \
+       }
+
+#define OFDM_GROUP_SHIFT                               \
+       GROUP_SHIFT(OFDM_DURATION(60))
+
+#define OFDM_GROUP __OFDM_GROUP(OFDM_GROUP_SHIFT)
+
 
 static bool minstrel_vht_only = true;
 module_param(minstrel_vht_only, bool, 0644);
@@ -203,6 +230,7 @@ const struct mcs_group minstrel_mcs_groups[] = {
        MCS_GROUP(4, 1, BW_40),
 
        CCK_GROUP,
+       OFDM_GROUP,
 
        VHT_GROUP(1, 0, BW_20),
        VHT_GROUP(2, 0, BW_20),
@@ -235,6 +263,8 @@ const struct mcs_group minstrel_mcs_groups[] = {
        VHT_GROUP(4, 1, BW_80),
 };
 
+const s16 minstrel_cck_bitrates[4] = { 10, 20, 55, 110 };
+const s16 minstrel_ofdm_bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 };
 static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
 
 static void
@@ -279,6 +309,13 @@ minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map)
        return 0x3ff & ~mask;
 }
 
+static bool
+minstrel_ht_is_legacy_group(int group)
+{
+       return group == MINSTREL_CCK_GROUP ||
+              group == MINSTREL_OFDM_GROUP;
+}
+
 /*
  * Look up an MCS group index based on mac80211 rate information
  */
@@ -308,21 +345,34 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        if (rate->flags & IEEE80211_TX_RC_MCS) {
                group = minstrel_ht_get_group_idx(rate);
                idx = rate->idx % 8;
-       } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+               goto out;
+       }
+
+       if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
                group = minstrel_vht_get_group_idx(rate);
                idx = ieee80211_rate_get_vht_mcs(rate);
-       } else {
-               group = MINSTREL_CCK_GROUP;
+               goto out;
+       }
 
-               for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++)
-                       if (rate->idx == mp->cck_rates[idx])
-                               break;
+       group = MINSTREL_CCK_GROUP;
+       for (idx = 0; idx < ARRAY_SIZE(mp->cck_rates); idx++) {
+               if (rate->idx != mp->cck_rates[idx])
+                       continue;
 
                /* short preamble */
                if ((mi->supported[group] & BIT(idx + 4)) &&
                    (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
-                       idx += 4;
+                               idx += 4;
+               goto out;
        }
+
+       group = MINSTREL_OFDM_GROUP;
+       for (idx = 0; idx < ARRAY_SIZE(mp->ofdm_rates[0]); idx++)
+               if (rate->idx == mp->ofdm_rates[mi->band][idx])
+                       goto out;
+
+       idx = 0;
+out:
        return &mi->groups[group].rates[idx];
 }
 
@@ -332,13 +382,37 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index)
        return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES];
 }
 
+static inline int minstrel_get_duration(int index)
+{
+       const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
+       unsigned int duration = group->duration[index % MCS_GROUP_RATES];
+
+       return duration << group->shift;
+}
+
 static unsigned int
 minstrel_ht_avg_ampdu_len(struct minstrel_ht_sta *mi)
 {
-       if (!mi->avg_ampdu_len)
-               return AVG_AMPDU_SIZE;
+       int duration;
 
-       return MINSTREL_TRUNC(mi->avg_ampdu_len);
+       if (mi->avg_ampdu_len)
+               return MINSTREL_TRUNC(mi->avg_ampdu_len);
+
+       if (minstrel_ht_is_legacy_group(mi->max_tp_rate[0] / MCS_GROUP_RATES))
+               return 1;
+
+       duration = minstrel_get_duration(mi->max_tp_rate[0]);
+
+       if (duration > 400 * 1000)
+               return 2;
+
+       if (duration > 250 * 1000)
+               return 4;
+
+       if (duration > 150 * 1000)
+               return 8;
+
+       return 16;
 }
 
 /*
@@ -349,15 +423,19 @@ int
 minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
                       int prob_avg)
 {
-       unsigned int nsecs = 0;
+       unsigned int nsecs = 0, overhead = mi->overhead;
+       unsigned int ampdu_len = 1;
 
        /* do not account throughput if sucess prob is below 10% */
        if (prob_avg < MINSTREL_FRAC(10, 100))
                return 0;
 
-       if (group != MINSTREL_CCK_GROUP)
-               nsecs = 1000 * mi->overhead / minstrel_ht_avg_ampdu_len(mi);
+       if (minstrel_ht_is_legacy_group(group))
+               overhead = mi->overhead_legacy;
+       else
+               ampdu_len = minstrel_ht_avg_ampdu_len(mi);
 
+       nsecs = 1000 * overhead / ampdu_len;
        nsecs += minstrel_mcs_groups[group].duration[rate] <<
                 minstrel_mcs_groups[group].shift;
 
@@ -367,10 +445,9 @@ minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
         * (prob is scaled - see MINSTREL_FRAC above)
         */
        if (prob_avg > MINSTREL_FRAC(90, 100))
-               return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000)
-                                                                     / nsecs));
-       else
-               return MINSTREL_TRUNC(100000 * ((prob_avg * 1000) / nsecs));
+               prob_avg = MINSTREL_FRAC(90, 100);
+
+       return MINSTREL_TRUNC(100 * ((prob_avg * 1000000) / nsecs));
 }
 
 /*
@@ -417,12 +494,13 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
  * Find and set the topmost probability rate per sta and per group
  */
 static void
-minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
+minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 *dest, u16 index)
 {
        struct minstrel_mcs_group_data *mg;
        struct minstrel_rate_stats *mrs;
        int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob;
-       int max_tp_group, cur_tp_avg, cur_group, cur_idx;
+       int max_tp_group, max_tp_idx, max_tp_prob;
+       int cur_tp_avg, cur_group, cur_idx;
        int max_gpr_group, max_gpr_idx;
        int max_gpr_tp_avg, max_gpr_prob;
 
@@ -431,16 +509,24 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
        mg = &mi->groups[index / MCS_GROUP_RATES];
        mrs = &mg->rates[index % MCS_GROUP_RATES];
 
-       tmp_group = mi->max_prob_rate / MCS_GROUP_RATES;
-       tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES;
+       tmp_group = *dest / MCS_GROUP_RATES;
+       tmp_idx = *dest % MCS_GROUP_RATES;
        tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
        tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
        /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from
         * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */
        max_tp_group = mi->max_tp_rate[0] / MCS_GROUP_RATES;
-       if((index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) &&
-           (max_tp_group != MINSTREL_CCK_GROUP))
+       max_tp_idx = mi->max_tp_rate[0] % MCS_GROUP_RATES;
+       max_tp_prob = mi->groups[max_tp_group].rates[max_tp_idx].prob_avg;
+
+       if (minstrel_ht_is_legacy_group(index / MCS_GROUP_RATES) &&
+           !minstrel_ht_is_legacy_group(max_tp_group))
+               return;
+
+       /* skip rates faster than max tp rate with lower prob */
+       if (minstrel_get_duration(mi->max_tp_rate[0]) > minstrel_get_duration(index) &&
+           mrs->prob_avg < max_tp_prob)
                return;
 
        max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES;
@@ -451,7 +537,7 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
                cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx,
                                                    mrs->prob_avg);
                if (cur_tp_avg > tmp_tp_avg)
-                       mi->max_prob_rate = index;
+                       *dest = index;
 
                max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group,
                                                        max_gpr_idx,
@@ -460,7 +546,7 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
                        mg->max_group_prob_rate = index;
        } else {
                if (mrs->prob_avg > tmp_prob)
-                       mi->max_prob_rate = index;
+                       *dest = index;
                if (mrs->prob_avg > max_gpr_prob)
                        mg->max_group_prob_rate = index;
        }
@@ -476,13 +562,13 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
 static void
 minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
                                 u16 tmp_mcs_tp_rate[MAX_THR_RATES],
-                                u16 tmp_cck_tp_rate[MAX_THR_RATES])
+                                u16 tmp_legacy_tp_rate[MAX_THR_RATES])
 {
        unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob;
        int i;
 
-       tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES;
-       tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES;
+       tmp_group = tmp_legacy_tp_rate[0] / MCS_GROUP_RATES;
+       tmp_idx = tmp_legacy_tp_rate[0] % MCS_GROUP_RATES;
        tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
        tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
 
@@ -493,7 +579,7 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
 
        if (tmp_cck_tp > tmp_mcs_tp) {
                for(i = 0; i < MAX_THR_RATES; i++) {
-                       minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i],
+                       minstrel_ht_sort_best_tp_rates(mi, tmp_legacy_tp_rate[i],
                                                       tmp_mcs_tp_rate);
                }
        }
@@ -511,6 +597,9 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
        int tmp_max_streams, group, tmp_idx, tmp_prob;
        int tmp_tp = 0;
 
+       if (!mi->sta->ht_cap.ht_supported)
+               return;
+
        tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] /
                          MCS_GROUP_RATES].streams;
        for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
@@ -531,14 +620,6 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi)
        }
 }
 
-static inline int
-minstrel_get_duration(int index)
-{
-       const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
-       unsigned int duration = group->duration[index % MCS_GROUP_RATES];
-       return duration << group->shift;
-}
-
 static bool
 minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group,
                                                int tp_idx, const struct mcs_group *group)
@@ -658,6 +739,74 @@ out:
        mi->sample_mode = MINSTREL_SAMPLE_ACTIVE;
 }
 
+static inline int
+minstrel_ewma(int old, int new, int weight)
+{
+       int diff, incr;
+
+       diff = new - old;
+       incr = (EWMA_DIV - weight) * diff / EWMA_DIV;
+
+       return old + incr;
+}
+
+static inline int minstrel_filter_avg_add(u16 *prev_1, u16 *prev_2, s32 in)
+{
+       s32 out_1 = *prev_1;
+       s32 out_2 = *prev_2;
+       s32 val;
+
+       if (!in)
+               in += 1;
+
+       if (!out_1) {
+               val = out_1 = in;
+               goto out;
+       }
+
+       val = MINSTREL_AVG_COEFF1 * in;
+       val += MINSTREL_AVG_COEFF2 * out_1;
+       val += MINSTREL_AVG_COEFF3 * out_2;
+       val >>= MINSTREL_SCALE;
+
+       if (val > 1 << MINSTREL_SCALE)
+               val = 1 << MINSTREL_SCALE;
+       if (val < 0)
+               val = 1;
+
+out:
+       *prev_2 = out_1;
+       *prev_1 = val;
+
+       return val;
+}
+
+/*
+* Recalculate statistics and counters of a given rate
+*/
+static void
+minstrel_ht_calc_rate_stats(struct minstrel_priv *mp,
+                           struct minstrel_rate_stats *mrs)
+{
+       unsigned int cur_prob;
+
+       if (unlikely(mrs->attempts > 0)) {
+               mrs->sample_skipped = 0;
+               cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
+               minstrel_filter_avg_add(&mrs->prob_avg,
+                                       &mrs->prob_avg_1, cur_prob);
+               mrs->att_hist += mrs->attempts;
+               mrs->succ_hist += mrs->success;
+       } else {
+               mrs->sample_skipped++;
+       }
+
+       mrs->last_success = mrs->success;
+       mrs->last_attempts = mrs->attempts;
+       mrs->success = 0;
+       mrs->attempts = 0;
+}
+
 /*
  * Update rate statistics and select new primary rates
  *
@@ -675,7 +824,9 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        struct minstrel_rate_stats *mrs;
        int group, i, j, cur_prob;
        u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
-       u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
+       u16 tmp_legacy_tp_rate[MAX_THR_RATES], tmp_max_prob_rate;
+       u16 index;
+       bool ht_supported = mi->sta->ht_cap.ht_supported;
 
        mi->sample_mode = MINSTREL_SAMPLE_IDLE;
 
@@ -704,21 +855,30 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        mi->sample_count = 0;
 
        memset(tmp_mcs_tp_rate, 0, sizeof(tmp_mcs_tp_rate));
-       memset(tmp_cck_tp_rate, 0, sizeof(tmp_cck_tp_rate));
+       memset(tmp_legacy_tp_rate, 0, sizeof(tmp_legacy_tp_rate));
        if (mi->supported[MINSTREL_CCK_GROUP])
-               for (j = 0; j < ARRAY_SIZE(tmp_cck_tp_rate); j++)
-                       tmp_cck_tp_rate[j] = MINSTREL_CCK_GROUP * MCS_GROUP_RATES;
+               for (j = 0; j < ARRAY_SIZE(tmp_legacy_tp_rate); j++)
+                       tmp_legacy_tp_rate[j] = MINSTREL_CCK_GROUP * MCS_GROUP_RATES;
+       else if (mi->supported[MINSTREL_OFDM_GROUP])
+               for (j = 0; j < ARRAY_SIZE(tmp_legacy_tp_rate); j++)
+                       tmp_legacy_tp_rate[j] = MINSTREL_OFDM_GROUP * MCS_GROUP_RATES;
 
        if (mi->supported[MINSTREL_VHT_GROUP_0])
                index = MINSTREL_VHT_GROUP_0 * MCS_GROUP_RATES;
-       else
+       else if (ht_supported)
                index = MINSTREL_HT_GROUP_0 * MCS_GROUP_RATES;
+       else if (mi->supported[MINSTREL_CCK_GROUP])
+               index = MINSTREL_CCK_GROUP * MCS_GROUP_RATES;
+       else
+               index = MINSTREL_OFDM_GROUP * MCS_GROUP_RATES;
 
+       tmp_max_prob_rate = index;
        for (j = 0; j < ARRAY_SIZE(tmp_mcs_tp_rate); j++)
                tmp_mcs_tp_rate[j] = index;
 
        /* Find best rate sets within all MCS groups*/
        for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+               u16 *tp_rate = tmp_mcs_tp_rate;
 
                mg = &mi->groups[group];
                if (!mi->supported[group])
@@ -730,6 +890,9 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                for(j = 0; j < MAX_THR_RATES; j++)
                        tmp_group_tp_rate[j] = MCS_GROUP_RATES * group;
 
+               if (group == MINSTREL_CCK_GROUP && ht_supported)
+                       tp_rate = tmp_legacy_tp_rate;
+
                for (i = 0; i < MCS_GROUP_RATES; i++) {
                        if (!(mi->supported[group] & BIT(i)))
                                continue;
@@ -738,27 +901,18 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 
                        mrs = &mg->rates[i];
                        mrs->retry_updated = false;
-                       minstrel_calc_rate_stats(mp, mrs);
+                       minstrel_ht_calc_rate_stats(mp, mrs);
                        cur_prob = mrs->prob_avg;
 
                        if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
                                continue;
 
                        /* Find max throughput rate set */
-                       if (group != MINSTREL_CCK_GROUP) {
-                               minstrel_ht_sort_best_tp_rates(mi, index,
-                                                              tmp_mcs_tp_rate);
-                       } else if (group == MINSTREL_CCK_GROUP) {
-                               minstrel_ht_sort_best_tp_rates(mi, index,
-                                                              tmp_cck_tp_rate);
-                       }
+                       minstrel_ht_sort_best_tp_rates(mi, index, tp_rate);
 
                        /* Find max throughput rate set within a group */
                        minstrel_ht_sort_best_tp_rates(mi, index,
                                                       tmp_group_tp_rate);
-
-                       /* Find max probability rate per group and global */
-                       minstrel_ht_set_best_prob_rate(mi, index);
                }
 
                memcpy(mg->max_group_tp_rate, tmp_group_tp_rate,
@@ -766,16 +920,36 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        }
 
        /* Assign new rate set per sta */
-       minstrel_ht_assign_best_tp_rates(mi, tmp_mcs_tp_rate, tmp_cck_tp_rate);
+       minstrel_ht_assign_best_tp_rates(mi, tmp_mcs_tp_rate,
+                                        tmp_legacy_tp_rate);
        memcpy(mi->max_tp_rate, tmp_mcs_tp_rate, sizeof(mi->max_tp_rate));
 
+       for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
+               if (!mi->supported[group])
+                       continue;
+
+               mg = &mi->groups[group];
+               mg->max_group_prob_rate = MCS_GROUP_RATES * group;
+
+               for (i = 0; i < MCS_GROUP_RATES; i++) {
+                       if (!(mi->supported[group] & BIT(i)))
+                               continue;
+
+                       index = MCS_GROUP_RATES * group + i;
+
+                       /* Find max probability rate per group and global */
+                       minstrel_ht_set_best_prob_rate(mi, &tmp_max_prob_rate,
+                                                      index);
+               }
+       }
+
+       mi->max_prob_rate = tmp_max_prob_rate;
+
        /* Try to increase robustness of max_prob_rate*/
        minstrel_ht_prob_rate_reduce_streams(mi);
 
-       /* try to sample all available rates during each interval */
-       mi->sample_count *= 8;
-       if (mp->new_avg)
-               mi->sample_count /= 2;
+       /* try to sample half of all available rates during each interval */
+       mi->sample_count *= 4;
 
        if (sample)
                minstrel_ht_rate_sample_switch(mp, mi);
@@ -795,8 +969,11 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 }
 
 static bool
-minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate)
+minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
+                        struct ieee80211_tx_rate *rate)
 {
+       int i;
+
        if (rate->idx < 0)
                return false;
 
@@ -807,10 +984,15 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
            rate->flags & IEEE80211_TX_RC_VHT_MCS)
                return true;
 
-       return rate->idx == mp->cck_rates[0] ||
-              rate->idx == mp->cck_rates[1] ||
-              rate->idx == mp->cck_rates[2] ||
-              rate->idx == mp->cck_rates[3];
+       for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++)
+               if (rate->idx == mp->cck_rates[i])
+                       return true;
+
+       for (i = 0; i < ARRAY_SIZE(mp->ofdm_rates[0]); i++)
+               if (rate->idx == mp->ofdm_rates[mi->band][i])
+                       return true;
+
+       return false;
 }
 
 static void
@@ -887,21 +1069,15 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
                       void *priv_sta, struct ieee80211_tx_status *st)
 {
        struct ieee80211_tx_info *info = st->info;
-       struct minstrel_ht_sta_priv *msp = priv_sta;
-       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_ht_sta *mi = priv_sta;
        struct ieee80211_tx_rate *ar = info->status.rates;
        struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL;
        struct minstrel_priv *mp = priv;
-       u32 update_interval = mp->update_interval / 2;
+       u32 update_interval = mp->update_interval;
        bool last, update = false;
        bool sample_status = false;
        int i;
 
-       if (!msp->is_ht)
-               return mac80211_minstrel.tx_status_ext(priv, sband,
-                                                      &msp->legacy, st);
-
-
        /* This packet was aggregated but doesn't carry status info */
        if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
            !(info->flags & IEEE80211_TX_STAT_AMPDU))
@@ -930,10 +1106,10 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
        if (mi->sample_mode != MINSTREL_SAMPLE_IDLE)
                rate_sample = minstrel_get_ratestats(mi, mi->sample_rate);
 
-       last = !minstrel_ht_txstat_valid(mp, &ar[0]);
+       last = !minstrel_ht_txstat_valid(mp, mi, &ar[0]);
        for (i = 0; !last; i++) {
                last = (i == IEEE80211_TX_MAX_RATES - 1) ||
-                      !minstrel_ht_txstat_valid(mp, &ar[i + 1]);
+                      !minstrel_ht_txstat_valid(mp, mi, &ar[i + 1]);
 
                rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
                if (rate == rate_sample)
@@ -947,9 +1123,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
 
        switch (mi->sample_mode) {
        case MINSTREL_SAMPLE_IDLE:
-               if (mp->new_avg &&
-                   (mp->hw->max_rates > 1 ||
-                    mi->total_packets_cur < SAMPLE_SWITCH_THR))
+               if (mp->hw->max_rates > 1 ||
+                    mi->total_packets_cur < SAMPLE_SWITCH_THR)
                        update_interval /= 2;
                break;
 
@@ -1031,7 +1206,10 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        ctime += (t_slot * cw) >> 1;
        cw = min((cw << 1) | 1, mp->cw_max);
 
-       if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) {
+       if (minstrel_ht_is_legacy_group(index / MCS_GROUP_RATES)) {
+               overhead = mi->overhead_legacy;
+               overhead_rtscts = mi->overhead_legacy_rtscts;
+       } else {
                overhead = mi->overhead;
                overhead_rtscts = mi->overhead_rtscts;
        }
@@ -1061,7 +1239,8 @@ static void
 minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                      struct ieee80211_sta_rates *ratetbl, int offset, int index)
 {
-       const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
+       int group_idx = index / MCS_GROUP_RATES;
+       const struct mcs_group *group = &minstrel_mcs_groups[group_idx];
        struct minstrel_rate_stats *mrs;
        u8 idx;
        u16 flags = group->flags;
@@ -1080,13 +1259,17 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
                ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts;
        }
 
-       if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
+       index %= MCS_GROUP_RATES;
+       if (group_idx == MINSTREL_CCK_GROUP)
                idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
+       else if (group_idx == MINSTREL_OFDM_GROUP)
+               idx = mp->ofdm_rates[mi->band][index %
+                                              ARRAY_SIZE(mp->ofdm_rates[0])];
        else if (flags & IEEE80211_TX_RC_VHT_MCS)
                idx = ((group->streams - 1) << 4) |
-                     ((index % MCS_GROUP_RATES) & 0xF);
+                     (index & 0xF);
        else
-               idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
+               idx = index + (group->streams - 1) * 8;
 
        /* enable RTS/CTS if needed:
         *  - if station is in dynamic SMPS (and streams > 1)
@@ -1224,13 +1407,13 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
        mrs = &mg->rates[sample_idx];
        sample_idx += sample_group * MCS_GROUP_RATES;
 
-       /* Set tp_rate1, tp_rate2 to the highest / second highest max_tp_rate */
+       tp_rate1 = mi->max_tp_rate[0];
+
+       /* Set tp_rate2 to the second highest max_tp_rate */
        if (minstrel_get_duration(mi->max_tp_rate[0]) >
            minstrel_get_duration(mi->max_tp_rate[1])) {
-               tp_rate1 = mi->max_tp_rate[1];
                tp_rate2 = mi->max_tp_rate[0];
        } else {
-               tp_rate1 = mi->max_tp_rate[0];
                tp_rate2 = mi->max_tp_rate[1];
        }
 
@@ -1296,16 +1479,12 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
        const struct mcs_group *sample_group;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
        struct ieee80211_tx_rate *rate = &info->status.rates[0];
-       struct minstrel_ht_sta_priv *msp = priv_sta;
-       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_ht_sta *mi = priv_sta;
        struct minstrel_priv *mp = priv;
        int sample_idx;
 
-       if (!msp->is_ht)
-               return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
-
        if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
-           mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
+           !minstrel_ht_is_legacy_group(mi->max_prob_rate / MCS_GROUP_RATES))
                minstrel_aggr_check(sta, txrc->skb);
 
        info->flags |= mi->tx_flags;
@@ -1346,6 +1525,9 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
        if (sample_group == &minstrel_mcs_groups[MINSTREL_CCK_GROUP]) {
                int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
                rate->idx = mp->cck_rates[idx];
+       } else if (sample_group == &minstrel_mcs_groups[MINSTREL_OFDM_GROUP]) {
+               int idx = sample_idx % ARRAY_SIZE(mp->ofdm_rates[0]);
+               rate->idx = mp->ofdm_rates[mi->band][idx];
        } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
                ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
                                       sample_group->streams);
@@ -1366,44 +1548,59 @@ minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
        if (sband->band != NL80211_BAND_2GHZ)
                return;
 
-       if (!ieee80211_hw_check(mp->hw, SUPPORTS_HT_CCK_RATES))
+       if (sta->ht_cap.ht_supported &&
+           !ieee80211_hw_check(mp->hw, SUPPORTS_HT_CCK_RATES))
                return;
 
-       mi->cck_supported = 0;
-       mi->cck_supported_short = 0;
        for (i = 0; i < 4; i++) {
-               if (!rate_supported(sta, sband->band, mp->cck_rates[i]))
+               if (mp->cck_rates[i] == 0xff ||
+                   !rate_supported(sta, sband->band, mp->cck_rates[i]))
                        continue;
 
-               mi->cck_supported |= BIT(i);
+               mi->supported[MINSTREL_CCK_GROUP] |= BIT(i);
                if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE)
-                       mi->cck_supported_short |= BIT(i);
+                       mi->supported[MINSTREL_CCK_GROUP] |= BIT(i + 4);
        }
+}
+
+static void
+minstrel_ht_update_ofdm(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
+                       struct ieee80211_supported_band *sband,
+                       struct ieee80211_sta *sta)
+{
+       const u8 *rates;
+       int i;
 
-       mi->supported[MINSTREL_CCK_GROUP] = mi->cck_supported;
+       if (sta->ht_cap.ht_supported)
+               return;
+
+       rates = mp->ofdm_rates[sband->band];
+       for (i = 0; i < ARRAY_SIZE(mp->ofdm_rates[0]); i++) {
+               if (rates[i] == 0xff ||
+                   !rate_supported(sta, sband->band, rates[i]))
+                       continue;
+
+               mi->supported[MINSTREL_OFDM_GROUP] |= BIT(i);
+       }
 }
 
 static void
 minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                        struct cfg80211_chan_def *chandef,
-                        struct ieee80211_sta *sta, void *priv_sta)
+                       struct ieee80211_sta *sta, void *priv_sta)
 {
        struct minstrel_priv *mp = priv;
-       struct minstrel_ht_sta_priv *msp = priv_sta;
-       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_ht_sta *mi = priv_sta;
        struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
        u16 ht_cap = sta->ht_cap.cap;
        struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+       const struct ieee80211_rate *ctl_rate;
+       bool ldpc, erp;
        int use_vht;
        int n_supported = 0;
        int ack_dur;
        int stbc;
        int i;
-       bool ldpc;
-
-       /* fall back to the old minstrel for legacy stations */
-       if (!sta->ht_cap.ht_supported)
-               goto use_legacy;
 
        BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
 
@@ -1412,10 +1609,10 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        else
                use_vht = 0;
 
-       msp->is_ht = true;
        memset(mi, 0, sizeof(*mi));
 
        mi->sta = sta;
+       mi->band = sband->band;
        mi->last_stats_update = jiffies;
 
        ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
@@ -1423,6 +1620,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
        mi->overhead += ack_dur;
        mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
 
+       ctl_rate = &sband->bitrates[rate_lowest_index(sband, sta)];
+       erp = ctl_rate->flags & IEEE80211_RATE_ERP_G;
+       ack_dur = ieee80211_frame_duration(sband->band, 10,
+                                          ctl_rate->bitrate, erp, 1,
+                                          ieee80211_chandef_get_shift(chandef));
+       mi->overhead_legacy = ack_dur;
+       mi->overhead_legacy_rtscts = mi->overhead_legacy + 2 * ack_dur;
+
        mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
 
        /* When using MRR, sample more on the first attempt, without delay */
@@ -1456,10 +1661,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                int bw, nss;
 
                mi->supported[i] = 0;
-               if (i == MINSTREL_CCK_GROUP) {
-                       minstrel_ht_update_cck(mp, mi, sband, sta);
+               if (minstrel_ht_is_legacy_group(i))
                        continue;
-               }
 
                if (gflags & IEEE80211_TX_RC_SHORT_GI) {
                        if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
@@ -1520,24 +1723,12 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
                        n_supported++;
        }
 
-       if (!n_supported)
-               goto use_legacy;
-
-       mi->supported[MINSTREL_CCK_GROUP] |= mi->cck_supported_short << 4;
+       minstrel_ht_update_cck(mp, mi, sband, sta);
+       minstrel_ht_update_ofdm(mp, mi, sband, sta);
 
        /* create an initial rate table with the lowest supported rates */
        minstrel_ht_update_stats(mp, mi, true);
        minstrel_ht_update_rates(mp, mi);
-
-       return;
-
-use_legacy:
-       msp->is_ht = false;
-       memset(&msp->legacy, 0, sizeof(msp->legacy));
-       msp->legacy.r = msp->ratelist;
-       msp->legacy.sample_table = msp->sample_table;
-       return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
-                                          &msp->legacy);
 }
 
 static void
@@ -1561,7 +1752,7 @@ static void *
 minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
 {
        struct ieee80211_supported_band *sband;
-       struct minstrel_ht_sta_priv *msp;
+       struct minstrel_ht_sta *mi;
        struct minstrel_priv *mp = priv;
        struct ieee80211_hw *hw = mp->hw;
        int max_rates = 0;
@@ -1573,72 +1764,80 @@ minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
                        max_rates = sband->n_bitrates;
        }
 
-       msp = kzalloc(sizeof(*msp), gfp);
-       if (!msp)
-               return NULL;
-
-       msp->ratelist = kcalloc(max_rates, sizeof(struct minstrel_rate), gfp);
-       if (!msp->ratelist)
-               goto error;
-
-       msp->sample_table = kmalloc_array(max_rates, SAMPLE_COLUMNS, gfp);
-       if (!msp->sample_table)
-               goto error1;
-
-       return msp;
-
-error1:
-       kfree(msp->ratelist);
-error:
-       kfree(msp);
-       return NULL;
+       return kzalloc(sizeof(*mi), gfp);
 }
 
 static void
 minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
 {
-       struct minstrel_ht_sta_priv *msp = priv_sta;
-
-       kfree(msp->sample_table);
-       kfree(msp->ratelist);
-       kfree(msp);
+       kfree(priv_sta);
 }
 
 static void
-minstrel_ht_init_cck_rates(struct minstrel_priv *mp)
+minstrel_ht_fill_rate_array(u8 *dest, struct ieee80211_supported_band *sband,
+                           const s16 *bitrates, int n_rates, u32 rate_flags)
 {
-       static const int bitrates[4] = { 10, 20, 55, 110 };
-       struct ieee80211_supported_band *sband;
-       u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
        int i, j;
 
-       sband = mp->hw->wiphy->bands[NL80211_BAND_2GHZ];
-       if (!sband)
-               return;
-
        for (i = 0; i < sband->n_bitrates; i++) {
                struct ieee80211_rate *rate = &sband->bitrates[i];
 
-               if (rate->flags & IEEE80211_RATE_ERP_G)
-                       continue;
-
                if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
                        continue;
 
-               for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
+               for (j = 0; j < n_rates; j++) {
                        if (rate->bitrate != bitrates[j])
                                continue;
 
-                       mp->cck_rates[j] = i;
+                       dest[j] = i;
                        break;
                }
        }
 }
 
+static void
+minstrel_ht_init_cck_rates(struct minstrel_priv *mp)
+{
+       static const s16 bitrates[4] = { 10, 20, 55, 110 };
+       struct ieee80211_supported_band *sband;
+       u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
+
+       memset(mp->cck_rates, 0xff, sizeof(mp->cck_rates));
+       sband = mp->hw->wiphy->bands[NL80211_BAND_2GHZ];
+       if (!sband)
+               return;
+
+       BUILD_BUG_ON(ARRAY_SIZE(mp->cck_rates) != ARRAY_SIZE(bitrates));
+       minstrel_ht_fill_rate_array(mp->cck_rates, sband,
+                                   minstrel_cck_bitrates,
+                                   ARRAY_SIZE(minstrel_cck_bitrates),
+                                   rate_flags);
+}
+
+static void
+minstrel_ht_init_ofdm_rates(struct minstrel_priv *mp, enum nl80211_band band)
+{
+       static const s16 bitrates[8] = { 60, 90, 120, 180, 240, 360, 480, 540 };
+       struct ieee80211_supported_band *sband;
+       u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
+
+       memset(mp->ofdm_rates[band], 0xff, sizeof(mp->ofdm_rates[band]));
+       sband = mp->hw->wiphy->bands[band];
+       if (!sband)
+               return;
+
+       BUILD_BUG_ON(ARRAY_SIZE(mp->ofdm_rates[band]) != ARRAY_SIZE(bitrates));
+       minstrel_ht_fill_rate_array(mp->ofdm_rates[band], sband,
+                                   minstrel_ofdm_bitrates,
+                                   ARRAY_SIZE(minstrel_ofdm_bitrates),
+                                   rate_flags);
+}
+
 static void *
 minstrel_ht_alloc(struct ieee80211_hw *hw)
 {
        struct minstrel_priv *mp;
+       int i;
 
        mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC);
        if (!mp)
@@ -1652,12 +1851,6 @@ minstrel_ht_alloc(struct ieee80211_hw *hw)
        mp->cw_min = 15;
        mp->cw_max = 1023;
 
-       /* number of packets (in %) to use for sampling other rates
-        * sample less often for non-mrr packets, because the overhead
-        * is much higher than with mrr */
-       mp->lookaround_rate = 5;
-       mp->lookaround_rate_mrr = 10;
-
        /* maximum time that the hw is allowed to stay in one MRR segment */
        mp->segment_size = 6000;
 
@@ -1672,9 +1865,10 @@ minstrel_ht_alloc(struct ieee80211_hw *hw)
 
        mp->hw = hw;
        mp->update_interval = HZ / 10;
-       mp->new_avg = true;
 
        minstrel_ht_init_cck_rates(mp);
+       for (i = 0; i < ARRAY_SIZE(mp->hw->wiphy->bands); i++)
+           minstrel_ht_init_ofdm_rates(mp, i);
 
        return mp;
 }
@@ -1690,8 +1884,6 @@ static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv,
                           &mp->fixed_rate_idx);
        debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir,
                           &mp->sample_switch);
-       debugfs_create_bool("new_avg", S_IRUGO | S_IWUSR, debugfsdir,
-                          &mp->new_avg);
 }
 #endif
 
@@ -1703,13 +1895,9 @@ minstrel_ht_free(void *priv)
 
 static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
 {
-       struct minstrel_ht_sta_priv *msp = priv_sta;
-       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_ht_sta *mi = priv_sta;
        int i, j, prob, tp_avg;
 
-       if (!msp->is_ht)
-               return mac80211_minstrel.get_expected_throughput(priv_sta);
-
        i = mi->max_tp_rate[0] / MCS_GROUP_RATES;
        j = mi->max_tp_rate[0] % MCS_GROUP_RATES;
        prob = mi->groups[i].rates[j].prob_avg;
index 53ea3c2..7d6d0b7 100644 (file)
@@ -6,6 +6,33 @@
 #ifndef __RC_MINSTREL_HT_H
 #define __RC_MINSTREL_HT_H
 
+/* number of highest throughput rates to consider*/
+#define MAX_THR_RATES 4
+#define SAMPLE_COLUMNS 10      /* number of columns in sample table */
+
+/* scaled fraction values */
+#define MINSTREL_SCALE  12
+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div)
+#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE)
+
+#define EWMA_LEVEL     96      /* ewma weighting factor [/EWMA_DIV] */
+#define EWMA_DIV       128
+
+/*
+ * Coefficients for moving average with noise filter (period=16),
+ * scaled by 10 bits
+ *
+ * a1 = exp(-pi * sqrt(2) / period)
+ * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period)
+ * coeff3 = -sqr(a1)
+ * coeff1 = 1 - coeff2 - coeff3
+ */
+#define MINSTREL_AVG_COEFF1            (MINSTREL_FRAC(1, 1) - \
+                                        MINSTREL_AVG_COEFF2 - \
+                                        MINSTREL_AVG_COEFF3)
+#define MINSTREL_AVG_COEFF2            0x00001499
+#define MINSTREL_AVG_COEFF3            -0x0000092e
+
 /*
  * The number of streams can be changed to 2 to reduce code
  * size and memory footprint.
                                 MINSTREL_HT_STREAM_GROUPS)
 #define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS *         \
                                 MINSTREL_VHT_STREAM_GROUPS)
-#define MINSTREL_CCK_GROUPS_NB 1
+#define MINSTREL_LEGACY_GROUPS_NB      2
 #define MINSTREL_GROUPS_NB     (MINSTREL_HT_GROUPS_NB +        \
                                 MINSTREL_VHT_GROUPS_NB +       \
-                                MINSTREL_CCK_GROUPS_NB)
+                                MINSTREL_LEGACY_GROUPS_NB)
 
 #define MINSTREL_HT_GROUP_0    0
 #define MINSTREL_CCK_GROUP     (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
-#define MINSTREL_VHT_GROUP_0   (MINSTREL_CCK_GROUP + 1)
+#define MINSTREL_OFDM_GROUP    (MINSTREL_CCK_GROUP + 1)
+#define MINSTREL_VHT_GROUP_0   (MINSTREL_OFDM_GROUP + 1)
 
 #define MCS_GROUP_RATES                10
 
+struct minstrel_priv {
+       struct ieee80211_hw *hw;
+       bool has_mrr;
+       u32 sample_switch;
+       unsigned int cw_min;
+       unsigned int cw_max;
+       unsigned int max_retry;
+       unsigned int segment_size;
+       unsigned int update_interval;
+
+       u8 cck_rates[4];
+       u8 ofdm_rates[NUM_NL80211_BANDS][8];
+
+#ifdef CONFIG_MAC80211_DEBUGFS
+       /*
+        * enable fixed rate processing per RC
+        *   - write static index to debugfs:ieee80211/phyX/rc/fixed_rate_idx
+        *   - write -1 to enable RC processing again
+        *   - setting will be applied on next update
+        */
+       u32 fixed_rate_idx;
+#endif
+};
+
+
 struct mcs_group {
        u16 flags;
        u8 streams;
@@ -37,8 +90,30 @@ struct mcs_group {
        u16 duration[MCS_GROUP_RATES];
 };
 
+extern const s16 minstrel_cck_bitrates[4];
+extern const s16 minstrel_ofdm_bitrates[8];
 extern const struct mcs_group minstrel_mcs_groups[];
 
+struct minstrel_rate_stats {
+       /* current / last sampling period attempts/success counters */
+       u16 attempts, last_attempts;
+       u16 success, last_success;
+
+       /* total attempts/success counters */
+       u32 att_hist, succ_hist;
+
+       /* prob_avg - moving average of prob */
+       u16 prob_avg;
+       u16 prob_avg_1;
+
+       /* maximum retry counts */
+       u8 retry_count;
+       u8 retry_count_rtscts;
+
+       u8 sample_skipped;
+       bool retry_updated;
+};
+
 struct minstrel_mcs_group_data {
        u8 index;
        u8 column;
@@ -77,6 +152,8 @@ struct minstrel_ht_sta {
        /* overhead time in usec for each frame */
        unsigned int overhead;
        unsigned int overhead_rtscts;
+       unsigned int overhead_legacy;
+       unsigned int overhead_legacy_rtscts;
 
        unsigned int total_packets_last;
        unsigned int total_packets_cur;
@@ -97,8 +174,7 @@ struct minstrel_ht_sta {
        /* current MCS group to be sampled */
        u8 sample_group;
 
-       u8 cck_supported;
-       u8 cck_supported_short;
+       u8 band;
 
        /* Bitfield of supported MCS rates of all groups */
        u16 supported[MINSTREL_GROUPS_NB];
@@ -107,16 +183,6 @@ struct minstrel_ht_sta {
        struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
 };
 
-struct minstrel_ht_sta_priv {
-       union {
-               struct minstrel_ht_sta ht;
-               struct minstrel_sta_info legacy;
-       };
-       void *ratelist;
-       void *sample_table;
-       bool is_ht;
-};
-
 void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
 int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
                           int prob_avg);
index bebb719..3b7af24 100644 (file)
@@ -9,9 +9,13 @@
 #include <linux/ieee80211.h>
 #include <linux/export.h>
 #include <net/mac80211.h>
-#include "rc80211_minstrel.h"
 #include "rc80211_minstrel_ht.h"
 
+struct minstrel_debugfs_info {
+       size_t len;
+       char buf[];
+};
+
 static ssize_t
 minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos)
 {
@@ -52,7 +56,6 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 
        for (j = 0; j < MCS_GROUP_RATES; j++) {
                struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
-               static const int bitrates[4] = { 10, 20, 55, 110 };
                int idx = i * MCS_GROUP_RATES + j;
                unsigned int duration;
 
@@ -67,6 +70,9 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
                        p += sprintf(p, "VHT%c0 ", htmode);
                        p += sprintf(p, "%cGI ", gimode);
                        p += sprintf(p, "%d  ", mg->streams);
+               } else if (i == MINSTREL_OFDM_GROUP) {
+                       p += sprintf(p, "OFDM       ");
+                       p += sprintf(p, "1 ");
                } else {
                        p += sprintf(p, "CCK    ");
                        p += sprintf(p, "%cP  ", j < 4 ? 'L' : 'S');
@@ -84,7 +90,12 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
                        p += sprintf(p, "  MCS%-1u/%1u", j, mg->streams);
                } else {
-                       int r = bitrates[j % 4];
+                       int r;
+
+                       if (i == MINSTREL_OFDM_GROUP)
+                               r = minstrel_ofdm_bitrates[j % 8];
+                       else
+                               r = minstrel_cck_bitrates[j % 4];
 
                        p += sprintf(p, "   %2u.%1uM", r / 10, r % 10);
                }
@@ -120,20 +131,11 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 static int
 minstrel_ht_stats_open(struct inode *inode, struct file *file)
 {
-       struct minstrel_ht_sta_priv *msp = inode->i_private;
-       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_ht_sta *mi = inode->i_private;
        struct minstrel_debugfs_info *ms;
        unsigned int i;
-       int ret;
        char *p;
 
-       if (!msp->is_ht) {
-               inode->i_private = &msp->legacy;
-               ret = minstrel_stats_open(inode, file);
-               inode->i_private = msp;
-               return ret;
-       }
-
        ms = kmalloc(32768, GFP_KERNEL);
        if (!ms)
                return -ENOMEM;
@@ -199,7 +201,6 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
 
        for (j = 0; j < MCS_GROUP_RATES; j++) {
                struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j];
-               static const int bitrates[4] = { 10, 20, 55, 110 };
                int idx = i * MCS_GROUP_RATES + j;
                unsigned int duration;
 
@@ -214,6 +215,8 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
                        p += sprintf(p, "VHT%c0,", htmode);
                        p += sprintf(p, "%cGI,", gimode);
                        p += sprintf(p, "%d,", mg->streams);
+               } else if (i == MINSTREL_OFDM_GROUP) {
+                       p += sprintf(p, "OFDM,,1,");
                } else {
                        p += sprintf(p, "CCK,");
                        p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S');
@@ -231,7 +234,13 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
                } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
                        p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams);
                } else {
-                       int r = bitrates[j % 4];
+                       int r;
+
+                       if (i == MINSTREL_OFDM_GROUP)
+                               r = minstrel_ofdm_bitrates[j % 8];
+                       else
+                               r = minstrel_cck_bitrates[j % 4];
+
                        p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10);
                }
 
@@ -270,22 +279,12 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p)
 static int
 minstrel_ht_stats_csv_open(struct inode *inode, struct file *file)
 {
-       struct minstrel_ht_sta_priv *msp = inode->i_private;
-       struct minstrel_ht_sta *mi = &msp->ht;
+       struct minstrel_ht_sta *mi = inode->i_private;
        struct minstrel_debugfs_info *ms;
        unsigned int i;
-       int ret;
        char *p;
 
-       if (!msp->is_ht) {
-               inode->i_private = &msp->legacy;
-               ret = minstrel_stats_csv_open(inode, file);
-               inode->i_private = msp;
-               return ret;
-       }
-
        ms = kmalloc(32768, GFP_KERNEL);
-
        if (!ms)
                return -ENOMEM;
 
@@ -316,10 +315,8 @@ static const struct file_operations minstrel_ht_stat_csv_fops = {
 void
 minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
 {
-       struct minstrel_ht_sta_priv *msp = priv_sta;
-
-       debugfs_create_file("rc_stats", 0444, dir, msp,
+       debugfs_create_file("rc_stats", 0444, dir, priv_sta,
                            &minstrel_ht_stat_fops);
-       debugfs_create_file("rc_stats_csv", 0444, dir, msp,
+       debugfs_create_file("rc_stats_csv", 0444, dir, priv_sta,
                            &minstrel_ht_stat_csv_fops);
 }
index 13b9bcc..c1343c0 100644 (file)
@@ -4095,7 +4095,9 @@ void ieee80211_check_fast_rx(struct sta_info *sta)
                .vif_type = sdata->vif.type,
                .control_port_protocol = sdata->control_port_protocol,
        }, *old, *new = NULL;
+       bool set_offload = false;
        bool assign = false;
+       bool offload;
 
        /* use sparse to check that we don't return without updating */
        __acquire(check_fast_rx);
@@ -4176,6 +4178,8 @@ void ieee80211_check_fast_rx(struct sta_info *sta)
 
        rcu_read_lock();
        key = rcu_dereference(sta->ptk[sta->ptk_idx]);
+       if (!key)
+               key = rcu_dereference(sdata->default_unicast_key);
        if (key) {
                switch (key->conf.cipher) {
                case WLAN_CIPHER_SUITE_TKIP:
@@ -4206,6 +4210,17 @@ void ieee80211_check_fast_rx(struct sta_info *sta)
        if (assign)
                new = kmemdup(&fastrx, sizeof(fastrx), GFP_KERNEL);
 
+       offload = assign &&
+                 (sdata->vif.offload_flags & IEEE80211_OFFLOAD_DECAP_ENABLED);
+
+       if (offload)
+               set_offload = !test_and_set_sta_flag(sta, WLAN_STA_DECAP_OFFLOAD);
+       else
+               set_offload = test_and_clear_sta_flag(sta, WLAN_STA_DECAP_OFFLOAD);
+
+       if (set_offload)
+               drv_sta_set_decap_offload(local, sdata, &sta->sta, assign);
+
        spin_lock_bh(&sta->lock);
        old = rcu_dereference_protected(sta->fast_rx, true);
        rcu_assign_pointer(sta->fast_rx, new);
@@ -4252,6 +4267,104 @@ void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
        mutex_unlock(&local->sta_mtx);
 }
 
+static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
+                             struct ieee80211_fast_rx *fast_rx,
+                             int orig_len)
+{
+       struct ieee80211_sta_rx_stats *stats;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+       struct sta_info *sta = rx->sta;
+       struct sk_buff *skb = rx->skb;
+       void *sa = skb->data + ETH_ALEN;
+       void *da = skb->data;
+
+       stats = &sta->rx_stats;
+       if (fast_rx->uses_rss)
+               stats = this_cpu_ptr(sta->pcpu_rx_stats);
+
+       /* statistics part of ieee80211_rx_h_sta_process() */
+       if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
+               stats->last_signal = status->signal;
+               if (!fast_rx->uses_rss)
+                       ewma_signal_add(&sta->rx_stats_avg.signal,
+                                       -status->signal);
+       }
+
+       if (status->chains) {
+               int i;
+
+               stats->chains = status->chains;
+               for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
+                       int signal = status->chain_signal[i];
+
+                       if (!(status->chains & BIT(i)))
+                               continue;
+
+                       stats->chain_signal_last[i] = signal;
+                       if (!fast_rx->uses_rss)
+                               ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
+                                               -signal);
+               }
+       }
+       /* end of statistics */
+
+       stats->last_rx = jiffies;
+       stats->last_rate = sta_stats_encode_rate(status);
+
+       stats->fragments++;
+       stats->packets++;
+
+       skb->dev = fast_rx->dev;
+
+       dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
+
+       /* The seqno index has the same property as needed
+        * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
+        * for non-QoS-data frames. Here we know it's a data
+        * frame, so count MSDUs.
+        */
+       u64_stats_update_begin(&stats->syncp);
+       stats->msdu[rx->seqno_idx]++;
+       stats->bytes += orig_len;
+       u64_stats_update_end(&stats->syncp);
+
+       if (fast_rx->internal_forward) {
+               struct sk_buff *xmit_skb = NULL;
+               if (is_multicast_ether_addr(da)) {
+                       xmit_skb = skb_copy(skb, GFP_ATOMIC);
+               } else if (!ether_addr_equal(da, sa) &&
+                          sta_info_get(rx->sdata, da)) {
+                       xmit_skb = skb;
+                       skb = NULL;
+               }
+
+               if (xmit_skb) {
+                       /*
+                        * Send to wireless media and increase priority by 256
+                        * to keep the received priority instead of
+                        * reclassifying the frame (see cfg80211_classify8021d).
+                        */
+                       xmit_skb->priority += 256;
+                       xmit_skb->protocol = htons(ETH_P_802_3);
+                       skb_reset_network_header(xmit_skb);
+                       skb_reset_mac_header(xmit_skb);
+                       dev_queue_xmit(xmit_skb);
+               }
+
+               if (!skb)
+                       return;
+       }
+
+       /* deliver to local stack */
+       skb->protocol = eth_type_trans(skb, fast_rx->dev);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       if (rx->list)
+               list_add_tail(&skb->list, rx->list);
+       else
+               netif_receive_skb(skb);
+
+}
+
 static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
                                     struct ieee80211_fast_rx *fast_rx)
 {
@@ -4272,9 +4385,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
        } addrs __aligned(2);
        struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
 
-       if (fast_rx->uses_rss)
-               stats = this_cpu_ptr(sta->pcpu_rx_stats);
-
        /* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
         * to a common data structure; drivers can implement that per queue
         * but we don't have that information in mac80211
@@ -4348,32 +4458,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
            pskb_trim(skb, skb->len - fast_rx->icv_len))
                goto drop;
 
-       /* statistics part of ieee80211_rx_h_sta_process() */
-       if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
-               stats->last_signal = status->signal;
-               if (!fast_rx->uses_rss)
-                       ewma_signal_add(&sta->rx_stats_avg.signal,
-                                       -status->signal);
-       }
-
-       if (status->chains) {
-               int i;
-
-               stats->chains = status->chains;
-               for (i = 0; i < ARRAY_SIZE(status->chain_signal); i++) {
-                       int signal = status->chain_signal[i];
-
-                       if (!(status->chains & BIT(i)))
-                               continue;
-
-                       stats->chain_signal_last[i] = signal;
-                       if (!fast_rx->uses_rss)
-                               ewma_signal_add(&sta->rx_stats_avg.chain_signal[i],
-                                               -signal);
-               }
-       }
-       /* end of statistics */
-
        if (rx->key && !ieee80211_has_protected(hdr->frame_control))
                goto drop;
 
@@ -4385,12 +4469,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
                return true;
        }
 
-       stats->last_rx = jiffies;
-       stats->last_rate = sta_stats_encode_rate(status);
-
-       stats->fragments++;
-       stats->packets++;
-
        /* do the header conversion - first grab the addresses */
        ether_addr_copy(addrs.da, skb->data + fast_rx->da_offs);
        ether_addr_copy(addrs.sa, skb->data + fast_rx->sa_offs);
@@ -4399,58 +4477,14 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
        /* push the addresses in front */
        memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs));
 
-       skb->dev = fast_rx->dev;
-
-       dev_sw_netstats_rx_add(fast_rx->dev, skb->len);
-
-       /* The seqno index has the same property as needed
-        * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
-        * for non-QoS-data frames. Here we know it's a data
-        * frame, so count MSDUs.
-        */
-       u64_stats_update_begin(&stats->syncp);
-       stats->msdu[rx->seqno_idx]++;
-       stats->bytes += orig_len;
-       u64_stats_update_end(&stats->syncp);
-
-       if (fast_rx->internal_forward) {
-               struct sk_buff *xmit_skb = NULL;
-               if (is_multicast_ether_addr(addrs.da)) {
-                       xmit_skb = skb_copy(skb, GFP_ATOMIC);
-               } else if (!ether_addr_equal(addrs.da, addrs.sa) &&
-                          sta_info_get(rx->sdata, addrs.da)) {
-                       xmit_skb = skb;
-                       skb = NULL;
-               }
-
-               if (xmit_skb) {
-                       /*
-                        * Send to wireless media and increase priority by 256
-                        * to keep the received priority instead of
-                        * reclassifying the frame (see cfg80211_classify8021d).
-                        */
-                       xmit_skb->priority += 256;
-                       xmit_skb->protocol = htons(ETH_P_802_3);
-                       skb_reset_network_header(xmit_skb);
-                       skb_reset_mac_header(xmit_skb);
-                       dev_queue_xmit(xmit_skb);
-               }
-
-               if (!skb)
-                       return true;
-       }
-
-       /* deliver to local stack */
-       skb->protocol = eth_type_trans(skb, fast_rx->dev);
-       memset(skb->cb, 0, sizeof(skb->cb));
-       if (rx->list)
-               list_add_tail(&skb->list, rx->list);
-       else
-               netif_receive_skb(skb);
+       ieee80211_rx_8023(rx, fast_rx, orig_len);
 
        return true;
  drop:
        dev_kfree_skb(skb);
+       if (fast_rx->uses_rss)
+               stats = this_cpu_ptr(sta->pcpu_rx_stats);
+
        stats->dropped++;
        return true;
 }
@@ -4504,6 +4538,43 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
        return true;
 }
 
+static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
+                                      struct ieee80211_sta *pubsta,
+                                      struct sk_buff *skb,
+                                      struct list_head *list)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+       struct ieee80211_fast_rx *fast_rx;
+       struct ieee80211_rx_data rx;
+
+       memset(&rx, 0, sizeof(rx));
+       rx.skb = skb;
+       rx.local = local;
+       rx.list = list;
+
+       I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
+
+       /* drop frame if too short for header */
+       if (skb->len < sizeof(struct ethhdr))
+               goto drop;
+
+       if (!pubsta)
+               goto drop;
+
+       rx.sta = container_of(pubsta, struct sta_info, sta);
+       rx.sdata = rx.sta->sdata;
+
+       fast_rx = rcu_dereference(rx.sta->fast_rx);
+       if (!fast_rx)
+               goto drop;
+
+       ieee80211_rx_8023(&rx, fast_rx, skb->len);
+       return;
+
+drop:
+       dev_kfree_skb(skb);
+}
+
 /*
  * This is the actual Rx frames handler. as it belongs to Rx path it must
  * be called with rcu_read_lock protection.
@@ -4735,13 +4806,17 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
         * if it was previously present.
         * Also, frames with less than 16 bytes are dropped.
         */
-       skb = ieee80211_rx_monitor(local, skb, rate);
+       if (!(status->flag & RX_FLAG_8023))
+               skb = ieee80211_rx_monitor(local, skb, rate);
        if (skb) {
                ieee80211_tpt_led_trig_rx(local,
                                          ((struct ieee80211_hdr *)skb->data)->frame_control,
                                          skb->len);
 
-               __ieee80211_rx_handle_packet(hw, pubsta, skb, list);
+               if (status->flag & RX_FLAG_8023)
+                       __ieee80211_rx_handle_8023(hw, pubsta, skb, list);
+               else
+                       __ieee80211_rx_handle_packet(hw, pubsta, skb, list);
        }
 
        kcov_remote_stop();
index ae1cb2c..76747bf 100644 (file)
@@ -133,16 +133,20 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
        }
 
        if (wide_bw_chansw_ie) {
+               u8 new_seg1 = wide_bw_chansw_ie->new_center_freq_seg1;
                struct ieee80211_vht_operation vht_oper = {
                        .chan_width =
                                wide_bw_chansw_ie->new_channel_width,
                        .center_freq_seg0_idx =
                                wide_bw_chansw_ie->new_center_freq_seg0,
-                       .center_freq_seg1_idx =
-                               wide_bw_chansw_ie->new_center_freq_seg1,
+                       .center_freq_seg1_idx = new_seg1,
                        /* .basic_mcs_set doesn't matter */
                };
-               struct ieee80211_ht_operation ht_oper = {};
+               struct ieee80211_ht_operation ht_oper = {
+                       .operation_mode =
+                               cpu_to_le16(new_seg1 <<
+                                           IEEE80211_HT_OP_MODE_CCFS2_SHIFT),
+               };
 
                /* default, for the case of IEEE80211_VHT_CHANWIDTH_USE_HT,
                 * to the previously parsed chandef
index 7afd076..78b9d0c 100644 (file)
@@ -71,6 +71,7 @@
  *     until pending frames are delivered
  * @WLAN_STA_USES_ENCRYPTION: This station was configured for encryption,
  *     so drop all packets without a key later.
+ * @WLAN_STA_DECAP_OFFLOAD: This station uses rx decap offload
  *
  * @NUM_WLAN_STA_FLAGS: number of defined flags
  */
@@ -102,6 +103,7 @@ enum ieee80211_sta_info_flags {
        WLAN_STA_MPSP_RECIPIENT,
        WLAN_STA_PS_DELIVER,
        WLAN_STA_USES_ENCRYPTION,
+       WLAN_STA_DECAP_OFFLOAD,
 
        NUM_WLAN_STA_FLAGS,
 };
index e01e4da..f91d02b 100644 (file)
@@ -1927,7 +1927,7 @@ ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_tdls_data *tf = (void *)skb->data;
        struct wiphy *wiphy = sdata->local->hw.wiphy;
 
-       ASSERT_RTNL();
+       lockdep_assert_wiphy(wiphy);
 
        /* make sure the driver supports it */
        if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
@@ -1979,7 +1979,7 @@ void ieee80211_tdls_chsw_work(struct work_struct *wk)
        struct sk_buff *skb;
        struct ieee80211_tdls_data *tf;
 
-       rtnl_lock();
+       wiphy_lock(local->hw.wiphy);
        while ((skb = skb_dequeue(&local->skb_queue_tdls_chsw))) {
                tf = (struct ieee80211_tdls_data *)skb->data;
                list_for_each_entry(sdata, &local->interfaces, list) {
@@ -1994,7 +1994,7 @@ void ieee80211_tdls_chsw_work(struct work_struct *wk)
 
                kfree_skb(skb);
        }
-       rtnl_unlock();
+       wiphy_unlock(local->hw.wiphy);
 }
 
 void ieee80211_tdls_handle_disconnect(struct ieee80211_sub_if_data *sdata,
index 601322e..8fcc390 100644 (file)
@@ -2761,7 +2761,7 @@ DEFINE_EVENT(local_sdata_addr_evt, drv_update_vif_offload,
        TP_ARGS(local, sdata)
 );
 
-TRACE_EVENT(drv_sta_set_4addr,
+DECLARE_EVENT_CLASS(sta_flag_evt,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
                 struct ieee80211_sta *sta, bool enabled),
@@ -2788,6 +2788,22 @@ TRACE_EVENT(drv_sta_set_4addr,
        )
 );
 
+DEFINE_EVENT(sta_flag_evt, drv_sta_set_4addr,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_sta *sta, bool enabled),
+
+       TP_ARGS(local, sdata, sta, enabled)
+);
+
+DEFINE_EVENT(sta_flag_evt, drv_sta_set_decap_offload,
+       TP_PROTO(struct ieee80211_local *local,
+                struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_sta *sta, bool enabled),
+
+       TP_ARGS(local, sdata, sta, enabled)
+);
+
 #endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
 
 #undef TRACE_INCLUDE_PATH
index 6422da6..d626e68 100644 (file)
@@ -649,7 +649,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                if (!skip_hw && tx->key &&
                    tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
                        info->control.hw_key = &tx->key->conf;
-       } else if (!ieee80211_is_mgmt(hdr->frame_control) && tx->sta &&
+       } else if (ieee80211_is_data_present(hdr->frame_control) && tx->sta &&
                   test_sta_flag(tx->sta, WLAN_STA_USES_ENCRYPTION)) {
                return TX_DROP;
        }
@@ -1309,7 +1309,7 @@ static struct sk_buff *codel_dequeue_func(struct codel_vars *cvars,
        fq = &local->fq;
 
        if (cvars == &txqi->def_cvars)
-               flow = &txqi->def_flow;
+               flow = &txqi->tin.default_flow;
        else
                flow = &fq->flows[cvars - local->cvars];
 
@@ -1352,7 +1352,7 @@ static struct sk_buff *fq_tin_dequeue_func(struct fq *fq,
                cparams = &local->cparams;
        }
 
-       if (flow == &txqi->def_flow)
+       if (flow == &tin->default_flow)
                cvars = &txqi->def_cvars;
        else
                cvars = &local->cvars[flow - fq->flows];
@@ -1379,17 +1379,6 @@ static void fq_skb_free_func(struct fq *fq,
        ieee80211_free_txskb(&local->hw, skb);
 }
 
-static struct fq_flow *fq_flow_get_default_func(struct fq *fq,
-                                               struct fq_tin *tin,
-                                               int idx,
-                                               struct sk_buff *skb)
-{
-       struct txq_info *txqi;
-
-       txqi = container_of(tin, struct txq_info, tin);
-       return &txqi->def_flow;
-}
-
 static void ieee80211_txq_enqueue(struct ieee80211_local *local,
                                  struct txq_info *txqi,
                                  struct sk_buff *skb)
@@ -1402,8 +1391,7 @@ static void ieee80211_txq_enqueue(struct ieee80211_local *local,
 
        spin_lock_bh(&fq->lock);
        fq_tin_enqueue(fq, tin, flow_idx, skb,
-                      fq_skb_free_func,
-                      fq_flow_get_default_func);
+                      fq_skb_free_func);
        spin_unlock_bh(&fq->lock);
 }
 
@@ -1446,7 +1434,6 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata,
                        struct txq_info *txqi, int tid)
 {
        fq_tin_init(&txqi->tin);
-       fq_flow_init(&txqi->def_flow);
        codel_vars_init(&txqi->def_cvars);
        codel_stats_init(&txqi->cstats);
        __skb_queue_head_init(&txqi->frags);
@@ -2133,6 +2120,10 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb,
                        if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_BW &&
                            mcs_bw == IEEE80211_RADIOTAP_MCS_BW_40)
                                rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+
+                       if (mcs_known & IEEE80211_RADIOTAP_MCS_HAVE_FEC &&
+                           mcs_flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC)
+                               info->flags |= IEEE80211_TX_CTL_LDPC;
                        break;
 
                case IEEE80211_RADIOTAP_VHT:
@@ -3283,8 +3274,7 @@ static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
         */
 
        tin = &txqi->tin;
-       flow = fq_flow_classify(fq, tin, flow_idx, skb,
-                               fq_flow_get_default_func);
+       flow = fq_flow_classify(fq, tin, flow_idx, skb);
        head = skb_peek_tail(&flow->queue);
        if (!head || skb_is_gso(head))
                goto out;
@@ -3351,8 +3341,6 @@ out_recalc:
        if (head->len != orig_len) {
                flow->backlog += head->len - orig_len;
                tin->backlog_bytes += head->len - orig_len;
-
-               fq_recalc_backlog(fq, tin, flow);
        }
 out:
        spin_unlock_bh(&fq->lock);
@@ -3809,7 +3797,7 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
                 * get immediately moved to the back of the list on the next
                 * call to ieee80211_next_txq().
                 */
-               if (txqi->txq.sta &&
+               if (txqi->txq.sta && local->airtime_flags &&
                    wiphy_ext_feature_isset(local->hw.wiphy,
                                            NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
                        list_add(&txqi->schedule_order,
@@ -3823,6 +3811,8 @@ void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(__ieee80211_schedule_txq);
 
+DEFINE_STATIC_KEY_FALSE(aql_disable);
+
 bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
                                 struct ieee80211_txq *txq)
 {
@@ -3832,6 +3822,9 @@ bool ieee80211_txq_airtime_check(struct ieee80211_hw *hw,
        if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
                return true;
 
+       if (static_branch_unlikely(&aql_disable))
+               return true;
+
        if (!txq->sta)
                return true;
 
@@ -4251,7 +4244,6 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
        struct ethhdr *ehdr = (struct ethhdr *)skb->data;
        struct ieee80211_key *key;
        struct sta_info *sta;
-       bool offload = true;
 
        if (unlikely(skb->len < ETH_HLEN)) {
                kfree_skb(skb);
@@ -4267,18 +4259,22 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
 
        if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
            !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
-               sdata->control_port_protocol == ehdr->h_proto))
-               offload = false;
-       else if ((key = rcu_dereference(sta->ptk[sta->ptk_idx])) &&
-                (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) ||
-                 key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
-               offload = false;
-
-       if (offload)
-               ieee80211_8023_xmit(sdata, dev, sta, key, skb);
-       else
-               ieee80211_subif_start_xmit(skb, dev);
+           sdata->control_port_protocol == ehdr->h_proto))
+               goto skip_offload;
+
+       key = rcu_dereference(sta->ptk[sta->ptk_idx]);
+       if (!key)
+               key = rcu_dereference(sdata->default_unicast_key);
+
+       if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) ||
+                   key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
+               goto skip_offload;
+
+       ieee80211_8023_xmit(sdata, dev, sta, key, skb);
+       goto out;
 
+skip_offload:
+       ieee80211_subif_start_xmit(skb, dev);
 out:
        rcu_read_unlock();
 
index 8d3ae6b..f080fcf 100644 (file)
@@ -832,7 +832,7 @@ void ieee80211_iterate_active_interfaces_atomic(
 }
 EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
 
-void ieee80211_iterate_active_interfaces_rtnl(
+void ieee80211_iterate_active_interfaces_mtx(
        struct ieee80211_hw *hw, u32 iter_flags,
        void (*iterator)(void *data, u8 *mac,
                         struct ieee80211_vif *vif),
@@ -840,12 +840,12 @@ void ieee80211_iterate_active_interfaces_rtnl(
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       ASSERT_RTNL();
+       lockdep_assert_wiphy(hw->wiphy);
 
        __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE,
                             iterator, data);
 }
-EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
+EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_mtx);
 
 static void __iterate_stations(struct ieee80211_local *local,
                               void (*iterator)(void *data,
@@ -2595,7 +2595,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mutex_unlock(&local->mtx);
 
        if (sched_scan_stopped)
-               cfg80211_sched_scan_stopped_rtnl(local->hw.wiphy, 0);
+               cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0);
 
  wake_up:
 
@@ -3811,7 +3811,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
        struct cfg80211_chan_def chandef;
 
        /* for interface list, to avoid linking iflist_mtx and chanctx_mtx */
-       ASSERT_RTNL();
+       lockdep_assert_wiphy(local->hw.wiphy);
 
        mutex_lock(&local->mtx);
        list_for_each_entry(sdata, &local->interfaces, list) {
@@ -3851,9 +3851,9 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work)
        }
        mutex_unlock(&local->chanctx_mtx);
 
-       rtnl_lock();
+       wiphy_lock(local->hw.wiphy);
        ieee80211_dfs_cac_cancel(local);
-       rtnl_unlock();
+       wiphy_unlock(local->hw.wiphy);
 
        if (num_chanctx > 1)
                /* XXX: multi-channel is not supported yet */
index c3ca973..e856f90 100644 (file)
@@ -484,6 +484,7 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
 void ieee80211_sta_set_rx_nss(struct sta_info *sta)
 {
        u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, rx_nss;
+       bool support_160;
 
        /* if we received a notification already don't overwrite it */
        if (sta->sta.rx_nss)
@@ -514,7 +515,13 @@ void ieee80211_sta_set_rx_nss(struct sta_info *sta)
                        }
                }
 
-               he_rx_nss = min(rx_mcs_80, rx_mcs_160);
+               support_160 = he_cap->he_cap_elem.phy_cap_info[0] &
+                             IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+               if (support_160)
+                       he_rx_nss = min(rx_mcs_80, rx_mcs_160);
+               else
+                       he_rx_nss = rx_mcs_80;
        }
 
        if (sta->sta.ht_cap.ht_supported) {
index b921cbd..8ca1964 100644 (file)
@@ -31,6 +31,8 @@ static const struct snmp_mib mptcp_snmp_list[] = {
        SNMP_MIB_ITEM("EchoAdd", MPTCP_MIB_ECHOADD),
        SNMP_MIB_ITEM("RmAddr", MPTCP_MIB_RMADDR),
        SNMP_MIB_ITEM("RmSubflow", MPTCP_MIB_RMSUBFLOW),
+       SNMP_MIB_ITEM("MPPrioTx", MPTCP_MIB_MPPRIOTX),
+       SNMP_MIB_ITEM("MPPrioRx", MPTCP_MIB_MPPRIORX),
        SNMP_MIB_SENTINEL
 };
 
index 47bcecc..63914a5 100644 (file)
@@ -24,6 +24,8 @@ enum linux_mptcp_mib_field {
        MPTCP_MIB_ECHOADD,              /* Received ADD_ADDR with echo-flag=1 */
        MPTCP_MIB_RMADDR,               /* Received RM_ADDR */
        MPTCP_MIB_RMSUBFLOW,            /* Remove a subflow */
+       MPTCP_MIB_MPPRIOTX,             /* Transmit a MP_PRIO */
+       MPTCP_MIB_MPPRIORX,             /* Received a MP_PRIO */
        __MPTCP_MIB_MAX
 };
 
index e0d21c0..c964334 100644 (file)
@@ -282,6 +282,15 @@ static void mptcp_parse_option(const struct sk_buff *skb,
                pr_debug("RM_ADDR: id=%d", mp_opt->rm_id);
                break;
 
+       case MPTCPOPT_MP_PRIO:
+               if (opsize != TCPOLEN_MPTCP_PRIO)
+                       break;
+
+               mp_opt->mp_prio = 1;
+               mp_opt->backup = *ptr++ & MPTCP_PRIO_BKUP;
+               pr_debug("MP_PRIO: prio=%d", mp_opt->backup);
+               break;
+
        case MPTCPOPT_MP_FASTCLOSE:
                if (opsize != TCPOLEN_MPTCP_FASTCLOSE)
                        break;
@@ -313,6 +322,7 @@ void mptcp_get_options(const struct sk_buff *skb,
        mp_opt->port = 0;
        mp_opt->rm_addr = 0;
        mp_opt->dss = 0;
+       mp_opt->mp_prio = 0;
 
        length = (th->doff * 4) - sizeof(struct tcphdr);
        ptr = (const unsigned char *)(th + 1);
@@ -679,6 +689,28 @@ static bool mptcp_established_options_rm_addr(struct sock *sk,
        return true;
 }
 
+static bool mptcp_established_options_mp_prio(struct sock *sk,
+                                             unsigned int *size,
+                                             unsigned int remaining,
+                                             struct mptcp_out_options *opts)
+{
+       struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
+
+       if (!subflow->send_mp_prio)
+               return false;
+
+       if (remaining < TCPOLEN_MPTCP_PRIO)
+               return false;
+
+       *size = TCPOLEN_MPTCP_PRIO;
+       opts->suboptions |= OPTION_MPTCP_PRIO;
+       opts->backup = subflow->request_bkup;
+
+       pr_debug("prio=%d", opts->backup);
+
+       return true;
+}
+
 bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
                               unsigned int *size, unsigned int remaining,
                               struct mptcp_out_options *opts)
@@ -721,6 +753,12 @@ bool mptcp_established_options(struct sock *sk, struct sk_buff *skb,
                ret = true;
        }
 
+       if (mptcp_established_options_mp_prio(sk, &opt_size, remaining, opts)) {
+               *size += opt_size;
+               remaining -= opt_size;
+               ret = true;
+       }
+
        return ret;
 }
 
@@ -994,6 +1032,12 @@ void mptcp_incoming_options(struct sock *sk, struct sk_buff *skb)
                mp_opt.rm_addr = 0;
        }
 
+       if (mp_opt.mp_prio) {
+               mptcp_pm_mp_prio_received(sk, mp_opt.backup);
+               MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPPRIORX);
+               mp_opt.mp_prio = 0;
+       }
+
        if (!mp_opt.dss)
                return;
 
@@ -1168,6 +1212,18 @@ mp_capable_done:
                                      0, opts->rm_id);
        }
 
+       if (OPTION_MPTCP_PRIO & opts->suboptions) {
+               const struct sock *ssk = (const struct sock *)tp;
+               struct mptcp_subflow_context *subflow;
+
+               subflow = mptcp_subflow_ctx(ssk);
+               subflow->send_mp_prio = 0;
+
+               *ptr++ = mptcp_option(MPTCPOPT_MP_PRIO,
+                                     TCPOLEN_MPTCP_PRIO,
+                                     opts->backup, TCPOPT_NOP);
+       }
+
        if (OPTION_MPTCP_MPJ_SYN & opts->suboptions) {
                *ptr++ = mptcp_option(MPTCPOPT_MP_JOIN,
                                      TCPOLEN_MPTCP_MPJ_SYN,
index da2ed57..0a6ebd0 100644 (file)
@@ -207,6 +207,14 @@ void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, u8 rm_id)
        spin_unlock_bh(&pm->lock);
 }
 
+void mptcp_pm_mp_prio_received(struct sock *sk, u8 bkup)
+{
+       struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(sk);
+
+       pr_debug("subflow->backup=%d, bkup=%d\n", subflow->backup, bkup);
+       subflow->backup = bkup;
+}
+
 /* path manager helpers */
 
 bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int remaining,
index a6d983d..83976b9 100644 (file)
@@ -36,6 +36,9 @@ struct mptcp_pm_add_entry {
        u8                      retrans_times;
 };
 
+#define MAX_ADDR_ID            255
+#define BITMAP_SZ DIV_ROUND_UP(MAX_ADDR_ID + 1, BITS_PER_LONG)
+
 struct pm_nl_pernet {
        /* protects pernet updates */
        spinlock_t              lock;
@@ -46,6 +49,7 @@ struct pm_nl_pernet {
        unsigned int            local_addr_max;
        unsigned int            subflows_max;
        unsigned int            next_id;
+       unsigned long           id_bitmap[BITMAP_SZ];
 };
 
 #define MPTCP_PM_ADDR_MAX      8
@@ -56,15 +60,20 @@ static bool addresses_equal(const struct mptcp_addr_info *a,
 {
        bool addr_equals = false;
 
-       if (a->family != b->family)
-               return false;
-
-       if (a->family == AF_INET)
-               addr_equals = a->addr.s_addr == b->addr.s_addr;
+       if (a->family == b->family) {
+               if (a->family == AF_INET)
+                       addr_equals = a->addr.s_addr == b->addr.s_addr;
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-       else
-               addr_equals = !ipv6_addr_cmp(&a->addr6, &b->addr6);
+               else
+                       addr_equals = !ipv6_addr_cmp(&a->addr6, &b->addr6);
+       } else if (a->family == AF_INET) {
+               if (ipv6_addr_v4mapped(&b->addr6))
+                       addr_equals = a->addr.s_addr == b->addr6.s6_addr32[3];
+       } else if (b->family == AF_INET) {
+               if (ipv6_addr_v4mapped(&a->addr6))
+                       addr_equals = a->addr6.s6_addr32[3] == b->addr.s_addr;
 #endif
+       }
 
        if (!addr_equals)
                return false;
@@ -133,6 +142,7 @@ select_local_address(const struct pm_nl_pernet *pernet,
                     struct mptcp_sock *msk)
 {
        struct mptcp_pm_addr_entry *entry, *ret = NULL;
+       struct sock *sk = (struct sock *)msk;
 
        rcu_read_lock();
        __mptcp_flush_join_list(msk);
@@ -140,11 +150,20 @@ select_local_address(const struct pm_nl_pernet *pernet,
                if (!(entry->addr.flags & MPTCP_PM_ADDR_FLAG_SUBFLOW))
                        continue;
 
+               if (entry->addr.family != sk->sk_family) {
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+                       if ((entry->addr.family == AF_INET &&
+                            !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) ||
+                           (sk->sk_family == AF_INET &&
+                            !ipv6_addr_v4mapped(&entry->addr.addr6)))
+#endif
+                               continue;
+               }
+
                /* avoid any address already in use by subflows and
                 * pending join
                 */
-               if (entry->addr.family == ((struct sock *)msk)->sk_family &&
-                   !lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) {
+               if (!lookup_subflow_by_saddr(&msk->conn_list, &entry->addr)) {
                        ret = entry;
                        break;
                }
@@ -306,7 +325,6 @@ void mptcp_pm_free_anno_list(struct mptcp_sock *msk)
 
 static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
 {
-       struct mptcp_addr_info remote = { 0 };
        struct sock *sk = (struct sock *)msk;
        struct mptcp_pm_addr_entry *local;
        struct pm_nl_pernet *pernet;
@@ -340,13 +358,14 @@ static void mptcp_pm_create_subflow_or_signal_addr(struct mptcp_sock *msk)
        /* check if should create a new subflow */
        if (msk->pm.local_addr_used < msk->pm.local_addr_max &&
            msk->pm.subflows < msk->pm.subflows_max) {
-               remote_address((struct sock_common *)sk, &remote);
-
                local = select_local_address(pernet, msk);
                if (local) {
+                       struct mptcp_addr_info remote = { 0 };
+
                        msk->pm.local_addr_used++;
                        msk->pm.subflows++;
                        check_work_pending(msk);
+                       remote_address((struct sock_common *)sk, &remote);
                        spin_unlock_bh(&msk->pm.lock);
                        __mptcp_subflow_connect(sk, &local->addr, &remote);
                        spin_lock_bh(&msk->pm.lock);
@@ -438,6 +457,41 @@ void mptcp_pm_nl_add_addr_send_ack(struct mptcp_sock *msk)
        }
 }
 
+int mptcp_pm_nl_mp_prio_send_ack(struct mptcp_sock *msk,
+                                struct mptcp_addr_info *addr,
+                                u8 bkup)
+{
+       struct mptcp_subflow_context *subflow;
+
+       pr_debug("bkup=%d", bkup);
+
+       mptcp_for_each_subflow(msk, subflow) {
+               struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+               struct sock *sk = (struct sock *)msk;
+               struct mptcp_addr_info local;
+
+               local_address((struct sock_common *)ssk, &local);
+               if (!addresses_equal(&local, addr, addr->port))
+                       continue;
+
+               subflow->backup = bkup;
+               subflow->send_mp_prio = 1;
+               subflow->request_bkup = bkup;
+               __MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_MPPRIOTX);
+
+               spin_unlock_bh(&msk->pm.lock);
+               pr_debug("send ack for mp_prio");
+               lock_sock(ssk);
+               tcp_send_ack(ssk);
+               release_sock(ssk);
+               spin_lock_bh(&msk->pm.lock);
+
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
 void mptcp_pm_nl_rm_addr_received(struct mptcp_sock *msk)
 {
        struct mptcp_subflow_context *subflow, *tmp;
@@ -524,10 +578,12 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
        /* to keep the code simple, don't do IDR-like allocation for address ID,
         * just bail when we exceed limits
         */
-       if (pernet->next_id > 255)
-               goto out;
+       if (pernet->next_id == MAX_ADDR_ID)
+               pernet->next_id = 1;
        if (pernet->addrs >= MPTCP_PM_ADDR_MAX)
                goto out;
+       if (test_bit(entry->addr.id, pernet->id_bitmap))
+               goto out;
 
        /* do not insert duplicate address, differentiate on port only
         * singled addresses
@@ -539,12 +595,30 @@ static int mptcp_pm_nl_append_new_local_addr(struct pm_nl_pernet *pernet,
                        goto out;
        }
 
+       if (!entry->addr.id) {
+find_next:
+               entry->addr.id = find_next_zero_bit(pernet->id_bitmap,
+                                                   MAX_ADDR_ID + 1,
+                                                   pernet->next_id);
+               if ((!entry->addr.id || entry->addr.id > MAX_ADDR_ID) &&
+                   pernet->next_id != 1) {
+                       pernet->next_id = 1;
+                       goto find_next;
+               }
+       }
+
+       if (!entry->addr.id || entry->addr.id > MAX_ADDR_ID)
+               goto out;
+
+       __set_bit(entry->addr.id, pernet->id_bitmap);
+       if (entry->addr.id > pernet->next_id)
+               pernet->next_id = entry->addr.id;
+
        if (entry->addr.flags & MPTCP_PM_ADDR_FLAG_SIGNAL)
                pernet->add_addr_signal_max++;
        if (entry->addr.flags & MPTCP_PM_ADDR_FLAG_SUBFLOW)
                pernet->local_addr_max++;
 
-       entry->addr.id = pernet->next_id++;
        pernet->addrs++;
        list_add_tail_rcu(&entry->list, &pernet->local_addr_list);
        ret = entry->addr.id;
@@ -597,6 +671,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct sock_common *skc)
        entry->addr = skc_local;
        entry->addr.ifindex = 0;
        entry->addr.flags = 0;
+       entry->addr.id = 0;
        ret = mptcp_pm_nl_append_new_local_addr(pernet, entry);
        if (ret < 0)
                kfree(entry);
@@ -857,6 +932,7 @@ static int mptcp_nl_cmd_del_addr(struct sk_buff *skb, struct genl_info *info)
 
        pernet->addrs--;
        list_del_rcu(&entry->list);
+       __clear_bit(entry->addr.id, pernet->id_bitmap);
        spin_unlock_bh(&pernet->lock);
 
        mptcp_nl_remove_subflow_and_signal_addr(sock_net(skb->sk), &entry->addr);
@@ -894,6 +970,8 @@ static int mptcp_nl_cmd_flush_addrs(struct sk_buff *skb, struct genl_info *info)
        spin_lock_bh(&pernet->lock);
        list_splice_init(&pernet->local_addr_list, &free_list);
        __reset_counters(pernet);
+       pernet->next_id = 1;
+       bitmap_zero(pernet->id_bitmap, MAX_ADDR_ID + 1);
        spin_unlock_bh(&pernet->lock);
        __flush_addrs(sock_net(skb->sk), &free_list);
        return 0;
@@ -994,27 +1072,34 @@ static int mptcp_nl_cmd_dump_addrs(struct sk_buff *msg,
        struct pm_nl_pernet *pernet;
        int id = cb->args[0];
        void *hdr;
+       int i;
 
        pernet = net_generic(net, pm_nl_pernet_id);
 
        spin_lock_bh(&pernet->lock);
-       list_for_each_entry(entry, &pernet->local_addr_list, list) {
-               if (entry->addr.id <= id)
-                       continue;
-
-               hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid,
-                                 cb->nlh->nlmsg_seq, &mptcp_genl_family,
-                                 NLM_F_MULTI, MPTCP_PM_CMD_GET_ADDR);
-               if (!hdr)
-                       break;
+       for (i = id; i < MAX_ADDR_ID + 1; i++) {
+               if (test_bit(i, pernet->id_bitmap)) {
+                       entry = __lookup_addr_by_id(pernet, i);
+                       if (!entry)
+                               break;
+
+                       if (entry->addr.id <= id)
+                               continue;
+
+                       hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).portid,
+                                         cb->nlh->nlmsg_seq, &mptcp_genl_family,
+                                         NLM_F_MULTI, MPTCP_PM_CMD_GET_ADDR);
+                       if (!hdr)
+                               break;
+
+                       if (mptcp_nl_fill_addr(msg, entry) < 0) {
+                               genlmsg_cancel(msg, hdr);
+                               break;
+                       }
 
-               if (mptcp_nl_fill_addr(msg, entry) < 0) {
-                       genlmsg_cancel(msg, hdr);
-                       break;
+                       id = entry->addr.id;
+                       genlmsg_end(msg, hdr);
                }
-
-               id = entry->addr.id;
-               genlmsg_end(msg, hdr);
        }
        spin_unlock_bh(&pernet->lock);
 
@@ -1096,6 +1181,66 @@ fail:
        return -EMSGSIZE;
 }
 
+static int mptcp_nl_addr_backup(struct net *net,
+                               struct mptcp_addr_info *addr,
+                               u8 bkup)
+{
+       long s_slot = 0, s_num = 0;
+       struct mptcp_sock *msk;
+       int ret = -EINVAL;
+
+       while ((msk = mptcp_token_iter_next(net, &s_slot, &s_num)) != NULL) {
+               struct sock *sk = (struct sock *)msk;
+
+               if (list_empty(&msk->conn_list))
+                       goto next;
+
+               lock_sock(sk);
+               spin_lock_bh(&msk->pm.lock);
+               ret = mptcp_pm_nl_mp_prio_send_ack(msk, addr, bkup);
+               spin_unlock_bh(&msk->pm.lock);
+               release_sock(sk);
+
+next:
+               sock_put(sk);
+               cond_resched();
+       }
+
+       return ret;
+}
+
+static int mptcp_nl_cmd_set_flags(struct sk_buff *skb, struct genl_info *info)
+{
+       struct nlattr *attr = info->attrs[MPTCP_PM_ATTR_ADDR];
+       struct pm_nl_pernet *pernet = genl_info_pm_nl(info);
+       struct mptcp_pm_addr_entry addr, *entry;
+       struct net *net = sock_net(skb->sk);
+       u8 bkup = 0;
+       int ret;
+
+       ret = mptcp_pm_parse_addr(attr, info, true, &addr);
+       if (ret < 0)
+               return ret;
+
+       if (addr.addr.flags & MPTCP_PM_ADDR_FLAG_BACKUP)
+               bkup = 1;
+
+       list_for_each_entry(entry, &pernet->local_addr_list, list) {
+               if (addresses_equal(&entry->addr, &addr.addr, true)) {
+                       ret = mptcp_nl_addr_backup(net, &entry->addr, bkup);
+                       if (ret)
+                               return ret;
+
+                       if (bkup)
+                               entry->addr.flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
+                       else
+                               entry->addr.flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP;
+               }
+       }
+
+       return 0;
+}
+
 static const struct genl_small_ops mptcp_pm_ops[] = {
        {
                .cmd    = MPTCP_PM_CMD_ADD_ADDR,
@@ -1126,6 +1271,11 @@ static const struct genl_small_ops mptcp_pm_ops[] = {
                .cmd    = MPTCP_PM_CMD_GET_LIMITS,
                .doit   = mptcp_nl_cmd_get_limits,
        },
+       {
+               .cmd    = MPTCP_PM_CMD_SET_FLAGS,
+               .doit   = mptcp_nl_cmd_set_flags,
+               .flags  = GENL_ADMIN_PERM,
+       },
 };
 
 static struct genl_family mptcp_genl_family __ro_after_init = {
@@ -1148,6 +1298,7 @@ static int __net_init pm_nl_init_net(struct net *net)
        INIT_LIST_HEAD_RCU(&pernet->local_addr_list);
        __reset_counters(pernet);
        pernet->next_id = 1;
+       bitmap_zero(pernet->id_bitmap, MAX_ADDR_ID + 1);
        spin_lock_init(&pernet->lock);
        return 0;
 }
index 09b19aa..a033bf9 100644 (file)
@@ -45,6 +45,9 @@ static struct percpu_counter mptcp_sockets_allocated;
 static void __mptcp_destroy_sock(struct sock *sk);
 static void __mptcp_check_send_data_fin(struct sock *sk);
 
+DEFINE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions);
+static struct net_device mptcp_napi_dev;
+
 /* If msk has an initial subflow socket, and the MP_CAPABLE handshake has not
  * completed yet or has failed, return the subflow socket.
  * Otherwise return NULL.
@@ -114,11 +117,7 @@ static int __mptcp_socket_create(struct mptcp_sock *msk)
        list_add(&subflow->node, &msk->conn_list);
        sock_hold(ssock->sk);
        subflow->request_mptcp = 1;
-
-       /* accept() will wait on first subflow sk_wq, and we always wakes up
-        * via msk->sk_socket
-        */
-       RCU_INIT_POINTER(msk->first->sk_wq, &sk->sk_socket->wq);
+       mptcp_sock_graft(msk->first, sk->sk_socket);
 
        return 0;
 }
@@ -427,7 +426,7 @@ static bool mptcp_subflow_active(struct mptcp_subflow_context *subflow)
 static bool tcp_can_send_ack(const struct sock *ssk)
 {
        return !((1 << inet_sk_state_load(ssk)) &
-              (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_TIME_WAIT | TCPF_CLOSE));
+              (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_TIME_WAIT | TCPF_CLOSE | TCPF_LISTEN));
 }
 
 static void mptcp_send_ack(struct mptcp_sock *msk)
@@ -734,10 +733,14 @@ wake:
 
 void __mptcp_flush_join_list(struct mptcp_sock *msk)
 {
+       struct mptcp_subflow_context *subflow;
+
        if (likely(list_empty(&msk->join_list)))
                return;
 
        spin_lock_bh(&msk->join_list_lock);
+       list_for_each_entry(subflow, &msk->join_list, node)
+               mptcp_propagate_sndbuf((struct sock *)msk, mptcp_subflow_tcp_sock(subflow));
        list_splice_tail_init(&msk->join_list, &msk->conn_list);
        spin_unlock_bh(&msk->join_list_lock);
 }
@@ -877,6 +880,9 @@ static void __mptcp_wmem_reserve(struct sock *sk, int size)
        struct mptcp_sock *msk = mptcp_sk(sk);
 
        WARN_ON_ONCE(msk->wmem_reserved);
+       if (WARN_ON_ONCE(amount < 0))
+               amount = 0;
+
        if (amount <= sk->sk_forward_alloc)
                goto reserve;
 
@@ -1034,13 +1040,6 @@ out:
                        __mptcp_update_wmem(sk);
                        sk_mem_reclaim_partial(sk);
                }
-
-               if (sk_stream_is_writeable(sk)) {
-                       /* pairs with memory barrier in mptcp_poll */
-                       smp_mb();
-                       if (test_and_clear_bit(MPTCP_NOSPACE, &msk->flags))
-                               sk_stream_write_space(sk);
-               }
        }
 
        if (snd_una == READ_ONCE(msk->snd_nxt)) {
@@ -1359,8 +1358,7 @@ struct subflow_send_info {
        u64 ratio;
 };
 
-static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk,
-                                          u32 *sndbuf)
+static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk)
 {
        struct subflow_send_info send_info[2];
        struct mptcp_subflow_context *subflow;
@@ -1371,24 +1369,17 @@ static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk,
 
        sock_owned_by_me((struct sock *)msk);
 
-       *sndbuf = 0;
        if (__mptcp_check_fallback(msk)) {
                if (!msk->first)
                        return NULL;
-               *sndbuf = msk->first->sk_sndbuf;
                return sk_stream_memory_free(msk->first) ? msk->first : NULL;
        }
 
        /* re-use last subflow, if the burst allow that */
        if (msk->last_snd && msk->snd_burst > 0 &&
            sk_stream_memory_free(msk->last_snd) &&
-           mptcp_subflow_active(mptcp_subflow_ctx(msk->last_snd))) {
-               mptcp_for_each_subflow(msk, subflow) {
-                       ssk =  mptcp_subflow_tcp_sock(subflow);
-                       *sndbuf = max(tcp_sk(ssk)->snd_wnd, *sndbuf);
-               }
+           mptcp_subflow_active(mptcp_subflow_ctx(msk->last_snd)))
                return msk->last_snd;
-       }
 
        /* pick the subflow with the lower wmem/wspace ratio */
        for (i = 0; i < 2; ++i) {
@@ -1401,8 +1392,7 @@ static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk,
                        continue;
 
                nr_active += !subflow->backup;
-               *sndbuf = max(tcp_sk(ssk)->snd_wnd, *sndbuf);
-               if (!sk_stream_memory_free(subflow->tcp_sock))
+               if (!sk_stream_memory_free(subflow->tcp_sock) || !tcp_sk(ssk)->snd_wnd)
                        continue;
 
                pace = READ_ONCE(ssk->sk_pacing_rate);
@@ -1428,9 +1418,10 @@ static struct sock *mptcp_subflow_get_send(struct mptcp_sock *msk,
        if (send_info[0].ssk) {
                msk->last_snd = send_info[0].ssk;
                msk->snd_burst = min_t(int, MPTCP_SEND_BURST_SIZE,
-                                      sk_stream_wspace(msk->last_snd));
+                                      tcp_sk(msk->last_snd)->snd_wnd);
                return msk->last_snd;
        }
+
        return NULL;
 }
 
@@ -1451,7 +1442,6 @@ static void mptcp_push_pending(struct sock *sk, unsigned int flags)
        };
        struct mptcp_data_frag *dfrag;
        int len, copied = 0;
-       u32 sndbuf;
 
        while ((dfrag = mptcp_send_head(sk))) {
                info.sent = dfrag->already_sent;
@@ -1462,12 +1452,7 @@ static void mptcp_push_pending(struct sock *sk, unsigned int flags)
 
                        prev_ssk = ssk;
                        __mptcp_flush_join_list(msk);
-                       ssk = mptcp_subflow_get_send(msk, &sndbuf);
-
-                       /* do auto tuning */
-                       if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK) &&
-                           sndbuf > READ_ONCE(sk->sk_sndbuf))
-                               WRITE_ONCE(sk->sk_sndbuf, sndbuf);
+                       ssk = mptcp_subflow_get_send(msk);
 
                        /* try to keep the subflow socket lock across
                         * consecutive xmit on the same socket
@@ -1524,7 +1509,9 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk)
        struct mptcp_sock *msk = mptcp_sk(sk);
        struct mptcp_sendmsg_info info;
        struct mptcp_data_frag *dfrag;
+       struct sock *xmit_ssk;
        int len, copied = 0;
+       bool first = true;
 
        info.flags = 0;
        while ((dfrag = mptcp_send_head(sk))) {
@@ -1534,10 +1521,17 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk)
                while (len > 0) {
                        int ret = 0;
 
-                       /* do auto tuning */
-                       if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK) &&
-                           ssk->sk_sndbuf > READ_ONCE(sk->sk_sndbuf))
-                               WRITE_ONCE(sk->sk_sndbuf, ssk->sk_sndbuf);
+                       /* the caller already invoked the packet scheduler,
+                        * check for a different subflow usage only after
+                        * spooling the first chunk of data
+                        */
+                       xmit_ssk = first ? ssk : mptcp_subflow_get_send(mptcp_sk(sk));
+                       if (!xmit_ssk)
+                               goto out;
+                       if (xmit_ssk != ssk) {
+                               mptcp_subflow_delegate(mptcp_subflow_ctx(xmit_ssk));
+                               goto out;
+                       }
 
                        if (unlikely(mptcp_must_reclaim_memory(sk, ssk))) {
                                __mptcp_update_wmem(sk);
@@ -1557,6 +1551,7 @@ static void __mptcp_subflow_push_pending(struct sock *sk, struct sock *ssk)
                        msk->tx_pending_data -= ret;
                        copied += ret;
                        len -= ret;
+                       first = false;
                }
                WRITE_ONCE(msk->first_pending, mptcp_send_next(sk));
        }
@@ -1576,6 +1571,15 @@ out:
        }
 }
 
+static void mptcp_set_nospace(struct sock *sk)
+{
+       /* enable autotune */
+       set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
+
+       /* will be cleared on avail space */
+       set_bit(MPTCP_NOSPACE, &mptcp_sk(sk)->flags);
+}
+
 static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 {
        struct mptcp_sock *msk = mptcp_sk(sk);
@@ -1587,7 +1591,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
        if (msg->msg_flags & ~(MSG_MORE | MSG_DONTWAIT | MSG_NOSIGNAL))
                return -EOPNOTSUPP;
 
-       mptcp_lock_sock(sk, __mptcp_wmem_reserve(sk, len));
+       mptcp_lock_sock(sk, __mptcp_wmem_reserve(sk, min_t(size_t, 1 << 20, len)));
 
        timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
 
@@ -1677,7 +1681,7 @@ static int mptcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
                continue;
 
 wait_for_memory:
-               set_bit(MPTCP_NOSPACE, &msk->flags);
+               mptcp_set_nospace(sk);
                mptcp_push_pending(sk, msg->msg_flags);
                ret = sk_stream_wait_memory(sk, &timeo);
                if (ret)
@@ -2113,9 +2117,6 @@ static struct sock *mptcp_subflow_get_retrans(const struct mptcp_sock *msk)
 void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
                       struct mptcp_subflow_context *subflow)
 {
-       bool dispose_socket = false;
-       struct socket *sock;
-
        list_del(&subflow->node);
 
        lock_sock_nested(ssk, SINGLE_DEPTH_NESTING);
@@ -2123,11 +2124,8 @@ void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
        /* if we are invoked by the msk cleanup code, the subflow is
         * already orphaned
         */
-       sock = ssk->sk_socket;
-       if (sock) {
-               dispose_socket = sock != sk->sk_socket;
+       if (ssk->sk_socket)
                sock_orphan(ssk);
-       }
 
        subflow->disposable = 1;
 
@@ -2145,8 +2143,6 @@ void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
                __sock_put(ssk);
        }
        release_sock(ssk);
-       if (dispose_socket)
-               iput(SOCK_INODE(sock));
 
        sock_put(ssk);
 }
@@ -2533,6 +2529,12 @@ static void __mptcp_destroy_sock(struct sock *sk)
 
        pr_debug("msk=%p", msk);
 
+       /* dispose the ancillatory tcp socket, if any */
+       if (msk->subflow) {
+               iput(SOCK_INODE(msk->subflow));
+               msk->subflow = NULL;
+       }
+
        /* be sure to always acquire the join list lock, to sync vs
         * mptcp_finish_join().
         */
@@ -2583,20 +2585,10 @@ cleanup:
        inet_csk(sk)->icsk_mtup.probe_timestamp = tcp_jiffies32;
        list_for_each_entry(subflow, &mptcp_sk(sk)->conn_list, node) {
                struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
-               bool slow, dispose_socket;
-               struct socket *sock;
+               bool slow = lock_sock_fast(ssk);
 
-               slow = lock_sock_fast(ssk);
-               sock = ssk->sk_socket;
-               dispose_socket = sock && sock != sk->sk_socket;
                sock_orphan(ssk);
                unlock_sock_fast(ssk, slow);
-
-               /* for the outgoing subflows we additionally need to free
-                * the associated socket
-                */
-               if (dispose_socket)
-                       iput(SOCK_INODE(sock));
        }
        sock_orphan(sk);
 
@@ -2639,11 +2631,17 @@ static void mptcp_copy_inaddrs(struct sock *msk, const struct sock *ssk)
 
 static int mptcp_disconnect(struct sock *sk, int flags)
 {
-       /* Should never be called.
-        * inet_stream_connect() calls ->disconnect, but that
-        * refers to the subflow socket, not the mptcp one.
-        */
-       WARN_ON_ONCE(1);
+       struct mptcp_subflow_context *subflow;
+       struct mptcp_sock *msk = mptcp_sk(sk);
+
+       __mptcp_flush_join_list(msk);
+       mptcp_for_each_subflow(msk, subflow) {
+               struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+               lock_sock(ssk);
+               tcp_disconnect(ssk, flags);
+               release_sock(ssk);
+       }
        return 0;
 }
 
@@ -2919,10 +2917,16 @@ void __mptcp_check_push(struct sock *sk, struct sock *ssk)
        if (!mptcp_send_head(sk))
                return;
 
-       if (!sock_owned_by_user(sk))
-               __mptcp_subflow_push_pending(sk, ssk);
-       else
+       if (!sock_owned_by_user(sk)) {
+               struct sock *xmit_ssk = mptcp_subflow_get_send(mptcp_sk(sk));
+
+               if (xmit_ssk == ssk)
+                       __mptcp_subflow_push_pending(sk, ssk);
+               else if (xmit_ssk)
+                       mptcp_subflow_delegate(mptcp_subflow_ctx(xmit_ssk));
+       } else {
                set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags);
+       }
 }
 
 #define MPTCP_DEFERRED_ALL (TCPF_WRITE_TIMER_DEFERRED)
@@ -2970,6 +2974,20 @@ static void mptcp_release_cb(struct sock *sk)
        }
 }
 
+void mptcp_subflow_process_delegated(struct sock *ssk)
+{
+       struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+       struct sock *sk = subflow->conn;
+
+       mptcp_data_lock(sk);
+       if (!sock_owned_by_user(sk))
+               __mptcp_subflow_push_pending(sk, ssk);
+       else
+               set_bit(MPTCP_PUSH_PENDING, &mptcp_sk(sk)->flags);
+       mptcp_data_unlock(sk);
+       mptcp_subflow_delegated_done(subflow);
+}
+
 static int mptcp_hash(struct sock *sk)
 {
        /* should never be called,
@@ -3032,7 +3050,7 @@ void mptcp_finish_connect(struct sock *ssk)
        mptcp_rcv_space_init(msk, ssk);
 }
 
-static void mptcp_sock_graft(struct sock *sk, struct socket *parent)
+void mptcp_sock_graft(struct sock *sk, struct socket *parent)
 {
        write_lock_bh(&sk->sk_callback_lock);
        rcu_assign_pointer(sk->sk_wq, &parent->wq);
@@ -3086,6 +3104,14 @@ bool mptcp_finish_join(struct sock *ssk)
        return true;
 }
 
+static void mptcp_shutdown(struct sock *sk, int how)
+{
+       pr_debug("sk=%p, how=%d", sk, how);
+
+       if ((how & SEND_SHUTDOWN) && mptcp_close_state(sk))
+               __mptcp_wr_shutdown(sk);
+}
+
 static struct proto mptcp_prot = {
        .name           = "MPTCP",
        .owner          = THIS_MODULE,
@@ -3095,7 +3121,7 @@ static struct proto mptcp_prot = {
        .accept         = mptcp_accept,
        .setsockopt     = mptcp_setsockopt,
        .getsockopt     = mptcp_getsockopt,
-       .shutdown       = tcp_shutdown,
+       .shutdown       = mptcp_shutdown,
        .destroy        = mptcp_destroy,
        .sendmsg        = mptcp_sendmsg,
        .recvmsg        = mptcp_recvmsg,
@@ -3267,6 +3293,7 @@ static int mptcp_stream_accept(struct socket *sock, struct socket *newsock,
 
                mptcp_copy_inaddrs(newsk, msk->first);
                mptcp_rcv_space_init(msk, msk->first);
+               mptcp_propagate_sndbuf(newsk, msk->first);
 
                /* set ssk->sk_socket of accept()ed flows to mptcp socket.
                 * This is needed so NOSPACE flag can be set from tcp stack.
@@ -3307,7 +3334,7 @@ static __poll_t mptcp_check_writeable(struct mptcp_sock *msk)
        if (sk_stream_is_writeable(sk))
                return EPOLLOUT | EPOLLWRNORM;
 
-       set_bit(MPTCP_NOSPACE, &msk->flags);
+       mptcp_set_nospace(sk);
        smp_mb__after_atomic(); /* msk->flags is changed by write_space cb */
        if (sk_stream_is_writeable(sk))
                return EPOLLOUT | EPOLLWRNORM;
@@ -3341,43 +3368,6 @@ static __poll_t mptcp_poll(struct file *file, struct socket *sock,
        return mask;
 }
 
-static int mptcp_shutdown(struct socket *sock, int how)
-{
-       struct mptcp_sock *msk = mptcp_sk(sock->sk);
-       struct sock *sk = sock->sk;
-       int ret = 0;
-
-       pr_debug("sk=%p, how=%d", msk, how);
-
-       lock_sock(sk);
-
-       how++;
-       if ((how & ~SHUTDOWN_MASK) || !how) {
-               ret = -EINVAL;
-               goto out_unlock;
-       }
-
-       if (sock->state == SS_CONNECTING) {
-               if ((1 << sk->sk_state) &
-                   (TCPF_SYN_SENT | TCPF_SYN_RECV | TCPF_CLOSE))
-                       sock->state = SS_DISCONNECTING;
-               else
-                       sock->state = SS_CONNECTED;
-       }
-
-       sk->sk_shutdown |= how;
-       if ((how & SEND_SHUTDOWN) && mptcp_close_state(sk))
-               __mptcp_wr_shutdown(sk);
-
-       /* Wake up anyone sleeping in poll. */
-       sk->sk_state_change(sk);
-
-out_unlock:
-       release_sock(sk);
-
-       return ret;
-}
-
 static const struct proto_ops mptcp_stream_ops = {
        .family            = PF_INET,
        .owner             = THIS_MODULE,
@@ -3391,7 +3381,7 @@ static const struct proto_ops mptcp_stream_ops = {
        .ioctl             = inet_ioctl,
        .gettstamp         = sock_gettstamp,
        .listen            = mptcp_listen,
-       .shutdown          = mptcp_shutdown,
+       .shutdown          = inet_shutdown,
        .setsockopt        = sock_common_setsockopt,
        .getsockopt        = sock_common_getsockopt,
        .sendmsg           = inet_sendmsg,
@@ -3408,13 +3398,58 @@ static struct inet_protosw mptcp_protosw = {
        .flags          = INET_PROTOSW_ICSK,
 };
 
+static int mptcp_napi_poll(struct napi_struct *napi, int budget)
+{
+       struct mptcp_delegated_action *delegated;
+       struct mptcp_subflow_context *subflow;
+       int work_done = 0;
+
+       delegated = container_of(napi, struct mptcp_delegated_action, napi);
+       while ((subflow = mptcp_subflow_delegated_next(delegated)) != NULL) {
+               struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
+
+               bh_lock_sock_nested(ssk);
+               if (!sock_owned_by_user(ssk) &&
+                   mptcp_subflow_has_delegated_action(subflow))
+                       mptcp_subflow_process_delegated(ssk);
+               /* ... elsewhere tcp_release_cb_override already processed
+                * the action or will do at next release_sock().
+                * In both case must dequeue the subflow here - on the same
+                * CPU that scheduled it.
+                */
+               bh_unlock_sock(ssk);
+               sock_put(ssk);
+
+               if (++work_done == budget)
+                       return budget;
+       }
+
+       /* always provide a 0 'work_done' argument, so that napi_complete_done
+        * will not try accessing the NULL napi->dev ptr
+        */
+       napi_complete_done(napi, 0);
+       return work_done;
+}
+
 void __init mptcp_proto_init(void)
 {
+       struct mptcp_delegated_action *delegated;
+       int cpu;
+
        mptcp_prot.h.hashinfo = tcp_prot.h.hashinfo;
 
        if (percpu_counter_init(&mptcp_sockets_allocated, 0, GFP_KERNEL))
                panic("Failed to allocate MPTCP pcpu counter\n");
 
+       init_dummy_netdev(&mptcp_napi_dev);
+       for_each_possible_cpu(cpu) {
+               delegated = per_cpu_ptr(&mptcp_delegated_actions, cpu);
+               INIT_LIST_HEAD(&delegated->head);
+               netif_tx_napi_add(&mptcp_napi_dev, &delegated->napi, mptcp_napi_poll,
+                                 NAPI_POLL_WEIGHT);
+               napi_enable(&delegated->napi);
+       }
+
        mptcp_subflow_init();
        mptcp_pm_init();
        mptcp_token_init();
@@ -3441,7 +3476,7 @@ static const struct proto_ops mptcp_v6_stream_ops = {
        .ioctl             = inet6_ioctl,
        .gettstamp         = sock_gettstamp,
        .listen            = mptcp_listen,
-       .shutdown          = mptcp_shutdown,
+       .shutdown          = inet_shutdown,
        .setsockopt        = sock_common_setsockopt,
        .getsockopt        = sock_common_getsockopt,
        .sendmsg           = inet6_sendmsg,
index d67de79..1460705 100644 (file)
@@ -24,6 +24,7 @@
 #define OPTION_MPTCP_ADD_ADDR6 BIT(7)
 #define OPTION_MPTCP_RM_ADDR   BIT(8)
 #define OPTION_MPTCP_FASTCLOSE BIT(9)
+#define OPTION_MPTCP_PRIO      BIT(10)
 
 /* MPTCP option subtypes */
 #define MPTCPOPT_MP_CAPABLE    0
@@ -59,6 +60,7 @@
 #define TCPOLEN_MPTCP_ADD_ADDR6_BASE_PORT      24
 #define TCPOLEN_MPTCP_PORT_LEN         4
 #define TCPOLEN_MPTCP_RM_ADDR_BASE     4
+#define TCPOLEN_MPTCP_PRIO             4
 #define TCPOLEN_MPTCP_FASTCLOSE                12
 
 /* MPTCP MP_JOIN flags */
@@ -86,6 +88,9 @@
 #define MPTCP_ADDR_IPVERSION_4 4
 #define MPTCP_ADDR_IPVERSION_6 6
 
+/* MPTCP MP_PRIO flags */
+#define MPTCP_PRIO_BKUP                BIT(0)
+
 /* MPTCP socket flags */
 #define MPTCP_DATA_READY       0
 #define MPTCP_NOSPACE          1
@@ -116,6 +121,7 @@ struct mptcp_options_received {
                dss : 1,
                add_addr : 1,
                rm_addr : 1,
+               mp_prio : 1,
                family : 4,
                echo : 1,
                backup : 1;
@@ -372,6 +378,15 @@ enum mptcp_data_avail {
        MPTCP_SUBFLOW_OOO_DATA
 };
 
+struct mptcp_delegated_action {
+       struct napi_struct napi;
+       struct list_head head;
+};
+
+DECLARE_PER_CPU(struct mptcp_delegated_action, mptcp_delegated_actions);
+
+#define MPTCP_DELEGATE_SEND            0
+
 /* MPTCP subflow context */
 struct mptcp_subflow_context {
        struct  list_head node;/* conn_list of subflows */
@@ -396,6 +411,7 @@ struct mptcp_subflow_context {
                map_valid : 1,
                mpc_map : 1,
                backup : 1,
+               send_mp_prio : 1,
                rx_eof : 1,
                can_ack : 1,        /* only after processing the remote a key */
                disposable : 1;     /* ctx can be free at ulp release time */
@@ -408,6 +424,9 @@ struct mptcp_subflow_context {
        u8      local_id;
        u8      remote_id;
 
+       long    delegated_status;
+       struct  list_head delegated_node;   /* link into delegated_action, protected by local BH */
+
        struct  sock *tcp_sock;     /* tcp sk backpointer */
        struct  sock *conn;         /* parent mptcp_sock */
        const   struct inet_connection_sock_af_ops *icsk_af_ops;
@@ -456,6 +475,61 @@ static inline void mptcp_add_pending_subflow(struct mptcp_sock *msk,
        spin_unlock_bh(&msk->join_list_lock);
 }
 
+void mptcp_subflow_process_delegated(struct sock *ssk);
+
+static inline void mptcp_subflow_delegate(struct mptcp_subflow_context *subflow)
+{
+       struct mptcp_delegated_action *delegated;
+       bool schedule;
+
+       /* The implied barrier pairs with mptcp_subflow_delegated_done(), and
+        * ensures the below list check sees list updates done prior to status
+        * bit changes
+        */
+       if (!test_and_set_bit(MPTCP_DELEGATE_SEND, &subflow->delegated_status)) {
+               /* still on delegated list from previous scheduling */
+               if (!list_empty(&subflow->delegated_node))
+                       return;
+
+               /* the caller held the subflow bh socket lock */
+               lockdep_assert_in_softirq();
+
+               delegated = this_cpu_ptr(&mptcp_delegated_actions);
+               schedule = list_empty(&delegated->head);
+               list_add_tail(&subflow->delegated_node, &delegated->head);
+               sock_hold(mptcp_subflow_tcp_sock(subflow));
+               if (schedule)
+                       napi_schedule(&delegated->napi);
+       }
+}
+
+static inline struct mptcp_subflow_context *
+mptcp_subflow_delegated_next(struct mptcp_delegated_action *delegated)
+{
+       struct mptcp_subflow_context *ret;
+
+       if (list_empty(&delegated->head))
+               return NULL;
+
+       ret = list_first_entry(&delegated->head, struct mptcp_subflow_context, delegated_node);
+       list_del_init(&ret->delegated_node);
+       return ret;
+}
+
+static inline bool mptcp_subflow_has_delegated_action(const struct mptcp_subflow_context *subflow)
+{
+       return test_bit(MPTCP_DELEGATE_SEND, &subflow->delegated_status);
+}
+
+static inline void mptcp_subflow_delegated_done(struct mptcp_subflow_context *subflow)
+{
+       /* pairs with mptcp_subflow_delegate, ensures delegate_node is updated before
+        * touching the status bit
+        */
+       smp_wmb();
+       clear_bit(MPTCP_DELEGATE_SEND, &subflow->delegated_status);
+}
+
 int mptcp_is_enabled(struct net *net);
 unsigned int mptcp_get_add_addr_timeout(struct net *net);
 void mptcp_subflow_fully_established(struct mptcp_subflow_context *subflow,
@@ -466,6 +540,7 @@ void mptcp_subflow_shutdown(struct sock *sk, struct sock *ssk, int how);
 void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
                       struct mptcp_subflow_context *subflow);
 void mptcp_subflow_reset(struct sock *ssk);
+void mptcp_sock_graft(struct sock *sk, struct socket *parent);
 
 /* called with sk socket lock held */
 int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
@@ -514,6 +589,25 @@ static inline bool mptcp_data_fin_enabled(const struct mptcp_sock *msk)
               READ_ONCE(msk->write_seq) == READ_ONCE(msk->snd_nxt);
 }
 
+static inline bool mptcp_propagate_sndbuf(struct sock *sk, struct sock *ssk)
+{
+       if ((sk->sk_userlocks & SOCK_SNDBUF_LOCK) || ssk->sk_sndbuf <= READ_ONCE(sk->sk_sndbuf))
+               return false;
+
+       WRITE_ONCE(sk->sk_sndbuf, ssk->sk_sndbuf);
+       return true;
+}
+
+static inline void mptcp_write_space(struct sock *sk)
+{
+       if (sk_stream_is_writeable(sk)) {
+               /* pairs with memory barrier in mptcp_poll */
+               smp_mb();
+               if (test_and_clear_bit(MPTCP_NOSPACE, &mptcp_sk(sk)->flags))
+                       sk_stream_write_space(sk);
+       }
+}
+
 void mptcp_destroy_common(struct mptcp_sock *msk);
 
 void __init mptcp_token_init(void);
@@ -550,6 +644,10 @@ void mptcp_pm_add_addr_received(struct mptcp_sock *msk,
                                const struct mptcp_addr_info *addr);
 void mptcp_pm_add_addr_send_ack(struct mptcp_sock *msk);
 void mptcp_pm_rm_addr_received(struct mptcp_sock *msk, u8 rm_id);
+void mptcp_pm_mp_prio_received(struct sock *sk, u8 bkup);
+int mptcp_pm_nl_mp_prio_send_ack(struct mptcp_sock *msk,
+                                struct mptcp_addr_info *addr,
+                                u8 bkup);
 void mptcp_pm_free_anno_list(struct mptcp_sock *msk);
 struct mptcp_pm_add_entry *
 mptcp_pm_del_add_timer(struct mptcp_sock *msk,
index 278cbe3..5861562 100644 (file)
 #include <net/tcp.h>
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
 #include <net/ip6_route.h>
+#include <net/transp_v6.h>
 #endif
 #include <net/mptcp.h>
 #include <uapi/linux/mptcp.h>
 #include "protocol.h"
 #include "mib.h"
 
+static void mptcp_subflow_ops_undo_override(struct sock *ssk);
+
 static void SUBFLOW_REQ_INC_STATS(struct request_sock *req,
                                  enum linux_mptcp_mib_field field)
 {
@@ -343,6 +346,7 @@ static void subflow_finish_connect(struct sock *sk, const struct sk_buff *skb)
        if (subflow->conn_finished)
                return;
 
+       mptcp_propagate_sndbuf(parent, sk);
        subflow->rel_write_seq = 1;
        subflow->conn_finished = 1;
        subflow->ssn_offset = TCP_SKB_CB(skb)->seq;
@@ -427,6 +431,7 @@ drop:
 static struct tcp_request_sock_ops subflow_request_sock_ipv6_ops;
 static struct inet_connection_sock_af_ops subflow_v6_specific;
 static struct inet_connection_sock_af_ops subflow_v6m_specific;
+static struct proto tcpv6_prot_override;
 
 static int subflow_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
@@ -508,6 +513,8 @@ static void subflow_ulp_fallback(struct sock *sk,
        icsk->icsk_ulp_ops = NULL;
        rcu_assign_pointer(icsk->icsk_ulp_data, NULL);
        tcp_sk(sk)->is_mptcp = 0;
+
+       mptcp_subflow_ops_undo_override(sk);
 }
 
 static void subflow_drop_ctx(struct sock *ssk)
@@ -681,6 +688,7 @@ dispose_child:
 }
 
 static struct inet_connection_sock_af_ops subflow_specific;
+static struct proto tcp_prot_override;
 
 enum mapping_status {
        MAPPING_OK,
@@ -1040,7 +1048,10 @@ static void subflow_data_ready(struct sock *sk)
 
 static void subflow_write_space(struct sock *ssk)
 {
-       /* we take action in __mptcp_clean_una() */
+       struct sock *sk = mptcp_subflow_ctx(ssk)->conn;
+
+       mptcp_propagate_sndbuf(sk, ssk);
+       mptcp_write_space(sk);
 }
 
 static struct inet_connection_sock_af_ops *
@@ -1074,21 +1085,31 @@ void mptcpv6_handle_mapped(struct sock *sk, bool mapped)
 #endif
 
 static void mptcp_info2sockaddr(const struct mptcp_addr_info *info,
-                               struct sockaddr_storage *addr)
+                               struct sockaddr_storage *addr,
+                               unsigned short family)
 {
        memset(addr, 0, sizeof(*addr));
-       addr->ss_family = info->family;
+       addr->ss_family = family;
        if (addr->ss_family == AF_INET) {
                struct sockaddr_in *in_addr = (struct sockaddr_in *)addr;
 
-               in_addr->sin_addr = info->addr;
+               if (info->family == AF_INET)
+                       in_addr->sin_addr = info->addr;
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+               else if (ipv6_addr_v4mapped(&info->addr6))
+                       in_addr->sin_addr.s_addr = info->addr6.s6_addr32[3];
+#endif
                in_addr->sin_port = info->port;
        }
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
        else if (addr->ss_family == AF_INET6) {
                struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)addr;
 
-               in6_addr->sin6_addr = info->addr6;
+               if (info->family == AF_INET)
+                       ipv6_addr_set_v4mapped(info->addr.s_addr,
+                                              &in6_addr->sin6_addr);
+               else
+                       in6_addr->sin6_addr = info->addr6;
                in6_addr->sin6_port = info->port;
        }
 #endif
@@ -1132,11 +1153,11 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
        subflow->remote_key = msk->remote_key;
        subflow->local_key = msk->local_key;
        subflow->token = msk->token;
-       mptcp_info2sockaddr(loc, &addr);
+       mptcp_info2sockaddr(loc, &addr, ssk->sk_family);
 
        addrlen = sizeof(struct sockaddr_in);
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
-       if (loc->family == AF_INET6)
+       if (addr.ss_family == AF_INET6)
                addrlen = sizeof(struct sockaddr_in6);
 #endif
        ssk->sk_bound_dev_if = loc->ifindex;
@@ -1152,13 +1173,16 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
        subflow->remote_id = remote_id;
        subflow->request_join = 1;
        subflow->request_bkup = !!(loc->flags & MPTCP_PM_ADDR_FLAG_BACKUP);
-       mptcp_info2sockaddr(remote, &addr);
+       mptcp_info2sockaddr(remote, &addr, ssk->sk_family);
 
        mptcp_add_pending_subflow(msk, subflow);
        err = kernel_connect(sf, (struct sockaddr *)&addr, addrlen, O_NONBLOCK);
        if (err && err != -EINPROGRESS)
                goto failed_unlink;
 
+       /* discard the subflow socket */
+       mptcp_sock_graft(ssk, sk->sk_socket);
+       iput(SOCK_INODE(sf));
        return err;
 
 failed_unlink:
@@ -1196,6 +1220,25 @@ static void mptcp_attach_cgroup(struct sock *parent, struct sock *child)
 #endif /* CONFIG_SOCK_CGROUP_DATA */
 }
 
+static void mptcp_subflow_ops_override(struct sock *ssk)
+{
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+       if (ssk->sk_prot == &tcpv6_prot)
+               ssk->sk_prot = &tcpv6_prot_override;
+       else
+#endif
+               ssk->sk_prot = &tcp_prot_override;
+}
+
+static void mptcp_subflow_ops_undo_override(struct sock *ssk)
+{
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+       if (ssk->sk_prot == &tcpv6_prot_override)
+               ssk->sk_prot = &tcpv6_prot;
+       else
+#endif
+               ssk->sk_prot = &tcp_prot;
+}
 int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock)
 {
        struct mptcp_subflow_context *subflow;
@@ -1251,6 +1294,7 @@ int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock)
        *new_sock = sf;
        sock_hold(sk);
        subflow->conn = sk;
+       mptcp_subflow_ops_override(sf->sk);
 
        return 0;
 }
@@ -1267,6 +1311,7 @@ static struct mptcp_subflow_context *subflow_create_ctx(struct sock *sk,
 
        rcu_assign_pointer(icsk->icsk_ulp_data, ctx);
        INIT_LIST_HEAD(&ctx->node);
+       INIT_LIST_HEAD(&ctx->delegated_node);
 
        pr_debug("subflow=%p", ctx);
 
@@ -1299,6 +1344,7 @@ static void subflow_state_change(struct sock *sk)
        __subflow_state_change(sk);
 
        if (subflow_simultaneous_connect(sk)) {
+               mptcp_propagate_sndbuf(parent, sk);
                mptcp_do_fallback(sk);
                mptcp_rcv_space_init(mptcp_sk(parent), sk);
                pr_fallback(mptcp_sk(parent));
@@ -1378,6 +1424,7 @@ static void subflow_ulp_release(struct sock *ssk)
                sock_put(sk);
        }
 
+       mptcp_subflow_ops_undo_override(ssk);
        if (release)
                kfree_rcu(ctx, rcu);
 }
@@ -1431,6 +1478,16 @@ static void subflow_ulp_clone(const struct request_sock *req,
        }
 }
 
+static void tcp_release_cb_override(struct sock *ssk)
+{
+       struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
+
+       if (mptcp_subflow_has_delegated_action(subflow))
+               mptcp_subflow_process_delegated(ssk);
+
+       tcp_release_cb(ssk);
+}
+
 static struct tcp_ulp_ops subflow_ulp_ops __read_mostly = {
        .name           = "mptcp",
        .owner          = THIS_MODULE,
@@ -1471,6 +1528,9 @@ void __init mptcp_subflow_init(void)
        subflow_specific.syn_recv_sock = subflow_syn_recv_sock;
        subflow_specific.sk_rx_dst_set = subflow_finish_connect;
 
+       tcp_prot_override = tcp_prot;
+       tcp_prot_override.release_cb = tcp_release_cb_override;
+
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
        subflow_request_sock_ipv6_ops = tcp_request_sock_ipv6_ops;
        subflow_request_sock_ipv6_ops.route_req = subflow_v6_route_req;
@@ -1486,6 +1546,9 @@ void __init mptcp_subflow_init(void)
        subflow_v6m_specific.net_header_len = ipv4_specific.net_header_len;
        subflow_v6m_specific.mtu_reduced = ipv4_specific.mtu_reduced;
        subflow_v6m_specific.net_frag_header_len = 0;
+
+       tcpv6_prot_override = tcpv6_prot;
+       tcpv6_prot_override.release_cb = tcp_release_cb_override;
 #endif
 
        mptcp_diag_subflow_init(&subflow_ulp_ops);
index 5b1f4ec..888ccc2 100644 (file)
@@ -1120,7 +1120,7 @@ int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
        int payload, i, ret;
 
        /* Find the NCSI device */
-       nd = ncsi_find_dev(dev);
+       nd = ncsi_find_dev(orig_dev);
        ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
        if (!ndp)
                return -ENODEV;
index 49fbef0..1a92063 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 menu "Core Netfilter Configuration"
-       depends on NET && INET && NETFILTER
+       depends on INET && NETFILTER
 
 config NETFILTER_INGRESS
        bool "Netfilter ingress support"
index 5f1208a..6186358 100644 (file)
@@ -141,20 +141,6 @@ htable_size(u8 hbits)
        return hsize * sizeof(struct hbucket *) + sizeof(struct htable);
 }
 
-/* Compute htable_bits from the user input parameter hashsize */
-static u8
-htable_bits(u32 hashsize)
-{
-       /* Assume that hashsize == 2^htable_bits */
-       u8 bits = fls(hashsize - 1);
-
-       if (jhash_size(bits) != hashsize)
-               /* Round up to the first 2^n value */
-               bits = fls(hashsize);
-
-       return bits;
-}
-
 #ifdef IP_SET_HASH_WITH_NETS
 #if IPSET_NET_COUNT > 1
 #define __CIDR(cidr, i)                (cidr[i])
@@ -640,7 +626,7 @@ mtype_resize(struct ip_set *set, bool retried)
        struct htype *h = set->data;
        struct htable *t, *orig;
        u8 htable_bits;
-       size_t dsize = set->dsize;
+       size_t hsize, dsize = set->dsize;
 #ifdef IP_SET_HASH_WITH_NETS
        u8 flags;
        struct mtype_elem *tmp;
@@ -664,14 +650,12 @@ mtype_resize(struct ip_set *set, bool retried)
 retry:
        ret = 0;
        htable_bits++;
-       if (!htable_bits) {
-               /* In case we have plenty of memory :-) */
-               pr_warn("Cannot increase the hashsize of set %s further\n",
-                       set->name);
-               ret = -IPSET_ERR_HASH_FULL;
-               goto out;
-       }
-       t = ip_set_alloc(htable_size(htable_bits));
+       if (!htable_bits)
+               goto hbwarn;
+       hsize = htable_size(htable_bits);
+       if (!hsize)
+               goto hbwarn;
+       t = ip_set_alloc(hsize);
        if (!t) {
                ret = -ENOMEM;
                goto out;
@@ -813,6 +797,12 @@ cleanup:
        if (ret == -EAGAIN)
                goto retry;
        goto out;
+
+hbwarn:
+       /* In case we have plenty of memory :-) */
+       pr_warn("Cannot increase the hashsize of set %s further\n", set->name);
+       ret = -IPSET_ERR_HASH_FULL;
+       goto out;
 }
 
 /* Get the current number of elements and ext_size in the set  */
@@ -1521,7 +1511,11 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
        if (!h)
                return -ENOMEM;
 
-       hbits = htable_bits(hashsize);
+       /* Compute htable_bits from the user input parameter hashsize.
+        * Assume that hashsize == 2^htable_bits,
+        * otherwise round up to the first 2^n value.
+        */
+       hbits = fls(hashsize - 1);
        hsize = htable_size(hbits);
        if (hsize == 0) {
                kfree(h);
index eb0e329..c39a1e3 100644 (file)
@@ -4,7 +4,7 @@
 #
 menuconfig IP_VS
        tristate "IP virtual server support"
-       depends on NET && INET && NETFILTER
+       depends on INET && NETFILTER
        depends on (NF_CONNTRACK || NF_CONNTRACK=n)
        help
          IP Virtual Server support will let you build a high-performance
index 46c5557..0ee702d 100644 (file)
@@ -523,6 +523,9 @@ nf_conntrack_hash_sysctl(struct ctl_table *table, int write,
 {
        int ret;
 
+       /* module_param hashsize could have changed value */
+       nf_conntrack_htable_size_user = nf_conntrack_htable_size;
+
        ret = proc_dointvec(table, write, buffer, lenp, ppos);
        if (ret < 0 || !write)
                return ret;
index ea923f8..b7c3c90 100644 (file)
@@ -1174,6 +1174,7 @@ static int __init nf_nat_init(void)
        ret = register_pernet_subsys(&nat_net_ops);
        if (ret < 0) {
                nf_ct_extend_unregister(&nat_extend);
+               kvfree(nf_nat_bysource);
                return ret;
        }
 
index 8d5aa0a..8d3aa97 100644 (file)
@@ -4162,7 +4162,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT |
                              NFT_SET_INTERVAL | NFT_SET_TIMEOUT |
                              NFT_SET_MAP | NFT_SET_EVAL |
-                             NFT_SET_OBJECT | NFT_SET_CONCAT))
+                             NFT_SET_OBJECT | NFT_SET_CONCAT | NFT_SET_EXPR))
                        return -EOPNOTSUPP;
                /* Only one of these operations is supported */
                if ((flags & (NFT_SET_MAP | NFT_SET_OBJECT)) ==
@@ -4304,6 +4304,10 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
                struct nlattr *tmp;
                int left;
 
+               if (!(flags & NFT_SET_EXPR)) {
+                       err = -EINVAL;
+                       goto err_set_alloc_name;
+               }
                i = 0;
                nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) {
                        if (i == NFT_SET_EXPR_MAX) {
@@ -5231,9 +5235,8 @@ static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
        kfree(elem);
 }
 
-static int nft_set_elem_expr_clone(const struct nft_ctx *ctx,
-                                  struct nft_set *set,
-                                  struct nft_expr *expr_array[])
+int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
+                           struct nft_expr *expr_array[])
 {
        struct nft_expr *expr;
        int err, i, k;
@@ -5254,8 +5257,8 @@ static int nft_set_elem_expr_clone(const struct nft_ctx *ctx,
        return 0;
 
 err_expr:
-       for (k = i - 1; k >= 0; k++)
-               nft_expr_destroy(ctx, expr_array[i]);
+       for (k = i - 1; k >= 0; k--)
+               nft_expr_destroy(ctx, expr_array[k]);
 
        return -ENOMEM;
 }
index 983a1d5..d164ef9 100644 (file)
@@ -19,6 +19,7 @@ struct nft_dynset {
        enum nft_registers              sreg_key:8;
        enum nft_registers              sreg_data:8;
        bool                            invert;
+       bool                            expr;
        u8                              num_exprs;
        u64                             timeout;
        struct nft_expr                 *expr_array[NFT_SET_EXPR_MAX];
@@ -175,11 +176,12 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
 
        if (tb[NFTA_DYNSET_FLAGS]) {
                u32 flags = ntohl(nla_get_be32(tb[NFTA_DYNSET_FLAGS]));
-
-               if (flags & ~NFT_DYNSET_F_INV)
-                       return -EINVAL;
+               if (flags & ~(NFT_DYNSET_F_INV | NFT_DYNSET_F_EXPR))
+                       return -EOPNOTSUPP;
                if (flags & NFT_DYNSET_F_INV)
                        priv->invert = true;
+               if (flags & NFT_DYNSET_F_EXPR)
+                       priv->expr = true;
        }
 
        set = nft_set_lookup_global(ctx->net, ctx->table,
@@ -210,7 +212,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
        timeout = 0;
        if (tb[NFTA_DYNSET_TIMEOUT] != NULL) {
                if (!(set->flags & NFT_SET_TIMEOUT))
-                       return -EINVAL;
+                       return -EOPNOTSUPP;
 
                err = nf_msecs_to_jiffies64(tb[NFTA_DYNSET_TIMEOUT], &timeout);
                if (err)
@@ -224,7 +226,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
 
        if (tb[NFTA_DYNSET_SREG_DATA] != NULL) {
                if (!(set->flags & NFT_SET_MAP))
-                       return -EINVAL;
+                       return -EOPNOTSUPP;
                if (set->dtype == NFT_DATA_VERDICT)
                        return -EOPNOTSUPP;
 
@@ -261,6 +263,9 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
                struct nlattr *tmp;
                int left;
 
+               if (!priv->expr)
+                       return -EINVAL;
+
                i = 0;
                nla_for_each_nested(tmp, tb[NFTA_DYNSET_EXPRESSIONS], left) {
                        if (i == NFT_SET_EXPR_MAX) {
@@ -290,6 +295,12 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
                        err = -EOPNOTSUPP;
                        goto err_expr_free;
                }
+       } else if (set->num_exprs > 0) {
+               err = nft_set_elem_expr_clone(ctx, set, priv->expr_array);
+               if (err < 0)
+                       return err;
+
+               priv->num_exprs = set->num_exprs;
        }
 
        nft_set_ext_prepare(&priv->tmpl);
@@ -301,8 +312,10 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
                nft_dynset_ext_add_expr(priv);
 
        if (set->flags & NFT_SET_TIMEOUT) {
-               if (timeout || set->timeout)
+               if (timeout || set->timeout) {
+                       nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_TIMEOUT);
                        nft_set_ext_add(&priv->tmpl, NFT_SET_EXT_EXPIRATION);
+               }
        }
 
        priv->timeout = timeout;
@@ -371,22 +384,25 @@ static int nft_dynset_dump(struct sk_buff *skb, const struct nft_expr *expr)
                         nf_jiffies64_to_msecs(priv->timeout),
                         NFTA_DYNSET_PAD))
                goto nla_put_failure;
-       if (priv->num_exprs == 1) {
-               if (nft_expr_dump(skb, NFTA_DYNSET_EXPR, priv->expr_array[0]))
-                       goto nla_put_failure;
-       } else if (priv->num_exprs > 1) {
-               struct nlattr *nest;
-
-               nest = nla_nest_start_noflag(skb, NFTA_DYNSET_EXPRESSIONS);
-               if (!nest)
-                       goto nla_put_failure;
-
-               for (i = 0; i < priv->num_exprs; i++) {
-                       if (nft_expr_dump(skb, NFTA_LIST_ELEM,
-                                         priv->expr_array[i]))
+       if (priv->set->num_exprs == 0) {
+               if (priv->num_exprs == 1) {
+                       if (nft_expr_dump(skb, NFTA_DYNSET_EXPR,
+                                         priv->expr_array[0]))
                                goto nla_put_failure;
+               } else if (priv->num_exprs > 1) {
+                       struct nlattr *nest;
+
+                       nest = nla_nest_start_noflag(skb, NFTA_DYNSET_EXPRESSIONS);
+                       if (!nest)
+                               goto nla_put_failure;
+
+                       for (i = 0; i < priv->num_exprs; i++) {
+                               if (nft_expr_dump(skb, NFTA_LIST_ELEM,
+                                                 priv->expr_array[i]))
+                                       goto nla_put_failure;
+                       }
+                       nla_nest_end(skb, nest);
                }
-               nla_nest_end(skb, nest);
        }
        if (nla_put_be32(skb, NFTA_DYNSET_FLAGS, htonl(flags)))
                goto nla_put_failure;
index 37253d3..0d5c422 100644 (file)
@@ -115,6 +115,9 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
        } cfg;
        int ret;
 
+       if (strnlen(info->name, sizeof(est->name)) >= sizeof(est->name))
+               return -ENAMETOOLONG;
+
        net_get_random_once(&jhash_rnd, sizeof(jhash_rnd));
 
        mutex_lock(&xn->hash_lock);
index 96b9167..466a027 100644 (file)
@@ -4,7 +4,6 @@
 #
 
 menuconfig NFC
-       depends on NET
        depends on RFKILL || !RFKILL
        tristate "NFC subsystem support"
        default n
index 0eb4ddc..c0c8fea 100644 (file)
@@ -236,7 +236,7 @@ static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc,
                goto exit;
        }
 
-       if (shdlc->t1_active == false) {
+       if (!shdlc->t1_active) {
                shdlc->t1_active = true;
                mod_timer(&shdlc->t1_timer, jiffies +
                          msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
index e64727e..5925740 100644 (file)
@@ -508,7 +508,7 @@ static int nci_open_device(struct nci_dev *ndev)
                };
                unsigned long opt = 0;
 
-               if (!(ndev->nci_ver & NCI_VER_2_MASK))
+               if (ndev->nci_ver & NCI_VER_2_MASK)
                        opt = (unsigned long)&nci_init_v2_cmd;
 
                rc = __nci_request(ndev, nci_init_req, opt,
@@ -579,11 +579,11 @@ static int nci_close_device(struct nci_dev *ndev)
 
        clear_bit(NCI_INIT, &ndev->flags);
 
-       del_timer_sync(&ndev->cmd_timer);
-
        /* Flush cmd wq */
        flush_workqueue(ndev->cmd_wq);
 
+       del_timer_sync(&ndev->cmd_timer);
+
        /* Clear flags */
        ndev->flags = 0;
 
index 573b38a..722f7ef 100644 (file)
@@ -852,6 +852,7 @@ static int nfc_genl_stop_poll(struct sk_buff *skb, struct genl_info *info)
 
        if (!dev->polling) {
                device_unlock(&dev->dev);
+               nfc_put_device(dev);
                return -EINVAL;
        }
 
@@ -1819,9 +1820,9 @@ static int nfc_genl_rcv_nl_event(struct notifier_block *this,
 
        w = kmalloc(sizeof(*w), GFP_ATOMIC);
        if (w) {
-               INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
+               INIT_WORK(&w->w, nfc_urelease_event_work);
                w->portid = n->portid;
-               schedule_work((struct work_struct *) w);
+               schedule_work(&w->w);
        }
 
 out:
index 955c195..9c7eb84 100644 (file)
@@ -105,7 +105,7 @@ static int rawsock_connect(struct socket *sock, struct sockaddr *_addr,
        if (addr->target_idx > dev->target_next_idx - 1 ||
            addr->target_idx < dev->target_next_idx - dev->n_targets) {
                rc = -EINVAL;
-               goto error;
+               goto put_dev;
        }
 
        rc = nfc_activate_target(dev, addr->target_idx, addr->nfc_protocol);
index e8902a7..92a0b67 100644 (file)
@@ -957,14 +957,14 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
 
 static int dec_ttl_exception_handler(struct datapath *dp, struct sk_buff *skb,
                                     struct sw_flow_key *key,
-                                    const struct nlattr *attr, bool last)
+                                    const struct nlattr *attr)
 {
        /* The first attribute is always 'OVS_DEC_TTL_ATTR_ACTION'. */
        struct nlattr *actions = nla_data(attr);
 
        if (nla_len(actions))
                return clone_execute(dp, skb, key, 0, nla_data(actions),
-                                    nla_len(actions), last, false);
+                                    nla_len(actions), true, false);
 
        consume_skb(skb);
        return 0;
@@ -1418,11 +1418,9 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
 
                case OVS_ACTION_ATTR_DEC_TTL:
                        err = execute_dec_ttl(skb, key);
-                       if (err == -EHOSTUNREACH) {
-                               err = dec_ttl_exception_handler(dp, skb, key,
-                                                               a, true);
-                               return err;
-                       }
+                       if (err == -EHOSTUNREACH)
+                               return dec_ttl_exception_handler(dp, skb,
+                                                                key, a);
                        break;
                }
 
index 4c5c233..fd1f809 100644 (file)
@@ -2515,15 +2515,25 @@ static int validate_and_copy_dec_ttl(struct net *net,
                if (type > OVS_DEC_TTL_ATTR_MAX)
                        continue;
 
-               if (!type || attrs[type])
+               if (!type || attrs[type]) {
+                       OVS_NLERR(log, "Duplicate or invalid key (type %d).",
+                                 type);
                        return -EINVAL;
+               }
 
                attrs[type] = a;
        }
 
+       if (rem) {
+               OVS_NLERR(log, "Message has %d unknown bytes.", rem);
+               return -EINVAL;
+       }
+
        actions = attrs[OVS_DEC_TTL_ATTR_ACTION];
-       if (rem || !actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN))
+       if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN)) {
+               OVS_NLERR(log, "Missing valid actions attribute.");
                return -EINVAL;
+       }
 
        start = add_nested_action_start(sfa, OVS_ACTION_ATTR_DEC_TTL, log);
        if (start < 0)
index de8e8db..6bbc7a4 100644 (file)
@@ -4595,7 +4595,9 @@ static void packet_seq_stop(struct seq_file *seq, void *v)
 static int packet_seq_show(struct seq_file *seq, void *v)
 {
        if (v == SEQ_START_TOKEN)
-               seq_puts(seq, "sk       RefCnt Type Proto  Iface R Rmem   User   Inode\n");
+               seq_printf(seq,
+                          "%*sRefCnt Type Proto  Iface R Rmem   User   Inode\n",
+                          IS_ENABLED(CONFIG_64BIT) ? -17 : -9, "sk");
        else {
                struct sock *s = sk_entry(v);
                const struct packet_sock *po = pkt_sk(s);
index baafc3f..5f61e59 100644 (file)
@@ -139,7 +139,7 @@ struct packet_sock {
        atomic_t                tp_drops ____cacheline_aligned_in_smp;
 };
 
-static struct packet_sock *pkt_sk(struct sock *sk)
+static inline struct packet_sock *pkt_sk(struct sock *sk)
 {
        return (struct packet_sock *)sk;
 }
index 028f514..be0b839 100644 (file)
@@ -4,7 +4,6 @@
 #
 
 menuconfig PSAMPLE
-       depends on NET
        tristate "Packet-sampling netlink channel"
        default n
        help
index 56aaf8c..8d00dfe 100644 (file)
@@ -755,7 +755,7 @@ static void qrtr_ns_data_ready(struct sock *sk)
        queue_work(qrtr_ns.workqueue, &qrtr_ns.work);
 }
 
-void qrtr_ns_init(void)
+int qrtr_ns_init(void)
 {
        struct sockaddr_qrtr sq;
        int ret;
@@ -766,7 +766,7 @@ void qrtr_ns_init(void)
        ret = sock_create_kern(&init_net, AF_QIPCRTR, SOCK_DGRAM,
                               PF_QIPCRTR, &qrtr_ns.sock);
        if (ret < 0)
-               return;
+               return ret;
 
        ret = kernel_getsockname(qrtr_ns.sock, (struct sockaddr *)&sq);
        if (ret < 0) {
@@ -797,12 +797,13 @@ void qrtr_ns_init(void)
        if (ret < 0)
                goto err_wq;
 
-       return;
+       return 0;
 
 err_wq:
        destroy_workqueue(qrtr_ns.workqueue);
 err_sock:
        sock_release(qrtr_ns.sock);
+       return ret;
 }
 EXPORT_SYMBOL_GPL(qrtr_ns_init);
 
index f4ab3ca..b343582 100644 (file)
@@ -1287,13 +1287,19 @@ static int __init qrtr_proto_init(void)
                return rc;
 
        rc = sock_register(&qrtr_family);
-       if (rc) {
-               proto_unregister(&qrtr_proto);
-               return rc;
-       }
+       if (rc)
+               goto err_proto;
 
-       qrtr_ns_init();
+       rc = qrtr_ns_init();
+       if (rc)
+               goto err_sock;
 
+       return 0;
+
+err_sock:
+       sock_unregister(qrtr_family.family);
+err_proto:
+       proto_unregister(&qrtr_proto);
        return rc;
 }
 postcore_initcall(qrtr_proto_init);
index dc2b67f..3f2d286 100644 (file)
@@ -29,7 +29,7 @@ void qrtr_endpoint_unregister(struct qrtr_endpoint *ep);
 
 int qrtr_endpoint_post(struct qrtr_endpoint *ep, const void *data, size_t len);
 
-void qrtr_ns_init(void);
+int qrtr_ns_init(void);
 
 void qrtr_ns_remove(void);
 
index 382add7..1ae90fb 100644 (file)
@@ -197,6 +197,7 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx)
        tail = b->peer_backlog_tail;
        while (CIRC_CNT(head, tail, size) > 0) {
                struct rxrpc_peer *peer = b->peer_backlog[tail];
+               rxrpc_put_local(peer->local);
                kfree(peer);
                tail = (tail + 1) & (size - 1);
        }
index 667c44a..dc20136 100644 (file)
@@ -430,7 +430,7 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb)
                return;
        }
 
-       if (call->state == RXRPC_CALL_SERVER_RECV_REQUEST) {
+       if (state == RXRPC_CALL_SERVER_RECV_REQUEST) {
                unsigned long timo = READ_ONCE(call->next_req_timo);
                unsigned long now, expect_req_by;
 
index 9631aa8..8d2073e 100644 (file)
@@ -598,7 +598,7 @@ static long rxrpc_read(const struct key *key,
                default: /* we have a ticket we can't encode */
                        pr_err("Unsupported key token type (%u)\n",
                               token->security_index);
-                       continue;
+                       return -ENOPKG;
                }
 
                _debug("token[%u]: toksize=%u", ntoks, toksize);
@@ -674,7 +674,9 @@ static long rxrpc_read(const struct key *key,
                        break;
 
                default:
-                       break;
+                       pr_err("Unsupported key token type (%u)\n",
+                              token->security_index);
+                       return -ENOPKG;
                }
 
                ASSERTCMP((unsigned long)xdr - (unsigned long)oldxdr, ==,
index 2e85b63..4dd235c 100644 (file)
@@ -928,19 +928,13 @@ static void tcf_idr_insert_many(struct tc_action *actions[])
        }
 }
 
-struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
-                                   struct nlattr *nla, struct nlattr *est,
-                                   char *name, int ovr, int bind,
-                                   bool rtnl_held,
-                                   struct netlink_ext_ack *extack)
+struct tc_action_ops *tc_action_load_ops(char *name, struct nlattr *nla,
+                                        bool rtnl_held,
+                                        struct netlink_ext_ack *extack)
 {
-       struct nla_bitfield32 flags = { 0, 0 };
-       u8 hw_stats = TCA_ACT_HW_STATS_ANY;
-       struct tc_action *a;
+       struct nlattr *tb[TCA_ACT_MAX + 1];
        struct tc_action_ops *a_o;
-       struct tc_cookie *cookie = NULL;
        char act_name[IFNAMSIZ];
-       struct nlattr *tb[TCA_ACT_MAX + 1];
        struct nlattr *kind;
        int err;
 
@@ -948,33 +942,21 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
                err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
                                                  tcf_action_policy, extack);
                if (err < 0)
-                       goto err_out;
+                       return ERR_PTR(err);
                err = -EINVAL;
                kind = tb[TCA_ACT_KIND];
                if (!kind) {
                        NL_SET_ERR_MSG(extack, "TC action kind must be specified");
-                       goto err_out;
+                       return ERR_PTR(err);
                }
                if (nla_strscpy(act_name, kind, IFNAMSIZ) < 0) {
                        NL_SET_ERR_MSG(extack, "TC action name too long");
-                       goto err_out;
-               }
-               if (tb[TCA_ACT_COOKIE]) {
-                       cookie = nla_memdup_cookie(tb);
-                       if (!cookie) {
-                               NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
-                               err = -ENOMEM;
-                               goto err_out;
-                       }
+                       return ERR_PTR(err);
                }
-               hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]);
-               if (tb[TCA_ACT_FLAGS])
-                       flags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]);
        } else {
                if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ) {
                        NL_SET_ERR_MSG(extack, "TC action name too long");
-                       err = -EINVAL;
-                       goto err_out;
+                       return ERR_PTR(-EINVAL);
                }
        }
 
@@ -996,24 +978,56 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
                 * indicate this using -EAGAIN.
                 */
                if (a_o != NULL) {
-                       err = -EAGAIN;
-                       goto err_mod;
+                       module_put(a_o->owner);
+                       return ERR_PTR(-EAGAIN);
                }
 #endif
                NL_SET_ERR_MSG(extack, "Failed to load TC action module");
-               err = -ENOENT;
-               goto err_free;
+               return ERR_PTR(-ENOENT);
        }
 
+       return a_o;
+}
+
+struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
+                                   struct nlattr *nla, struct nlattr *est,
+                                   char *name, int ovr, int bind,
+                                   struct tc_action_ops *a_o, bool rtnl_held,
+                                   struct netlink_ext_ack *extack)
+{
+       struct nla_bitfield32 flags = { 0, 0 };
+       u8 hw_stats = TCA_ACT_HW_STATS_ANY;
+       struct nlattr *tb[TCA_ACT_MAX + 1];
+       struct tc_cookie *cookie = NULL;
+       struct tc_action *a;
+       int err;
+
        /* backward compatibility for policer */
-       if (name == NULL)
+       if (name == NULL) {
+               err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
+                                                 tcf_action_policy, extack);
+               if (err < 0)
+                       return ERR_PTR(err);
+               if (tb[TCA_ACT_COOKIE]) {
+                       cookie = nla_memdup_cookie(tb);
+                       if (!cookie) {
+                               NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
+                               err = -ENOMEM;
+                               goto err_out;
+                       }
+               }
+               hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]);
+               if (tb[TCA_ACT_FLAGS])
+                       flags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]);
+
                err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind,
                                rtnl_held, tp, flags.value, extack);
-       else
+       } else {
                err = a_o->init(net, nla, est, &a, ovr, bind, rtnl_held,
                                tp, flags.value, extack);
+       }
        if (err < 0)
-               goto err_mod;
+               goto err_out;
 
        if (!name && tb[TCA_ACT_COOKIE])
                tcf_set_action_cookie(&a->act_cookie, cookie);
@@ -1030,14 +1044,11 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
 
        return a;
 
-err_mod:
-       module_put(a_o->owner);
-err_free:
+err_out:
        if (cookie) {
                kfree(cookie->data);
                kfree(cookie);
        }
-err_out:
        return ERR_PTR(err);
 }
 
@@ -1048,6 +1059,7 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
                    struct tc_action *actions[], size_t *attr_size,
                    bool rtnl_held, struct netlink_ext_ack *extack)
 {
+       struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {};
        struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
        struct tc_action *act;
        size_t sz = 0;
@@ -1059,9 +1071,20 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
        if (err < 0)
                return err;
 
+       for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
+               struct tc_action_ops *a_o;
+
+               a_o = tc_action_load_ops(name, tb[i], rtnl_held, extack);
+               if (IS_ERR(a_o)) {
+                       err = PTR_ERR(a_o);
+                       goto err_mod;
+               }
+               ops[i - 1] = a_o;
+       }
+
        for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
                act = tcf_action_init_1(net, tp, tb[i], est, name, ovr, bind,
-                                       rtnl_held, extack);
+                                       ops[i - 1], rtnl_held, extack);
                if (IS_ERR(act)) {
                        err = PTR_ERR(act);
                        goto err;
@@ -1081,6 +1104,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
 
 err:
        tcf_action_destroy(actions, bind);
+err_mod:
+       for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
+               if (ops[i])
+                       module_put(ops[i]->owner);
+       }
        return err;
 }
 
index 83a5c67..f0a0aa1 100644 (file)
@@ -183,6 +183,7 @@ static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct,
                                             IP_CT_ESTABLISHED_REPLY;
        /* aligns with the CT reference on the SKB nf_ct_set */
        entry->ct_metadata.cookie = (unsigned long)ct | ctinfo;
+       entry->ct_metadata.orig_dir = dir == IP_CT_DIR_ORIGINAL;
 
        act_ct_labels = entry->ct_metadata.labels;
        ct_labels = nf_ct_labels_find(ct);
@@ -1030,6 +1031,7 @@ out_push:
 
 out:
        tcf_action_update_bstats(&c->common, skb);
+       qdisc_skb_cb(skb)->post_ct = true;
        if (defrag)
                qdisc_skb_cb(skb)->pkt_len = skb->len;
        return retval;
index 37b77bd..a67c66a 100644 (file)
@@ -3043,12 +3043,19 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
                size_t attr_size = 0;
 
                if (exts->police && tb[exts->police]) {
+                       struct tc_action_ops *a_o;
+
+                       a_o = tc_action_load_ops("police", tb[exts->police], rtnl_held, extack);
+                       if (IS_ERR(a_o))
+                               return PTR_ERR(a_o);
                        act = tcf_action_init_1(net, tp, tb[exts->police],
                                                rate_tlv, "police", ovr,
-                                               TCA_ACT_BIND, rtnl_held,
+                                               TCA_ACT_BIND, a_o, rtnl_held,
                                                extack);
-                       if (IS_ERR(act))
+                       if (IS_ERR(act)) {
+                               module_put(a_o->owner);
                                return PTR_ERR(act);
+                       }
 
                        act->type = exts->type = TCA_OLD_COMPAT;
                        exts->actions[0] = act;
index 1319986..caf7643 100644 (file)
@@ -291,9 +291,11 @@ static u16 fl_ct_info_to_flower_map[] = {
        [IP_CT_RELATED] =               TCA_FLOWER_KEY_CT_FLAGS_TRACKED |
                                        TCA_FLOWER_KEY_CT_FLAGS_RELATED,
        [IP_CT_ESTABLISHED_REPLY] =     TCA_FLOWER_KEY_CT_FLAGS_TRACKED |
-                                       TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED,
+                                       TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED |
+                                       TCA_FLOWER_KEY_CT_FLAGS_REPLY,
        [IP_CT_RELATED_REPLY] =         TCA_FLOWER_KEY_CT_FLAGS_TRACKED |
-                                       TCA_FLOWER_KEY_CT_FLAGS_RELATED,
+                                       TCA_FLOWER_KEY_CT_FLAGS_RELATED |
+                                       TCA_FLOWER_KEY_CT_FLAGS_REPLY,
        [IP_CT_NEW] =                   TCA_FLOWER_KEY_CT_FLAGS_TRACKED |
                                        TCA_FLOWER_KEY_CT_FLAGS_NEW,
 };
@@ -302,6 +304,7 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                       struct tcf_result *res)
 {
        struct cls_fl_head *head = rcu_dereference_bh(tp->root);
+       bool post_ct = qdisc_skb_cb(skb)->post_ct;
        struct fl_flow_key skb_key;
        struct fl_flow_mask *mask;
        struct cls_fl_filter *f;
@@ -318,7 +321,8 @@ static int fl_classify(struct sk_buff *skb, const struct tcf_proto *tp,
                skb_flow_dissect_tunnel_info(skb, &mask->dissector, &skb_key);
                skb_flow_dissect_ct(skb, &mask->dissector, &skb_key,
                                    fl_ct_info_to_flower_map,
-                                   ARRAY_SIZE(fl_ct_info_to_flower_map));
+                                   ARRAY_SIZE(fl_ct_info_to_flower_map),
+                                   post_ct);
                skb_flow_dissect_hash(skb, &mask->dissector, &skb_key);
                skb_flow_dissect(skb, &mask->dissector, &skb_key, 0);
 
@@ -1272,6 +1276,10 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
 
                nla_opt_msk = nla_data(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
                msk_depth = nla_len(tb[TCA_FLOWER_KEY_ENC_OPTS_MASK]);
+               if (!nla_ok(nla_opt_msk, msk_depth)) {
+                       NL_SET_ERR_MSG(extack, "Invalid nested attribute for masks");
+                       return -EINVAL;
+               }
        }
 
        nla_for_each_attr(nla_opt_key, nla_enc_key,
@@ -1307,9 +1315,6 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
                                NL_SET_ERR_MSG(extack, "Key and mask miss aligned");
                                return -EINVAL;
                        }
-
-                       if (msk_depth)
-                               nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
                        break;
                case TCA_FLOWER_KEY_ENC_OPTS_VXLAN:
                        if (key->enc_opts.dst_opt_type) {
@@ -1340,9 +1345,6 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
                                NL_SET_ERR_MSG(extack, "Key and mask miss aligned");
                                return -EINVAL;
                        }
-
-                       if (msk_depth)
-                               nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
                        break;
                case TCA_FLOWER_KEY_ENC_OPTS_ERSPAN:
                        if (key->enc_opts.dst_opt_type) {
@@ -1373,14 +1375,20 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
                                NL_SET_ERR_MSG(extack, "Key and mask miss aligned");
                                return -EINVAL;
                        }
-
-                       if (msk_depth)
-                               nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
                        break;
                default:
                        NL_SET_ERR_MSG(extack, "Unknown tunnel option type");
                        return -EINVAL;
                }
+
+               if (!msk_depth)
+                       continue;
+
+               if (!nla_ok(nla_opt_msk, msk_depth)) {
+                       NL_SET_ERR_MSG(extack, "A mask attribute is invalid");
+                       return -EINVAL;
+               }
+               nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
        }
 
        return 0;
index 78bec34..c4007b9 100644 (file)
@@ -366,9 +366,13 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
        if (tb[TCA_TCINDEX_MASK])
                cp->mask = nla_get_u16(tb[TCA_TCINDEX_MASK]);
 
-       if (tb[TCA_TCINDEX_SHIFT])
+       if (tb[TCA_TCINDEX_SHIFT]) {
                cp->shift = nla_get_u32(tb[TCA_TCINDEX_SHIFT]);
-
+               if (cp->shift > 16) {
+                       err = -EINVAL;
+                       goto errout;
+               }
+       }
        if (!cp->hash) {
                /* Hash not specified, use perfect hash if the upper limit
                 * of the hashing index is below the threshold.
index 51cb553..e2e4353 100644 (file)
@@ -412,7 +412,8 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
 {
        struct qdisc_rate_table *rtab;
 
-       if (tab == NULL || r->rate == 0 || r->cell_log == 0 ||
+       if (tab == NULL || r->rate == 0 ||
+           r->cell_log == 0 || r->cell_log >= 32 ||
            nla_len(tab) != TC_RTAB_SIZE) {
                NL_SET_ERR_MSG(extack, "Invalid rate table parameters for searching");
                return NULL;
@@ -1865,7 +1866,8 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb,
 static int tclass_del_notify(struct net *net,
                             const struct Qdisc_class_ops *cops,
                             struct sk_buff *oskb, struct nlmsghdr *n,
-                            struct Qdisc *q, unsigned long cl)
+                            struct Qdisc *q, unsigned long cl,
+                            struct netlink_ext_ack *extack)
 {
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
        struct sk_buff *skb;
@@ -1884,7 +1886,7 @@ static int tclass_del_notify(struct net *net,
                return -EINVAL;
        }
 
-       err = cops->delete(q, cl);
+       err = cops->delete(q, cl, extack);
        if (err) {
                kfree_skb(skb);
                return err;
@@ -2087,7 +2089,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n,
                                goto out;
                        break;
                case RTM_DELTCLASS:
-                       err = tclass_del_notify(net, cops, skb, n, q, cl);
+                       err = tclass_del_notify(net, cops, skb, n, q, cl, extack);
                        /* Unbind the class with flilters with 0 */
                        tc_bind_tclass(q, portid, clid, 0);
                        goto out;
index 007bd2d..d0c9a57 100644 (file)
@@ -320,7 +320,8 @@ err_out:
        return error;
 }
 
-static int atm_tc_delete(struct Qdisc *sch, unsigned long arg)
+static int atm_tc_delete(struct Qdisc *sch, unsigned long arg,
+                        struct netlink_ext_ack *extack)
 {
        struct atm_qdisc_data *p = qdisc_priv(sch);
        struct atm_flow_data *flow = (struct atm_flow_data *)arg;
index 53d45e0..320b3d3 100644 (file)
@@ -1675,7 +1675,8 @@ failure:
        return err;
 }
 
-static int cbq_delete(struct Qdisc *sch, unsigned long arg)
+static int cbq_delete(struct Qdisc *sch, unsigned long arg,
+                     struct netlink_ext_ack *extack)
 {
        struct cbq_sched_data *q = qdisc_priv(sch);
        struct cbq_class *cl = (struct cbq_class *)arg;
index bd618b0..50f680f 100644 (file)
@@ -362,7 +362,7 @@ static int choke_change(struct Qdisc *sch, struct nlattr *opt,
 
        ctl = nla_data(tb[TCA_CHOKE_PARMS]);
 
-       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog))
+       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Scell_log))
                return -EINVAL;
 
        if (ctl->limit > CHOKE_MAX_QUEUE)
index dde5646..fc1e470 100644 (file)
@@ -146,7 +146,8 @@ static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
        kfree(cl);
 }
 
-static int drr_delete_class(struct Qdisc *sch, unsigned long arg)
+static int drr_delete_class(struct Qdisc *sch, unsigned long arg,
+                           struct netlink_ext_ack *extack)
 {
        struct drr_sched *q = qdisc_priv(sch);
        struct drr_class *cl = (struct drr_class *)arg;
index 2b88710..cd2748e 100644 (file)
@@ -150,7 +150,8 @@ errout:
        return err;
 }
 
-static int dsmark_delete(struct Qdisc *sch, unsigned long arg)
+static int dsmark_delete(struct Qdisc *sch, unsigned long arg,
+                        struct netlink_ext_ack *extack)
 {
        struct dsmark_qdisc_data *p = qdisc_priv(sch);
 
index 8599c6f..e0bc775 100644 (file)
@@ -480,7 +480,7 @@ static inline int gred_change_vq(struct Qdisc *sch, int dp,
        struct gred_sched *table = qdisc_priv(sch);
        struct gred_sched_data *q = table->tab[dp];
 
-       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) {
+       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Scell_log)) {
                NL_SET_ERR_MSG_MOD(extack, "invalid RED parameters");
                return -EINVAL;
        }
index d1902fc..bf0034c 100644 (file)
@@ -1090,7 +1090,8 @@ hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
 }
 
 static int
-hfsc_delete_class(struct Qdisc *sch, unsigned long arg)
+hfsc_delete_class(struct Qdisc *sch, unsigned long arg,
+                 struct netlink_ext_ack *extack)
 {
        struct hfsc_sched *q = qdisc_priv(sch);
        struct hfsc_class *cl = (struct hfsc_class *)arg;
index cd70dbc..dff3adf 100644 (file)
@@ -114,6 +114,7 @@ struct htb_class {
         * Written often fields
         */
        struct gnet_stats_basic_packed bstats;
+       struct gnet_stats_basic_packed bstats_bias;
        struct tc_htb_xstats    xstats; /* our special stats */
 
        /* token bucket parameters */
@@ -174,6 +175,11 @@ struct htb_sched {
        int                     row_mask[TC_HTB_MAXDEPTH];
 
        struct htb_level        hlevel[TC_HTB_MAXDEPTH];
+
+       struct Qdisc            **direct_qdiscs;
+       unsigned int            num_direct_qdiscs;
+
+       bool                    offload;
 };
 
 /* find class in global hash table using given handle */
@@ -957,7 +963,7 @@ static void htb_reset(struct Qdisc *sch)
                        if (cl->level)
                                memset(&cl->inner, 0, sizeof(cl->inner));
                        else {
-                               if (cl->leaf.q)
+                               if (cl->leaf.q && !q->offload)
                                        qdisc_reset(cl->leaf.q);
                        }
                        cl->prio_activity = 0;
@@ -980,6 +986,7 @@ static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = {
        [TCA_HTB_DIRECT_QLEN] = { .type = NLA_U32 },
        [TCA_HTB_RATE64] = { .type = NLA_U64 },
        [TCA_HTB_CEIL64] = { .type = NLA_U64 },
+       [TCA_HTB_OFFLOAD] = { .type = NLA_FLAG },
 };
 
 static void htb_work_func(struct work_struct *work)
@@ -992,12 +999,27 @@ static void htb_work_func(struct work_struct *work)
        rcu_read_unlock();
 }
 
+static void htb_set_lockdep_class_child(struct Qdisc *q)
+{
+       static struct lock_class_key child_key;
+
+       lockdep_set_class(qdisc_lock(q), &child_key);
+}
+
+static int htb_offload(struct net_device *dev, struct tc_htb_qopt_offload *opt)
+{
+       return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_HTB, opt);
+}
+
 static int htb_init(struct Qdisc *sch, struct nlattr *opt,
                    struct netlink_ext_ack *extack)
 {
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_htb_qopt_offload offload_opt;
        struct htb_sched *q = qdisc_priv(sch);
        struct nlattr *tb[TCA_HTB_MAX + 1];
        struct tc_htb_glob *gopt;
+       unsigned int ntx;
        int err;
 
        qdisc_watchdog_init(&q->watchdog, sch);
@@ -1022,9 +1044,26 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt,
        if (gopt->version != HTB_VER >> 16)
                return -EINVAL;
 
+       q->offload = nla_get_flag(tb[TCA_HTB_OFFLOAD]);
+
+       if (q->offload) {
+               if (sch->parent != TC_H_ROOT)
+                       return -EOPNOTSUPP;
+
+               if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc)
+                       return -EOPNOTSUPP;
+
+               q->num_direct_qdiscs = dev->real_num_tx_queues;
+               q->direct_qdiscs = kcalloc(q->num_direct_qdiscs,
+                                          sizeof(*q->direct_qdiscs),
+                                          GFP_KERNEL);
+               if (!q->direct_qdiscs)
+                       return -ENOMEM;
+       }
+
        err = qdisc_class_hash_init(&q->clhash);
        if (err < 0)
-               return err;
+               goto err_free_direct_qdiscs;
 
        qdisc_skb_head_init(&q->direct_queue);
 
@@ -1037,7 +1076,107 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt,
                q->rate2quantum = 1;
        q->defcls = gopt->defcls;
 
+       if (!q->offload)
+               return 0;
+
+       for (ntx = 0; ntx < q->num_direct_qdiscs; ntx++) {
+               struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
+               struct Qdisc *qdisc;
+
+               qdisc = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
+                                         TC_H_MAKE(sch->handle, 0), extack);
+               if (!qdisc) {
+                       err = -ENOMEM;
+                       goto err_free_qdiscs;
+               }
+
+               htb_set_lockdep_class_child(qdisc);
+               q->direct_qdiscs[ntx] = qdisc;
+               qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
+       }
+
+       sch->flags |= TCQ_F_MQROOT;
+
+       offload_opt = (struct tc_htb_qopt_offload) {
+               .command = TC_HTB_CREATE,
+               .parent_classid = TC_H_MAJ(sch->handle) >> 16,
+               .classid = TC_H_MIN(q->defcls),
+               .extack = extack,
+       };
+       err = htb_offload(dev, &offload_opt);
+       if (err)
+               goto err_free_qdiscs;
+
        return 0;
+
+err_free_qdiscs:
+       /* TC_HTB_CREATE call failed, avoid any further calls to the driver. */
+       q->offload = false;
+
+       for (ntx = 0; ntx < q->num_direct_qdiscs && q->direct_qdiscs[ntx];
+            ntx++)
+               qdisc_put(q->direct_qdiscs[ntx]);
+
+       qdisc_class_hash_destroy(&q->clhash);
+       /* Prevent use-after-free and double-free when htb_destroy gets called.
+        */
+       q->clhash.hash = NULL;
+       q->clhash.hashsize = 0;
+
+err_free_direct_qdiscs:
+       kfree(q->direct_qdiscs);
+       q->direct_qdiscs = NULL;
+       return err;
+}
+
+static void htb_attach_offload(struct Qdisc *sch)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct htb_sched *q = qdisc_priv(sch);
+       unsigned int ntx;
+
+       for (ntx = 0; ntx < q->num_direct_qdiscs; ntx++) {
+               struct Qdisc *old, *qdisc = q->direct_qdiscs[ntx];
+
+               old = dev_graft_qdisc(qdisc->dev_queue, qdisc);
+               qdisc_put(old);
+               qdisc_hash_add(qdisc, false);
+       }
+       for (ntx = q->num_direct_qdiscs; ntx < dev->num_tx_queues; ntx++) {
+               struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
+               struct Qdisc *old = dev_graft_qdisc(dev_queue, NULL);
+
+               qdisc_put(old);
+       }
+
+       kfree(q->direct_qdiscs);
+       q->direct_qdiscs = NULL;
+}
+
+static void htb_attach_software(struct Qdisc *sch)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       unsigned int ntx;
+
+       /* Resemble qdisc_graft behavior. */
+       for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
+               struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
+               struct Qdisc *old = dev_graft_qdisc(dev_queue, sch);
+
+               qdisc_refcount_inc(sch);
+
+               qdisc_put(old);
+       }
+}
+
+static void htb_attach(struct Qdisc *sch)
+{
+       struct htb_sched *q = qdisc_priv(sch);
+
+       if (q->offload)
+               htb_attach_offload(sch);
+       else
+               htb_attach_software(sch);
 }
 
 static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
@@ -1046,6 +1185,11 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
        struct nlattr *nest;
        struct tc_htb_glob gopt;
 
+       if (q->offload)
+               sch->flags |= TCQ_F_OFFLOADED;
+       else
+               sch->flags &= ~TCQ_F_OFFLOADED;
+
        sch->qstats.overlimits = q->overlimits;
        /* Its safe to not acquire qdisc lock. As we hold RTNL,
         * no change can happen on the qdisc parameters.
@@ -1063,6 +1207,8 @@ static int htb_dump(struct Qdisc *sch, struct sk_buff *skb)
        if (nla_put(skb, TCA_HTB_INIT, sizeof(gopt), &gopt) ||
            nla_put_u32(skb, TCA_HTB_DIRECT_QLEN, q->direct_qlen))
                goto nla_put_failure;
+       if (q->offload && nla_put_flag(skb, TCA_HTB_OFFLOAD))
+               goto nla_put_failure;
 
        return nla_nest_end(skb, nest);
 
@@ -1075,6 +1221,7 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
                          struct sk_buff *skb, struct tcmsg *tcm)
 {
        struct htb_class *cl = (struct htb_class *)arg;
+       struct htb_sched *q = qdisc_priv(sch);
        struct nlattr *nest;
        struct tc_htb_opt opt;
 
@@ -1101,6 +1248,8 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
        opt.level = cl->level;
        if (nla_put(skb, TCA_HTB_PARMS, sizeof(opt), &opt))
                goto nla_put_failure;
+       if (q->offload && nla_put_flag(skb, TCA_HTB_OFFLOAD))
+               goto nla_put_failure;
        if ((cl->rate.rate_bytes_ps >= (1ULL << 32)) &&
            nla_put_u64_64bit(skb, TCA_HTB_RATE64, cl->rate.rate_bytes_ps,
                              TCA_HTB_PAD))
@@ -1117,10 +1266,39 @@ nla_put_failure:
        return -1;
 }
 
+static void htb_offload_aggregate_stats(struct htb_sched *q,
+                                       struct htb_class *cl)
+{
+       struct htb_class *c;
+       unsigned int i;
+
+       memset(&cl->bstats, 0, sizeof(cl->bstats));
+
+       for (i = 0; i < q->clhash.hashsize; i++) {
+               hlist_for_each_entry(c, &q->clhash.hash[i], common.hnode) {
+                       struct htb_class *p = c;
+
+                       while (p && p->level < cl->level)
+                               p = p->parent;
+
+                       if (p != cl)
+                               continue;
+
+                       cl->bstats.bytes += c->bstats_bias.bytes;
+                       cl->bstats.packets += c->bstats_bias.packets;
+                       if (c->level == 0) {
+                               cl->bstats.bytes += c->leaf.q->bstats.bytes;
+                               cl->bstats.packets += c->leaf.q->bstats.packets;
+                       }
+               }
+       }
+}
+
 static int
 htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
 {
        struct htb_class *cl = (struct htb_class *)arg;
+       struct htb_sched *q = qdisc_priv(sch);
        struct gnet_stats_queue qs = {
                .drops = cl->drops,
                .overlimits = cl->overlimits,
@@ -1135,6 +1313,19 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
        cl->xstats.ctokens = clamp_t(s64, PSCHED_NS2TICKS(cl->ctokens),
                                     INT_MIN, INT_MAX);
 
+       if (q->offload) {
+               if (!cl->level) {
+                       if (cl->leaf.q)
+                               cl->bstats = cl->leaf.q->bstats;
+                       else
+                               memset(&cl->bstats, 0, sizeof(cl->bstats));
+                       cl->bstats.bytes += cl->bstats_bias.bytes;
+                       cl->bstats.packets += cl->bstats_bias.packets;
+               } else {
+                       htb_offload_aggregate_stats(q, cl);
+               }
+       }
+
        if (gnet_stats_copy_basic(qdisc_root_sleeping_running(sch),
                                  d, NULL, &cl->bstats) < 0 ||
            gnet_stats_copy_rate_est(d, &cl->rate_est) < 0 ||
@@ -1144,19 +1335,97 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
        return gnet_stats_copy_app(d, &cl->xstats, sizeof(cl->xstats));
 }
 
+static struct netdev_queue *
+htb_select_queue(struct Qdisc *sch, struct tcmsg *tcm)
+{
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_htb_qopt_offload offload_opt;
+       int err;
+
+       offload_opt = (struct tc_htb_qopt_offload) {
+               .command = TC_HTB_LEAF_QUERY_QUEUE,
+               .classid = TC_H_MIN(tcm->tcm_parent),
+       };
+       err = htb_offload(dev, &offload_opt);
+       if (err || offload_opt.qid >= dev->num_tx_queues)
+               return NULL;
+       return netdev_get_tx_queue(dev, offload_opt.qid);
+}
+
+static struct Qdisc *
+htb_graft_helper(struct netdev_queue *dev_queue, struct Qdisc *new_q)
+{
+       struct net_device *dev = dev_queue->dev;
+       struct Qdisc *old_q;
+
+       if (dev->flags & IFF_UP)
+               dev_deactivate(dev);
+       old_q = dev_graft_qdisc(dev_queue, new_q);
+       if (new_q)
+               new_q->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
+       if (dev->flags & IFF_UP)
+               dev_activate(dev);
+
+       return old_q;
+}
+
+static void htb_offload_move_qdisc(struct Qdisc *sch, u16 qid_old, u16 qid_new)
+{
+       struct netdev_queue *queue_old, *queue_new;
+       struct net_device *dev = qdisc_dev(sch);
+       struct Qdisc *qdisc;
+
+       queue_old = netdev_get_tx_queue(dev, qid_old);
+       queue_new = netdev_get_tx_queue(dev, qid_new);
+
+       if (dev->flags & IFF_UP)
+               dev_deactivate(dev);
+       qdisc = dev_graft_qdisc(queue_old, NULL);
+       qdisc->dev_queue = queue_new;
+       qdisc = dev_graft_qdisc(queue_new, qdisc);
+       if (dev->flags & IFF_UP)
+               dev_activate(dev);
+
+       WARN_ON(!(qdisc->flags & TCQ_F_BUILTIN));
+}
+
 static int htb_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
                     struct Qdisc **old, struct netlink_ext_ack *extack)
 {
+       struct netdev_queue *dev_queue = sch->dev_queue;
        struct htb_class *cl = (struct htb_class *)arg;
+       struct htb_sched *q = qdisc_priv(sch);
+       struct Qdisc *old_q;
 
        if (cl->level)
                return -EINVAL;
-       if (new == NULL &&
-           (new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
-                                    cl->common.classid, extack)) == NULL)
-               return -ENOBUFS;
+
+       if (q->offload) {
+               dev_queue = new->dev_queue;
+               WARN_ON(dev_queue != cl->leaf.q->dev_queue);
+       }
+
+       if (!new) {
+               new = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
+                                       cl->common.classid, extack);
+               if (!new)
+                       return -ENOBUFS;
+       }
+
+       if (q->offload) {
+               htb_set_lockdep_class_child(new);
+               /* One ref for cl->leaf.q, the other for dev_queue->qdisc. */
+               qdisc_refcount_inc(new);
+               old_q = htb_graft_helper(dev_queue, new);
+       }
 
        *old = qdisc_replace(sch, new, &cl->leaf.q);
+
+       if (q->offload) {
+               WARN_ON(old_q != *old);
+               qdisc_put(old_q);
+       }
+
        return 0;
 }
 
@@ -1184,9 +1453,10 @@ static inline int htb_parent_last_child(struct htb_class *cl)
        return 1;
 }
 
-static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
+static void htb_parent_to_leaf(struct Qdisc *sch, struct htb_class *cl,
                               struct Qdisc *new_q)
 {
+       struct htb_sched *q = qdisc_priv(sch);
        struct htb_class *parent = cl->parent;
 
        WARN_ON(cl->level || !cl->leaf.q || cl->prio_activity);
@@ -1204,6 +1474,76 @@ static void htb_parent_to_leaf(struct htb_sched *q, struct htb_class *cl,
        parent->cmode = HTB_CAN_SEND;
 }
 
+static void htb_parent_to_leaf_offload(struct Qdisc *sch,
+                                      struct netdev_queue *dev_queue,
+                                      struct Qdisc *new_q)
+{
+       struct Qdisc *old_q;
+
+       /* One ref for cl->leaf.q, the other for dev_queue->qdisc. */
+       qdisc_refcount_inc(new_q);
+       old_q = htb_graft_helper(dev_queue, new_q);
+       WARN_ON(!(old_q->flags & TCQ_F_BUILTIN));
+}
+
+static int htb_destroy_class_offload(struct Qdisc *sch, struct htb_class *cl,
+                                    bool last_child, bool destroying,
+                                    struct netlink_ext_ack *extack)
+{
+       struct tc_htb_qopt_offload offload_opt;
+       struct Qdisc *q = cl->leaf.q;
+       struct Qdisc *old = NULL;
+       int err;
+
+       if (cl->level)
+               return -EINVAL;
+
+       WARN_ON(!q);
+       if (!destroying) {
+               /* On destroy of HTB, two cases are possible:
+                * 1. q is a normal qdisc, but q->dev_queue has noop qdisc.
+                * 2. q is a noop qdisc (for nodes that were inner),
+                *    q->dev_queue is noop_netdev_queue.
+                */
+               old = htb_graft_helper(q->dev_queue, NULL);
+               WARN_ON(!old);
+               WARN_ON(old != q);
+       }
+
+       if (cl->parent) {
+               cl->parent->bstats_bias.bytes += q->bstats.bytes;
+               cl->parent->bstats_bias.packets += q->bstats.packets;
+       }
+
+       offload_opt = (struct tc_htb_qopt_offload) {
+               .command = !last_child ? TC_HTB_LEAF_DEL :
+                          destroying ? TC_HTB_LEAF_DEL_LAST_FORCE :
+                          TC_HTB_LEAF_DEL_LAST,
+               .classid = cl->common.classid,
+               .extack = extack,
+       };
+       err = htb_offload(qdisc_dev(sch), &offload_opt);
+
+       if (!err || destroying)
+               qdisc_put(old);
+       else
+               htb_graft_helper(q->dev_queue, old);
+
+       if (last_child)
+               return err;
+
+       if (!err && offload_opt.moved_qid != 0) {
+               if (destroying)
+                       q->dev_queue = netdev_get_tx_queue(qdisc_dev(sch),
+                                                          offload_opt.qid);
+               else
+                       htb_offload_move_qdisc(sch, offload_opt.moved_qid,
+                                              offload_opt.qid);
+       }
+
+       return err;
+}
+
 static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
 {
        if (!cl->level) {
@@ -1217,8 +1557,11 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
 
 static void htb_destroy(struct Qdisc *sch)
 {
+       struct net_device *dev = qdisc_dev(sch);
+       struct tc_htb_qopt_offload offload_opt;
        struct htb_sched *q = qdisc_priv(sch);
        struct hlist_node *next;
+       bool nonempty, changed;
        struct htb_class *cl;
        unsigned int i;
 
@@ -1237,21 +1580,68 @@ static void htb_destroy(struct Qdisc *sch)
                        cl->block = NULL;
                }
        }
-       for (i = 0; i < q->clhash.hashsize; i++) {
-               hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
-                                         common.hnode)
-                       htb_destroy_class(sch, cl);
-       }
+
+       do {
+               nonempty = false;
+               changed = false;
+               for (i = 0; i < q->clhash.hashsize; i++) {
+                       hlist_for_each_entry_safe(cl, next, &q->clhash.hash[i],
+                                                 common.hnode) {
+                               bool last_child;
+
+                               if (!q->offload) {
+                                       htb_destroy_class(sch, cl);
+                                       continue;
+                               }
+
+                               nonempty = true;
+
+                               if (cl->level)
+                                       continue;
+
+                               changed = true;
+
+                               last_child = htb_parent_last_child(cl);
+                               htb_destroy_class_offload(sch, cl, last_child,
+                                                         true, NULL);
+                               qdisc_class_hash_remove(&q->clhash,
+                                                       &cl->common);
+                               if (cl->parent)
+                                       cl->parent->children--;
+                               if (last_child)
+                                       htb_parent_to_leaf(sch, cl, NULL);
+                               htb_destroy_class(sch, cl);
+                       }
+               }
+       } while (changed);
+       WARN_ON(nonempty);
+
        qdisc_class_hash_destroy(&q->clhash);
        __qdisc_reset_queue(&q->direct_queue);
+
+       if (!q->offload)
+               return;
+
+       offload_opt = (struct tc_htb_qopt_offload) {
+               .command = TC_HTB_DESTROY,
+       };
+       htb_offload(dev, &offload_opt);
+
+       if (!q->direct_qdiscs)
+               return;
+       for (i = 0; i < q->num_direct_qdiscs && q->direct_qdiscs[i]; i++)
+               qdisc_put(q->direct_qdiscs[i]);
+       kfree(q->direct_qdiscs);
 }
 
-static int htb_delete(struct Qdisc *sch, unsigned long arg)
+static int htb_delete(struct Qdisc *sch, unsigned long arg,
+                     struct netlink_ext_ack *extack)
 {
        struct htb_sched *q = qdisc_priv(sch);
        struct htb_class *cl = (struct htb_class *)arg;
        struct Qdisc *new_q = NULL;
        int last_child = 0;
+       int err;
 
        /* TODO: why don't allow to delete subtree ? references ? does
         * tc subsys guarantee us that in htb_destroy it holds no class
@@ -1260,11 +1650,28 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
        if (cl->children || cl->filter_cnt)
                return -EBUSY;
 
-       if (!cl->level && htb_parent_last_child(cl)) {
-               new_q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
+       if (!cl->level && htb_parent_last_child(cl))
+               last_child = 1;
+
+       if (q->offload) {
+               err = htb_destroy_class_offload(sch, cl, last_child, false,
+                                               extack);
+               if (err)
+                       return err;
+       }
+
+       if (last_child) {
+               struct netdev_queue *dev_queue;
+
+               dev_queue = q->offload ? cl->leaf.q->dev_queue : sch->dev_queue;
+               new_q = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
                                          cl->parent->common.classid,
                                          NULL);
-               last_child = 1;
+               if (q->offload) {
+                       if (new_q)
+                               htb_set_lockdep_class_child(new_q);
+                       htb_parent_to_leaf_offload(sch, dev_queue, new_q);
+               }
        }
 
        sch_tree_lock(sch);
@@ -1285,7 +1692,7 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg)
                                  &q->hlevel[cl->level].wait_pq);
 
        if (last_child)
-               htb_parent_to_leaf(q, cl, new_q);
+               htb_parent_to_leaf(sch, cl, new_q);
 
        sch_tree_unlock(sch);
 
@@ -1300,9 +1707,11 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
        int err = -EINVAL;
        struct htb_sched *q = qdisc_priv(sch);
        struct htb_class *cl = (struct htb_class *)*arg, *parent;
+       struct tc_htb_qopt_offload offload_opt;
        struct nlattr *opt = tca[TCA_OPTIONS];
        struct nlattr *tb[TCA_HTB_MAX + 1];
        struct Qdisc *parent_qdisc = NULL;
+       struct netdev_queue *dev_queue;
        struct tc_htb_opt *hopt;
        u64 rate64, ceil64;
        int warn = 0;
@@ -1335,8 +1744,12 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                qdisc_put_rtab(qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB],
                                              NULL));
 
+       rate64 = tb[TCA_HTB_RATE64] ? nla_get_u64(tb[TCA_HTB_RATE64]) : 0;
+       ceil64 = tb[TCA_HTB_CEIL64] ? nla_get_u64(tb[TCA_HTB_CEIL64]) : 0;
+
        if (!cl) {              /* new class */
-               struct Qdisc *new_q;
+               struct net_device *dev = qdisc_dev(sch);
+               struct Qdisc *new_q, *old_q;
                int prio;
                struct {
                        struct nlattr           nla;
@@ -1379,11 +1792,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                                                NULL,
                                                qdisc_root_sleeping_running(sch),
                                                tca[TCA_RATE] ? : &est.nla);
-                       if (err) {
-                               tcf_block_put(cl->block);
-                               kfree(cl);
-                               goto failure;
-                       }
+                       if (err)
+                               goto err_block_put;
                }
 
                cl->children = 0;
@@ -1392,12 +1802,76 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                for (prio = 0; prio < TC_HTB_NUMPRIO; prio++)
                        RB_CLEAR_NODE(&cl->node[prio]);
 
+               cl->common.classid = classid;
+
+               /* Make sure nothing interrupts us in between of two
+                * ndo_setup_tc calls.
+                */
+               ASSERT_RTNL();
+
                /* create leaf qdisc early because it uses kmalloc(GFP_KERNEL)
                 * so that can't be used inside of sch_tree_lock
                 * -- thanks to Karlis Peisenieks
                 */
-               new_q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops,
+               if (!q->offload) {
+                       dev_queue = sch->dev_queue;
+               } else if (!(parent && !parent->level)) {
+                       /* Assign a dev_queue to this classid. */
+                       offload_opt = (struct tc_htb_qopt_offload) {
+                               .command = TC_HTB_LEAF_ALLOC_QUEUE,
+                               .classid = cl->common.classid,
+                               .parent_classid = parent ?
+                                       TC_H_MIN(parent->common.classid) :
+                                       TC_HTB_CLASSID_ROOT,
+                               .rate = max_t(u64, hopt->rate.rate, rate64),
+                               .ceil = max_t(u64, hopt->ceil.rate, ceil64),
+                               .extack = extack,
+                       };
+                       err = htb_offload(dev, &offload_opt);
+                       if (err) {
+                               pr_err("htb: TC_HTB_LEAF_ALLOC_QUEUE failed with err = %d\n",
+                                      err);
+                               goto err_kill_estimator;
+                       }
+                       dev_queue = netdev_get_tx_queue(dev, offload_opt.qid);
+               } else { /* First child. */
+                       dev_queue = parent->leaf.q->dev_queue;
+                       old_q = htb_graft_helper(dev_queue, NULL);
+                       WARN_ON(old_q != parent->leaf.q);
+                       offload_opt = (struct tc_htb_qopt_offload) {
+                               .command = TC_HTB_LEAF_TO_INNER,
+                               .classid = cl->common.classid,
+                               .parent_classid =
+                                       TC_H_MIN(parent->common.classid),
+                               .rate = max_t(u64, hopt->rate.rate, rate64),
+                               .ceil = max_t(u64, hopt->ceil.rate, ceil64),
+                               .extack = extack,
+                       };
+                       err = htb_offload(dev, &offload_opt);
+                       if (err) {
+                               pr_err("htb: TC_HTB_LEAF_TO_INNER failed with err = %d\n",
+                                      err);
+                               htb_graft_helper(dev_queue, old_q);
+                               goto err_kill_estimator;
+                       }
+                       parent->bstats_bias.bytes += old_q->bstats.bytes;
+                       parent->bstats_bias.packets += old_q->bstats.packets;
+                       qdisc_put(old_q);
+               }
+               new_q = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops,
                                          classid, NULL);
+               if (q->offload) {
+                       if (new_q) {
+                               htb_set_lockdep_class_child(new_q);
+                               /* One ref for cl->leaf.q, the other for
+                                * dev_queue->qdisc.
+                                */
+                               qdisc_refcount_inc(new_q);
+                       }
+                       old_q = htb_graft_helper(dev_queue, new_q);
+                       /* No qdisc_put needed. */
+                       WARN_ON(!(old_q->flags & TCQ_F_BUILTIN));
+               }
                sch_tree_lock(sch);
                if (parent && !parent->level) {
                        /* turn parent into inner node */
@@ -1415,10 +1889,10 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                                         : TC_HTB_MAXDEPTH) - 1;
                        memset(&parent->inner, 0, sizeof(parent->inner));
                }
+
                /* leaf (we) needs elementary qdisc */
                cl->leaf.q = new_q ? new_q : &noop_qdisc;
 
-               cl->common.classid = classid;
                cl->parent = parent;
 
                /* set class to be in HTB_CAN_SEND state */
@@ -1444,12 +1918,30 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
                        if (err)
                                return err;
                }
-               sch_tree_lock(sch);
-       }
 
-       rate64 = tb[TCA_HTB_RATE64] ? nla_get_u64(tb[TCA_HTB_RATE64]) : 0;
+               if (q->offload) {
+                       struct net_device *dev = qdisc_dev(sch);
+
+                       offload_opt = (struct tc_htb_qopt_offload) {
+                               .command = TC_HTB_NODE_MODIFY,
+                               .classid = cl->common.classid,
+                               .rate = max_t(u64, hopt->rate.rate, rate64),
+                               .ceil = max_t(u64, hopt->ceil.rate, ceil64),
+                               .extack = extack,
+                       };
+                       err = htb_offload(dev, &offload_opt);
+                       if (err)
+                               /* Estimator was replaced, and rollback may fail
+                                * as well, so we don't try to recover it, and
+                                * the estimator won't work property with the
+                                * offload anyway, because bstats are updated
+                                * only when the stats are queried.
+                                */
+                               return err;
+               }
 
-       ceil64 = tb[TCA_HTB_CEIL64] ? nla_get_u64(tb[TCA_HTB_CEIL64]) : 0;
+               sch_tree_lock(sch);
+       }
 
        psched_ratecfg_precompute(&cl->rate, &hopt->rate, rate64);
        psched_ratecfg_precompute(&cl->ceil, &hopt->ceil, ceil64);
@@ -1492,6 +1984,11 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
        *arg = (unsigned long)cl;
        return 0;
 
+err_kill_estimator:
+       gen_kill_estimator(&cl->rate_est);
+err_block_put:
+       tcf_block_put(cl->block);
+       kfree(cl);
 failure:
        return err;
 }
@@ -1557,6 +2054,7 @@ static void htb_walk(struct Qdisc *sch, struct qdisc_walker *arg)
 }
 
 static const struct Qdisc_class_ops htb_class_ops = {
+       .select_queue   =       htb_select_queue,
        .graft          =       htb_graft,
        .leaf           =       htb_leaf,
        .qlen_notify    =       htb_qlen_notify,
@@ -1579,6 +2077,7 @@ static struct Qdisc_ops htb_qdisc_ops __read_mostly = {
        .dequeue        =       htb_dequeue,
        .peek           =       qdisc_peek_dequeued,
        .init           =       htb_init,
+       .attach         =       htb_attach,
        .reset          =       htb_reset,
        .destroy        =       htb_destroy,
        .dump           =       htb_dump,
index 6335230..1db9d4a 100644 (file)
@@ -529,7 +529,8 @@ static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
        kfree(cl);
 }
 
-static int qfq_delete_class(struct Qdisc *sch, unsigned long arg)
+static int qfq_delete_class(struct Qdisc *sch, unsigned long arg,
+                           struct netlink_ext_ack *extack)
 {
        struct qfq_sched *q = qdisc_priv(sch);
        struct qfq_class *cl = (struct qfq_class *)arg;
index e89fab6..b4ae34d 100644 (file)
@@ -250,7 +250,7 @@ static int __red_change(struct Qdisc *sch, struct nlattr **tb,
        max_P = tb[TCA_RED_MAX_P] ? nla_get_u32(tb[TCA_RED_MAX_P]) : 0;
 
        ctl = nla_data(tb[TCA_RED_PARMS]);
-       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog))
+       if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog, ctl->Scell_log))
                return -EINVAL;
 
        err = red_get_flags(ctl->flags, TC_RED_HISTORIC_FLAGS,
index da047a3..dde829d 100644 (file)
@@ -649,7 +649,8 @@ static int sfb_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
        return -ENOSYS;
 }
 
-static int sfb_delete(struct Qdisc *sch, unsigned long cl)
+static int sfb_delete(struct Qdisc *sch, unsigned long cl,
+                     struct netlink_ext_ack *extack)
 {
        return -ENOSYS;
 }
index bca2be5..b25e514 100644 (file)
@@ -647,7 +647,7 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt)
        }
 
        if (ctl_v1 && !red_check_params(ctl_v1->qth_min, ctl_v1->qth_max,
-                                       ctl_v1->Wlog))
+                                       ctl_v1->Wlog, ctl_v1->Scell_log))
                return -EINVAL;
        if (ctl_v1 && ctl_v1->qth_min) {
                p = kmalloc(sizeof(*p), GFP_KERNEL);
index c74817e..8287894 100644 (file)
@@ -241,7 +241,7 @@ static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
                                /* Here, we are just trying to find out the
                                 * first available interval in the next cycle.
                                 */
-                               entry_available = 1;
+                               entry_available = true;
                                entry_found = entry;
                                *interval_start = ktime_add_ns(curr_intv_start, cycle);
                                *interval_end = ktime_add_ns(curr_intv_end, cycle);
@@ -372,7 +372,7 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
        packet_transmit_time = length_to_duration(q, len);
 
        do {
-               sched_changed = 0;
+               sched_changed = false;
 
                entry = find_entry_to_transmit(skb, sch, sched, admin,
                                               minimum_time,
@@ -390,7 +390,7 @@ static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
                if (admin && admin != sched &&
                    ktime_after(txtime, admin->base_time)) {
                        sched = admin;
-                       sched_changed = 1;
+                       sched_changed = true;
                        continue;
                }
 
@@ -1605,8 +1605,9 @@ static void taprio_reset(struct Qdisc *sch)
 
        hrtimer_cancel(&q->advance_timer);
        if (q->qdiscs) {
-               for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
-                       qdisc_reset(q->qdiscs[i]);
+               for (i = 0; i < dev->num_tx_queues; i++)
+                       if (q->qdiscs[i])
+                               qdisc_reset(q->qdiscs[i]);
        }
        sch->qstats.backlog = 0;
        sch->q.qlen = 0;
@@ -1626,7 +1627,7 @@ static void taprio_destroy(struct Qdisc *sch)
        taprio_disable_offload(dev, q, NULL);
 
        if (q->qdiscs) {
-               for (i = 0; i < dev->num_tx_queues && q->qdiscs[i]; i++)
+               for (i = 0; i < dev->num_tx_queues; i++)
                        qdisc_put(q->qdiscs[i]);
 
                kfree(q->qdiscs);
index ce281a9..eb874e3 100644 (file)
@@ -68,7 +68,7 @@ static struct sk_buff *sctp_gso_segment(struct sk_buff *skb,
                goto out;
        }
 
-       segs = skb_segment(skb, features | NETIF_F_HW_CSUM | NETIF_F_SG);
+       segs = skb_segment(skb, (features | NETIF_F_HW_CSUM) & ~NETIF_F_SG);
        if (IS_ERR(segs))
                goto out;
 
index 59342b5..0df85a1 100644 (file)
@@ -246,7 +246,8 @@ int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb)
                goto errattr;
        smc_clc_get_hostname(&host);
        if (host) {
-               snprintf(hostname, sizeof(hostname), "%s", host);
+               memcpy(hostname, host, SMC_MAX_HOSTNAME_LEN);
+               hostname[SMC_MAX_HOSTNAME_LEN] = 0;
                if (nla_put_string(skb, SMC_NLA_SYS_LOCAL_HOST, hostname))
                        goto errattr;
        }
@@ -257,7 +258,8 @@ int smc_nl_get_sys_info(struct sk_buff *skb, struct netlink_callback *cb)
                smc_ism_get_system_eid(smcd_dev, &seid);
        mutex_unlock(&smcd_dev_list.mutex);
        if (seid && smc_ism_is_v2_capable()) {
-               snprintf(smc_seid, sizeof(smc_seid), "%s", seid);
+               memcpy(smc_seid, seid, SMC_MAX_EID_LEN);
+               smc_seid[SMC_MAX_EID_LEN] = 0;
                if (nla_put_string(skb, SMC_NLA_SYS_SEID, smc_seid))
                        goto errattr;
        }
@@ -295,7 +297,8 @@ static int smc_nl_fill_lgr(struct smc_link_group *lgr,
                goto errattr;
        if (nla_put_u8(skb, SMC_NLA_LGR_R_VLAN_ID, lgr->vlan_id))
                goto errattr;
-       snprintf(smc_target, sizeof(smc_target), "%s", lgr->pnet_id);
+       memcpy(smc_target, lgr->pnet_id, SMC_MAX_PNETID_LEN);
+       smc_target[SMC_MAX_PNETID_LEN] = 0;
        if (nla_put_string(skb, SMC_NLA_LGR_R_PNETID, smc_target))
                goto errattr;
 
@@ -312,7 +315,7 @@ static int smc_nl_fill_lgr_link(struct smc_link_group *lgr,
                                struct sk_buff *skb,
                                struct netlink_callback *cb)
 {
-       char smc_ibname[IB_DEVICE_NAME_MAX + 1];
+       char smc_ibname[IB_DEVICE_NAME_MAX];
        u8 smc_gid_target[41];
        struct nlattr *attrs;
        u32 link_uid = 0;
@@ -461,7 +464,8 @@ static int smc_nl_fill_smcd_lgr(struct smc_link_group *lgr,
                goto errattr;
        if (nla_put_u32(skb, SMC_NLA_LGR_D_CHID, smc_ism_get_chid(lgr->smcd)))
                goto errattr;
-       snprintf(smc_pnet, sizeof(smc_pnet), "%s", lgr->smcd->pnetid);
+       memcpy(smc_pnet, lgr->smcd->pnetid, SMC_MAX_PNETID_LEN);
+       smc_pnet[SMC_MAX_PNETID_LEN] = 0;
        if (nla_put_string(skb, SMC_NLA_LGR_D_PNETID, smc_pnet))
                goto errattr;
 
@@ -474,10 +478,12 @@ static int smc_nl_fill_smcd_lgr(struct smc_link_group *lgr,
                goto errv2attr;
        if (nla_put_u8(skb, SMC_NLA_LGR_V2_OS, lgr->peer_os))
                goto errv2attr;
-       snprintf(smc_host, sizeof(smc_host), "%s", lgr->peer_hostname);
+       memcpy(smc_host, lgr->peer_hostname, SMC_MAX_HOSTNAME_LEN);
+       smc_host[SMC_MAX_HOSTNAME_LEN] = 0;
        if (nla_put_string(skb, SMC_NLA_LGR_V2_PEER_HOST, smc_host))
                goto errv2attr;
-       snprintf(smc_eid, sizeof(smc_eid), "%s", lgr->negotiated_eid);
+       memcpy(smc_eid, lgr->negotiated_eid, SMC_MAX_EID_LEN);
+       smc_eid[SMC_MAX_EID_LEN] = 0;
        if (nla_put_string(skb, SMC_NLA_LGR_V2_NEG_EID, smc_eid))
                goto errv2attr;
 
index ddd7fac..7d7ba03 100644 (file)
@@ -371,8 +371,8 @@ static int smc_nl_handle_dev_port(struct sk_buff *skb,
        if (nla_put_u8(skb, SMC_NLA_DEV_PORT_PNET_USR,
                       smcibdev->pnetid_by_user[port]))
                goto errattr;
-       snprintf(smc_pnet, sizeof(smc_pnet), "%s",
-                (char *)&smcibdev->pnetid[port]);
+       memcpy(smc_pnet, &smcibdev->pnetid[port], SMC_MAX_PNETID_LEN);
+       smc_pnet[SMC_MAX_PNETID_LEN] = 0;
        if (nla_put_string(skb, SMC_NLA_DEV_PORT_PNETID, smc_pnet))
                goto errattr;
        if (nla_put_u32(skb, SMC_NLA_DEV_PORT_NETDEV,
@@ -414,7 +414,7 @@ static int smc_nl_handle_smcr_dev(struct smc_ib_device *smcibdev,
                                  struct sk_buff *skb,
                                  struct netlink_callback *cb)
 {
-       char smc_ibname[IB_DEVICE_NAME_MAX + 1];
+       char smc_ibname[IB_DEVICE_NAME_MAX];
        struct smc_pci_dev smc_pci_dev;
        struct pci_dev *pci_dev;
        unsigned char is_crit;
index 524ef64..9c6e958 100644 (file)
@@ -250,7 +250,8 @@ static int smc_nl_handle_smcd_dev(struct smcd_dev *smcd,
                goto errattr;
        if (nla_put_u8(skb, SMC_NLA_DEV_PORT_PNET_USR, smcd->pnetid_by_user))
                goto errportattr;
-       snprintf(smc_pnet, sizeof(smc_pnet), "%s", smcd->pnetid);
+       memcpy(smc_pnet, smcd->pnetid, SMC_MAX_PNETID_LEN);
+       smc_pnet[SMC_MAX_PNETID_LEN] = 0;
        if (nla_put_string(skb, SMC_NLA_DEV_PORT_PNETID, smc_pnet))
                goto errportattr;
 
index 010dcb8..6e4dbd5 100644 (file)
@@ -185,7 +185,7 @@ static int rpc_parse_scope_id(struct net *net, const char *buf,
                        scope_id = dev->ifindex;
                        dev_put(dev);
                } else {
-                       if (kstrtou32(p, 10, &scope_id) == 0) {
+                       if (kstrtou32(p, 10, &scope_id) != 0) {
                                kfree(p);
                                return 0;
                        }
index 5fb9164..dcc50ae 100644 (file)
@@ -857,6 +857,7 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
        err = -EAGAIN;
        if (len <= 0)
                goto out_release;
+       trace_svc_xdr_recvfrom(&rqstp->rq_arg);
 
        clear_bit(XPT_OLD, &xprt->xpt_flags);
 
@@ -866,7 +867,6 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
 
        if (serv->sv_stats)
                serv->sv_stats->netcnt++;
-       trace_svc_xdr_recvfrom(rqstp, &rqstp->rq_arg);
        return len;
 out_release:
        rqstp->rq_res.len = 0;
@@ -904,7 +904,7 @@ int svc_send(struct svc_rqst *rqstp)
        xb->len = xb->head[0].iov_len +
                xb->page_len +
                xb->tail[0].iov_len;
-       trace_svc_xdr_sendto(rqstp, xb);
+       trace_svc_xdr_sendto(rqstp->rq_xid, xb);
        trace_svc_stats_latency(rqstp);
 
        len = xprt->xpt_ops->xpo_sendto(rqstp);
index b248f23..c9766d0 100644 (file)
@@ -1062,6 +1062,90 @@ err_noclose:
        return 0;       /* record not complete */
 }
 
+static int svc_tcp_send_kvec(struct socket *sock, const struct kvec *vec,
+                             int flags)
+{
+       return kernel_sendpage(sock, virt_to_page(vec->iov_base),
+                              offset_in_page(vec->iov_base),
+                              vec->iov_len, flags);
+}
+
+/*
+ * kernel_sendpage() is used exclusively to reduce the number of
+ * copy operations in this path. Therefore the caller must ensure
+ * that the pages backing @xdr are unchanging.
+ *
+ * In addition, the logic assumes that * .bv_len is never larger
+ * than PAGE_SIZE.
+ */
+static int svc_tcp_sendmsg(struct socket *sock, struct msghdr *msg,
+                          struct xdr_buf *xdr, rpc_fraghdr marker,
+                          unsigned int *sentp)
+{
+       const struct kvec *head = xdr->head;
+       const struct kvec *tail = xdr->tail;
+       struct kvec rm = {
+               .iov_base       = &marker,
+               .iov_len        = sizeof(marker),
+       };
+       int flags, ret;
+
+       *sentp = 0;
+       xdr_alloc_bvec(xdr, GFP_KERNEL);
+
+       msg->msg_flags = MSG_MORE;
+       ret = kernel_sendmsg(sock, msg, &rm, 1, rm.iov_len);
+       if (ret < 0)
+               return ret;
+       *sentp += ret;
+       if (ret != rm.iov_len)
+               return -EAGAIN;
+
+       flags = head->iov_len < xdr->len ? MSG_MORE | MSG_SENDPAGE_NOTLAST : 0;
+       ret = svc_tcp_send_kvec(sock, head, flags);
+       if (ret < 0)
+               return ret;
+       *sentp += ret;
+       if (ret != head->iov_len)
+               goto out;
+
+       if (xdr->page_len) {
+               unsigned int offset, len, remaining;
+               struct bio_vec *bvec;
+
+               bvec = xdr->bvec;
+               offset = xdr->page_base;
+               remaining = xdr->page_len;
+               flags = MSG_MORE | MSG_SENDPAGE_NOTLAST;
+               while (remaining > 0) {
+                       if (remaining <= PAGE_SIZE && tail->iov_len == 0)
+                               flags = 0;
+                       len = min(remaining, bvec->bv_len);
+                       ret = kernel_sendpage(sock, bvec->bv_page,
+                                             bvec->bv_offset + offset,
+                                             len, flags);
+                       if (ret < 0)
+                               return ret;
+                       *sentp += ret;
+                       if (ret != len)
+                               goto out;
+                       remaining -= len;
+                       offset = 0;
+                       bvec++;
+               }
+       }
+
+       if (tail->iov_len) {
+               ret = svc_tcp_send_kvec(sock, tail, 0);
+               if (ret < 0)
+                       return ret;
+               *sentp += ret;
+       }
+
+out:
+       return 0;
+}
+
 /**
  * svc_tcp_sendto - Send out a reply on a TCP socket
  * @rqstp: completed svc_rqst
@@ -1089,7 +1173,7 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
        mutex_lock(&xprt->xpt_mutex);
        if (svc_xprt_is_dead(xprt))
                goto out_notconn;
-       err = xprt_sock_sendmsg(svsk->sk_sock, &msg, xdr, 0, marker, &sent);
+       err = svc_tcp_sendmsg(svsk->sk_sock, &msg, xdr, marker, &sent);
        xdr_free_bvec(xdr);
        trace_svcsock_tcp_send(xprt, err < 0 ? err : sent);
        if (err < 0 || sent != (xdr->len + sizeof(marker)))
index bd69a31..c5561d7 100644 (file)
@@ -3,4 +3,4 @@
 # Makefile for the Switch device API
 #
 
-obj-$(CONFIG_NET_SWITCHDEV) += switchdev.o
+obj-y += switchdev.o
index 23d8685..94113ca 100644 (file)
@@ -100,15 +100,13 @@ static int switchdev_deferred_enqueue(struct net_device *dev,
 
 static int switchdev_port_attr_notify(enum switchdev_notifier_type nt,
                                      struct net_device *dev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans)
+                                     const struct switchdev_attr *attr)
 {
        int err;
        int rc;
 
        struct switchdev_notifier_port_attr_info attr_info = {
                .attr = attr,
-               .trans = trans,
                .handled = false,
        };
 
@@ -129,34 +127,7 @@ static int switchdev_port_attr_notify(enum switchdev_notifier_type nt,
 static int switchdev_port_attr_set_now(struct net_device *dev,
                                       const struct switchdev_attr *attr)
 {
-       struct switchdev_trans trans;
-       int err;
-
-       /* Phase I: prepare for attr set. Driver/device should fail
-        * here if there are going to be issues in the commit phase,
-        * such as lack of resources or support.  The driver/device
-        * should reserve resources needed for the commit phase here,
-        * but should not commit the attr.
-        */
-
-       trans.ph_prepare = true;
-       err = switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
-                                        &trans);
-       if (err)
-               return err;
-
-       /* Phase II: commit attr set.  This cannot fail as a fault
-        * of driver/device.  If it does, it's a bug in the driver/device
-        * because the driver said everythings was OK in phase I.
-        */
-
-       trans.ph_prepare = false;
-       err = switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr,
-                                        &trans);
-       WARN(err, "%s: Commit of attribute (id=%d) failed.\n",
-            dev->name, attr->id);
-
-       return err;
+       return switchdev_port_attr_notify(SWITCHDEV_PORT_ATTR_SET, dev, attr);
 }
 
 static void switchdev_port_attr_set_deferred(struct net_device *dev,
@@ -186,10 +157,6 @@ static int switchdev_port_attr_set_defer(struct net_device *dev,
  *     @dev: port device
  *     @attr: attribute to set
  *
- *     Use a 2-phase prepare-commit transaction model to ensure
- *     system is not left in a partially updated state due to
- *     failure from driver/device.
- *
  *     rtnl_lock must be held and must not be in atomic section,
  *     in case SWITCHDEV_F_DEFER flag is not set.
  */
@@ -221,7 +188,6 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj)
 static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
                                     struct net_device *dev,
                                     const struct switchdev_obj *obj,
-                                    struct switchdev_trans *trans,
                                     struct netlink_ext_ack *extack)
 {
        int rc;
@@ -229,7 +195,6 @@ static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
 
        struct switchdev_notifier_port_obj_info obj_info = {
                .obj = obj,
-               .trans = trans,
                .handled = false,
        };
 
@@ -244,48 +209,15 @@ static int switchdev_port_obj_notify(enum switchdev_notifier_type nt,
        return 0;
 }
 
-static int switchdev_port_obj_add_now(struct net_device *dev,
-                                     const struct switchdev_obj *obj,
-                                     struct netlink_ext_ack *extack)
-{
-       struct switchdev_trans trans;
-       int err;
-
-       ASSERT_RTNL();
-
-       /* Phase I: prepare for obj add. Driver/device should fail
-        * here if there are going to be issues in the commit phase,
-        * such as lack of resources or support.  The driver/device
-        * should reserve resources needed for the commit phase here,
-        * but should not commit the obj.
-        */
-
-       trans.ph_prepare = true;
-       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
-                                       dev, obj, &trans, extack);
-       if (err)
-               return err;
-
-       /* Phase II: commit obj add.  This cannot fail as a fault
-        * of driver/device.  If it does, it's a bug in the driver/device
-        * because the driver said everythings was OK in phase I.
-        */
-
-       trans.ph_prepare = false;
-       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
-                                       dev, obj, &trans, extack);
-       WARN(err, "%s: Commit of object (id=%d) failed.\n", dev->name, obj->id);
-
-       return err;
-}
-
 static void switchdev_port_obj_add_deferred(struct net_device *dev,
                                            const void *data)
 {
        const struct switchdev_obj *obj = data;
        int err;
 
-       err = switchdev_port_obj_add_now(dev, obj, NULL);
+       ASSERT_RTNL();
+       err = switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
+                                       dev, obj, NULL);
        if (err && err != -EOPNOTSUPP)
                netdev_err(dev, "failed (err=%d) to add object (id=%d)\n",
                           err, obj->id);
@@ -307,10 +239,6 @@ static int switchdev_port_obj_add_defer(struct net_device *dev,
  *     @obj: object to add
  *     @extack: netlink extended ack
  *
- *     Use a 2-phase prepare-commit transaction model to ensure
- *     system is not left in a partially updated state due to
- *     failure from driver/device.
- *
  *     rtnl_lock must be held and must not be in atomic section,
  *     in case SWITCHDEV_F_DEFER flag is not set.
  */
@@ -321,7 +249,8 @@ int switchdev_port_obj_add(struct net_device *dev,
        if (obj->flags & SWITCHDEV_F_DEFER)
                return switchdev_port_obj_add_defer(dev, obj);
        ASSERT_RTNL();
-       return switchdev_port_obj_add_now(dev, obj, extack);
+       return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_ADD,
+                                        dev, obj, extack);
 }
 EXPORT_SYMBOL_GPL(switchdev_port_obj_add);
 
@@ -329,7 +258,7 @@ static int switchdev_port_obj_del_now(struct net_device *dev,
                                      const struct switchdev_obj *obj)
 {
        return switchdev_port_obj_notify(SWITCHDEV_PORT_OBJ_DEL,
-                                        dev, obj, NULL, NULL);
+                                        dev, obj, NULL);
 }
 
 static void switchdev_port_obj_del_deferred(struct net_device *dev,
@@ -449,7 +378,6 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*add_cb)(struct net_device *dev,
                                      const struct switchdev_obj *obj,
-                                     struct switchdev_trans *trans,
                                      struct netlink_ext_ack *extack))
 {
        struct netlink_ext_ack *extack;
@@ -460,10 +388,10 @@ static int __switchdev_handle_port_obj_add(struct net_device *dev,
        extack = switchdev_notifier_info_to_extack(&port_obj_info->info);
 
        if (check_cb(dev)) {
-               /* This flag is only checked if the return value is success. */
-               port_obj_info->handled = true;
-               return add_cb(dev, port_obj_info->obj, port_obj_info->trans,
-                             extack);
+               err = add_cb(dev, port_obj_info->obj, extack);
+               if (err != -EOPNOTSUPP)
+                       port_obj_info->handled = true;
+               return err;
        }
 
        /* Switch ports might be stacked under e.g. a LAG. Ignore the
@@ -491,7 +419,6 @@ int switchdev_handle_port_obj_add(struct net_device *dev,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*add_cb)(struct net_device *dev,
                                      const struct switchdev_obj *obj,
-                                     struct switchdev_trans *trans,
                                      struct netlink_ext_ack *extack))
 {
        int err;
@@ -515,9 +442,10 @@ static int __switchdev_handle_port_obj_del(struct net_device *dev,
        int err = -EOPNOTSUPP;
 
        if (check_cb(dev)) {
-               /* This flag is only checked if the return value is success. */
-               port_obj_info->handled = true;
-               return del_cb(dev, port_obj_info->obj);
+               err = del_cb(dev, port_obj_info->obj);
+               if (err != -EOPNOTSUPP)
+                       port_obj_info->handled = true;
+               return err;
        }
 
        /* Switch ports might be stacked under e.g. a LAG. Ignore the
@@ -560,17 +488,17 @@ static int __switchdev_handle_port_attr_set(struct net_device *dev,
                        struct switchdev_notifier_port_attr_info *port_attr_info,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*set_cb)(struct net_device *dev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans))
+                                     const struct switchdev_attr *attr))
 {
        struct net_device *lower_dev;
        struct list_head *iter;
        int err = -EOPNOTSUPP;
 
        if (check_cb(dev)) {
-               port_attr_info->handled = true;
-               return set_cb(dev, port_attr_info->attr,
-                             port_attr_info->trans);
+               err = set_cb(dev, port_attr_info->attr);
+               if (err != -EOPNOTSUPP)
+                       port_attr_info->handled = true;
+               return err;
        }
 
        /* Switch ports might be stacked under e.g. a LAG. Ignore the
@@ -597,8 +525,7 @@ int switchdev_handle_port_attr_set(struct net_device *dev,
                        struct switchdev_notifier_port_attr_info *port_attr_info,
                        bool (*check_cb)(const struct net_device *dev),
                        int (*set_cb)(struct net_device *dev,
-                                     const struct switchdev_attr *attr,
-                                     struct switchdev_trans *trans))
+                                     const struct switchdev_attr *attr))
 {
        int err;
 
index 6ae2140..1151092 100644 (file)
@@ -1030,7 +1030,6 @@ void tipc_link_reset(struct tipc_link *l)
 int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
                   struct sk_buff_head *xmitq)
 {
-       struct tipc_msg *hdr = buf_msg(skb_peek(list));
        struct sk_buff_head *backlogq = &l->backlogq;
        struct sk_buff_head *transmq = &l->transmq;
        struct sk_buff *skb, *_skb;
@@ -1038,13 +1037,18 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
        u16 ack = l->rcv_nxt - 1;
        u16 seqno = l->snd_nxt;
        int pkt_cnt = skb_queue_len(list);
-       int imp = msg_importance(hdr);
        unsigned int mss = tipc_link_mss(l);
        unsigned int cwin = l->window;
        unsigned int mtu = l->mtu;
+       struct tipc_msg *hdr;
        bool new_bundle;
        int rc = 0;
+       int imp;
+
+       if (pkt_cnt <= 0)
+               return 0;
 
+       hdr = buf_msg(skb_peek(list));
        if (unlikely(msg_size(hdr) > mtu)) {
                pr_warn("Too large msg, purging xmit list %d %d %d %d %d!\n",
                        skb_queue_len(list), msg_user(hdr),
@@ -1053,6 +1057,7 @@ int tipc_link_xmit(struct tipc_link *l, struct sk_buff_head *list,
                return -EMSGSIZE;
        }
 
+       imp = msg_importance(hdr);
        /* Allow oversubscription of one data msg per source at congestion */
        if (unlikely(l->backlog[imp].len >= l->backlog[imp].limit)) {
                if (imp == TIPC_SYSTEM_IMPORTANCE) {
@@ -2539,7 +2544,7 @@ void tipc_link_set_queue_limits(struct tipc_link *l, u32 min_win, u32 max_win)
 }
 
 /**
- * link_reset_stats - reset link statistics
+ * tipc_link_reset_stats - reset link statistics
  * @l: pointer to link
  */
 void tipc_link_reset_stats(struct tipc_link *l)
index 6dce2ab..48fac3b 100644 (file)
@@ -108,7 +108,7 @@ const int tipc_max_domain_size = sizeof(struct tipc_mon_domain);
  */
 static int dom_rec_len(struct tipc_mon_domain *dom, u16 mcnt)
 {
-       return ((void *)&dom->members - (void *)dom) + (mcnt * sizeof(u32));
+       return (offsetof(struct tipc_mon_domain, members)) + (mcnt * sizeof(u32));
 }
 
 /* dom_size() : calculate size of own domain based on number of peers
index 2aca860..e926328 100644 (file)
@@ -117,10 +117,6 @@ struct sk_buff *tipc_msg_create(uint user, uint type,
        msg_set_origport(msg, oport);
        msg_set_destport(msg, dport);
        msg_set_errcode(msg, errcode);
-       if (hdr_sz > SHORT_H_SIZE) {
-               msg_set_orignode(msg, onode);
-               msg_set_destnode(msg, dnode);
-       }
        return buf;
 }
 
index 83d9eb8..008670d 100644 (file)
@@ -1665,7 +1665,7 @@ static void tipc_lxc_xmit(struct net *peer_net, struct sk_buff_head *list)
 }
 
 /**
- * tipc_node_xmit() is the general link level function for message sending
+ * tipc_node_xmit() - general link level function for message sending
  * @net: the applicable net namespace
  * @list: chain of buffers containing message
  * @dnode: address of destination node
index f7fb7d2..d9cd229 100644 (file)
@@ -113,7 +113,7 @@ static struct net_device *get_netdev_for_sock(struct sock *sk)
        struct net_device *netdev = NULL;
 
        if (likely(dst)) {
-               netdev = dst->dev;
+               netdev = netdev_sk_get_lowest_dev(dst->dev, sk);
                dev_hold(netdev);
        }
 
@@ -1329,6 +1329,8 @@ static int tls_dev_event(struct notifier_block *this, unsigned long event,
        switch (event) {
        case NETDEV_REGISTER:
        case NETDEV_FEAT_CHANGE:
+               if (netif_is_bond_master(dev))
+                       return NOTIFY_DONE;
                if ((dev->features & NETIF_F_HW_TLS_RX) &&
                    !dev->tlsdev_ops->tls_dev_resync)
                        return NOTIFY_BAD;
index d946817..cacf040 100644 (file)
@@ -424,7 +424,7 @@ struct sk_buff *tls_validate_xmit_skb(struct sock *sk,
                                      struct net_device *dev,
                                      struct sk_buff *skb)
 {
-       if (dev == tls_get_ctx(sk)->netdev)
+       if (dev == tls_get_ctx(sk)->netdev || netif_is_bond_master(dev))
                return skb;
 
        return tls_sw_fallback(sk, skb);
index 27026f5..f620acd 100644 (file)
@@ -21,6 +21,7 @@ config CFG80211
        tristate "cfg80211 - wireless configuration API"
        depends on RFKILL || !RFKILL
        select FW_LOADER
+       select CRC32
        # may need to update this when certificates are changed and are
        # using a different algorithm, though right now they shouldn't
        # (this is here rather than below to allow it to be a module)
index e4030f1..285b807 100644 (file)
@@ -1093,7 +1093,7 @@ static bool cfg80211_ir_permissive_chan(struct wiphy *wiphy,
        struct wireless_dev *wdev;
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
            !(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
@@ -1216,9 +1216,10 @@ bool cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
                                   struct cfg80211_chan_def *chandef,
                                   enum nl80211_iftype iftype)
 {
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        bool check_no_ir;
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        /*
         * Under certain conditions suggested by some regulatory bodies a
index 4b1f35e..200cd9f 100644 (file)
@@ -222,7 +222,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
 void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
                              struct wireless_dev *wdev)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
                return;
@@ -247,7 +247,7 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
 void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
                       struct wireless_dev *wdev)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_NAN))
                return;
@@ -273,7 +273,11 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
                        dev_close(wdev->netdev);
                        continue;
                }
+
                /* otherwise, check iftype */
+
+               wiphy_lock(wiphy);
+
                switch (wdev->iftype) {
                case NL80211_IFTYPE_P2P_DEVICE:
                        cfg80211_stop_p2p_device(rdev, wdev);
@@ -284,6 +288,8 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
                default:
                        break;
                }
+
+               wiphy_unlock(wiphy);
        }
 }
 EXPORT_SYMBOL_GPL(cfg80211_shutdown_all_interfaces);
@@ -318,9 +324,9 @@ static void cfg80211_event_work(struct work_struct *work)
        rdev = container_of(work, struct cfg80211_registered_device,
                            event_work);
 
-       rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        cfg80211_process_rdev_events(rdev);
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 }
 
 void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev)
@@ -475,6 +481,7 @@ use_default_name:
                }
        }
 
+       mutex_init(&rdev->wiphy.mtx);
        INIT_LIST_HEAD(&rdev->wiphy.wdev_list);
        INIT_LIST_HEAD(&rdev->beacon_registrations);
        spin_lock_init(&rdev->beacon_registrations_lock);
@@ -1007,15 +1014,16 @@ void wiphy_unregister(struct wiphy *wiphy)
 
        wait_event(rdev->dev_wait, ({
                int __count;
-               rtnl_lock();
+               wiphy_lock(&rdev->wiphy);
                __count = rdev->opencount;
-               rtnl_unlock();
+               wiphy_unlock(&rdev->wiphy);
                __count == 0; }));
 
        if (rdev->rfkill)
                rfkill_unregister(rdev->rfkill);
 
        rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
        rdev->wiphy.registered = false;
 
@@ -1038,6 +1046,7 @@ void wiphy_unregister(struct wiphy *wiphy)
        cfg80211_rdev_list_generation++;
        device_del(&rdev->wiphy.dev);
 
+       wiphy_unlock(&rdev->wiphy);
        rtnl_unlock();
 
        flush_work(&rdev->scan_done_wk);
@@ -1070,6 +1079,7 @@ void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
        }
        list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
                cfg80211_put_bss(&rdev->wiphy, &scan->pub);
+       mutex_destroy(&rdev->wiphy.mtx);
        kfree(rdev);
 }
 
@@ -1094,19 +1104,28 @@ void cfg80211_cqm_config_free(struct wireless_dev *wdev)
        wdev->cqm_config = NULL;
 }
 
-static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync)
+static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
+                                     bool unregister_netdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
        ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        flush_work(&wdev->pmsr_free_wk);
 
        nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
 
+       wdev->registered = false;
+
+       if (wdev->netdev) {
+               sysfs_remove_link(&wdev->netdev->dev.kobj, "phy80211");
+               if (unregister_netdev)
+                       unregister_netdevice(wdev->netdev);
+       }
+
        list_del_rcu(&wdev->list);
-       if (sync)
-               synchronize_rcu();
+       synchronize_net();
        rdev->devlist_generation++;
 
        cfg80211_mlme_purge_registrations(wdev);
@@ -1131,14 +1150,23 @@ static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync)
                flush_work(&wdev->disconnect_wk);
 
        cfg80211_cqm_config_free(wdev);
+
+       /*
+        * Ensure that all events have been processed and
+        * freed.
+        */
+       cfg80211_process_wdev_events(wdev);
+
+       if (WARN_ON(wdev->current_bss)) {
+               cfg80211_unhold_bss(wdev->current_bss);
+               cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+               wdev->current_bss = NULL;
+       }
 }
 
 void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 {
-       if (WARN_ON(wdev->netdev))
-               return;
-
-       __cfg80211_unregister_wdev(wdev, true);
+       _cfg80211_unregister_wdev(wdev, true);
 }
 EXPORT_SYMBOL(cfg80211_unregister_wdev);
 
@@ -1149,7 +1177,7 @@ static const struct device_type wiphy_type = {
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                               enum nl80211_iftype iftype, int num)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        rdev->num_running_ifaces += num;
        if (iftype == NL80211_IFTYPE_MONITOR)
@@ -1162,7 +1190,7 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
        struct net_device *dev = wdev->netdev;
        struct cfg80211_sched_scan_request *pos, *tmp;
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
        ASSERT_WDEV_LOCK(wdev);
 
        cfg80211_pmsr_wdev_down(wdev);
@@ -1279,6 +1307,9 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
 void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
                            struct wireless_dev *wdev)
 {
+       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
+
        /*
         * We get here also when the interface changes network namespaces,
         * as it's registered into the new one, but we don't want it to
@@ -1290,10 +1321,49 @@ void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
                wdev->identifier = ++rdev->wdev_id;
        list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
        rdev->devlist_generation++;
+       wdev->registered = true;
 
        nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
 }
 
+int cfg80211_register_netdevice(struct net_device *dev)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev;
+       int ret;
+
+       ASSERT_RTNL();
+
+       if (WARN_ON(!wdev))
+               return -EINVAL;
+
+       rdev = wiphy_to_rdev(wdev->wiphy);
+
+       lockdep_assert_held(&rdev->wiphy.mtx);
+
+       /* we'll take care of this */
+       wdev->registered = true;
+       ret = register_netdevice(dev);
+       if (ret)
+               goto out;
+
+       if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
+                             "phy80211")) {
+               pr_err("failed to add phy80211 symlink to netdev!\n");
+               unregister_netdevice(dev);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cfg80211_register_wdev(rdev, wdev);
+       ret = 0;
+out:
+       if (ret)
+               wdev->registered = false;
+       return ret;
+}
+EXPORT_SYMBOL(cfg80211_register_netdevice);
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state, void *ptr)
 {
@@ -1319,22 +1389,30 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                cfg80211_init_wdev(wdev);
                break;
        case NETDEV_REGISTER:
+               if (!wdev->registered) {
+                       wiphy_lock(&rdev->wiphy);
+                       cfg80211_register_wdev(rdev, wdev);
+                       wiphy_unlock(&rdev->wiphy);
+               }
+               break;
+       case NETDEV_UNREGISTER:
                /*
-                * NB: cannot take rdev->mtx here because this may be
-                * called within code protected by it when interfaces
-                * are added with nl80211.
+                * It is possible to get NETDEV_UNREGISTER multiple times,
+                * so check wdev->registered.
                 */
-               if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
-                                     "phy80211")) {
-                       pr_err("failed to add phy80211 symlink to netdev!\n");
+               if (wdev->registered) {
+                       wiphy_lock(&rdev->wiphy);
+                       _cfg80211_unregister_wdev(wdev, false);
+                       wiphy_unlock(&rdev->wiphy);
                }
-
-               cfg80211_register_wdev(rdev, wdev);
                break;
        case NETDEV_GOING_DOWN:
+               wiphy_lock(&rdev->wiphy);
                cfg80211_leave(rdev, wdev);
+               wiphy_unlock(&rdev->wiphy);
                break;
        case NETDEV_DOWN:
+               wiphy_lock(&rdev->wiphy);
                cfg80211_update_iface_num(rdev, wdev->iftype, -1);
                if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
                        if (WARN_ON(!rdev->scan_req->notified &&
@@ -1351,9 +1429,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                }
 
                rdev->opencount--;
+               wiphy_unlock(&rdev->wiphy);
                wake_up(&rdev->dev_wait);
                break;
        case NETDEV_UP:
+               wiphy_lock(&rdev->wiphy);
                cfg80211_update_iface_num(rdev, wdev->iftype, 1);
                wdev_lock(wdev);
                switch (wdev->iftype) {
@@ -1400,38 +1480,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                        /* assume this means it's off */
                        wdev->ps = false;
                }
-               break;
-       case NETDEV_UNREGISTER:
-               /*
-                * It is possible to get NETDEV_UNREGISTER
-                * multiple times. To detect that, check
-                * that the interface is still on the list
-                * of registered interfaces, and only then
-                * remove and clean it up.
-                */
-               if (!list_empty(&wdev->list)) {
-                       __cfg80211_unregister_wdev(wdev, false);
-                       sysfs_remove_link(&dev->dev.kobj, "phy80211");
-               }
-               /*
-                * synchronise (so that we won't find this netdev
-                * from other code any more) and then clear the list
-                * head so that the above code can safely check for
-                * !list_empty() to avoid double-cleanup.
-                */
-               synchronize_rcu();
-               INIT_LIST_HEAD(&wdev->list);
-               /*
-                * Ensure that all events have been processed and
-                * freed.
-                */
-               cfg80211_process_wdev_events(wdev);
-
-               if (WARN_ON(wdev->current_bss)) {
-                       cfg80211_unhold_bss(wdev->current_bss);
-                       cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
-                       wdev->current_bss = NULL;
-               }
+               wiphy_unlock(&rdev->wiphy);
                break;
        case NETDEV_PRE_UP:
                if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype,
index 7df91f9..a7d19b4 100644 (file)
@@ -231,7 +231,7 @@ static inline void wdev_unlock(struct wireless_dev *wdev)
 
 static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
               rdev->num_running_ifaces > 0;
index 76b845f..aab4346 100644 (file)
@@ -73,8 +73,6 @@ static ssize_t ht40allow_map_read(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       rtnl_lock();
-
        for (band = 0; band < NUM_NL80211_BANDS; band++) {
                sband = wiphy->bands[band];
                if (!sband)
@@ -84,8 +82,6 @@ static ssize_t ht40allow_map_read(struct file *file,
                                                buf, buf_size, offset);
        }
 
-       rtnl_unlock();
-
        r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
 
        kfree(buf);
index a0621bb..8f98e54 100644 (file)
@@ -3,6 +3,7 @@
  * Some IBSS support code for cfg80211.
  *
  * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2020-2021 Intel Corporation
  */
 
 #include <linux/etherdevice.h>
@@ -92,7 +93,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        int err;
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
        ASSERT_WDEV_LOCK(wdev);
 
        if (wdev->ssid_len)
index e1e9076..3aa69b3 100644 (file)
@@ -450,7 +450,7 @@ static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev)
        struct cfg80211_mgmt_registration *reg;
        struct mgmt_frame_regs upd = {};
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        spin_lock_bh(&wdev->mgmt_registrations_lock);
        if (!wdev->mgmt_registrations_need_update) {
@@ -492,10 +492,10 @@ void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk)
        rdev = container_of(wk, struct cfg80211_registered_device,
                            mgmt_registrations_update_wk);
 
-       rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
                cfg80211_mgmt_registrations_update(wdev);
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 }
 
 int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
index 775d0c4..e5e9d88 100644 (file)
@@ -64,9 +64,9 @@ static const struct genl_multicast_group nl80211_mcgrps[] = {
 
 /* returns ERR_PTR values */
 static struct wireless_dev *
-__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
+__cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
+                          struct net *netns, struct nlattr **attrs)
 {
-       struct cfg80211_registered_device *rdev;
        struct wireless_dev *result = NULL;
        bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
        bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
@@ -74,8 +74,6 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
        int wiphy_idx = -1;
        int ifidx = -1;
 
-       ASSERT_RTNL();
-
        if (!have_ifidx && !have_wdev_id)
                return ERR_PTR(-EINVAL);
 
@@ -86,6 +84,28 @@ __cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
                wiphy_idx = wdev_id >> 32;
        }
 
+       if (rdev) {
+               struct wireless_dev *wdev;
+
+               lockdep_assert_held(&rdev->wiphy.mtx);
+
+               list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+                       if (have_ifidx && wdev->netdev &&
+                           wdev->netdev->ifindex == ifidx) {
+                               result = wdev;
+                               break;
+                       }
+                       if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
+                               result = wdev;
+                               break;
+                       }
+               }
+
+               return result ?: ERR_PTR(-ENODEV);
+       }
+
+       ASSERT_RTNL();
+
        list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
                struct wireless_dev *wdev;
 
@@ -914,22 +934,31 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
                        return err;
                }
 
-               *wdev = __cfg80211_wdev_from_attrs(sock_net(cb->skb->sk),
+               rtnl_lock();
+               *wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(cb->skb->sk),
                                                   attrbuf);
                kfree(attrbuf);
-               if (IS_ERR(*wdev))
+               if (IS_ERR(*wdev)) {
+                       rtnl_unlock();
                        return PTR_ERR(*wdev);
+               }
                *rdev = wiphy_to_rdev((*wdev)->wiphy);
+               mutex_lock(&(*rdev)->wiphy.mtx);
+               rtnl_unlock();
                /* 0 is the first index - add 1 to parse only once */
                cb->args[0] = (*rdev)->wiphy_idx + 1;
                cb->args[1] = (*wdev)->identifier;
        } else {
                /* subtract the 1 again here */
-               struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+               struct wiphy *wiphy;
                struct wireless_dev *tmp;
 
-               if (!wiphy)
+               rtnl_lock();
+               wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
+               if (!wiphy) {
+                       rtnl_unlock();
                        return -ENODEV;
+               }
                *rdev = wiphy_to_rdev(wiphy);
                *wdev = NULL;
 
@@ -940,8 +969,12 @@ int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
                        }
                }
 
-               if (!*wdev)
+               if (!*wdev) {
+                       rtnl_unlock();
                        return -ENODEV;
+               }
+               mutex_lock(&(*rdev)->wiphy.mtx);
+               rtnl_unlock();
        }
 
        return 0;
@@ -3141,7 +3174,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *rdev;
+       struct cfg80211_registered_device *rdev = NULL;
        struct net_device *netdev = NULL;
        struct wireless_dev *wdev;
        int result = 0, rem_txq_params = 0;
@@ -3152,8 +3185,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        u8 coverage_class = 0;
        u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
 
-       ASSERT_RTNL();
-
+       rtnl_lock();
        /*
         * Try to find the wiphy and netdev. Normally this
         * function shouldn't need the netdev, but this is
@@ -3177,14 +3209,19 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        if (!netdev) {
                rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
                                                  info->attrs);
-               if (IS_ERR(rdev))
+               if (IS_ERR(rdev)) {
+                       rtnl_unlock();
                        return PTR_ERR(rdev);
+               }
                wdev = NULL;
                netdev = NULL;
                result = 0;
        } else
                wdev = netdev->ieee80211_ptr;
 
+       wiphy_lock(&rdev->wiphy);
+       rtnl_unlock();
+
        /*
         * end workaround code, by now the rdev is available
         * and locked, and wdev may or may not be NULL.
@@ -3195,24 +3232,32 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
 
        if (result)
-               return result;
+               goto out;
 
        if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
                struct ieee80211_txq_params txq_params;
                struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
 
-               if (!rdev->ops->set_txq_params)
-                       return -EOPNOTSUPP;
+               if (!rdev->ops->set_txq_params) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
 
-               if (!netdev)
-                       return -EINVAL;
+               if (!netdev) {
+                       result = -EINVAL;
+                       goto out;
+               }
 
                if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-                   netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
-                       return -EINVAL;
+                   netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
+                       result = -EINVAL;
+                       goto out;
+               }
 
-               if (!netif_running(netdev))
-                       return -ENETDOWN;
+               if (!netif_running(netdev)) {
+                       result = -ENETDOWN;
+                       goto out;
+               }
 
                nla_for_each_nested(nl_txq_params,
                                    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
@@ -3223,15 +3268,15 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                                                             txq_params_policy,
                                                             info->extack);
                        if (result)
-                               return result;
+                               goto out;
                        result = parse_txq_params(tb, &txq_params);
                        if (result)
-                               return result;
+                               goto out;
 
                        result = rdev_set_txq_params(rdev, netdev,
                                                     &txq_params);
                        if (result)
-                               return result;
+                               goto out;
                }
        }
 
@@ -3241,7 +3286,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
                        info);
                if (result)
-                       return result;
+                       goto out;
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
@@ -3252,15 +3297,19 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
                        txp_wdev = NULL;
 
-               if (!rdev->ops->set_tx_power)
-                       return -EOPNOTSUPP;
+               if (!rdev->ops->set_tx_power) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
 
                idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
                type = nla_get_u32(info->attrs[idx]);
 
                if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
-                   (type != NL80211_TX_POWER_AUTOMATIC))
-                       return -EINVAL;
+                   (type != NL80211_TX_POWER_AUTOMATIC)) {
+                       result = -EINVAL;
+                       goto out;
+               }
 
                if (type != NL80211_TX_POWER_AUTOMATIC) {
                        idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
@@ -3269,7 +3318,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
                result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
                if (result)
-                       return result;
+                       goto out;
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
@@ -3278,8 +3327,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
                if ((!rdev->wiphy.available_antennas_tx &&
                     !rdev->wiphy.available_antennas_rx) ||
-                   !rdev->ops->set_antenna)
-                       return -EOPNOTSUPP;
+                   !rdev->ops->set_antenna) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
 
                tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
                rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
@@ -3287,15 +3338,17 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                /* reject antenna configurations which don't match the
                 * available antenna masks, except for the "all" mask */
                if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
-                   (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx)))
-                       return -EINVAL;
+                   (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
+                       result = -EINVAL;
+                       goto out;
+               }
 
                tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
                rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
 
                result = rdev_set_antenna(rdev, tx_ant, rx_ant);
                if (result)
-                       return result;
+                       goto out;
        }
 
        changed = 0;
@@ -3317,8 +3370,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
                frag_threshold = nla_get_u32(
                        info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
-               if (frag_threshold < 256)
-                       return -EINVAL;
+               if (frag_threshold < 256) {
+                       result = -EINVAL;
+                       goto out;
+               }
 
                if (frag_threshold != (u32) -1) {
                        /*
@@ -3339,8 +3394,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
-               if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK])
-                       return -EINVAL;
+               if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) {
+                       result = -EINVAL;
+                       goto out;
+               }
 
                coverage_class = nla_get_u8(
                        info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
@@ -3348,16 +3405,20 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) {
-               if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION))
-                       return -EOPNOTSUPP;
+               if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION)) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
 
                changed |= WIPHY_PARAM_DYN_ACK;
        }
 
        if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) {
                if (!wiphy_ext_feature_isset(&rdev->wiphy,
-                                            NL80211_EXT_FEATURE_TXQS))
-                       return -EOPNOTSUPP;
+                                            NL80211_EXT_FEATURE_TXQS)) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
                txq_limit = nla_get_u32(
                        info->attrs[NL80211_ATTR_TXQ_LIMIT]);
                changed |= WIPHY_PARAM_TXQ_LIMIT;
@@ -3365,8 +3426,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
        if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) {
                if (!wiphy_ext_feature_isset(&rdev->wiphy,
-                                            NL80211_EXT_FEATURE_TXQS))
-                       return -EOPNOTSUPP;
+                                            NL80211_EXT_FEATURE_TXQS)) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
                txq_memory_limit = nla_get_u32(
                        info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]);
                changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT;
@@ -3374,8 +3437,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
        if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) {
                if (!wiphy_ext_feature_isset(&rdev->wiphy,
-                                            NL80211_EXT_FEATURE_TXQS))
-                       return -EOPNOTSUPP;
+                                            NL80211_EXT_FEATURE_TXQS)) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
                txq_quantum = nla_get_u32(
                        info->attrs[NL80211_ATTR_TXQ_QUANTUM]);
                changed |= WIPHY_PARAM_TXQ_QUANTUM;
@@ -3387,8 +3452,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                u8 old_coverage_class;
                u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;
 
-               if (!rdev->ops->set_wiphy_params)
-                       return -EOPNOTSUPP;
+               if (!rdev->ops->set_wiphy_params) {
+                       result = -EOPNOTSUPP;
+                       goto out;
+               }
 
                old_retry_short = rdev->wiphy.retry_short;
                old_retry_long = rdev->wiphy.retry_long;
@@ -3426,10 +3493,15 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        rdev->wiphy.txq_limit = old_txq_limit;
                        rdev->wiphy.txq_memory_limit = old_txq_memory_limit;
                        rdev->wiphy.txq_quantum = old_txq_quantum;
-                       return result;
+                       goto out;
                }
        }
-       return 0;
+
+       result = 0;
+
+out:
+       wiphy_unlock(&rdev->wiphy);
+       return result;
 }
 
 static int nl80211_send_chandef(struct sk_buff *msg,
@@ -3959,6 +4031,17 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->del_virtual_intf)
                return -EOPNOTSUPP;
 
+       /*
+        * We hold RTNL, so this is safe, without RTNL opencount cannot
+        * reach 0, and thus the rdev cannot be deleted.
+        *
+        * We need to do it for the dev_close(), since that will call
+        * the netdev notifiers, and we need to acquire the mutex there
+        * but don't know if we get there from here or from some other
+        * place (e.g. "ip link set ... down").
+        */
+       mutex_unlock(&rdev->wiphy.mtx);
+
        /*
         * If we remove a wireless device without a netdev then clear
         * user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -3968,6 +4051,10 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
         */
        if (!wdev->netdev)
                info->user_ptr[1] = NULL;
+       else
+               dev_close(wdev->netdev);
+
+       mutex_lock(&rdev->wiphy.mtx);
 
        return rdev_del_virtual_intf(rdev, wdev);
 }
@@ -5884,10 +5971,11 @@ static int nl80211_dump_station(struct sk_buff *skb,
        int sta_idx = cb->args[2];
        int err;
 
-       rtnl_lock();
        err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
        if (err)
-               goto out_err;
+               return err;
+       /* nl80211_prepare_wdev_dump acquired it in the successful case */
+       __acquire(&rdev->wiphy.mtx);
 
        if (!wdev->netdev) {
                err = -EINVAL;
@@ -5922,7 +6010,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
        cb->args[2] = sta_idx;
        err = skb->len;
  out_err:
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 
        return err;
 }
@@ -6780,10 +6868,11 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        int path_idx = cb->args[2];
        int err;
 
-       rtnl_lock();
        err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
        if (err)
-               goto out_err;
+               return err;
+       /* nl80211_prepare_wdev_dump acquired it in the successful case */
+       __acquire(&rdev->wiphy.mtx);
 
        if (!rdev->ops->dump_mpath) {
                err = -EOPNOTSUPP;
@@ -6816,7 +6905,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        cb->args[2] = path_idx;
        err = skb->len;
  out_err:
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
        return err;
 }
 
@@ -6979,10 +7068,11 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
        int path_idx = cb->args[2];
        int err;
 
-       rtnl_lock();
        err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
        if (err)
-               goto out_err;
+               return err;
+       /* nl80211_prepare_wdev_dump acquired it in the successful case */
+       __acquire(&rdev->wiphy.mtx);
 
        if (!rdev->ops->dump_mpp) {
                err = -EOPNOTSUPP;
@@ -7015,7 +7105,7 @@ static int nl80211_dump_mpp(struct sk_buff *skb,
        cb->args[2] = path_idx;
        err = skb->len;
  out_err:
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
        return err;
 }
 
@@ -7634,12 +7724,15 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
        if (!hdr)
                goto put_failure;
 
+       rtnl_lock();
+
        if (info->attrs[NL80211_ATTR_WIPHY]) {
                bool self_managed;
 
                rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
                if (IS_ERR(rdev)) {
                        nlmsg_free(msg);
+                       rtnl_unlock();
                        return PTR_ERR(rdev);
                }
 
@@ -7651,6 +7744,7 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
                /* a self-managed-reg device must have a private regdom */
                if (WARN_ON(!regdom && self_managed)) {
                        nlmsg_free(msg);
+                       rtnl_unlock();
                        return -EINVAL;
                }
 
@@ -7675,11 +7769,13 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
        rcu_read_unlock();
 
        genlmsg_end(msg, hdr);
+       rtnl_unlock();
        return genlmsg_reply(msg, info);
 
 nla_put_failure_rcu:
        rcu_read_unlock();
 nla_put_failure:
+       rtnl_unlock();
 put_failure:
        nlmsg_free(msg);
        return -EMSGSIZE;
@@ -7842,12 +7938,17 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
-       if (!reg_is_valid_request(alpha2))
-               return -EINVAL;
+       rtnl_lock();
+       if (!reg_is_valid_request(alpha2)) {
+               r = -EINVAL;
+               goto out;
+       }
 
        rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL);
-       if (!rd)
-               return -ENOMEM;
+       if (!rd) {
+               r = -ENOMEM;
+               goto out;
+       }
 
        rd->n_reg_rules = num_rules;
        rd->alpha2[0] = alpha2[0];
@@ -7879,10 +7980,13 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
+       r = set_regdom(rd, REGD_SOURCE_CRDA);
        /* set_regdom takes ownership of rd */
-       return set_regdom(rd, REGD_SOURCE_CRDA);
+       rd = NULL;
  bad_reg:
        kfree(rd);
+ out:
+       rtnl_unlock();
        return r;
 }
 #endif /* CONFIG_CFG80211_CRDA_SUPPORT */
@@ -9050,10 +9154,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        struct net_device *dev = info->user_ptr[1];
        struct wireless_dev *wdev = dev->ieee80211_ptr;
        struct cfg80211_csa_settings params;
-       /* csa_attrs is defined static to avoid waste of stack size - this
-        * function is called under RTNL lock, so this should not be a problem.
-        */
-       static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+       struct nlattr **csa_attrs = NULL;
        int err;
        bool need_new_beacon = false;
        bool need_handle_dfs_flag = true;
@@ -9118,28 +9219,39 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
+       csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs),
+                           GFP_KERNEL);
+       if (!csa_attrs)
+               return -ENOMEM;
+
        err = nla_parse_nested_deprecated(csa_attrs, NL80211_ATTR_MAX,
                                          info->attrs[NL80211_ATTR_CSA_IES],
                                          nl80211_policy, info->extack);
        if (err)
-               return err;
+               goto free;
 
        err = nl80211_parse_beacon(rdev, csa_attrs, &params.beacon_csa);
        if (err)
-               return err;
+               goto free;
 
-       if (!csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON])
-               return -EINVAL;
+       if (!csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON]) {
+               err = -EINVAL;
+               goto free;
+       }
 
        len = nla_len(csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON]);
-       if (!len || (len % sizeof(u16)))
-               return -EINVAL;
+       if (!len || (len % sizeof(u16))) {
+               err = -EINVAL;
+               goto free;
+       }
 
        params.n_counter_offsets_beacon = len / sizeof(u16);
        if (rdev->wiphy.max_num_csa_counters &&
            (params.n_counter_offsets_beacon >
-            rdev->wiphy.max_num_csa_counters))
-               return -EINVAL;
+            rdev->wiphy.max_num_csa_counters)) {
+               err = -EINVAL;
+               goto free;
+       }
 
        params.counter_offsets_beacon =
                nla_data(csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON]);
@@ -9148,23 +9260,31 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        for (i = 0; i < params.n_counter_offsets_beacon; i++) {
                u16 offset = params.counter_offsets_beacon[i];
 
-               if (offset >= params.beacon_csa.tail_len)
-                       return -EINVAL;
+               if (offset >= params.beacon_csa.tail_len) {
+                       err = -EINVAL;
+                       goto free;
+               }
 
-               if (params.beacon_csa.tail[offset] != params.count)
-                       return -EINVAL;
+               if (params.beacon_csa.tail[offset] != params.count) {
+                       err = -EINVAL;
+                       goto free;
+               }
        }
 
        if (csa_attrs[NL80211_ATTR_CNTDWN_OFFS_PRESP]) {
                len = nla_len(csa_attrs[NL80211_ATTR_CNTDWN_OFFS_PRESP]);
-               if (!len || (len % sizeof(u16)))
-                       return -EINVAL;
+               if (!len || (len % sizeof(u16))) {
+                       err = -EINVAL;
+                       goto free;
+               }
 
                params.n_counter_offsets_presp = len / sizeof(u16);
                if (rdev->wiphy.max_num_csa_counters &&
                    (params.n_counter_offsets_presp >
-                    rdev->wiphy.max_num_csa_counters))
-                       return -EINVAL;
+                    rdev->wiphy.max_num_csa_counters)) {
+                       err = -EINVAL;
+                       goto free;
+               }
 
                params.counter_offsets_presp =
                        nla_data(csa_attrs[NL80211_ATTR_CNTDWN_OFFS_PRESP]);
@@ -9173,35 +9293,42 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                for (i = 0; i < params.n_counter_offsets_presp; i++) {
                        u16 offset = params.counter_offsets_presp[i];
 
-                       if (offset >= params.beacon_csa.probe_resp_len)
-                               return -EINVAL;
+                       if (offset >= params.beacon_csa.probe_resp_len) {
+                               err = -EINVAL;
+                               goto free;
+                       }
 
                        if (params.beacon_csa.probe_resp[offset] !=
-                           params.count)
-                               return -EINVAL;
+                           params.count) {
+                               err = -EINVAL;
+                               goto free;
+                       }
                }
        }
 
 skip_beacons:
        err = nl80211_parse_chandef(rdev, info, &params.chandef);
        if (err)
-               return err;
+               goto free;
 
        if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
-                                          wdev->iftype))
-               return -EINVAL;
+                                          wdev->iftype)) {
+               err = -EINVAL;
+               goto free;
+       }
 
        err = cfg80211_chandef_dfs_required(wdev->wiphy,
                                            &params.chandef,
                                            wdev->iftype);
        if (err < 0)
-               return err;
+               goto free;
 
        if (err > 0) {
                params.radar_required = true;
                if (need_handle_dfs_flag &&
                    !nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS])) {
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto free;
                }
        }
 
@@ -9212,6 +9339,8 @@ skip_beacons:
        err = rdev_channel_switch(rdev, dev, &params);
        wdev_unlock(wdev);
 
+free:
+       kfree(csa_attrs);
        return err;
 }
 
@@ -9362,12 +9491,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
        int start = cb->args[2], idx = 0;
        int err;
 
-       rtnl_lock();
        err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
-       if (err) {
-               rtnl_unlock();
+       if (err)
                return err;
-       }
+       /* nl80211_prepare_wdev_dump acquired it in the successful case */
+       __acquire(&rdev->wiphy.mtx);
 
        wdev_lock(wdev);
        spin_lock_bh(&rdev->bss_lock);
@@ -9398,7 +9526,7 @@ static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
        wdev_unlock(wdev);
 
        cb->args[2] = idx;
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 
        return skb->len;
 }
@@ -9496,10 +9624,13 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
        if (!attrbuf)
                return -ENOMEM;
 
-       rtnl_lock();
        res = nl80211_prepare_wdev_dump(cb, &rdev, &wdev);
-       if (res)
-               goto out_err;
+       if (res) {
+               kfree(attrbuf);
+               return res;
+       }
+       /* nl80211_prepare_wdev_dump acquired it in the successful case */
+       __acquire(&rdev->wiphy.mtx);
 
        /* prepare_wdev_dump parsed the attributes */
        radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS];
@@ -9541,7 +9672,7 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
        res = skb->len;
  out_err:
        kfree(attrbuf);
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
        return res;
 }
 
@@ -10403,10 +10534,14 @@ EXPORT_SYMBOL(__cfg80211_send_event_skb);
 static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
-       struct wireless_dev *wdev =
-               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+       struct wireless_dev *wdev;
        int err;
 
+       lockdep_assert_held(&rdev->wiphy.mtx);
+
+       wdev = __cfg80211_wdev_from_attrs(rdev, genl_info_net(info),
+                                         info->attrs);
+
        if (!rdev->ops->testmode_cmd)
                return -EOPNOTSUPP;
 
@@ -13591,7 +13726,8 @@ static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev =
-               __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
+               __cfg80211_wdev_from_attrs(rdev, genl_info_net(info),
+                                          info->attrs);
        int i, err;
        u32 vid, subcmd;
 
@@ -13715,7 +13851,7 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
                goto out;
        }
 
-       *wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk), attrbuf);
+       *wdev = __cfg80211_wdev_from_attrs(NULL, sock_net(skb->sk), attrbuf);
        if (IS_ERR(*wdev))
                *wdev = NULL;
 
@@ -14650,31 +14786,24 @@ bad_tid_conf:
 static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info)
 {
-       struct cfg80211_registered_device *rdev;
+       struct cfg80211_registered_device *rdev = NULL;
        struct wireless_dev *wdev;
        struct net_device *dev;
-       bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
-
-       if (rtnl)
-               rtnl_lock();
 
+       rtnl_lock();
        if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
                rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
                if (IS_ERR(rdev)) {
-                       if (rtnl)
-                               rtnl_unlock();
+                       rtnl_unlock();
                        return PTR_ERR(rdev);
                }
                info->user_ptr[0] = rdev;
        } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
                   ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
-               ASSERT_RTNL();
-
-               wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
+               wdev = __cfg80211_wdev_from_attrs(NULL, genl_info_net(info),
                                                  info->attrs);
                if (IS_ERR(wdev)) {
-                       if (rtnl)
-                               rtnl_unlock();
+                       rtnl_unlock();
                        return PTR_ERR(wdev);
                }
 
@@ -14683,8 +14812,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 
                if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
                        if (!dev) {
-                               if (rtnl)
-                                       rtnl_unlock();
+                               rtnl_unlock();
                                return -EINVAL;
                        }
 
@@ -14695,8 +14823,7 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
 
                if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
                    !wdev_running(wdev)) {
-                       if (rtnl)
-                               rtnl_unlock();
+                       rtnl_unlock();
                        return -ENETDOWN;
                }
 
@@ -14706,6 +14833,14 @@ static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                info->user_ptr[0] = rdev;
        }
 
+       if (rdev) {
+               wiphy_lock(&rdev->wiphy);
+               /* we keep the mutex locked until post_doit */
+               __release(&rdev->wiphy.mtx);
+       }
+       if (!(ops->internal_flags & NL80211_FLAG_NEED_RTNL))
+               rtnl_unlock();
+
        return 0;
 }
 
@@ -14723,6 +14858,14 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                }
        }
 
+       if (info->user_ptr[0]) {
+               struct cfg80211_registered_device *rdev = info->user_ptr[0];
+
+               /* we kept the mutex locked since pre_doit */
+               __acquire(&rdev->wiphy.mtx);
+               wiphy_unlock(&rdev->wiphy);
+       }
+
        if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
                rtnl_unlock();
 
@@ -14851,8 +14994,7 @@ static const struct genl_ops nl80211_ops[] = {
                .dumpit = nl80211_dump_wiphy,
                .done = nl80211_dump_wiphy_done,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
 };
 
@@ -14862,7 +15004,6 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_wiphy,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_RTNL,
        },
        {
                .cmd = NL80211_CMD_GET_INTERFACE,
@@ -14870,8 +15011,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_get_interface,
                .dumpit = nl80211_dump_interface,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV,
        },
        {
                .cmd = NL80211_CMD_SET_INTERFACE,
@@ -14902,8 +15042,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_key,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_KEY,
@@ -14911,7 +15050,6 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_set_key,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -14920,7 +15058,6 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_new_key,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -14928,64 +15065,56 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_del_key,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_BEACON,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .flags = GENL_UNS_ADMIN_PERM,
                .doit = nl80211_set_beacon,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_START_AP,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .flags = GENL_UNS_ADMIN_PERM,
                .doit = nl80211_start_ap,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_STOP_AP,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .flags = GENL_UNS_ADMIN_PERM,
                .doit = nl80211_stop_ap,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_STATION,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_station,
                .dumpit = nl80211_dump_station,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_SET_STATION,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_station,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_NEW_STATION,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_new_station,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_DEL_STATION,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_del_station,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_MPATH,
@@ -14993,8 +15122,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_get_mpath,
                .dumpit = nl80211_dump_mpath,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_MPP,
@@ -15002,47 +15130,42 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_get_mpp,
                .dumpit = nl80211_dump_mpp,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_MPATH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_mpath,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_NEW_MPATH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_new_mpath,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_DEL_MPATH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_del_mpath,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_BSS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_bss,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_REG,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_reg_do,
                .dumpit = nl80211_get_reg_dump,
-               .internal_flags = NL80211_FLAG_NEED_RTNL,
+               .internal_flags = 0,
                /* can be retrieved by unprivileged users */
        },
 #ifdef CONFIG_CFG80211_CRDA_SUPPORT
@@ -15051,7 +15174,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_reg,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_RTNL,
+               .internal_flags = 0,
        },
 #endif
        {
@@ -15071,32 +15194,28 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_mesh_config,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_MESH_CONFIG,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_update_mesh_config,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_TRIGGER_SCAN,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_trigger_scan,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_ABORT_SCAN,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_abort_scan,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_SCAN,
@@ -15108,16 +15227,14 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_start_sched_scan,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_STOP_SCHED_SCAN,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_stop_sched_scan,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_AUTHENTICATE,
@@ -15125,7 +15242,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_authenticate,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15134,7 +15251,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_associate,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15142,32 +15259,28 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_deauthenticate,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_DISASSOCIATE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_disassociate,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_JOIN_IBSS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_join_ibss,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_LEAVE_IBSS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_leave_ibss,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
 #ifdef CONFIG_NL80211_TESTMODE
        {
@@ -15176,8 +15289,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_testmode_do,
                .dumpit = nl80211_testmode_dump,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
 #endif
        {
@@ -15186,7 +15298,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_connect,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15195,7 +15307,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_update_connect_params,
                .flags = GENL_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15203,16 +15315,14 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_disconnect,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_WIPHY_NETNS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_wiphy_netns,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
        {
                .cmd = NL80211_CMD_GET_SURVEY,
@@ -15225,7 +15335,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_setdel_pmksa,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15233,128 +15343,112 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_setdel_pmksa,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_FLUSH_PMKSA,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_flush_pmksa,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_remain_on_channel,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_cancel_remain_on_channel,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_tx_bitrate_mask,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_REGISTER_FRAME,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_register_mgmt,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV,
        },
        {
                .cmd = NL80211_CMD_FRAME,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tx_mgmt,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tx_mgmt_cancel_wait,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_POWER_SAVE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_power_save,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_GET_POWER_SAVE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_power_save,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_SET_CQM,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_cqm,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_SET_CHANNEL,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_channel,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_JOIN_MESH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_join_mesh,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_LEAVE_MESH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_leave_mesh,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_JOIN_OCB,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_join_ocb,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_LEAVE_OCB,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_leave_ocb,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
 #ifdef CONFIG_PM
        {
@@ -15362,16 +15456,14 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_wowlan,
                /* can be retrieved by unprivileged users */
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
        {
                .cmd = NL80211_CMD_SET_WOWLAN,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_wowlan,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
 #endif
        {
@@ -15380,7 +15472,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .doit = nl80211_set_rekey_data,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15388,48 +15480,42 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tdls_mgmt,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_TDLS_OPER,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tdls_oper,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_UNEXPECTED_FRAME,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_register_unexpected_frame,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_PROBE_CLIENT,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_probe_client,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_REGISTER_BEACONS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_register_beacons,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
        {
                .cmd = NL80211_CMD_SET_NOACK_MAP,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_noack_map,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_START_P2P_DEVICE,
@@ -15468,48 +15554,42 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_nan_add_func,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_DEL_NAN_FUNCTION,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_nan_del_func,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_CHANGE_NAN_CONFIG,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_nan_change_config,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_MCAST_RATE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_mcast_rate,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_SET_MAC_ACL,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_mac_acl,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_RADAR_DETECT,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_start_radar_detection,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
@@ -15521,47 +15601,41 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_update_ft_ies,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_CRIT_PROTOCOL_START,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_crit_protocol_start,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_CRIT_PROTOCOL_STOP,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_crit_protocol_stop,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_COALESCE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_coalesce,
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
        {
                .cmd = NL80211_CMD_SET_COALESCE,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_coalesce,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WIPHY,
        },
        {
                .cmd = NL80211_CMD_CHANNEL_SWITCH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_channel_switch,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_VENDOR,
@@ -15570,7 +15644,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .dumpit = nl80211_vendor_cmd_dump,
                .flags = GENL_UNS_ADMIN_PERM,
                .internal_flags = NL80211_FLAG_NEED_WIPHY |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
@@ -15578,123 +15652,108 @@ static const struct genl_small_ops nl80211_small_ops[] = {
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_qos_map,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_ADD_TX_TS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_add_tx_ts,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_DEL_TX_TS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_del_tx_ts,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tdls_channel_switch,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tdls_cancel_channel_switch,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_multicast_to_unicast,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_SET_PMK,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_set_pmk,
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL |
+                                 0 |
                                  NL80211_FLAG_CLEAR_SKB,
        },
        {
                .cmd = NL80211_CMD_DEL_PMK,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_del_pmk,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_EXTERNAL_AUTH,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_external_auth,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_CONTROL_PORT_FRAME,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_tx_control_port,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_get_ftm_responder_stats,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_PEER_MEASUREMENT_START,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_pmsr_start,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_WDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_WDEV_UP,
        },
        {
                .cmd = NL80211_CMD_NOTIFY_RADAR,
                .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
                .doit = nl80211_notify_radar_detection,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_UPDATE_OWE_INFO,
                .doit = nl80211_update_owe_info,
                .flags = GENL_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_PROBE_MESH_LINK,
                .doit = nl80211_probe_mesh_link,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
        },
        {
                .cmd = NL80211_CMD_SET_TID_CONFIG,
                .doit = nl80211_set_tid_config,
                .flags = GENL_UNS_ADMIN_PERM,
-               .internal_flags = NL80211_FLAG_NEED_NETDEV |
-                                 NL80211_FLAG_NEED_RTNL,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV,
        },
        {
                .cmd = NL80211_CMD_SET_SAR_SPECS,
index bb72447..452b698 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright      2017  Intel Deutschland GmbH
- * Copyright (C) 2018 - 2019 Intel Corporation
+ * Copyright (C) 2018 - 2021 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -139,10 +139,18 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
        return rcu_dereference_rtnl(cfg80211_regdomain);
 }
 
+/*
+ * Returns the regulatory domain associated with the wiphy.
+ *
+ * Requires any of RTNL, wiphy mutex or RCU protection.
+ */
 const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
 {
-       return rcu_dereference_rtnl(wiphy->regd);
+       return rcu_dereference_check(wiphy->regd,
+                                    lockdep_is_held(&wiphy->mtx) ||
+                                    lockdep_rtnl_is_held());
 }
+EXPORT_SYMBOL(get_wiphy_regdom);
 
 static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
 {
@@ -164,7 +172,9 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
        const struct ieee80211_regdomain *regd = NULL;
        const struct ieee80211_regdomain *wiphy_regd = NULL;
 
+       rcu_read_lock();
        regd = get_cfg80211_regdom();
+
        if (!wiphy)
                goto out;
 
@@ -181,6 +191,8 @@ enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy)
                 reg_dfs_region_str(regd->dfs_region));
 
 out:
+       rcu_read_unlock();
+
        return regd->dfs_region;
 }
 
@@ -2571,9 +2583,15 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
        if (IS_ERR(new_regd))
                return;
 
+       rtnl_lock();
+       wiphy_lock(wiphy);
+
        tmp = get_wiphy_regdom(wiphy);
        rcu_assign_pointer(wiphy->regd, new_regd);
        rcu_free_regdom(tmp);
+
+       wiphy_unlock(wiphy);
+       rtnl_unlock();
 }
 EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
 
@@ -2735,7 +2753,10 @@ reg_process_hint_driver(struct wiphy *wiphy,
                        return REG_REQ_IGNORE;
 
                tmp = get_wiphy_regdom(wiphy);
+               ASSERT_RTNL();
+               wiphy_lock(wiphy);
                rcu_assign_pointer(wiphy->regd, regd);
+               wiphy_unlock(wiphy);
                rcu_free_regdom(tmp);
        }
 
@@ -3067,41 +3088,52 @@ static void reg_process_pending_beacon_hints(void)
        spin_unlock_bh(&reg_pending_beacons_lock);
 }
 
-static void reg_process_self_managed_hints(void)
+static void reg_process_self_managed_hint(struct wiphy *wiphy)
 {
-       struct cfg80211_registered_device *rdev;
-       struct wiphy *wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
        const struct ieee80211_regdomain *tmp;
        const struct ieee80211_regdomain *regd;
        enum nl80211_band band;
        struct regulatory_request request = {};
 
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
-               wiphy = &rdev->wiphy;
+       ASSERT_RTNL();
+       lockdep_assert_wiphy(wiphy);
 
-               spin_lock(&reg_requests_lock);
-               regd = rdev->requested_regd;
-               rdev->requested_regd = NULL;
-               spin_unlock(&reg_requests_lock);
+       spin_lock(&reg_requests_lock);
+       regd = rdev->requested_regd;
+       rdev->requested_regd = NULL;
+       spin_unlock(&reg_requests_lock);
 
-               if (regd == NULL)
-                       continue;
+       if (!regd)
+               return;
 
-               tmp = get_wiphy_regdom(wiphy);
-               rcu_assign_pointer(wiphy->regd, regd);
-               rcu_free_regdom(tmp);
+       tmp = get_wiphy_regdom(wiphy);
+       rcu_assign_pointer(wiphy->regd, regd);
+       rcu_free_regdom(tmp);
 
-               for (band = 0; band < NUM_NL80211_BANDS; band++)
-                       handle_band_custom(wiphy, wiphy->bands[band], regd);
+       for (band = 0; band < NUM_NL80211_BANDS; band++)
+               handle_band_custom(wiphy, wiphy->bands[band], regd);
+
+       reg_process_ht_flags(wiphy);
 
-               reg_process_ht_flags(wiphy);
+       request.wiphy_idx = get_wiphy_idx(wiphy);
+       request.alpha2[0] = regd->alpha2[0];
+       request.alpha2[1] = regd->alpha2[1];
+       request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
 
-               request.wiphy_idx = get_wiphy_idx(wiphy);
-               request.alpha2[0] = regd->alpha2[0];
-               request.alpha2[1] = regd->alpha2[1];
-               request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
+       nl80211_send_wiphy_reg_change_event(&request);
+}
 
-               nl80211_send_wiphy_reg_change_event(&request);
+static void reg_process_self_managed_hints(void)
+{
+       struct cfg80211_registered_device *rdev;
+
+       ASSERT_RTNL();
+
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               wiphy_lock(&rdev->wiphy);
+               reg_process_self_managed_hint(&rdev->wiphy);
+               wiphy_unlock(&rdev->wiphy);
        }
 
        reg_check_channels();
@@ -3780,14 +3812,21 @@ static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
                return -ENODEV;
 
        if (!driver_request->intersect) {
-               if (request_wiphy->regd)
+               ASSERT_RTNL();
+               wiphy_lock(request_wiphy);
+               if (request_wiphy->regd) {
+                       wiphy_unlock(request_wiphy);
                        return -EALREADY;
+               }
 
                regd = reg_copy_regd(rd);
-               if (IS_ERR(regd))
+               if (IS_ERR(regd)) {
+                       wiphy_unlock(request_wiphy);
                        return PTR_ERR(regd);
+               }
 
                rcu_assign_pointer(request_wiphy->regd, regd);
+               wiphy_unlock(request_wiphy);
                reset_regdomains(false, rd);
                return 0;
        }
@@ -3969,8 +4008,8 @@ int regulatory_set_wiphy_regd(struct wiphy *wiphy,
 }
 EXPORT_SYMBOL(regulatory_set_wiphy_regd);
 
-int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
-                                       struct ieee80211_regdomain *rd)
+int regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
+                                  struct ieee80211_regdomain *rd)
 {
        int ret;
 
@@ -3981,10 +4020,11 @@ int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy,
                return ret;
 
        /* process the request immediately */
-       reg_process_self_managed_hints();
+       reg_process_self_managed_hint(wiphy);
+       reg_check_channels();
        return 0;
 }
-EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl);
+EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync);
 
 void wiphy_regulatory_register(struct wiphy *wiphy)
 {
index f9e8303..f3707f7 100644 (file)
@@ -63,7 +63,6 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
                                   const struct ieee80211_reg_rule *rule);
 
 bool reg_last_request_cell_base(void);
-const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy);
 
 /**
  * regulatory_hint_found_beacon - hints a beacon was found on a channel
index 1b7fec3..019952d 100644 (file)
@@ -918,7 +918,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
        union iwreq_data wrqu;
 #endif
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        if (rdev->scan_msg) {
                nl80211_send_scan_msg(rdev, rdev->scan_msg);
@@ -987,9 +987,9 @@ void __cfg80211_scan_done(struct work_struct *wk)
        rdev = container_of(wk, struct cfg80211_registered_device,
                            scan_done_wk);
 
-       rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        ___cfg80211_scan_done(rdev, true);
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 }
 
 void cfg80211_scan_done(struct cfg80211_scan_request *request,
@@ -1022,7 +1022,7 @@ EXPORT_SYMBOL(cfg80211_scan_done);
 void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
                                 struct cfg80211_sched_scan_request *req)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        list_add_rcu(&req->list, &rdev->sched_scan_req_list);
 }
@@ -1030,7 +1030,7 @@ void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
 static void cfg80211_del_sched_scan_req(struct cfg80211_registered_device *rdev,
                                        struct cfg80211_sched_scan_request *req)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        list_del_rcu(&req->list);
        kfree_rcu(req, rcu_head);
@@ -1042,7 +1042,7 @@ cfg80211_find_sched_scan_req(struct cfg80211_registered_device *rdev, u64 reqid)
        struct cfg80211_sched_scan_request *pos;
 
        list_for_each_entry_rcu(pos, &rdev->sched_scan_req_list, list,
-                               lockdep_rtnl_is_held()) {
+                               lockdep_is_held(&rdev->wiphy.mtx)) {
                if (pos->reqid == reqid)
                        return pos;
        }
@@ -1090,7 +1090,7 @@ void cfg80211_sched_scan_results_wk(struct work_struct *work)
        rdev = container_of(work, struct cfg80211_registered_device,
                           sched_scan_res_wk);
 
-       rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        list_for_each_entry_safe(req, tmp, &rdev->sched_scan_req_list, list) {
                if (req->report_results) {
                        req->report_results = false;
@@ -1105,7 +1105,7 @@ void cfg80211_sched_scan_results_wk(struct work_struct *work)
                                                NL80211_CMD_SCHED_SCAN_RESULTS);
                }
        }
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 }
 
 void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
@@ -1126,23 +1126,23 @@ void cfg80211_sched_scan_results(struct wiphy *wiphy, u64 reqid)
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_results);
 
-void cfg80211_sched_scan_stopped_rtnl(struct wiphy *wiphy, u64 reqid)
+void cfg80211_sched_scan_stopped_locked(struct wiphy *wiphy, u64 reqid)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&wiphy->mtx);
 
        trace_cfg80211_sched_scan_stopped(wiphy, reqid);
 
        __cfg80211_stop_sched_scan(rdev, reqid, true);
 }
-EXPORT_SYMBOL(cfg80211_sched_scan_stopped_rtnl);
+EXPORT_SYMBOL(cfg80211_sched_scan_stopped_locked);
 
 void cfg80211_sched_scan_stopped(struct wiphy *wiphy, u64 reqid)
 {
-       rtnl_lock();
-       cfg80211_sched_scan_stopped_rtnl(wiphy, reqid);
-       rtnl_unlock();
+       wiphy_lock(wiphy);
+       cfg80211_sched_scan_stopped_locked(wiphy, reqid);
+       wiphy_unlock(wiphy);
 }
 EXPORT_SYMBOL(cfg80211_sched_scan_stopped);
 
@@ -1150,7 +1150,7 @@ int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
                                 struct cfg80211_sched_scan_request *req,
                                 bool driver_initiated)
 {
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        if (!driver_initiated) {
                int err = rdev_sched_scan_stop(rdev, req->dev, req->reqid);
@@ -1170,7 +1170,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
 {
        struct cfg80211_sched_scan_request *sched_scan_req;
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        sched_scan_req = cfg80211_find_sched_scan_req(rdev, reqid);
        if (!sched_scan_req)
@@ -2774,6 +2774,8 @@ int cfg80211_wext_siwscan(struct net_device *dev,
 
        eth_broadcast_addr(creq->bssid);
 
+       wiphy_lock(&rdev->wiphy);
+
        rdev->scan_req = creq;
        err = rdev_scan(rdev, creq);
        if (err) {
@@ -2785,6 +2787,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
                creq = NULL;
                dev_hold(dev);
        }
+       wiphy_unlock(&rdev->wiphy);
  out:
        kfree(creq);
        return err;
index 38df713..07756ca 100644 (file)
@@ -67,7 +67,6 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
        struct cfg80211_scan_request *request;
        int n_channels, err;
 
-       ASSERT_RTNL();
        ASSERT_WDEV_LOCK(wdev);
 
        if (rdev->scan_req || rdev->scan_msg)
@@ -233,7 +232,7 @@ void cfg80211_conn_work(struct work_struct *work)
        u8 bssid_buf[ETH_ALEN], *bssid = NULL;
        enum nl80211_timeout_reason treason;
 
-       rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
 
        list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
                if (!wdev->netdev)
@@ -266,7 +265,7 @@ void cfg80211_conn_work(struct work_struct *work)
                wdev_unlock(wdev);
        }
 
-       rtnl_unlock();
+       wiphy_unlock(&rdev->wiphy);
 }
 
 /* Returned bss is reference counted and must be cleaned up appropriately. */
index 3ac1f48..0437623 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Copyright 2005-2006 Jiri Benc <jbenc@suse.cz>
  * Copyright 2006      Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (C) 2020-2021 Intel Corporation
  */
 
 #include <linux/device.h>
@@ -104,6 +105,7 @@ static int wiphy_suspend(struct device *dev)
        rdev->suspend_at = ktime_get_boottime_seconds();
 
        rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        if (rdev->wiphy.registered) {
                if (!rdev->wiphy.wowlan_config) {
                        cfg80211_leave_all(rdev);
@@ -118,6 +120,7 @@ static int wiphy_suspend(struct device *dev)
                        ret = rdev_suspend(rdev, NULL);
                }
        }
+       wiphy_unlock(&rdev->wiphy);
        rtnl_unlock();
 
        return ret;
@@ -132,8 +135,10 @@ static int wiphy_resume(struct device *dev)
        cfg80211_bss_age(rdev, ktime_get_boottime_seconds() - rdev->suspend_at);
 
        rtnl_lock();
+       wiphy_lock(&rdev->wiphy);
        if (rdev->wiphy.registered && rdev->ops->resume)
                ret = rdev_resume(rdev);
+       wiphy_unlock(&rdev->wiphy);
        rtnl_unlock();
 
        return ret;
index b4acc80..1bf0200 100644 (file)
@@ -997,7 +997,7 @@ void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev)
 {
        struct wireless_dev *wdev;
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
                cfg80211_process_wdev_events(wdev);
@@ -1010,7 +1010,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
        int err;
        enum nl80211_iftype otype = dev->ieee80211_ptr->iftype;
 
-       ASSERT_RTNL();
+       lockdep_assert_held(&rdev->wiphy.mtx);
 
        /* don't support changing VLANs, you just re-create them */
        if (otype == NL80211_IFTYPE_AP_VLAN)
@@ -1188,6 +1188,25 @@ static u32 cfg80211_calculate_bitrate_dmg(struct rate_info *rate)
        return __mcs2bitrate[rate->mcs];
 }
 
+static u32 cfg80211_calculate_bitrate_extended_sc_dmg(struct rate_info *rate)
+{
+       static const u32 __mcs2bitrate[] = {
+               [6 - 6] = 26950, /* MCS 9.1 : 2695.0 mbps */
+               [7 - 6] = 50050, /* MCS 12.1 */
+               [8 - 6] = 53900,
+               [9 - 6] = 57750,
+               [10 - 6] = 63900,
+               [11 - 6] = 75075,
+               [12 - 6] = 80850,
+       };
+
+       /* Extended SC MCS not defined for base MCS below 6 or above 12 */
+       if (WARN_ON_ONCE(rate->mcs < 6 || rate->mcs > 12))
+               return 0;
+
+       return __mcs2bitrate[rate->mcs - 6];
+}
+
 static u32 cfg80211_calculate_bitrate_edmg(struct rate_info *rate)
 {
        static const u32 __mcs2bitrate[] = {
@@ -1224,7 +1243,7 @@ static u32 cfg80211_calculate_bitrate_edmg(struct rate_info *rate)
 
 static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
 {
-       static const u32 base[4][10] = {
+       static const u32 base[4][12] = {
                {   6500000,
                   13000000,
                   19500000,
@@ -1235,7 +1254,9 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
                   65000000,
                   78000000,
                /* not in the spec, but some devices use this: */
-                  86500000,
+                  86700000,
+                  97500000,
+                 108300000,
                },
                {  13500000,
                   27000000,
@@ -1247,6 +1268,8 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
                  135000000,
                  162000000,
                  180000000,
+                 202500000,
+                 225000000,
                },
                {  29300000,
                   58500000,
@@ -1258,6 +1281,8 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
                  292500000,
                  351000000,
                  390000000,
+                 438800000,
+                 487500000,
                },
                {  58500000,
                  117000000,
@@ -1269,12 +1294,14 @@ static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
                  585000000,
                  702000000,
                  780000000,
+                 877500000,
+                 975000000,
                },
        };
        u32 bitrate;
        int idx;
 
-       if (rate->mcs > 9)
+       if (rate->mcs > 11)
                goto warn;
 
        switch (rate->bw) {
@@ -1398,6 +1425,8 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
                return cfg80211_calculate_bitrate_ht(rate);
        if (rate->flags & RATE_INFO_FLAGS_DMG)
                return cfg80211_calculate_bitrate_dmg(rate);
+       if (rate->flags & RATE_INFO_FLAGS_EXTENDED_SC_DMG)
+               return cfg80211_calculate_bitrate_extended_sc_dmg(rate);
        if (rate->flags & RATE_INFO_FLAGS_EDMG)
                return cfg80211_calculate_bitrate_edmg(rate);
        if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
index fd9ad74..2e35cb7 100644 (file)
@@ -7,7 +7,7 @@
  * we directly assign the wireless handlers of wireless interfaces.
  *
  * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2019 Intel Corporation
+ * Copyright (C) 2019-2021 Intel Corporation
  */
 
 #include <linux/export.h>
@@ -253,17 +253,23 @@ int cfg80211_wext_siwrts(struct net_device *dev,
        u32 orts = wdev->wiphy->rts_threshold;
        int err;
 
-       if (rts->disabled || !rts->fixed)
+       wiphy_lock(&rdev->wiphy);
+       if (rts->disabled || !rts->fixed) {
                wdev->wiphy->rts_threshold = (u32) -1;
-       else if (rts->value < 0)
-               return -EINVAL;
-       else
+       } else if (rts->value < 0) {
+               err = -EINVAL;
+               goto out;
+       } else {
                wdev->wiphy->rts_threshold = rts->value;
+       }
 
        err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_RTS_THRESHOLD);
+
        if (err)
                wdev->wiphy->rts_threshold = orts;
 
+out:
+       wiphy_unlock(&rdev->wiphy);
        return err;
 }
 EXPORT_WEXT_HANDLER(cfg80211_wext_siwrts);
@@ -291,11 +297,13 @@ int cfg80211_wext_siwfrag(struct net_device *dev,
        u32 ofrag = wdev->wiphy->frag_threshold;
        int err;
 
-       if (frag->disabled || !frag->fixed)
+       wiphy_lock(&rdev->wiphy);
+       if (frag->disabled || !frag->fixed) {
                wdev->wiphy->frag_threshold = (u32) -1;
-       else if (frag->value < 256)
-               return -EINVAL;
-       else {
+       } else if (frag->value < 256) {
+               err = -EINVAL;
+               goto out;
+       } else {
                /* Fragment length must be even, so strip LSB. */
                wdev->wiphy->frag_threshold = frag->value & ~0x1;
        }
@@ -303,6 +311,8 @@ int cfg80211_wext_siwfrag(struct net_device *dev,
        err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_FRAG_THRESHOLD);
        if (err)
                wdev->wiphy->frag_threshold = ofrag;
+out:
+       wiphy_unlock(&rdev->wiphy);
 
        return err;
 }
@@ -337,6 +347,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
            (retry->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
                return -EINVAL;
 
+       wiphy_lock(&rdev->wiphy);
        if (retry->flags & IW_RETRY_LONG) {
                wdev->wiphy->retry_long = retry->value;
                changed |= WIPHY_PARAM_RETRY_LONG;
@@ -355,6 +366,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
                wdev->wiphy->retry_short = oshort;
                wdev->wiphy->retry_long = olong;
        }
+       wiphy_unlock(&rdev->wiphy);
 
        return err;
 }
@@ -577,15 +589,18 @@ static int cfg80211_wext_siwencode(struct net_device *dev,
            !rdev->ops->set_default_key)
                return -EOPNOTSUPP;
 
+       wiphy_lock(&rdev->wiphy);
        idx = erq->flags & IW_ENCODE_INDEX;
        if (idx == 0) {
                idx = wdev->wext.default_key;
                if (idx < 0)
                        idx = 0;
-       } else if (idx < 1 || idx > 4)
-               return -EINVAL;
-       else
+       } else if (idx < 1 || idx > 4) {
+               err = -EINVAL;
+               goto out;
+       } else {
                idx--;
+       }
 
        if (erq->flags & IW_ENCODE_DISABLED)
                remove = true;
@@ -599,22 +614,28 @@ static int cfg80211_wext_siwencode(struct net_device *dev,
                if (!err)
                        wdev->wext.default_key = idx;
                wdev_unlock(wdev);
-               return err;
+               goto out;
        }
 
        memset(&params, 0, sizeof(params));
        params.key = keybuf;
        params.key_len = erq->length;
-       if (erq->length == 5)
+       if (erq->length == 5) {
                params.cipher = WLAN_CIPHER_SUITE_WEP40;
-       else if (erq->length == 13)
+       } else if (erq->length == 13) {
                params.cipher = WLAN_CIPHER_SUITE_WEP104;
-       else if (!remove)
-               return -EINVAL;
+       } else if (!remove) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = cfg80211_set_encryption(rdev, dev, false, NULL, remove,
+                                     wdev->wext.default_key == -1,
+                                     idx, &params);
+out:
+       wiphy_unlock(&rdev->wiphy);
 
-       return cfg80211_set_encryption(rdev, dev, false, NULL, remove,
-                                      wdev->wext.default_key == -1,
-                                      idx, &params);
+       return err;
 }
 
 static int cfg80211_wext_siwencodeext(struct net_device *dev,
@@ -754,38 +775,61 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
        struct cfg80211_chan_def chandef = {
                .width = NL80211_CHAN_WIDTH_20_NOHT,
        };
-       int freq;
+       int freq, ret;
+
+       wiphy_lock(&rdev->wiphy);
 
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
-               return cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
+               ret = cfg80211_mgd_wext_siwfreq(dev, info, wextfreq, extra);
+               break;
        case NL80211_IFTYPE_ADHOC:
-               return cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
+               ret = cfg80211_ibss_wext_siwfreq(dev, info, wextfreq, extra);
+               break;
        case NL80211_IFTYPE_MONITOR:
                freq = cfg80211_wext_freq(wextfreq);
-               if (freq < 0)
-                       return freq;
-               if (freq == 0)
-                       return -EINVAL;
+               if (freq < 0) {
+                       ret = freq;
+                       break;
+               }
+               if (freq == 0) {
+                       ret = -EINVAL;
+                       break;
+               }
                chandef.center_freq1 = freq;
                chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
-               if (!chandef.chan)
-                       return -EINVAL;
-               return cfg80211_set_monitor_channel(rdev, &chandef);
+               if (!chandef.chan) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = cfg80211_set_monitor_channel(rdev, &chandef);
+               break;
        case NL80211_IFTYPE_MESH_POINT:
                freq = cfg80211_wext_freq(wextfreq);
-               if (freq < 0)
-                       return freq;
-               if (freq == 0)
-                       return -EINVAL;
+               if (freq < 0) {
+                       ret = freq;
+                       break;
+               }
+               if (freq == 0) {
+                       ret = -EINVAL;
+                       break;
+               }
                chandef.center_freq1 = freq;
                chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
-               if (!chandef.chan)
-                       return -EINVAL;
-               return cfg80211_set_mesh_channel(rdev, wdev, &chandef);
+               if (!chandef.chan) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_giwfreq(struct net_device *dev,
@@ -797,24 +841,35 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
        struct cfg80211_chan_def chandef = {};
        int ret;
 
+       wiphy_lock(&rdev->wiphy);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_STATION:
-               return cfg80211_mgd_wext_giwfreq(dev, info, freq, extra);
+               ret = cfg80211_mgd_wext_giwfreq(dev, info, freq, extra);
+               break;
        case NL80211_IFTYPE_ADHOC:
-               return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
+               ret = cfg80211_ibss_wext_giwfreq(dev, info, freq, extra);
+               break;
        case NL80211_IFTYPE_MONITOR:
-               if (!rdev->ops->get_channel)
-                       return -EINVAL;
+               if (!rdev->ops->get_channel) {
+                       ret = -EINVAL;
+                       break;
+               }
 
                ret = rdev_get_channel(rdev, wdev, &chandef);
                if (ret)
-                       return ret;
+                       break;
                freq->m = chandef.chan->center_freq;
                freq->e = 6;
-               return 0;
+               ret = 0;
+               break;
        default:
-               return -EINVAL;
+               ret = -EINVAL;
+               break;
        }
+
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_siwtxpower(struct net_device *dev,
@@ -825,6 +880,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        enum nl80211_tx_power_setting type;
        int dbm = 0;
+       int ret;
 
        if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM)
                return -EINVAL;
@@ -866,7 +922,11 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
                return 0;
        }
 
-       return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
+       wiphy_lock(&rdev->wiphy);
+       ret = rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_giwtxpower(struct net_device *dev,
@@ -885,7 +945,9 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
        if (!rdev->ops->get_tx_power)
                return -EOPNOTSUPP;
 
+       wiphy_lock(&rdev->wiphy);
        err = rdev_get_tx_power(rdev, wdev, &val);
+       wiphy_unlock(&rdev->wiphy);
        if (err)
                return err;
 
@@ -1125,7 +1187,9 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
                        timeout = wrq->value / 1000;
        }
 
+       wiphy_lock(&rdev->wiphy);
        err = rdev_set_power_mgmt(rdev, dev, ps, timeout);
+       wiphy_unlock(&rdev->wiphy);
        if (err)
                return err;
 
@@ -1156,7 +1220,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev,
        struct cfg80211_bitrate_mask mask;
        u32 fixed, maxrate;
        struct ieee80211_supported_band *sband;
-       int band, ridx;
+       int band, ridx, ret;
        bool match = false;
 
        if (!rdev->ops->set_bitrate_mask)
@@ -1195,7 +1259,11 @@ static int cfg80211_wext_siwrate(struct net_device *dev,
        if (!match)
                return -EINVAL;
 
-       return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
+       wiphy_lock(&rdev->wiphy);
+       ret = rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_giwrate(struct net_device *dev,
@@ -1224,7 +1292,9 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
        if (err)
                return err;
 
+       wiphy_lock(&rdev->wiphy);
        err = rdev_get_station(rdev, dev, addr, &sinfo);
+       wiphy_unlock(&rdev->wiphy);
        if (err)
                return err;
 
@@ -1249,6 +1319,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
        static struct iw_statistics wstats;
        static struct station_info sinfo = {};
        u8 bssid[ETH_ALEN];
+       int ret;
 
        if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
                return NULL;
@@ -1267,7 +1338,11 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
 
        memset(&sinfo, 0, sizeof(sinfo));
 
-       if (rdev_get_station(rdev, dev, bssid, &sinfo))
+       wiphy_lock(&rdev->wiphy);
+       ret = rdev_get_station(rdev, dev, bssid, &sinfo);
+       wiphy_unlock(&rdev->wiphy);
+
+       if (ret)
                return NULL;
 
        memset(&wstats, 0, sizeof(wstats));
@@ -1318,15 +1393,24 @@ static int cfg80211_wext_siwap(struct net_device *dev,
                               struct sockaddr *ap_addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       int ret;
 
+       wiphy_lock(&rdev->wiphy);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
-               return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
+               ret = cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra);
+               break;
        case NL80211_IFTYPE_STATION:
-               return cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra);
+               ret = cfg80211_mgd_wext_siwap(dev, info, ap_addr, extra);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_giwap(struct net_device *dev,
@@ -1334,15 +1418,24 @@ static int cfg80211_wext_giwap(struct net_device *dev,
                               struct sockaddr *ap_addr, char *extra)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       int ret;
 
+       wiphy_lock(&rdev->wiphy);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
-               return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
+               ret = cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra);
+               break;
        case NL80211_IFTYPE_STATION:
-               return cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra);
+               ret = cfg80211_mgd_wext_giwap(dev, info, ap_addr, extra);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_siwessid(struct net_device *dev,
@@ -1350,15 +1443,24 @@ static int cfg80211_wext_siwessid(struct net_device *dev,
                                  struct iw_point *data, char *ssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       int ret;
 
+       wiphy_lock(&rdev->wiphy);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
-               return cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
+               ret = cfg80211_ibss_wext_siwessid(dev, info, data, ssid);
+               break;
        case NL80211_IFTYPE_STATION:
-               return cfg80211_mgd_wext_siwessid(dev, info, data, ssid);
+               ret = cfg80211_mgd_wext_siwessid(dev, info, data, ssid);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_giwessid(struct net_device *dev,
@@ -1366,18 +1468,27 @@ static int cfg80211_wext_giwessid(struct net_device *dev,
                                  struct iw_point *data, char *ssid)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+       int ret;
 
        data->flags = 0;
        data->length = 0;
 
+       wiphy_lock(&rdev->wiphy);
        switch (wdev->iftype) {
        case NL80211_IFTYPE_ADHOC:
-               return cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
+               ret = cfg80211_ibss_wext_giwessid(dev, info, data, ssid);
+               break;
        case NL80211_IFTYPE_STATION:
-               return cfg80211_mgd_wext_giwessid(dev, info, data, ssid);
+               ret = cfg80211_mgd_wext_giwessid(dev, info, data, ssid);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 static int cfg80211_wext_siwpmksa(struct net_device *dev,
@@ -1388,6 +1499,7 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
        struct cfg80211_pmksa cfg_pmksa;
        struct iw_pmksa *pmksa = (struct iw_pmksa *)extra;
+       int ret;
 
        memset(&cfg_pmksa, 0, sizeof(struct cfg80211_pmksa));
 
@@ -1397,28 +1509,39 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,
        cfg_pmksa.bssid = pmksa->bssid.sa_data;
        cfg_pmksa.pmkid = pmksa->pmkid;
 
+       wiphy_lock(&rdev->wiphy);
        switch (pmksa->cmd) {
        case IW_PMKSA_ADD:
-               if (!rdev->ops->set_pmksa)
-                       return -EOPNOTSUPP;
-
-               return rdev_set_pmksa(rdev, dev, &cfg_pmksa);
+               if (!rdev->ops->set_pmksa) {
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
 
+               ret = rdev_set_pmksa(rdev, dev, &cfg_pmksa);
+               break;
        case IW_PMKSA_REMOVE:
-               if (!rdev->ops->del_pmksa)
-                       return -EOPNOTSUPP;
-
-               return rdev_del_pmksa(rdev, dev, &cfg_pmksa);
+               if (!rdev->ops->del_pmksa) {
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
 
+               ret = rdev_del_pmksa(rdev, dev, &cfg_pmksa);
+               break;
        case IW_PMKSA_FLUSH:
-               if (!rdev->ops->flush_pmksa)
-                       return -EOPNOTSUPP;
-
-               return rdev_flush_pmksa(rdev, dev);
+               if (!rdev->ops->flush_pmksa) {
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
 
+               ret = rdev_flush_pmksa(rdev, dev);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+       wiphy_unlock(&rdev->wiphy);
+
+       return ret;
 }
 
 #define DEFINE_WEXT_COMPAT_STUB(func, type)                    \
index 69102fd..76a80a4 100644 (file)
@@ -896,8 +896,9 @@ out:
 int call_commit_handler(struct net_device *dev)
 {
 #ifdef CONFIG_WIRELESS_EXT
-       if ((netif_running(dev)) &&
-          (dev->wireless_handlers->standard[0] != NULL))
+       if (netif_running(dev) &&
+           dev->wireless_handlers &&
+           dev->wireless_handlers->standard[0])
                /* Call the commit handler on the driver */
                return dev->wireless_handlers->standard[0](dev, NULL,
                                                           NULL, NULL);
index 73df235..193a18a 100644 (file)
@@ -3,7 +3,7 @@
  * cfg80211 wext compat for managed mode.
  *
  * Copyright 2009      Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2009   Intel Corporation. All rights reserved.
+ * Copyright (C) 2009, 2020-2021 Intel Corporation.
  */
 
 #include <linux/export.h>
@@ -379,6 +379,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
        if (mlme->addr.sa_family != ARPHRD_ETHER)
                return -EINVAL;
 
+       wiphy_lock(&rdev->wiphy);
        wdev_lock(wdev);
        switch (mlme->cmd) {
        case IW_MLME_DEAUTH:
@@ -390,6 +391,7 @@ int cfg80211_wext_siwmlme(struct net_device *dev,
                break;
        }
        wdev_unlock(wdev);
+       wiphy_unlock(&rdev->wiphy);
 
        return err;
 }
index ac4a317..4a83117 100644 (file)
@@ -108,9 +108,9 @@ EXPORT_SYMBOL(xsk_get_pool_from_qid);
 
 void xsk_clear_pool_at_qid(struct net_device *dev, u16 queue_id)
 {
-       if (queue_id < dev->real_num_rx_queues)
+       if (queue_id < dev->num_rx_queues)
                dev->_rx[queue_id].pool = NULL;
-       if (queue_id < dev->real_num_tx_queues)
+       if (queue_id < dev->num_tx_queues)
                dev->_tx[queue_id].pool = NULL;
 }
 
@@ -423,9 +423,9 @@ static void xsk_destruct_skb(struct sk_buff *skb)
        struct xdp_sock *xs = xdp_sk(skb->sk);
        unsigned long flags;
 
-       spin_lock_irqsave(&xs->tx_completion_lock, flags);
+       spin_lock_irqsave(&xs->pool->cq_lock, flags);
        xskq_prod_submit_addr(xs->pool->cq, addr);
-       spin_unlock_irqrestore(&xs->tx_completion_lock, flags);
+       spin_unlock_irqrestore(&xs->pool->cq_lock, flags);
 
        sock_wfree(skb);
 }
@@ -437,6 +437,7 @@ static int xsk_generic_xmit(struct sock *sk)
        bool sent_frame = false;
        struct xdp_desc desc;
        struct sk_buff *skb;
+       unsigned long flags;
        int err = 0;
 
        mutex_lock(&xs->mutex);
@@ -468,10 +469,13 @@ static int xsk_generic_xmit(struct sock *sk)
                 * if there is space in it. This avoids having to implement
                 * any buffering in the Tx path.
                 */
+               spin_lock_irqsave(&xs->pool->cq_lock, flags);
                if (unlikely(err) || xskq_prod_reserve(xs->pool->cq)) {
+                       spin_unlock_irqrestore(&xs->pool->cq_lock, flags);
                        kfree_skb(skb);
                        goto out;
                }
+               spin_unlock_irqrestore(&xs->pool->cq_lock, flags);
 
                skb->dev = xs->dev;
                skb->priority = sk->sk_priority;
@@ -483,6 +487,9 @@ static int xsk_generic_xmit(struct sock *sk)
                if  (err == NETDEV_TX_BUSY) {
                        /* Tell user-space to retry the send */
                        skb->destructor = sock_wfree;
+                       spin_lock_irqsave(&xs->pool->cq_lock, flags);
+                       xskq_prod_cancel(xs->pool->cq);
+                       spin_unlock_irqrestore(&xs->pool->cq_lock, flags);
                        /* Free skb without triggering the perf drop trace */
                        consume_skb(skb);
                        err = -EAGAIN;
@@ -878,6 +885,10 @@ static int xsk_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
                }
        }
 
+       /* FQ and CQ are now owned by the buffer pool and cleaned up with it. */
+       xs->fq_tmp = NULL;
+       xs->cq_tmp = NULL;
+
        xs->dev = dev;
        xs->zc = xs->umem->zc;
        xs->queue_id = qid;
@@ -1299,7 +1310,6 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
        xs->state = XSK_READY;
        mutex_init(&xs->mutex);
        spin_lock_init(&xs->rx_lock);
-       spin_lock_init(&xs->tx_completion_lock);
 
        INIT_LIST_HEAD(&xs->map_list);
        spin_lock_init(&xs->map_list_lock);
index 67a4494..20598ee 100644 (file)
@@ -71,12 +71,11 @@ struct xsk_buff_pool *xp_create_and_assign_umem(struct xdp_sock *xs,
        INIT_LIST_HEAD(&pool->free_list);
        INIT_LIST_HEAD(&pool->xsk_tx_list);
        spin_lock_init(&pool->xsk_tx_list_lock);
+       spin_lock_init(&pool->cq_lock);
        refcount_set(&pool->users, 1);
 
        pool->fq = xs->fq_tmp;
        pool->cq = xs->cq_tmp;
-       xs->fq_tmp = NULL;
-       xs->cq_tmp = NULL;
 
        for (i = 0; i < pool->free_heads_cnt; i++) {
                xskb = &pool->heads[i];
index 4a9663a..2823b7c 100644 (file)
@@ -334,6 +334,11 @@ static inline bool xskq_prod_is_full(struct xsk_queue *q)
        return xskq_prod_nb_free(q, 1) ? false : true;
 }
 
+static inline void xskq_prod_cancel(struct xsk_queue *q)
+{
+       q->cached_prod--;
+}
+
 static inline int xskq_prod_reserve(struct xsk_queue *q)
 {
        if (xskq_prod_is_full(q))
index be6351e..1158cd0 100644 (file)
@@ -660,7 +660,7 @@ resume:
                /* only the first xfrm gets the encap type */
                encap_type = 0;
 
-               if (async && x->repl->recheck(x, skb, seq)) {
+               if (x->repl->recheck(x, skb, seq)) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATESEQERROR);
                        goto drop_unlock;
                }
index d622c25..b74f28c 100644 (file)
@@ -793,15 +793,22 @@ static int xfrm_policy_addr_delta(const xfrm_address_t *a,
                                  const xfrm_address_t *b,
                                  u8 prefixlen, u16 family)
 {
+       u32 ma, mb, mask;
        unsigned int pdw, pbi;
        int delta = 0;
 
        switch (family) {
        case AF_INET:
-               if (sizeof(long) == 4 && prefixlen == 0)
-                       return ntohl(a->a4) - ntohl(b->a4);
-               return (ntohl(a->a4) & ((~0UL << (32 - prefixlen)))) -
-                      (ntohl(b->a4) & ((~0UL << (32 - prefixlen))));
+               if (prefixlen == 0)
+                       return 0;
+               mask = ~0U << (32 - prefixlen);
+               ma = ntohl(a->a4) & mask;
+               mb = ntohl(b->a4) & mask;
+               if (ma < mb)
+                       delta = -1;
+               else if (ma > mb)
+                       delta = 1;
+               break;
        case AF_INET6:
                pdw = prefixlen >> 5;
                pbi = prefixlen & 0x1f;
@@ -812,10 +819,13 @@ static int xfrm_policy_addr_delta(const xfrm_address_t *a,
                                return delta;
                }
                if (pbi) {
-                       u32 mask = ~0u << (32 - pbi);
-
-                       delta = (ntohl(a->a6[pdw]) & mask) -
-                               (ntohl(b->a6[pdw]) & mask);
+                       mask = ~0U << (32 - pbi);
+                       ma = ntohl(a->a6[pdw]) & mask;
+                       mb = ntohl(b->a6[pdw]) & mask;
+                       if (ma < mb)
+                               delta = -1;
+                       else if (ma > mb)
+                               delta = 1;
                }
                break;
        default:
@@ -3078,8 +3088,8 @@ struct dst_entry *xfrm_lookup_with_ifid(struct net *net,
                xflo.flags = flags;
 
                /* To accelerate a bit...  */
-               if ((dst_orig->flags & DST_NOXFRM) ||
-                   !net->xfrm.policy_count[XFRM_POLICY_OUT])
+               if (!if_id && ((dst_orig->flags & DST_NOXFRM) ||
+                              !net->xfrm.policy_count[XFRM_POLICY_OUT]))
                        goto nopol;
 
                xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo, if_id);
index 5442379..db67a28 100644 (file)
@@ -138,11 +138,11 @@ struct bpf_insn;
 
 #define BPF_STX_XADD(SIZE, DST, SRC, OFF)                      \
        ((struct bpf_insn) {                                    \
-               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD,   \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = OFF,                                   \
-               .imm   = 0 })
+               .imm   = BPF_ADD })
 
 /* Memory store, *(uint *) (dst_reg + off16) = imm32 */
 
index deb0e3e..c5ff7a1 100644 (file)
@@ -147,12 +147,12 @@ static void prog_load(void)
                 */
                BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
                BPF_MOV64_IMM(BPF_REG_1, 1),
-               BPF_STX_XADD(BPF_DW, BPF_REG_9, BPF_REG_1,
-                               offsetof(struct stats, packets)),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,
+                             offsetof(struct stats, packets)),
                BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6,
                                offsetof(struct __sk_buff, len)),
-               BPF_STX_XADD(BPF_DW, BPF_REG_9, BPF_REG_1,
-                               offsetof(struct stats, bytes)),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_9, BPF_REG_1,
+                             offsetof(struct stats, bytes)),
                BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
                                offsetof(struct __sk_buff, len)),
                BPF_EXIT_INSN(),
index 00aae1d..23d1930 100644 (file)
@@ -54,7 +54,7 @@ static int test_sock(void)
                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
                BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
                BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
-               BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
                BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0 */
                BPF_EXIT_INSN(),
        };
index 20fbd12..390ff38 100644 (file)
@@ -53,7 +53,7 @@ static int prog_load(int map_fd, int verdict)
                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
                BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
                BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
-               BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
 
                /* Count bytes */
                BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_BYTES), /* r0 = 1 */
@@ -64,7 +64,8 @@ static int prog_load(int map_fd, int verdict)
                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
                BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
                BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), /* r1 = skb->len */
-               BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
 
                BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
                BPF_EXIT_INSN(),
index 0008530..92e888e 100755 (executable)
@@ -6646,6 +6646,12 @@ sub process {
 #                      }
 #              }
 
+# strlcpy uses that should likely be strscpy
+               if ($line =~ /\bstrlcpy\s*\(/) {
+                       WARN("STRLCPY",
+                            "Prefer strscpy over strlcpy - see: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw\@mail.gmail.com/\n" . $herecurr);
+               }
+
 # typecasts on min/max could be min_t/max_t
                if ($perl_version_ok &&
                    defined $stat &&
index 8c8d7c3..ff88e2f 100755 (executable)
@@ -223,6 +223,7 @@ while [ "$1" != "" ] ; do
                ;;
 
        *)
+               echo "bad command: $CMD" >&2
                usage
                ;;
        esac
index e083bca..3643b4f 100755 (executable)
@@ -15,6 +15,8 @@ if ! test -r System.map ; then
        exit 0
 fi
 
+# legacy behavior: "depmod" in /sbin, no /sbin in PATH
+PATH="$PATH:/sbin"
 if [ -z $(command -v $DEPMOD) ]; then
        echo "Warning: 'make modules_install' requires $DEPMOD. Please install it." >&2
        echo "This is probably in the kmod package." >&2
index d66949b..b5487cc 100644 (file)
@@ -22,9 +22,9 @@ always-y += $(GCC_PLUGIN)
 GCC_PLUGINS_DIR = $(shell $(CC) -print-file-name=plugin)
 
 plugin_cxxflags        = -Wp,-MMD,$(depfile) $(KBUILD_HOSTCXXFLAGS) -fPIC \
-                  -I $(GCC_PLUGINS_DIR)/include -I $(obj) -std=gnu++98 \
+                  -I $(GCC_PLUGINS_DIR)/include -I $(obj) -std=gnu++11 \
                   -fno-rtti -fno-exceptions -fasynchronous-unwind-tables \
-                  -ggdb -Wno-narrowing -Wno-unused-variable -Wno-c++11-compat \
+                  -ggdb -Wno-narrowing -Wno-unused-variable \
                   -Wno-format-diag
 
 plugin_ldflags = -shared
index e46df0a..2c40e68 100644 (file)
@@ -94,16 +94,6 @@ configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/c
        $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles)
        $(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
 
-PHONY += kvmconfig
-kvmconfig: kvm_guest.config
-       @echo >&2 "WARNING: 'make $@' will be removed after Linux 5.10"
-       @echo >&2 "         Please use 'make $<' instead."
-
-PHONY += xenconfig
-xenconfig: xen.config
-       @echo >&2 "WARNING: 'make $@' will be removed after Linux 5.10"
-       @echo >&2 "         Please use 'make $<' instead."
-
 PHONY += tinyconfig
 tinyconfig:
        $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config
index aa68ec9..fcd4acd 100755 (executable)
@@ -33,7 +33,9 @@ if [ -f /usr/include/ncurses/ncurses.h ]; then
        exit 0
 fi
 
-if [ -f /usr/include/ncurses.h ]; then
+# As a final fallback before giving up, check if $HOSTCC knows of a default
+# ncurses installation (e.g. from a vendor-specific sysroot).
+if echo '#include <ncurses.h>' | "${HOSTCC}" -E - >/dev/null 2>&1; then
        echo cflags=\"-D_GNU_SOURCE\"
        echo libs=\"-lncurses\"
        exit 0
index 7d8026f..a0cd28c 100644 (file)
@@ -275,7 +275,9 @@ static void dump_common_audit_data(struct audit_buffer *ab,
                struct inode *inode;
 
                audit_log_format(ab, " name=");
+               spin_lock(&a->u.dentry->d_lock);
                audit_log_untrustedstring(ab, a->u.dentry->d_name.name);
+               spin_unlock(&a->u.dentry->d_lock);
 
                inode = d_backing_inode(a->u.dentry);
                if (inode) {
@@ -293,8 +295,9 @@ static void dump_common_audit_data(struct audit_buffer *ab,
                dentry = d_find_alias(inode);
                if (dentry) {
                        audit_log_format(ab, " name=");
-                       audit_log_untrustedstring(ab,
-                                        dentry->d_name.name);
+                       spin_lock(&dentry->d_lock);
+                       audit_log_untrustedstring(ab, dentry->d_name.name);
+                       spin_unlock(&dentry->d_lock);
                        dput(dentry);
                }
                audit_log_format(ab, " dev=");
index 9f3f8e9..c4aac70 100644 (file)
@@ -382,8 +382,8 @@ retry:
                        continue;
 
                /*
-                * The 'deps' array includes maximum three dependencies
-                * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fourth
+                * The 'deps' array includes maximum four dependencies
+                * to SNDRV_PCM_HW_PARAM_XXXs for this rule. The fifth
                 * member of this array is a sentinel and should be
                 * negative value.
                 *
index 11554d0..1b8409e 100644 (file)
@@ -611,7 +611,8 @@ snd_seq_oss_synth_make_info(struct seq_oss_devinfo *dp, int dev, struct synth_in
 
        if (info->is_midi) {
                struct midi_info minf;
-               snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf);
+               if (snd_seq_oss_midi_make_info(dp, info->midi_mapped, &minf))
+                       return -ENXIO;
                inf->synth_type = SYNTH_TYPE_MIDI;
                inf->synth_subtype = 0;
                inf->nr_voices = 16;
index 7f82762..ee7122c 100644 (file)
@@ -88,7 +88,7 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
 
        /* Set interval to next transaction. */
        ff->next_ktime[port] = ktime_add_ns(ktime_get(),
-                               ff->rx_bytes[port] * 8 * NSEC_PER_SEC / 31250);
+                       ff->rx_bytes[port] * 8 * (NSEC_PER_SEC / 31250));
 
        if (quad_count == 1)
                tcode = TCODE_WRITE_QUADLET_REQUEST;
index 90288b4..a073cec 100644 (file)
@@ -209,7 +209,7 @@ static void midi_port_work(struct work_struct *work)
 
        /* Set interval to next transaction. */
        port->next_ktime = ktime_add_ns(ktime_get(),
-                               port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+                       port->consume_bytes * 8 * (NSEC_PER_SEC / 31250));
 
        /* Start this transaction. */
        port->idling = false;
index 6a0d070..c456861 100644 (file)
@@ -307,6 +307,10 @@ static const struct config_entry config_table[] = {
                .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
                .device = 0xa0c8,
        },
+       {
+               .flags = FLAG_SOF | FLAG_SOF_ONLY_IF_DMIC_OR_SOUNDWIRE,
+               .device = 0x43c8,
+       },
 #endif
 
 /* Elkhart Lake */
index 687216e..eec1775 100644 (file)
@@ -2934,7 +2934,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
        snd_hdac_leave_pm(&codec->core);
 }
 
-static int hda_codec_suspend(struct device *dev)
+static int hda_codec_runtime_suspend(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
        unsigned int state;
@@ -2953,7 +2953,7 @@ static int hda_codec_suspend(struct device *dev)
        return 0;
 }
 
-static int hda_codec_resume(struct device *dev)
+static int hda_codec_runtime_resume(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
 
@@ -2968,16 +2968,6 @@ static int hda_codec_resume(struct device *dev)
        return 0;
 }
 
-static int hda_codec_runtime_suspend(struct device *dev)
-{
-       return hda_codec_suspend(dev);
-}
-
-static int hda_codec_runtime_resume(struct device *dev)
-{
-       return hda_codec_resume(dev);
-}
-
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_PM_SLEEP
@@ -2998,31 +2988,31 @@ static void hda_codec_pm_complete(struct device *dev)
 static int hda_codec_pm_suspend(struct device *dev)
 {
        dev->power.power_state = PMSG_SUSPEND;
-       return hda_codec_suspend(dev);
+       return pm_runtime_force_suspend(dev);
 }
 
 static int hda_codec_pm_resume(struct device *dev)
 {
        dev->power.power_state = PMSG_RESUME;
-       return hda_codec_resume(dev);
+       return pm_runtime_force_resume(dev);
 }
 
 static int hda_codec_pm_freeze(struct device *dev)
 {
        dev->power.power_state = PMSG_FREEZE;
-       return hda_codec_suspend(dev);
+       return pm_runtime_force_suspend(dev);
 }
 
 static int hda_codec_pm_thaw(struct device *dev)
 {
        dev->power.power_state = PMSG_THAW;
-       return hda_codec_resume(dev);
+       return pm_runtime_force_resume(dev);
 }
 
 static int hda_codec_pm_restore(struct device *dev)
 {
        dev->power.power_state = PMSG_RESTORE;
-       return hda_codec_resume(dev);
+       return pm_runtime_force_resume(dev);
 }
 #endif /* CONFIG_PM_SLEEP */
 
index 6852668..5a50d3a 100644 (file)
@@ -2220,8 +2220,6 @@ static const struct snd_pci_quirk power_save_denylist[] = {
        SND_PCI_QUIRK(0x1849, 0x7662, "Asrock H81M-HDS", 0),
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
        SND_PCI_QUIRK(0x1043, 0x8733, "Asus Prime X370-Pro", 0),
-       /* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */
-       SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0),
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
        SND_PCI_QUIRK(0x1558, 0x6504, "Clevo W65_67SB", 0),
        /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */
@@ -2486,6 +2484,9 @@ static const struct pci_device_id azx_ids[] = {
        /* CometLake-S */
        { PCI_DEVICE(0x8086, 0xa3f0),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* CometLake-R */
+       { PCI_DEVICE(0x8086, 0xf0c8),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Icelake */
        { PCI_DEVICE(0x8086, 0x34c8),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
@@ -2509,6 +2510,9 @@ static const struct pci_device_id azx_ids[] = {
        /* Alderlake-S */
        { PCI_DEVICE(0x8086, 0x7ad0),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
+       /* Alderlake-P */
+       { PCI_DEVICE(0x8086, 0x51c8),
+         .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
        /* Elkhart Lake */
        { PCI_DEVICE(0x8086, 0x4b55),
          .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE},
@@ -2600,7 +2604,8 @@ static const struct pci_device_id azx_ids[] = {
          .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
        /* ATI HDMI */
        { PCI_DEVICE(0x1002, 0x0002),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
        { PCI_DEVICE(0x1002, 0x1308),
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
        { PCI_DEVICE(0x1002, 0x157a),
@@ -2662,9 +2667,11 @@ static const struct pci_device_id azx_ids[] = {
        { PCI_DEVICE(0x1002, 0xaab0),
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
        { PCI_DEVICE(0x1002, 0xaac0),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
        { PCI_DEVICE(0x1002, 0xaac8),
-         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+         .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
+         AZX_DCAPS_PM_RUNTIME },
        { PCI_DEVICE(0x1002, 0xaad8),
          .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
          AZX_DCAPS_PM_RUNTIME },
index 70164d1..361cf20 100644 (file)
@@ -388,7 +388,7 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev)
         * in powers of 2, next available ratio is 16 which can be
         * used as a limiting factor here.
         */
-       if (of_device_is_compatible(np, "nvidia,tegra194-hda"))
+       if (of_device_is_compatible(np, "nvidia,tegra30-hda"))
                chip->bus.core.sdo_limit = 16;
 
        /* codec detection */
index be5000d..d49cc44 100644 (file)
@@ -1070,6 +1070,7 @@ static int patch_conexant_auto(struct hda_codec *codec)
 static const struct hda_device_id snd_hda_id_conexant[] = {
        HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto),
        HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto),
        HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
        HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
        HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
index 1e4a4b8..97adff0 100644 (file)
@@ -1733,7 +1733,7 @@ static void silent_stream_disable(struct hda_codec *codec,
        per_pin->silent_stream = false;
 
  unlock_out:
-       mutex_unlock(&spec->pcm_lock);
+       mutex_unlock(&per_pin->lock);
 }
 
 /* update ELD and jack state via audio component */
@@ -4346,6 +4346,7 @@ HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI",       patch_i915_icl_hdmi),
 HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI",  patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI",        patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862815, "Alderlake HDMI",  patch_i915_tgl_hdmi),
+HDA_CODEC_ENTRY(0x8086281c, "Alderlake-P HDMI", patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi),
 HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi),
 HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI",        patch_i915_icl_hdmi),
index dde5ba2..2906455 100644 (file)
@@ -6289,6 +6289,7 @@ enum {
        ALC221_FIXUP_HP_FRONT_MIC,
        ALC292_FIXUP_TPT460,
        ALC298_FIXUP_SPK_VOLUME,
+       ALC298_FIXUP_LENOVO_SPK_VOLUME,
        ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
        ALC269_FIXUP_ATIV_BOOK_8,
        ALC221_FIXUP_HP_MIC_NO_PRESENCE,
@@ -6370,6 +6371,7 @@ enum {
        ALC256_FIXUP_HP_HEADSET_MIC,
        ALC236_FIXUP_DELL_AIO_HEADSET_MIC,
        ALC282_FIXUP_ACER_DISABLE_LINEOUT,
+       ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -7119,6 +7121,10 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC298_FIXUP_DELL_AIO_MIC_NO_PRESENCE,
        },
+       [ALC298_FIXUP_LENOVO_SPK_VOLUME] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc298_fixup_speaker_volume,
+       },
        [ALC295_FIXUP_DISABLE_DAC3] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = alc295_fixup_disable_dac3,
@@ -7803,6 +7809,12 @@ static const struct hda_fixup alc269_fixups[] = {
                .chained = true,
                .chain_id = ALC269_FIXUP_HEADSET_MODE
        },
+       [ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc269_fixup_limit_int_mic_boost,
+               .chained = true,
+               .chain_id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -7821,6 +7833,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x102b, "Acer Aspire C24-860", ALC286_FIXUP_ACER_AIO_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x1065, "Acer Aspire C20-820", ALC269VC_FIXUP_ACER_HEADSET_MIC),
        SND_PCI_QUIRK(0x1025, 0x106d, "Acer Cloudbook 14", ALC283_FIXUP_CHROME_BOOK),
+       SND_PCI_QUIRK(0x1025, 0x1094, "Acer Aspire E5-575T", ALC255_FIXUP_ACER_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x1025, 0x1099, "Acer Aspire E5-523G", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x110e, "Acer Aspire ES1-432", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1025, 0x1166, "Acer Veriton N4640G", ALC269_FIXUP_LIFEBOOK),
@@ -7885,7 +7898,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1028, 0x09bf, "Dell Precision", ALC233_FIXUP_ASUS_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x0a2e, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
        SND_PCI_QUIRK(0x1028, 0x0a30, "Dell", ALC236_FIXUP_DELL_AIO_HEADSET_MIC),
-       SND_PCI_QUIRK(0x1028, 0x0a58, "Dell Precision 3650 Tower", ALC255_FIXUP_DELL_HEADSET_MIC),
+       SND_PCI_QUIRK(0x1028, 0x0a58, "Dell", ALC255_FIXUP_DELL_HEADSET_MIC),
        SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -7959,11 +7972,17 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x8497, "HP Envy x360", ALC269_FIXUP_HP_MUTE_LED_MIC3),
        SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
        SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+       SND_PCI_QUIRK(0x103c, 0x8724, "HP EliteBook 850 G7", ALC285_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT),
        SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED),
+       SND_PCI_QUIRK(0x103c, 0x8780, "HP ZBook Fury 17 G7 Mobile Workstation",
+                     ALC285_FIXUP_HP_GPIO_AMP_INIT),
+       SND_PCI_QUIRK(0x103c, 0x8783, "HP ZBook Fury 15 G7 Mobile Workstation",
+                     ALC285_FIXUP_HP_GPIO_AMP_INIT),
+       SND_PCI_QUIRK(0x103c, 0x87c8, "HP", ALC287_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x87f4, "HP", ALC287_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x87f5, "HP", ALC287_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
@@ -7987,6 +8006,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1043, 0x18b1, "Asus MJ401TA", ALC256_FIXUP_ASUS_HEADSET_MIC),
        SND_PCI_QUIRK(0x1043, 0x18f1, "Asus FX505DT", ALC256_FIXUP_ASUS_HEADSET_MIC),
        SND_PCI_QUIRK(0x1043, 0x194e, "ASUS UX563FD", ALC294_FIXUP_ASUS_HPE),
+       SND_PCI_QUIRK(0x1043, 0x1982, "ASUS B1400CEPE", ALC256_FIXUP_ASUS_HPE),
        SND_PCI_QUIRK(0x1043, 0x19ce, "ASUS B9450FA", ALC294_FIXUP_ASUS_HPE),
        SND_PCI_QUIRK(0x1043, 0x19e1, "ASUS UX581LV", ALC295_FIXUP_ASUS_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
@@ -8021,6 +8041,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
        SND_PCI_QUIRK(0x10ec, 0x10f2, "Intel Reference board", ALC700_FIXUP_INTEL_REFERENCE),
        SND_PCI_QUIRK(0x10ec, 0x1230, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+       SND_PCI_QUIRK(0x10ec, 0x1252, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
+       SND_PCI_QUIRK(0x10ec, 0x1254, "Intel Reference board", ALC295_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK(0x10f7, 0x8338, "Panasonic CF-SZ6", ALC269_FIXUP_HEADSET_MODE),
        SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x144d, 0xc169, "Samsung Notebook 9 Pen (NP930SBE-K01US)", ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET),
@@ -8126,6 +8148,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x3151, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x17aa, 0x3176, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
        SND_PCI_QUIRK(0x17aa, 0x3178, "ThinkCentre Station", ALC283_FIXUP_HEADSET_MIC),
+       SND_PCI_QUIRK(0x17aa, 0x3818, "Lenovo C940", ALC298_FIXUP_LENOVO_SPK_VOLUME),
        SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
        SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
        SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo B50-70", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
index 7ef8f31..a5c1a2c 100644 (file)
@@ -113,6 +113,7 @@ static struct via_spec *via_new_spec(struct hda_codec *codec)
                spec->codec_type = VT1708S;
        spec->gen.indep_hp = 1;
        spec->gen.keep_eapd_on = 1;
+       spec->gen.dac_min_mute = 1;
        spec->gen.pcm_playback_hook = via_playback_pcm_hook;
        spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
        codec->power_save_node = 1;
@@ -1002,6 +1003,7 @@ static const struct hda_verb vt1802_init_verbs[] = {
 enum {
        VIA_FIXUP_INTMIC_BOOST,
        VIA_FIXUP_ASUS_G75,
+       VIA_FIXUP_POWER_SAVE,
 };
 
 static void via_fixup_intmic_boost(struct hda_codec *codec,
@@ -1011,6 +1013,13 @@ static void via_fixup_intmic_boost(struct hda_codec *codec,
                override_mic_boost(codec, 0x30, 0, 2, 40);
 }
 
+static void via_fixup_power_save(struct hda_codec *codec,
+                                const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               codec->power_save_node = 0;
+}
+
 static const struct hda_fixup via_fixups[] = {
        [VIA_FIXUP_INTMIC_BOOST] = {
                .type = HDA_FIXUP_FUNC,
@@ -1025,11 +1034,16 @@ static const struct hda_fixup via_fixups[] = {
                        { }
                }
        },
+       [VIA_FIXUP_POWER_SAVE] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = via_fixup_power_save,
+       },
 };
 
 static const struct snd_pci_quirk vt2002p_fixups[] = {
        SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
        SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
+       SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
        {}
 };
 
index 8c138e4..d3536fd 100644 (file)
@@ -140,21 +140,14 @@ static int snd_acp3x_probe(struct pci_dev *pci,
                goto release_regions;
        }
 
-       /* check for msi interrupt support */
-       ret = pci_enable_msi(pci);
-       if (ret)
-               /* msi is not enabled */
-               irqflags = IRQF_SHARED;
-       else
-               /* msi is enabled */
-               irqflags = 0;
+       irqflags = IRQF_SHARED;
 
        addr = pci_resource_start(pci, 0);
        adata->acp3x_base = devm_ioremap(&pci->dev, addr,
                                        pci_resource_len(pci, 0));
        if (!adata->acp3x_base) {
                ret = -ENOMEM;
-               goto disable_msi;
+               goto release_regions;
        }
        pci_set_master(pci);
        pci_set_drvdata(pci, adata);
@@ -162,7 +155,7 @@ static int snd_acp3x_probe(struct pci_dev *pci,
        adata->pme_en = rv_readl(adata->acp3x_base + mmACP_PME_EN);
        ret = acp3x_init(adata);
        if (ret)
-               goto disable_msi;
+               goto release_regions;
 
        val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG);
        switch (val) {
@@ -251,8 +244,6 @@ unregister_devs:
 de_init:
        if (acp3x_deinit(adata->acp3x_base))
                dev_err(&pci->dev, "ACP de-init failed\n");
-disable_msi:
-       pci_disable_msi(pci);
 release_regions:
        pci_release_regions(pci);
 disable_pci:
@@ -311,7 +302,6 @@ static void snd_acp3x_remove(struct pci_dev *pci)
                dev_err(&pci->dev, "ACP de-init failed\n");
        pm_runtime_forbid(&pci->dev);
        pm_runtime_get_noresume(&pci->dev);
-       pci_disable_msi(pci);
        pci_release_regions(pci);
        pci_disable_device(pci);
 }
index fa169bf..050a61f 100644 (file)
@@ -165,10 +165,38 @@ static int rn_acp_deinit(void __iomem *acp_base)
 
 static const struct dmi_system_id rn_acp_quirk_table[] = {
        {
-               /* Lenovo IdeaPad Flex 5 14ARE05, IdeaPad 5 15ARE05 */
+               /* Lenovo IdeaPad S340-14API */
                .matches = {
                        DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
-                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "LNVNB161216"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81NB"),
+               }
+       },
+       {
+               /* Lenovo IdeaPad Flex 5 14ARE05 */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81X2"),
+               }
+       },
+       {
+               /* Lenovo IdeaPad 5 15ARE05 */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "81YQ"),
+               }
+       },
+       {
+               /* Lenovo ThinkPad E14 Gen 2 */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "20T6CTO1WW"),
+               }
+       },
+       {
+               /* Lenovo ThinkPad X395 */
+               .matches = {
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "20NLCTO1WW"),
                }
        },
        {}
index 142373e..9fe9471 100644 (file)
@@ -143,7 +143,7 @@ config SND_MCHP_SOC_SPDIFTX
          - sama7g5
 
          This S/PDIF TX driver is compliant with IEC-60958 standard and
-         includes programable User Data and Channel Status fields.
+         includes programmable User Data and Channel Status fields.
 
 config SND_MCHP_SOC_SPDIFRX
        tristate "Microchip ASoC driver for boards using S/PDIF RX"
@@ -157,5 +157,5 @@ config SND_MCHP_SOC_SPDIFRX
          - sama7g5
 
          This S/PDIF RX driver is compliant with IEC-60958 standard and
-         includes programable User Data and Channel Status fields.
+         includes programmable User Data and Channel Status fields.
 endif
index ba4eb54..9bf6bfd 100644 (file)
@@ -457,7 +457,7 @@ config SND_SOC_ADAU7118_HW
        help
          Enable support for the Analog Devices ADAU7118 8 Channel PDM-to-I2S/TDM
          Converter. In this mode, the device works in standalone mode which
-         means that there is no bus to comunicate with it. Stereo mode is not
+         means that there is no bus to communicate with it. Stereo mode is not
          supported in this mode.
 
          To compile this driver as a module, choose M here: the module
index 1010c9e..472caad 100644 (file)
@@ -595,18 +595,10 @@ static struct snd_soc_dai_driver ak4497_dai = {
        .ops = &ak4458_dai_ops,
 };
 
-static void ak4458_power_off(struct ak4458_priv *ak4458)
+static void ak4458_reset(struct ak4458_priv *ak4458, bool active)
 {
        if (ak4458->reset_gpiod) {
-               gpiod_set_value_cansleep(ak4458->reset_gpiod, 0);
-               usleep_range(1000, 2000);
-       }
-}
-
-static void ak4458_power_on(struct ak4458_priv *ak4458)
-{
-       if (ak4458->reset_gpiod) {
-               gpiod_set_value_cansleep(ak4458->reset_gpiod, 1);
+               gpiod_set_value_cansleep(ak4458->reset_gpiod, active);
                usleep_range(1000, 2000);
        }
 }
@@ -620,7 +612,7 @@ static int ak4458_init(struct snd_soc_component *component)
        if (ak4458->mute_gpiod)
                gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
 
-       ak4458_power_on(ak4458);
+       ak4458_reset(ak4458, false);
 
        ret = snd_soc_component_update_bits(component, AK4458_00_CONTROL1,
                            0x80, 0x80);   /* ACKS bit = 1; 10000000 */
@@ -650,7 +642,7 @@ static void ak4458_remove(struct snd_soc_component *component)
 {
        struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component);
 
-       ak4458_power_off(ak4458);
+       ak4458_reset(ak4458, true);
 }
 
 #ifdef CONFIG_PM
@@ -660,7 +652,7 @@ static int __maybe_unused ak4458_runtime_suspend(struct device *dev)
 
        regcache_cache_only(ak4458->regmap, true);
 
-       ak4458_power_off(ak4458);
+       ak4458_reset(ak4458, true);
 
        if (ak4458->mute_gpiod)
                gpiod_set_value_cansleep(ak4458->mute_gpiod, 0);
@@ -685,8 +677,8 @@ static int __maybe_unused ak4458_runtime_resume(struct device *dev)
        if (ak4458->mute_gpiod)
                gpiod_set_value_cansleep(ak4458->mute_gpiod, 1);
 
-       ak4458_power_off(ak4458);
-       ak4458_power_on(ak4458);
+       ak4458_reset(ak4458, true);
+       ak4458_reset(ak4458, false);
 
        regcache_cache_only(ak4458->regmap, false);
        regcache_mark_dirty(ak4458->regmap);
index d5fcc4d..0f3ac22 100644 (file)
@@ -717,7 +717,7 @@ static int hdmi_codec_set_jack(struct snd_soc_component *component,
                               void *data)
 {
        struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
-       int ret = -EOPNOTSUPP;
+       int ret = -ENOTSUPP;
 
        if (hcp->hcd.ops->hook_plugged_cb) {
                hcp->jack = jack;
index 92921e3..85f6865 100644 (file)
 #include <sound/tlv.h>
 #include "max98373.h"
 
+static const u32 max98373_i2c_cache_reg[] = {
+       MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+       MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+       MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
 static struct reg_default max98373_reg[] = {
        {MAX98373_R2000_SW_RESET, 0x00},
        {MAX98373_R2001_INT_RAW1, 0x00},
@@ -472,6 +478,11 @@ static struct snd_soc_dai_driver max98373_dai[] = {
 static int max98373_suspend(struct device *dev)
 {
        struct max98373_priv *max98373 = dev_get_drvdata(dev);
+       int i;
+
+       /* cache feedback register values before suspend */
+       for (i = 0; i < max98373->cache_num; i++)
+               regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
 
        regcache_cache_only(max98373->regmap, true);
        regcache_mark_dirty(max98373->regmap);
@@ -509,6 +520,7 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
 {
        int ret = 0;
        int reg = 0;
+       int i;
        struct max98373_priv *max98373 = NULL;
 
        max98373 = devm_kzalloc(&i2c->dev, sizeof(*max98373), GFP_KERNEL);
@@ -534,6 +546,14 @@ static int max98373_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
+       max98373->cache_num = ARRAY_SIZE(max98373_i2c_cache_reg);
+       max98373->cache = devm_kcalloc(&i2c->dev, max98373->cache_num,
+                                      sizeof(*max98373->cache),
+                                      GFP_KERNEL);
+
+       for (i = 0; i < max98373->cache_num; i++)
+               max98373->cache[i].reg = max98373_i2c_cache_reg[i];
+
        /* voltage/current slot & gpio configuration */
        max98373_slot_config(&i2c->dev, max98373);
 
index ec2e79c..b8d471d 100644 (file)
@@ -23,6 +23,12 @@ struct sdw_stream_data {
        struct sdw_stream_runtime *sdw_stream;
 };
 
+static const u32 max98373_sdw_cache_reg[] = {
+       MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
+       MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
+       MAX98373_R20B6_BDE_CUR_STATE_READBACK,
+};
+
 static struct reg_default max98373_reg[] = {
        {MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
        {MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
@@ -245,6 +251,11 @@ static const struct regmap_config max98373_sdw_regmap = {
 static __maybe_unused int max98373_suspend(struct device *dev)
 {
        struct max98373_priv *max98373 = dev_get_drvdata(dev);
+       int i;
+
+       /* cache feedback register values before suspend */
+       for (i = 0; i < max98373->cache_num; i++)
+               regmap_read(max98373->regmap, max98373->cache[i].reg, &max98373->cache[i].val);
 
        regcache_cache_only(max98373->regmap, true);
 
@@ -757,6 +768,7 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
 {
        struct max98373_priv *max98373;
        int ret;
+       int i;
        struct device *dev = &slave->dev;
 
        /*  Allocate and assign private driver data structure  */
@@ -768,6 +780,14 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
        max98373->regmap = regmap;
        max98373->slave = slave;
 
+       max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg);
+       max98373->cache = devm_kcalloc(dev, max98373->cache_num,
+                                      sizeof(*max98373->cache),
+                                      GFP_KERNEL);
+
+       for (i = 0; i < max98373->cache_num; i++)
+               max98373->cache[i].reg = max98373_sdw_cache_reg[i];
+
        /* Read voltage and slot configuration */
        max98373_slot_config(dev, max98373);
 
index 929bb17..31d571d 100644 (file)
@@ -168,6 +168,31 @@ static SOC_ENUM_SINGLE_DECL(max98373_adc_samplerate_enum,
                            MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0,
                            max98373_ADC_samplerate_text);
 
+static int max98373_feedback_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component);
+       int i;
+
+       if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
+               /*
+                * Register values will be cached before suspend. The cached value
+                * will be a valid value and userspace will happy with that.
+                */
+               for (i = 0; i < max98373->cache_num; i++) {
+                       if (mc->reg == max98373->cache[i].reg) {
+                               ucontrol->value.integer.value[0] = max98373->cache[i].val;
+                               return 0;
+                       }
+               }
+       }
+
+       return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
 static const struct snd_kcontrol_new max98373_snd_controls[] = {
 SOC_SINGLE("Digital Vol Sel Switch", MAX98373_R203F_AMP_DSP_CFG,
        MAX98373_AMP_VOL_SEL_SHIFT, 1, 0),
@@ -209,8 +234,10 @@ SOC_SINGLE("ADC PVDD FLT Switch", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
        MAX98373_FLT_EN_SHIFT, 1, 0),
 SOC_SINGLE("ADC TEMP FLT Switch", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
        MAX98373_FLT_EN_SHIFT, 1, 0),
-SOC_SINGLE("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0),
-SOC_SINGLE("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0),
+SOC_SINGLE_EXT("ADC PVDD", MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0, 0xFF, 0,
+       max98373_feedback_get, NULL),
+SOC_SINGLE_EXT("ADC TEMP", MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0, 0xFF, 0,
+       max98373_feedback_get, NULL),
 SOC_SINGLE("ADC PVDD FLT Coeff", MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG,
        0, 0x3, 0),
 SOC_SINGLE("ADC TEMP FLT Coeff", MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG,
@@ -226,7 +253,8 @@ SOC_SINGLE("BDE LVL1 Thresh", MAX98373_R2097_BDE_L1_THRESH, 0, 0xFF, 0),
 SOC_SINGLE("BDE LVL2 Thresh", MAX98373_R2098_BDE_L2_THRESH, 0, 0xFF, 0),
 SOC_SINGLE("BDE LVL3 Thresh", MAX98373_R2099_BDE_L3_THRESH, 0, 0xFF, 0),
 SOC_SINGLE("BDE LVL4 Thresh", MAX98373_R209A_BDE_L4_THRESH, 0, 0xFF, 0),
-SOC_SINGLE("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0),
+SOC_SINGLE_EXT("BDE Active Level", MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0, 8, 0,
+       max98373_feedback_get, NULL),
 SOC_SINGLE("BDE Clip Mode Switch", MAX98373_R2092_BDE_CLIPPER_MODE, 0, 1, 0),
 SOC_SINGLE("BDE Thresh Hysteresis", MAX98373_R209B_BDE_THRESH_HYST, 0, 0xFF, 0),
 SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0),
index 4ab29b9..71f5a52 100644 (file)
 /* MAX98373_R2000_SW_RESET */
 #define MAX98373_SOFT_RESET (0x1 << 0)
 
+struct max98373_cache {
+       u32 reg;
+       u32 val;
+};
+
 struct max98373_priv {
        struct regmap *regmap;
        int reset_gpio;
@@ -212,6 +217,9 @@ struct max98373_priv {
        bool interleave_mode;
        unsigned int ch_size;
        bool tdm_mode;
+       /* cache for reading a valid fake feedback value */
+       struct max98373_cache *cache;
+       int cache_num;
        /* variables to support soundwire */
        struct sdw_slave *slave;
        bool hw_init;
index 5771c02..85f7441 100644 (file)
@@ -462,6 +462,8 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol,
        unsigned int read_ll, read_rl;
        int i;
 
+       mutex_lock(&rt711->calibrate_mutex);
+
        /* Can't use update bit function, so read the original value first */
        addr_h = mc->reg;
        addr_l = mc->rreg;
@@ -547,6 +549,8 @@ static int rt711_set_amp_gain_put(struct snd_kcontrol *kcontrol,
        if (dapm->bias_level <= SND_SOC_BIAS_STANDBY)
                regmap_write(rt711->regmap,
                                RT711_SET_AUDIO_POWER_STATE, AC_PWRST_D3);
+
+       mutex_unlock(&rt711->calibrate_mutex);
        return 0;
 }
 
@@ -859,9 +863,11 @@ static int rt711_set_bias_level(struct snd_soc_component *component,
                break;
 
        case SND_SOC_BIAS_STANDBY:
+               mutex_lock(&rt711->calibrate_mutex);
                regmap_write(rt711->regmap,
                        RT711_SET_AUDIO_POWER_STATE,
                        AC_PWRST_D3);
+               mutex_unlock(&rt711->calibrate_mutex);
                break;
 
        default:
index dec8716..985b2dc 100644 (file)
@@ -2031,11 +2031,14 @@ static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp,
                                             unsigned int alg)
 {
        struct wm_coeff_ctl *pos, *rslt = NULL;
+       const char *fw_txt = wm_adsp_fw_text[dsp->fw];
 
        list_for_each_entry(pos, &dsp->ctl_list, list) {
                if (!pos->subname)
                        continue;
                if (strncmp(pos->subname, name, pos->subname_len) == 0 &&
+                   strncmp(pos->fw_name, fw_txt,
+                           SNDRV_CTL_ELEM_ID_NAME_MAXLEN) == 0 &&
                                pos->alg_region.alg == alg &&
                                pos->alg_region.type == type) {
                        rslt = pos;
index 2c2a76a..dbbb761 100644 (file)
@@ -90,7 +90,7 @@ static int imx_hdmi_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        ret = snd_soc_component_set_jack(component, &data->hdmi_jack, NULL);
-       if (ret && ret != -EOPNOTSUPP) {
+       if (ret && ret != -ENOTSUPP) {
                dev_err(card->dev, "Can't set HDMI Jack %d\n", ret);
                return ret;
        }
@@ -164,6 +164,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
 
        if ((hdmi_out && hdmi_in) || (!hdmi_out && !hdmi_in)) {
                dev_err(&pdev->dev, "Invalid HDMI DAI link\n");
+               ret = -EINVAL;
                goto fail;
        }
 
index c55d123..c763bfe 100644 (file)
@@ -189,6 +189,7 @@ static struct platform_driver haswell_audio = {
        .probe = haswell_audio_probe,
        .driver = {
                .name = "haswell-audio",
+               .pm = &snd_soc_pm_ops,
        },
 };
 
index ca96890..6d0d6ef 100644 (file)
@@ -67,6 +67,16 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
                .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 |
                                        SOF_RT715_DAI_ID_FIX),
        },
+       {
+               .callback = sof_sdw_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A5E")
+               },
+               .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 |
+                                       SOF_RT715_DAI_ID_FIX |
+                                       SOF_SDW_FOUR_SPK),
+       },
        {
                .callback = sof_sdw_quirk_cb,
                .matches = {
index fcd8dff..1275c14 100644 (file)
@@ -224,6 +224,7 @@ static int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
                                "dsp boot timeout, status=%#x error=%#x\n",
                                sst_dsp_shim_read(ctx, CNL_ADSP_FW_STATUS),
                                sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE));
+                       ret = -ETIMEDOUT;
                        goto err;
                }
        } else {
index ae466cd..b824086 100644 (file)
@@ -3619,19 +3619,20 @@ static void skl_tplg_complete(struct snd_soc_component *component)
 
        list_for_each_entry(dobj, &component->dobj_list, list) {
                struct snd_kcontrol *kcontrol = dobj->control.kcontrol;
-               struct soc_enum *se =
-                       (struct soc_enum *)kcontrol->private_value;
-               char **texts = dobj->control.dtexts;
+               struct soc_enum *se;
+               char **texts;
                char chan_text[4];
 
-               if (dobj->type != SND_SOC_DOBJ_ENUM ||
-                   dobj->control.kcontrol->put !=
-                   skl_tplg_multi_config_set_dmic)
+               if (dobj->type != SND_SOC_DOBJ_ENUM || !kcontrol ||
+                   kcontrol->put != skl_tplg_multi_config_set_dmic)
                        continue;
+
+               se = (struct soc_enum *)kcontrol->private_value;
+               texts = dobj->control.dtexts;
                sprintf(chan_text, "c%d", mach->mach_params.dmic_num);
 
                for (i = 0; i < se->items; i++) {
-                       struct snd_ctl_elem_value val;
+                       struct snd_ctl_elem_value val = {};
 
                        if (strstr(texts[i], chan_text)) {
                                val.value.enumerated.item[0] = i;
index 078e58f..cfbd0c6 100644 (file)
@@ -532,6 +532,7 @@ static struct snd_soc_dai_link mt8183_da7219_dai_links[] = {
                .dpcm_playback = 1,
                .ignore_suspend = 1,
                .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
+               .ignore = 1,
                .init = mt8183_da7219_max98357_hdmi_init,
                SND_SOC_DAILINK_REG(tdm),
        },
@@ -754,8 +755,10 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev)
                        }
                }
 
-               if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0)
+               if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
                        dai_link->codecs->of_node = hdmi_codec;
+                       dai_link->ignore = 0;
+               }
 
                if (!dai_link->platforms->name)
                        dai_link->platforms->of_node = platform_node;
index 8c83408..1ce3edd 100644 (file)
@@ -515,6 +515,7 @@ static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = {
                .ignore_suspend = 1,
                .be_hw_params_fixup = mt8183_i2s_hw_params_fixup,
                .ops = &mt8183_mt6358_tdm_ops,
+               .ignore = 1,
                .init = mt8183_mt6358_ts3a227_max98357_hdmi_init,
                SND_SOC_DAILINK_REG(tdm),
        },
@@ -661,8 +662,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev)
                                                    SND_SOC_DAIFMT_CBM_CFM;
                }
 
-               if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0)
+               if (hdmi_codec && strcmp(dai_link->name, "TDM") == 0) {
                        dai_link->codecs->of_node = hdmi_codec;
+                       dai_link->ignore = 0;
+               }
 
                if (!dai_link->platforms->name)
                        dai_link->platforms->of_node = platform_node;
index 716fbb4..ae2c748 100644 (file)
@@ -401,6 +401,53 @@ static const struct snd_soc_ops mt8192_mt6359_rt1015_rt5682_capture1_ops = {
        .startup = mt8192_mt6359_rt1015_rt5682_cap1_startup,
 };
 
+static int
+mt8192_mt6359_rt5682_startup(struct snd_pcm_substream *substream)
+{
+       static const unsigned int channels[] = {
+               1, 2
+       };
+       static const struct snd_pcm_hw_constraint_list constraints_channels = {
+               .count = ARRAY_SIZE(channels),
+               .list = channels,
+               .mask = 0,
+       };
+       static const unsigned int rates[] = {
+               48000
+       };
+       static const struct snd_pcm_hw_constraint_list constraints_rates = {
+               .count = ARRAY_SIZE(rates),
+               .list  = rates,
+               .mask = 0,
+       };
+
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int ret;
+
+       ret = snd_pcm_hw_constraint_list(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_CHANNELS,
+                                        &constraints_channels);
+       if (ret < 0) {
+               dev_err(rtd->dev, "hw_constraint_list channels failed\n");
+               return ret;
+       }
+
+       ret = snd_pcm_hw_constraint_list(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_RATE,
+                                        &constraints_rates);
+       if (ret < 0) {
+               dev_err(rtd->dev, "hw_constraint_list rate failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_ops mt8192_mt6359_rt5682_ops = {
+       .startup = mt8192_mt6359_rt5682_startup,
+};
+
 /* FE */
 SND_SOC_DAILINK_DEFS(playback1,
                     DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
@@ -648,6 +695,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
                            SND_SOC_DPCM_TRIGGER_PRE},
                .dynamic = 1,
                .dpcm_playback = 1,
+               .ops = &mt8192_mt6359_rt5682_ops,
                SND_SOC_DAILINK_REG(playback3),
        },
        {
@@ -721,6 +769,7 @@ static struct snd_soc_dai_link mt8192_mt6359_dai_links[] = {
                            SND_SOC_DPCM_TRIGGER_PRE},
                .dynamic = 1,
                .dpcm_capture = 1,
+               .ops = &mt8192_mt6359_rt5682_ops,
                SND_SOC_DAILINK_REG(capture2),
        },
        {
index c8664ab..87cac44 100644 (file)
@@ -467,8 +467,20 @@ static int axg_tdm_iface_set_bias_level(struct snd_soc_component *component,
        return ret;
 }
 
+static const struct snd_soc_dapm_widget axg_tdm_iface_dapm_widgets[] = {
+       SND_SOC_DAPM_SIGGEN("Playback Signal"),
+};
+
+static const struct snd_soc_dapm_route axg_tdm_iface_dapm_routes[] = {
+       { "Loopback", NULL, "Playback Signal" },
+};
+
 static const struct snd_soc_component_driver axg_tdm_iface_component_drv = {
-       .set_bias_level = axg_tdm_iface_set_bias_level,
+       .dapm_widgets           = axg_tdm_iface_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(axg_tdm_iface_dapm_widgets),
+       .dapm_routes            = axg_tdm_iface_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(axg_tdm_iface_dapm_routes),
+       .set_bias_level         = axg_tdm_iface_set_bias_level,
 };
 
 static const struct of_device_id axg_tdm_iface_of_match[] = {
index 88ed95a..b4faf9d 100644 (file)
@@ -224,15 +224,6 @@ static const struct axg_tdm_formatter_ops axg_tdmin_ops = {
 };
 
 static const struct axg_tdm_formatter_driver axg_tdmin_drv = {
-       .component_drv  = &axg_tdmin_component_drv,
-       .regmap_cfg     = &axg_tdmin_regmap_cfg,
-       .ops            = &axg_tdmin_ops,
-       .quirks         = &(const struct axg_tdm_formatter_hw) {
-               .skew_offset    = 2,
-       },
-};
-
-static const struct axg_tdm_formatter_driver g12a_tdmin_drv = {
        .component_drv  = &axg_tdmin_component_drv,
        .regmap_cfg     = &axg_tdmin_regmap_cfg,
        .ops            = &axg_tdmin_ops,
@@ -247,10 +238,10 @@ static const struct of_device_id axg_tdmin_of_match[] = {
                .data = &axg_tdmin_drv,
        }, {
                .compatible = "amlogic,g12a-tdmin",
-               .data = &g12a_tdmin_drv,
+               .data = &axg_tdmin_drv,
        }, {
                .compatible = "amlogic,sm1-tdmin",
-               .data = &g12a_tdmin_drv,
+               .data = &axg_tdmin_drv,
        }, {}
 };
 MODULE_DEVICE_TABLE(of, axg_tdmin_of_match);
index af684fd..66b8343 100644 (file)
@@ -270,18 +270,6 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
        struct lpaif_i2sctl *i2sctl = drvdata->i2sctl;
        unsigned int id = dai->driver->id;
        int ret = -EINVAL;
-       unsigned int val = 0;
-
-       ret = regmap_read(drvdata->lpaif_map,
-                               LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), &val);
-       if (ret) {
-               dev_err(dai->dev, "error reading from i2sctl reg: %d\n", ret);
-               return ret;
-       }
-       if (val == LPAIF_I2SCTL_RESET_STATE) {
-               dev_err(dai->dev, "error in i2sctl register state\n");
-               return -ENOTRECOVERABLE;
-       }
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -356,8 +344,30 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai)
 }
 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe);
 
+static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component,
+                                  struct of_phandle_args *args,
+                                  const char **dai_name)
+{
+       struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+       struct lpass_variant *variant = drvdata->variant;
+       int id = args->args[0];
+       int ret = -EINVAL;
+       int i;
+
+       for (i = 0; i  < variant->num_dai; i++) {
+               if (variant->dai_driver[i].id == id) {
+                       *dai_name = variant->dai_driver[i].name;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
 static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
        .name = "lpass-cpu",
+       .of_xlate_dai_name = asoc_qcom_of_xlate_dai_name,
 };
 
 static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
@@ -454,20 +464,16 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
        struct lpass_variant *v = drvdata->variant;
        int i;
 
-       for (i = 0; i < v->i2s_ports; ++i)
-               if (reg == LPAIF_I2SCTL_REG(v, i))
-                       return true;
        for (i = 0; i < v->irq_ports; ++i)
                if (reg == LPAIF_IRQSTAT_REG(v, i))
                        return true;
 
        for (i = 0; i < v->rdma_channels; ++i)
-               if (reg == LPAIF_RDMACURR_REG(v, i) || reg == LPAIF_RDMACTL_REG(v, i))
+               if (reg == LPAIF_RDMACURR_REG(v, i))
                        return true;
 
        for (i = 0; i < v->wrdma_channels; ++i)
-               if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start) ||
-                       reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+               if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
                        return true;
 
        return false;
index 92f98b4..ef8a798 100644 (file)
@@ -131,7 +131,7 @@ static struct lpass_variant ipq806x_data = {
        .micmode                = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4),
        .micmono                = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4),
        .wssrc                  = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4),
-       .bitwidth               = REG_FIELD_ID(0x0010, 0, 0, 5, 0x4),
+       .bitwidth               = REG_FIELD_ID(0x0010, 0, 1, 5, 0x4),
 
        .rdma_dyncclk           = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000),
        .rdma_bursten           = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000),
index 4055428..baf72f1 100644 (file)
 #define        LPAIF_WRDMAPERCNT_REG(v, chan)  LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
 
 #define LPAIF_INTFDMA_REG(v, chan, reg, dai_id)  \
-               ((v->dai_driver[dai_id].id ==  LPASS_DP_RX) ? \
+       ((dai_id ==  LPASS_DP_RX) ? \
                LPAIF_HDMI_RDMA##reg##_REG(v, chan) : \
                 LPAIF_RDMA##reg##_REG(v, chan))
 
index 80b09de..0074b7f 100644 (file)
@@ -257,6 +257,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
                break;
        case MI2S_PRIMARY:
        case MI2S_SECONDARY:
+       case MI2S_TERTIARY:
+       case MI2S_QUATERNARY:
+       case MI2S_QUINARY:
                ret = regmap_fields_write(dmactl->intf, id,
                                                LPAIF_DMACTL_AUDINTF(dma_port));
                if (ret) {
@@ -452,7 +455,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
        unsigned int reg_irqclr = 0, val_irqclr = 0;
        unsigned int  reg_irqen = 0, val_irqen = 0, val_mask = 0;
        unsigned int dai_id = cpu_dai->driver->id;
-       unsigned int dma_ctrl_reg = 0;
 
        ch = pcm_data->dma_ch;
        if (dir ==  SNDRV_PCM_STREAM_PLAYBACK) {
@@ -469,17 +471,7 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
                id = pcm_data->dma_ch - v->wrdma_channel_start;
                map = drvdata->lpaif_map;
        }
-       ret = regmap_read(map, LPAIF_DMACTL_REG(v, ch, dir, dai_id), &dma_ctrl_reg);
-       if (ret) {
-               dev_err(soc_runtime->dev, "error reading from rdmactl reg: %d\n", ret);
-               return ret;
-       }
 
-       if (dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE ||
-               dma_ctrl_reg == LPAIF_DMACTL_RESET_STATE + 1) {
-               dev_err(soc_runtime->dev, "error in rdmactl register state\n");
-               return -ENOTRECOVERABLE;
-       }
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
@@ -500,7 +492,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
                                        "error writing to rdmactl reg: %d\n", ret);
                                return ret;
                        }
-                       map = drvdata->hdmiif_map;
                        reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v);
                        val_irqclr = (LPAIF_IRQ_ALL(ch) |
                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
@@ -519,7 +510,9 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
                        break;
                case MI2S_PRIMARY:
                case MI2S_SECONDARY:
-                       map = drvdata->lpaif_map;
+               case MI2S_TERTIARY:
+               case MI2S_QUATERNARY:
+               case MI2S_QUINARY:
                        reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
                        val_irqclr = LPAIF_IRQ_ALL(ch);
 
@@ -563,7 +556,6 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
                                        "error writing to rdmactl reg: %d\n", ret);
                                return ret;
                        }
-                       map = drvdata->hdmiif_map;
                        reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v);
                        val_mask = (LPAIF_IRQ_ALL(ch) |
                                        LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) |
@@ -573,7 +565,9 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
                        break;
                case MI2S_PRIMARY:
                case MI2S_SECONDARY:
-                       map = drvdata->lpaif_map;
+               case MI2S_TERTIARY:
+               case MI2S_QUATERNARY:
+               case MI2S_QUINARY:
                        reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
                        val_mask = LPAIF_IRQ_ALL(ch);
                        val_irqen = 0;
@@ -670,6 +664,9 @@ static irqreturn_t lpass_dma_interrupt_handler(
        break;
        case MI2S_PRIMARY:
        case MI2S_SECONDARY:
+       case MI2S_TERTIARY:
+       case MI2S_QUATERNARY:
+       case MI2S_QUINARY:
                map = drvdata->lpaif_map;
                reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
                val = 0;
@@ -838,6 +835,39 @@ static void lpass_platform_pcm_free(struct snd_soc_component *component,
        }
 }
 
+static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
+{
+       struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+       struct regmap *map;
+       unsigned int dai_id = component->id;
+
+       if (dai_id == LPASS_DP_RX)
+               map = drvdata->hdmiif_map;
+       else
+               map = drvdata->lpaif_map;
+
+       regcache_cache_only(map, true);
+       regcache_mark_dirty(map);
+
+       return 0;
+}
+
+static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
+{
+       struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
+       struct regmap *map;
+       unsigned int dai_id = component->id;
+
+       if (dai_id == LPASS_DP_RX)
+               map = drvdata->hdmiif_map;
+       else
+               map = drvdata->lpaif_map;
+
+       regcache_cache_only(map, false);
+       return regcache_sync(map);
+}
+
+
 static const struct snd_soc_component_driver lpass_component_driver = {
        .name           = DRV_NAME,
        .open           = lpass_platform_pcmops_open,
@@ -850,6 +880,8 @@ static const struct snd_soc_component_driver lpass_component_driver = {
        .mmap           = lpass_platform_pcmops_mmap,
        .pcm_construct  = lpass_platform_pcm_new,
        .pcm_destruct   = lpass_platform_pcm_free,
+       .suspend                = lpass_platform_pcmops_suspend,
+       .resume                 = lpass_platform_pcmops_resume,
 
 };
 
index 85db650..735c9da 100644 (file)
@@ -20,7 +20,7 @@
 #include "lpass.h"
 
 static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = {
-       [MI2S_PRIMARY] = {
+       {
                .id = MI2S_PRIMARY,
                .name = "Primary MI2S",
                .playback = {
@@ -44,9 +44,7 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = {
                },
                .probe  = &asoc_qcom_lpass_cpu_dai_probe,
                .ops    = &asoc_qcom_lpass_cpu_dai_ops,
-       },
-
-       [MI2S_SECONDARY] = {
+       }, {
                .id = MI2S_SECONDARY,
                .name = "Secondary MI2S",
                .playback = {
@@ -60,8 +58,7 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = {
                },
                .probe  = &asoc_qcom_lpass_cpu_dai_probe,
                .ops    = &asoc_qcom_lpass_cpu_dai_ops,
-       },
-       [LPASS_DP_RX] = {
+       }, {
                .id = LPASS_DP_RX,
                .name = "Hdmi",
                .playback = {
@@ -174,7 +171,7 @@ static struct lpass_variant sc7180_data = {
        .rdma_channels          = 5,
        .hdmi_rdma_reg_base             = 0x64000,
        .hdmi_rdma_reg_stride   = 0x1000,
-       .hdmi_rdma_channels             = 4,
+       .hdmi_rdma_channels             = 3,
        .dmactl_audif_start     = 1,
        .wrdma_reg_base         = 0x18000,
        .wrdma_reg_stride       = 0x1000,
index 0195372..2d68af0 100644 (file)
@@ -12,7 +12,7 @@
 #include <linux/compiler.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
-#include <dt-bindings/sound/sc7180-lpass.h>
+#include <dt-bindings/sound/qcom,lpass.h>
 #include "lpass-hdmi.h"
 
 #define LPASS_AHBIX_CLOCK_FREQUENCY            131072000
index b9aacf3..abdfd9c 100644 (file)
@@ -366,25 +366,27 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct clk *clk;
-       int i, ret;
+       int i;
 
        for_each_rsnd_clk(clk, adg, i) {
-               ret = 0;
                if (enable) {
-                       ret = clk_prepare_enable(clk);
+                       int ret = clk_prepare_enable(clk);
 
                        /*
                         * We shouldn't use clk_get_rate() under
                         * atomic context. Let's keep it when
                         * rsnd_adg_clk_enable() was called
                         */
-                       adg->clk_rate[i] = clk_get_rate(adg->clk[i]);
+                       adg->clk_rate[i] = 0;
+                       if (ret < 0)
+                               dev_warn(dev, "can't use clk %d\n", i);
+                       else
+                               adg->clk_rate[i] = clk_get_rate(clk);
                } else {
-                       clk_disable_unprepare(clk);
+                       if (adg->clk_rate[i])
+                               clk_disable_unprepare(clk);
+                       adg->clk_rate[i] = 0;
                }
-
-               if (ret < 0)
-                       dev_warn(dev, "can't use clk %d\n", i);
        }
 }
 
index 9f0c86c..2b75d01 100644 (file)
@@ -2486,6 +2486,7 @@ void snd_soc_dapm_free_widget(struct snd_soc_dapm_widget *w)
        enum snd_soc_dapm_direction dir;
 
        list_del(&w->list);
+       list_del(&w->dirty);
        /*
         * remove source and sink paths associated to this widget.
         * While removing the path, remove reference to it from both
index 950c450..22e7b4c 100644 (file)
@@ -447,7 +447,7 @@ static void remove_dai(struct snd_soc_component *comp,
 {
        struct snd_soc_dai_driver *dai_drv =
                container_of(dobj, struct snd_soc_dai_driver, dobj);
-       struct snd_soc_dai *dai;
+       struct snd_soc_dai *dai, *_dai;
 
        if (pass != SOC_TPLG_PASS_PCM_DAI)
                return;
@@ -455,9 +455,9 @@ static void remove_dai(struct snd_soc_component *comp,
        if (dobj->ops && dobj->ops->dai_unload)
                dobj->ops->dai_unload(comp, dobj);
 
-       for_each_component_dais(comp, dai)
+       for_each_component_dais_safe(comp, dai, _dai)
                if (dai->driver == dai_drv)
-                       dai->driver = NULL;
+                       snd_soc_unregister_dai(dai);
 
        list_del(&dobj->list);
 }
@@ -902,7 +902,7 @@ static int soc_tplg_denum_create_values(struct soc_tplg *tplg, struct soc_enum *
                return -EINVAL;
 
        se->dobj.control.dvalues = devm_kcalloc(tplg->dev, le32_to_cpu(ec->items),
-                                          sizeof(u32),
+                                          sizeof(*se->dobj.control.dvalues),
                                           GFP_KERNEL);
        if (!se->dobj.control.dvalues)
                return -ENOMEM;
@@ -1742,7 +1742,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
        list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list);
 
        /* register the DAI to the component */
-       dai = devm_snd_soc_register_dai(tplg->dev, tplg->comp, dai_drv, false);
+       dai = snd_soc_register_dai(tplg->comp, dai_drv, false);
        if (!dai)
                return -ENOMEM;
 
@@ -1750,6 +1750,7 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
        ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
        if (ret != 0) {
                dev_err(dai->dev, "Failed to create DAI widgets %d\n", ret);
+               snd_soc_unregister_dai(dai);
                return ret;
        }
 
index 031dad5..3e8b6c0 100644 (file)
@@ -122,7 +122,7 @@ config SND_SOC_SOF_DEBUG_XRUN_STOP
        bool "SOF stop on XRUN"
        help
          This option forces PCMs to stop on any XRUN event. This is useful to
-         preserve any trace data ond pipeline status prior to the XRUN.
+         preserve any trace data and pipeline status prior to the XRUN.
          Say Y if you are debugging SOF FW pipeline XRUNs.
          If unsure select "N".
 
index d306c37..4797a1c 100644 (file)
@@ -355,7 +355,7 @@ config SND_SOC_SOF_HDA
 
 config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK
        bool "SOF support for SoundWire"
-       depends on SOUNDWIRE && ACPI
+       depends on ACPI
        help
          This adds support for SoundWire with Sound Open Firmware
          for Intel(R) platforms.
@@ -371,6 +371,7 @@ config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
 
 config SND_SOC_SOF_INTEL_SOUNDWIRE
        tristate
+       select SOUNDWIRE
        select SOUNDWIRE_INTEL
        help
          This option is not user-selectable but automagically handled by
index 6875fa5..6744318 100644 (file)
@@ -63,16 +63,18 @@ static int hda_codec_load_module(struct hda_codec *codec)
 }
 
 /* enable controller wake up event for all codecs with jack connectors */
-void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev)
+void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable)
 {
        struct hda_bus *hbus = sof_to_hbus(sdev);
        struct hdac_bus *bus = sof_to_bus(sdev);
        struct hda_codec *codec;
        unsigned int mask = 0;
 
-       list_for_each_codec(codec, hbus)
-               if (codec->jacktbl.used)
-                       mask |= BIT(codec->core.addr);
+       if (enable) {
+               list_for_each_codec(codec, hbus)
+                       if (codec->jacktbl.used)
+                               mask |= BIT(codec->core.addr);
+       }
 
        snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, mask);
 }
@@ -81,23 +83,18 @@ void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev)
 void hda_codec_jack_check(struct snd_sof_dev *sdev)
 {
        struct hda_bus *hbus = sof_to_hbus(sdev);
-       struct hdac_bus *bus = sof_to_bus(sdev);
        struct hda_codec *codec;
 
-       /* disable controller Wake Up event*/
-       snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
-
        list_for_each_codec(codec, hbus)
                /*
                 * Wake up all jack-detecting codecs regardless whether an event
                 * has been recorded in STATESTS
                 */
                if (codec->jacktbl.used)
-                       schedule_delayed_work(&codec->jackpoll_work,
-                                             codec->jackpoll_interval);
+                       pm_request_resume(&codec->core.dev);
 }
 #else
-void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev) {}
+void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable) {}
 void hda_codec_jack_check(struct snd_sof_dev *sdev) {}
 #endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
 EXPORT_SYMBOL_NS(hda_codec_jack_wake_enable, SND_SOC_SOF_HDA_AUDIO_CODEC);
@@ -156,7 +153,8 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
                if (!hdev->bus->audio_component) {
                        dev_dbg(sdev->dev,
                                "iDisp hw present but no driver\n");
-                       goto error;
+                       ret = -ENOENT;
+                       goto out;
                }
                hda_priv->need_display_power = true;
        }
@@ -173,24 +171,23 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address,
                 * other return codes without modification
                 */
                if (ret == 0)
-                       goto error;
+                       ret = -ENOENT;
        }
 
-       return ret;
-
-error:
-       snd_hdac_ext_bus_device_exit(hdev);
-       return -ENOENT;
-
+out:
+       if (ret < 0) {
+               snd_hdac_device_unregister(hdev);
+               put_device(&hdev->dev);
+       }
 #else
        hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL);
        if (!hdev)
                return -ENOMEM;
 
        ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, HDA_DEV_ASOC);
+#endif
 
        return ret;
-#endif
 }
 
 /* Codec initialization */
index 2b00115..1c5e05b 100644 (file)
@@ -617,7 +617,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend)
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
        if (runtime_suspend)
-               hda_codec_jack_wake_enable(sdev);
+               hda_codec_jack_wake_enable(sdev, true);
 
        /* power down all hda link */
        snd_hdac_ext_bus_link_power_down_all(bus);
@@ -683,8 +683,11 @@ static int hda_resume(struct snd_sof_dev *sdev, bool runtime_resume)
 
 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
        /* check jack status */
-       if (runtime_resume)
-               hda_codec_jack_check(sdev);
+       if (runtime_resume) {
+               hda_codec_jack_wake_enable(sdev, false);
+               if (sdev->system_suspend_target == SOF_SUSPEND_NONE)
+                       hda_codec_jack_check(sdev);
+       }
 
        /* turn off the links that were off before suspend */
        list_for_each_entry(hlink, &bus->hlink_list, list) {
index 9ec8ae0..a3b6f3e 100644 (file)
@@ -650,7 +650,7 @@ void sof_hda_bus_init(struct hdac_bus *bus, struct device *dev);
  */
 void hda_codec_probe_bus(struct snd_sof_dev *sdev,
                         bool hda_codec_use_common_hdmi);
-void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev);
+void hda_codec_jack_wake_enable(struct snd_sof_dev *sdev, bool enable);
 void hda_codec_jack_check(struct snd_sof_dev *sdev);
 
 #endif /* CONFIG_SND_SOC_SOF_HDA */
index 2a369c2..cc2e257 100644 (file)
@@ -131,12 +131,13 @@ static int sof_acpi_probe(struct platform_device *pdev)
        if (!id)
                return -ENODEV;
 
-       ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
-       if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
-               dev_dbg(dev, "SOF ACPI driver not selected, aborting probe\n");
-               return -ENODEV;
+       if (IS_REACHABLE(CONFIG_SND_INTEL_DSP_CONFIG)) {
+               ret = snd_intel_acpi_dsp_driver_probe(dev, id->id);
+               if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
+                       dev_dbg(dev, "SOF ACPI driver not selected, aborting probe\n");
+                       return -ENODEV;
+               }
        }
-
        dev_dbg(dev, "ACPI DSP detected");
 
        sof_pdata = devm_kzalloc(dev, sizeof(*sof_pdata), GFP_KERNEL);
index 63b989e..215711a 100644 (file)
@@ -344,10 +344,12 @@ static int sof_pci_probe(struct pci_dev *pci,
        const struct snd_sof_dsp_ops *ops;
        int ret;
 
-       ret = snd_intel_dsp_driver_probe(pci);
-       if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
-               dev_dbg(&pci->dev, "SOF PCI driver not selected, aborting probe\n");
-               return -ENODEV;
+       if (IS_REACHABLE(CONFIG_SND_INTEL_DSP_CONFIG)) {
+               ret = snd_intel_dsp_driver_probe(pci);
+               if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) {
+                       dev_dbg(&pci->dev, "SOF PCI driver not selected, aborting probe\n");
+                       return -ENODEV;
+               }
        }
        dev_dbg(&pci->dev, "PCI DSP detected");
 
index d731ca6..e08fbf8 100644 (file)
@@ -450,10 +450,8 @@ lookup_device_name(u32 id)
 static void snd_usb_audio_free(struct snd_card *card)
 {
        struct snd_usb_audio *chip = card->private_data;
-       struct snd_usb_endpoint *ep, *n;
 
-       list_for_each_entry_safe(ep, n, &chip->ep_list, list)
-               snd_usb_endpoint_free(ep);
+       snd_usb_endpoint_free_all(chip);
 
        mutex_destroy(&chip->mutex);
        if (!atomic_read(&chip->shutdown))
@@ -611,6 +609,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        chip->usb_id = usb_id;
        INIT_LIST_HEAD(&chip->pcm_list);
        INIT_LIST_HEAD(&chip->ep_list);
+       INIT_LIST_HEAD(&chip->iface_ref_list);
        INIT_LIST_HEAD(&chip->midi_list);
        INIT_LIST_HEAD(&chip->mixer_list);
 
index 6a027c3..37091b1 100644 (file)
@@ -18,6 +18,7 @@ struct audioformat {
        unsigned int frame_size;        /* samples per frame for non-audio */
        unsigned char iface;            /* interface number */
        unsigned char altsetting;       /* corresponding alternate setting */
+       unsigned char ep_idx;           /* endpoint array index */
        unsigned char altset_idx;       /* array index of altenate setting */
        unsigned char attributes;       /* corresponding attributes of cs endpoint */
        unsigned char endpoint;         /* endpoint */
@@ -42,6 +43,7 @@ struct audioformat {
 };
 
 struct snd_usb_substream;
+struct snd_usb_iface_ref;
 struct snd_usb_endpoint;
 struct snd_usb_power_domain;
 
@@ -58,6 +60,7 @@ struct snd_urb_ctx {
 
 struct snd_usb_endpoint {
        struct snd_usb_audio *chip;
+       struct snd_usb_iface_ref *iface_ref;
 
        int opened;             /* open refcount; protect with chip->mutex */
        atomic_t running;       /* running status */
index 31051f2..dc68ed6 100644 (file)
@@ -485,18 +485,9 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip,
                              const struct audioformat *fmt, int rate)
 {
        struct usb_device *dev = chip->dev;
-       struct usb_host_interface *alts;
-       unsigned int ep;
        unsigned char data[3];
        int err, crate;
 
-       alts = snd_usb_get_host_interface(chip, fmt->iface, fmt->altsetting);
-       if (!alts)
-               return -EINVAL;
-       if (get_iface_desc(alts)->bNumEndpoints < 1)
-               return -EINVAL;
-       ep = get_endpoint(alts, 0)->bEndpointAddress;
-
        /* if endpoint doesn't have sampling rate control, bail out */
        if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE))
                return 0;
@@ -506,11 +497,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip,
        data[2] = rate >> 16;
        err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
                              USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
-                             UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
-                             data, sizeof(data));
+                             UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
+                             fmt->endpoint, data, sizeof(data));
        if (err < 0) {
                dev_err(&dev->dev, "%d:%d: cannot set freq %d to ep %#x\n",
-                       fmt->iface, fmt->altsetting, rate, ep);
+                       fmt->iface, fmt->altsetting, rate, fmt->endpoint);
                return err;
        }
 
@@ -524,11 +515,11 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip,
 
        err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
                              USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
-                             UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
-                             data, sizeof(data));
+                             UAC_EP_CS_ATTR_SAMPLE_RATE << 8,
+                             fmt->endpoint, data, sizeof(data));
        if (err < 0) {
                dev_err(&dev->dev, "%d:%d: cannot get freq at ep %#x\n",
-                       fmt->iface, fmt->altsetting, ep);
+                       fmt->iface, fmt->altsetting, fmt->endpoint);
                chip->sample_rate_read_error++;
                return 0; /* some devices don't support reading */
        }
index 162da7a..8e56882 100644 (file)
 #define EP_FLAG_RUNNING                1
 #define EP_FLAG_STOPPING       2
 
+/* interface refcounting */
+struct snd_usb_iface_ref {
+       unsigned char iface;
+       bool need_setup;
+       int opened;
+       struct list_head list;
+};
+
 /*
  * snd_usb_endpoint is a model that abstracts everything related to an
  * USB endpoint and its streaming.
@@ -488,6 +496,28 @@ exit_clear:
        clear_bit(ctx->index, &ep->active_mask);
 }
 
+/*
+ * Find or create a refcount object for the given interface
+ *
+ * The objects are released altogether in snd_usb_endpoint_free_all()
+ */
+static struct snd_usb_iface_ref *
+iface_ref_find(struct snd_usb_audio *chip, int iface)
+{
+       struct snd_usb_iface_ref *ip;
+
+       list_for_each_entry(ip, &chip->iface_ref_list, list)
+               if (ip->iface == iface)
+                       return ip;
+
+       ip = kzalloc(sizeof(*ip), GFP_KERNEL);
+       if (!ip)
+               return NULL;
+       ip->iface = iface;
+       list_add_tail(&ip->list, &chip->iface_ref_list);
+       return ip;
+}
+
 /*
  * Get the existing endpoint object corresponding EP
  * Returns NULL if not present.
@@ -520,8 +550,8 @@ snd_usb_get_endpoint(struct snd_usb_audio *chip, int ep_num)
  *
  * Returns zero on success or a negative error code.
  *
- * New endpoints will be added to chip->ep_list and must be freed by
- * calling snd_usb_endpoint_free().
+ * New endpoints will be added to chip->ep_list and freed by
+ * calling snd_usb_endpoint_free_all().
  *
  * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
  * bNumEndpoints > 1 beforehand.
@@ -653,11 +683,17 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
                } else {
                        ep->iface = fp->iface;
                        ep->altsetting = fp->altsetting;
-                       ep->ep_idx = 0;
+                       ep->ep_idx = fp->ep_idx;
                }
                usb_audio_dbg(chip, "Open EP 0x%x, iface=%d:%d, idx=%d\n",
                              ep_num, ep->iface, ep->altsetting, ep->ep_idx);
 
+               ep->iface_ref = iface_ref_find(chip, ep->iface);
+               if (!ep->iface_ref) {
+                       ep = NULL;
+                       goto unlock;
+               }
+
                ep->cur_audiofmt = fp;
                ep->cur_channels = fp->channels;
                ep->cur_rate = params_rate(params);
@@ -681,6 +717,11 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
                              ep->implicit_fb_sync);
 
        } else {
+               if (WARN_ON(!ep->iface_ref)) {
+                       ep = NULL;
+                       goto unlock;
+               }
+
                if (!endpoint_compatible(ep, fp, params)) {
                        usb_audio_err(chip, "Incompatible EP setup for 0x%x\n",
                                      ep_num);
@@ -692,6 +733,9 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip,
                              ep_num, ep->opened);
        }
 
+       if (!ep->iface_ref->opened++)
+               ep->iface_ref->need_setup = true;
+
        ep->opened++;
 
  unlock:
@@ -760,12 +804,16 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
        mutex_lock(&chip->mutex);
        usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
                      ep->ep_num, ep->opened);
-       if (!--ep->opened) {
+
+       if (!--ep->iface_ref->opened)
                endpoint_set_interface(chip, ep, false);
+
+       if (!--ep->opened) {
                ep->iface = 0;
                ep->altsetting = 0;
                ep->cur_audiofmt = NULL;
                ep->cur_rate = 0;
+               ep->iface_ref = NULL;
                usb_audio_dbg(chip, "EP 0x%x closed\n", ep->ep_num);
        }
        mutex_unlock(&chip->mutex);
@@ -775,6 +823,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
 void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep)
 {
        ep->need_setup = true;
+       if (ep->iface_ref)
+               ep->iface_ref->need_setup = true;
 }
 
 /*
@@ -1195,11 +1245,22 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
        int err = 0;
 
        mutex_lock(&chip->mutex);
+       if (WARN_ON(!ep->iface_ref))
+               goto unlock;
        if (!ep->need_setup)
                goto unlock;
 
-       /* No need to (re-)configure the sync EP belonging to the same altset */
-       if (ep->ep_idx) {
+       /* If the interface has been already set up, just set EP parameters */
+       if (!ep->iface_ref->need_setup) {
+               /* sample rate setup of UAC1 is per endpoint, and we need
+                * to update at each EP configuration
+                */
+               if (ep->cur_audiofmt->protocol == UAC_VERSION_1) {
+                       err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt,
+                                                      ep->cur_rate);
+                       if (err < 0)
+                               goto unlock;
+               }
                err = snd_usb_endpoint_set_params(chip, ep);
                if (err < 0)
                        goto unlock;
@@ -1242,6 +1303,8 @@ int snd_usb_endpoint_configure(struct snd_usb_audio *chip,
                        goto unlock;
        }
 
+       ep->iface_ref->need_setup = false;
+
  done:
        ep->need_setup = false;
        err = 1;
@@ -1387,15 +1450,21 @@ void snd_usb_endpoint_release(struct snd_usb_endpoint *ep)
 }
 
 /**
- * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
+ * snd_usb_endpoint_free_all: Free the resources of an snd_usb_endpoint
+ * @card: The chip
  *
- * @ep: the endpoint to free
- *
- * This free all resources of the given ep.
+ * This free all endpoints and those resources
  */
-void snd_usb_endpoint_free(struct snd_usb_endpoint *ep)
+void snd_usb_endpoint_free_all(struct snd_usb_audio *chip)
 {
-       kfree(ep);
+       struct snd_usb_endpoint *ep, *en;
+       struct snd_usb_iface_ref *ip, *in;
+
+       list_for_each_entry_safe(ep, en, &chip->ep_list, list)
+               kfree(ep);
+
+       list_for_each_entry_safe(ip, in, &chip->iface_ref_list, list)
+               kfree(ip);
 }
 
 /*
index 11e3bb8..eea4ca4 100644 (file)
@@ -42,7 +42,7 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_suspend(struct snd_usb_endpoint *ep);
 int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
 void snd_usb_endpoint_release(struct snd_usb_endpoint *ep);
-void snd_usb_endpoint_free(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free_all(struct snd_usb_audio *chip);
 
 int snd_usb_endpoint_implicit_feedback_sink(struct snd_usb_endpoint *ep);
 int snd_usb_endpoint_next_packet_size(struct snd_usb_endpoint *ep,
index 9ebc5d2..e6ff317 100644 (file)
@@ -466,6 +466,17 @@ static int validate_sample_rate_table_v2v3(struct snd_usb_audio *chip,
        unsigned int nr_rates;
        int i, err;
 
+       /* performing the rate verification may lead to unexpected USB bus
+        * behavior afterwards by some unknown reason.  Do this only for the
+        * known devices.
+        */
+       switch (USB_ID_VENDOR(chip->usb_id)) {
+       case 0x07fd: /* MOTU */
+               break;
+       default:
+               return 0; /* don't perform the validation as default */
+       }
+
        table = kcalloc(fp->nr_rates, sizeof(*table), GFP_KERNEL);
        if (!table)
                return -ENOMEM;
index eb3a4c4..521cc84 100644 (file)
@@ -58,8 +58,6 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
        IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */
        IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */
        IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */
-       IMPLICIT_FB_FIXED_DEV(0x2b73, 0x000a, 0x82, 0), /* Pioneer DJ DJM-900NXS2 */
-       IMPLICIT_FB_FIXED_DEV(0x2b73, 0x0017, 0x82, 0), /* Pioneer DJ DJM-250MK2 */
        IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */
        IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */
        IMPLICIT_FB_FIXED_DEV(0x0499, 0x172a, 0x86, 2), /* Yamaha MODX */
@@ -74,10 +72,12 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
 
        /* No quirk for playback but with capture quirk (see below) */
        IMPLICIT_FB_SKIP_DEV(0x0582, 0x0130),   /* BOSS BR-80 */
+       IMPLICIT_FB_SKIP_DEV(0x0582, 0x0171),   /* BOSS RC-505 */
        IMPLICIT_FB_SKIP_DEV(0x0582, 0x0189),   /* BOSS GT-100v2 */
        IMPLICIT_FB_SKIP_DEV(0x0582, 0x01d6),   /* BOSS GT-1 */
        IMPLICIT_FB_SKIP_DEV(0x0582, 0x01d8),   /* BOSS Katana */
        IMPLICIT_FB_SKIP_DEV(0x0582, 0x01e5),   /* BOSS GT-001 */
+       IMPLICIT_FB_SKIP_DEV(0x0582, 0x0203),   /* BOSS AD-10 */
 
        {} /* terminator */
 };
@@ -85,10 +85,12 @@ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = {
 /* Implicit feedback quirk table for capture: only FIXED type */
 static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = {
        IMPLICIT_FB_FIXED_DEV(0x0582, 0x0130, 0x0d, 0x01), /* BOSS BR-80 */
+       IMPLICIT_FB_FIXED_DEV(0x0582, 0x0171, 0x0d, 0x01), /* BOSS RC-505 */
        IMPLICIT_FB_FIXED_DEV(0x0582, 0x0189, 0x0d, 0x01), /* BOSS GT-100v2 */
        IMPLICIT_FB_FIXED_DEV(0x0582, 0x01d6, 0x0d, 0x01), /* BOSS GT-1 */
        IMPLICIT_FB_FIXED_DEV(0x0582, 0x01d8, 0x0d, 0x01), /* BOSS Katana */
        IMPLICIT_FB_FIXED_DEV(0x0582, 0x01e5, 0x0d, 0x01), /* BOSS GT-001 */
+       IMPLICIT_FB_FIXED_DEV(0x0582, 0x0203, 0x0d, 0x01), /* BOSS AD-10 */
 
        {} /* terminator */
 };
@@ -96,7 +98,7 @@ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = {
 /* set up sync EP information on the audioformat */
 static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip,
                                   struct audioformat *fmt,
-                                  int ep, int ifnum,
+                                  int ep, int ep_idx, int ifnum,
                                   const struct usb_host_interface *alts)
 {
        struct usb_interface *iface;
@@ -111,7 +113,7 @@ static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip,
        fmt->sync_ep = ep;
        fmt->sync_iface = ifnum;
        fmt->sync_altsetting = alts->desc.bAlternateSetting;
-       fmt->sync_ep_idx = 0;
+       fmt->sync_ep_idx = ep_idx;
        fmt->implicit_fb = 1;
        usb_audio_dbg(chip,
                      "%d:%d: added %s implicit_fb sync_ep %x, iface %d:%d\n",
@@ -143,7 +145,7 @@ static int add_generic_uac2_implicit_fb(struct snd_usb_audio *chip,
            (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
                                        USB_ENDPOINT_USAGE_IMPLICIT_FB)
                return 0;
-       return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress,
+       return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
                                       ifnum, alts);
 }
 
@@ -169,10 +171,33 @@ static int add_roland_implicit_fb(struct snd_usb_audio *chip,
            (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
                                        USB_ENDPOINT_USAGE_IMPLICIT_FB)
                return 0;
-       return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress,
+       return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
                                       ifnum, alts);
 }
 
+/* Playback and capture EPs on Pioneer devices share the same iface/altset,
+ * but they don't seem working with the implicit fb mode well, hence we
+ * just return as if the sync were already set up.
+ */
+static int skip_pioneer_sync_ep(struct snd_usb_audio *chip,
+                               struct audioformat *fmt,
+                               struct usb_host_interface *alts)
+{
+       struct usb_endpoint_descriptor *epd;
+
+       if (alts->desc.bNumEndpoints != 2)
+               return 0;
+
+       epd = get_endpoint(alts, 1);
+       if (!usb_endpoint_is_isoc_in(epd) ||
+           (epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC ||
+           ((epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
+            USB_ENDPOINT_USAGE_DATA &&
+            (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) !=
+            USB_ENDPOINT_USAGE_IMPLICIT_FB))
+               return 0;
+       return 1; /* don't handle with the implicit fb, just skip sync EP */
+}
 
 static int __add_generic_implicit_fb(struct snd_usb_audio *chip,
                                     struct audioformat *fmt,
@@ -193,7 +218,7 @@ static int __add_generic_implicit_fb(struct snd_usb_audio *chip,
        if (!usb_endpoint_is_isoc_in(epd) ||
            (epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC)
                return 0;
-       return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress,
+       return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0,
                                       iface, alts);
 }
 
@@ -246,7 +271,7 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
                case IMPLICIT_FB_NONE:
                        return 0; /* No quirk */
                case IMPLICIT_FB_FIXED:
-                       return add_implicit_fb_sync_ep(chip, fmt, p->ep_num,
+                       return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0,
                                                       p->iface, NULL);
                }
        }
@@ -274,6 +299,14 @@ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip,
                        return 1;
        }
 
+       /* Pioneer devices with vendor spec class */
+       if (attr == USB_ENDPOINT_SYNC_ASYNC &&
+           alts->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+           USB_ID_VENDOR(chip->usb_id) == 0x2b73 /* Pioneer */) {
+               if (skip_pioneer_sync_ep(chip, fmt, alts))
+                       return 1;
+       }
+
        /* Try the generic implicit fb if available */
        if (chip->generic_implicit_fb)
                return add_generic_implicit_fb(chip, fmt, alts);
@@ -291,8 +324,8 @@ static int audioformat_capture_quirk(struct snd_usb_audio *chip,
 
        p = find_implicit_fb_entry(chip, capture_implicit_fb_quirks, alts);
        if (p && p->type == IMPLICIT_FB_FIXED)
-               return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, p->iface,
-                                              NULL);
+               return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0,
+                                              p->iface, NULL);
        return 0;
 }
 
@@ -374,20 +407,19 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip,
                                     int stream)
 {
        struct snd_usb_substream *subs;
-       const struct audioformat *fp, *sync_fmt;
+       const struct audioformat *fp, *sync_fmt = NULL;
        int score, high_score;
 
-       /* When sharing the same altset, use the original audioformat */
+       /* Use the original audioformat as fallback for the shared altset */
        if (target->iface == target->sync_iface &&
            target->altsetting == target->sync_altsetting)
-               return target;
+               sync_fmt = target;
 
        subs = find_matching_substream(chip, stream, target->sync_ep,
                                       target->fmt_type);
        if (!subs)
-               return NULL;
+               return sync_fmt;
 
-       sync_fmt = NULL;
        high_score = 0;
        list_for_each_entry(fp, &subs->fmt_list, list) {
                score = match_endpoint_audioformats(subs, fp,
index c821365..0c23fa6 100644 (file)
@@ -1889,6 +1889,8 @@ static int snd_usbmidi_get_ms_info(struct snd_usb_midi *umidi,
                ms_ep = find_usb_ms_endpoint_descriptor(hostep);
                if (!ms_ep)
                        continue;
+               if (ms_ep->bNumEmbMIDIJack > 0x10)
+                       continue;
                if (usb_endpoint_dir_out(ep)) {
                        if (endpoints[epidx].out_ep) {
                                if (++epidx >= MIDI_MAX_ENDPOINTS) {
@@ -2141,6 +2143,8 @@ static int snd_usbmidi_detect_roland(struct snd_usb_midi *umidi,
                    cs_desc[1] == USB_DT_CS_INTERFACE &&
                    cs_desc[2] == 0xf1 &&
                    cs_desc[3] == 0x02) {
+                       if (cs_desc[4] > 0x10 || cs_desc[5] > 0x10)
+                               continue;
                        endpoint->in_cables  = (1 << cs_desc[4]) - 1;
                        endpoint->out_cables = (1 << cs_desc[5]) - 1;
                        return snd_usbmidi_detect_endpoints(umidi, endpoint, 1);
index 5607990..078bb4c 100644 (file)
@@ -663,7 +663,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
        check_fmts.bits[1] = (u32)(fp->formats >> 32);
        snd_mask_intersect(&check_fmts, fmts);
        if (snd_mask_empty(&check_fmts)) {
-               hwc_debug("   > check: no supported format %d\n", fp->format);
+               hwc_debug("   > check: no supported format 0x%llx\n", fp->formats);
                return 0;
        }
        /* check the channels */
@@ -775,24 +775,11 @@ static int hw_rule_channels(struct snd_pcm_hw_params *params,
        return apply_hw_params_minmax(it, rmin, rmax);
 }
 
-static int hw_rule_format(struct snd_pcm_hw_params *params,
-                         struct snd_pcm_hw_rule *rule)
+static int apply_hw_params_format_bits(struct snd_mask *fmt, u64 fbits)
 {
-       struct snd_usb_substream *subs = rule->private;
-       const struct audioformat *fp;
-       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
-       u64 fbits;
        u32 oldbits[2];
        int changed;
 
-       hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
-       fbits = 0;
-       list_for_each_entry(fp, &subs->fmt_list, list) {
-               if (!hw_check_valid_format(subs, params, fp))
-                       continue;
-               fbits |= fp->formats;
-       }
-
        oldbits[0] = fmt->bits[0];
        oldbits[1] = fmt->bits[1];
        fmt->bits[0] &= (u32)fbits;
@@ -806,6 +793,24 @@ static int hw_rule_format(struct snd_pcm_hw_params *params,
        return changed;
 }
 
+static int hw_rule_format(struct snd_pcm_hw_params *params,
+                         struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       const struct audioformat *fp;
+       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       u64 fbits;
+
+       hwc_debug("hw_rule_format: %x:%x\n", fmt->bits[0], fmt->bits[1]);
+       fbits = 0;
+       list_for_each_entry(fp, &subs->fmt_list, list) {
+               if (!hw_check_valid_format(subs, params, fp))
+                       continue;
+               fbits |= fp->formats;
+       }
+       return apply_hw_params_format_bits(fmt, fbits);
+}
+
 static int hw_rule_period_time(struct snd_pcm_hw_params *params,
                               struct snd_pcm_hw_rule *rule)
 {
@@ -833,64 +838,92 @@ static int hw_rule_period_time(struct snd_pcm_hw_params *params,
        return apply_hw_params_minmax(it, pmin, UINT_MAX);
 }
 
-/* apply PCM hw constraints from the concurrent sync EP */
-static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime,
-                                        struct snd_usb_substream *subs)
+/* get the EP or the sync EP for implicit fb when it's already set up */
+static const struct snd_usb_endpoint *
+get_sync_ep_from_substream(struct snd_usb_substream *subs)
 {
        struct snd_usb_audio *chip = subs->stream->chip;
-       struct snd_usb_endpoint *ep;
        const struct audioformat *fp;
-       int err;
+       const struct snd_usb_endpoint *ep;
 
        list_for_each_entry(fp, &subs->fmt_list, list) {
                ep = snd_usb_get_endpoint(chip, fp->endpoint);
                if (ep && ep->cur_rate)
-                       goto found;
+                       return ep;
                if (!fp->implicit_fb)
                        continue;
                /* for the implicit fb, check the sync ep as well */
                ep = snd_usb_get_endpoint(chip, fp->sync_ep);
                if (ep && ep->cur_rate)
-                       goto found;
+                       return ep;
        }
-       return 0;
+       return NULL;
+}
 
- found:
-       if (!find_format(&subs->fmt_list, ep->cur_format, ep->cur_rate,
-                        ep->cur_channels, false, NULL)) {
-               usb_audio_dbg(chip, "EP 0x%x being used, but not applicable\n",
-                             ep->ep_num);
+/* additional hw constraints for implicit feedback mode */
+static int hw_rule_format_implicit_fb(struct snd_pcm_hw_params *params,
+                                     struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       const struct snd_usb_endpoint *ep;
+       struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+       ep = get_sync_ep_from_substream(subs);
+       if (!ep)
                return 0;
-       }
 
-       usb_audio_dbg(chip, "EP 0x%x being used, using fixed params:\n",
-                     ep->ep_num);
-       usb_audio_dbg(chip, "rate=%d, period_size=%d, periods=%d\n",
-                     ep->cur_rate, ep->cur_period_frames,
-                     ep->cur_buffer_periods);
+       hwc_debug("applying %s\n", __func__);
+       return apply_hw_params_format_bits(fmt, pcm_format_to_bits(ep->cur_format));
+}
 
-       runtime->hw.formats = subs->formats;
-       runtime->hw.rate_min = runtime->hw.rate_max = ep->cur_rate;
-       runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
-       runtime->hw.periods_min = runtime->hw.periods_max =
-               ep->cur_buffer_periods;
+static int hw_rule_rate_implicit_fb(struct snd_pcm_hw_params *params,
+                                   struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       const struct snd_usb_endpoint *ep;
+       struct snd_interval *it;
 
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                 hw_rule_channels, subs,
-                                 SNDRV_PCM_HW_PARAM_FORMAT,
-                                 SNDRV_PCM_HW_PARAM_RATE,
-                                 -1);
-       if (err < 0)
-               return err;
+       ep = get_sync_ep_from_substream(subs);
+       if (!ep)
+               return 0;
 
-       err = snd_pcm_hw_constraint_minmax(runtime,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                                          ep->cur_period_frames,
-                                          ep->cur_period_frames);
-       if (err < 0)
-               return err;
+       hwc_debug("applying %s\n", __func__);
+       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       return apply_hw_params_minmax(it, ep->cur_rate, ep->cur_rate);
+}
 
-       return 1; /* notify the finding */
+static int hw_rule_period_size_implicit_fb(struct snd_pcm_hw_params *params,
+                                          struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       const struct snd_usb_endpoint *ep;
+       struct snd_interval *it;
+
+       ep = get_sync_ep_from_substream(subs);
+       if (!ep)
+               return 0;
+
+       hwc_debug("applying %s\n", __func__);
+       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
+       return apply_hw_params_minmax(it, ep->cur_period_frames,
+                                     ep->cur_period_frames);
+}
+
+static int hw_rule_periods_implicit_fb(struct snd_pcm_hw_params *params,
+                                      struct snd_pcm_hw_rule *rule)
+{
+       struct snd_usb_substream *subs = rule->private;
+       const struct snd_usb_endpoint *ep;
+       struct snd_interval *it;
+
+       ep = get_sync_ep_from_substream(subs);
+       if (!ep)
+               return 0;
+
+       hwc_debug("applying %s\n", __func__);
+       it = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIODS);
+       return apply_hw_params_minmax(it, ep->cur_buffer_periods,
+                                     ep->cur_buffer_periods);
 }
 
 /*
@@ -899,20 +932,11 @@ static int apply_hw_constraint_from_sync(struct snd_pcm_runtime *runtime,
 
 static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substream *subs)
 {
-       struct snd_usb_audio *chip = subs->stream->chip;
        const struct audioformat *fp;
        unsigned int pt, ptmin;
        int param_period_time_if_needed = -1;
        int err;
 
-       mutex_lock(&chip->mutex);
-       err = apply_hw_constraint_from_sync(runtime, subs);
-       mutex_unlock(&chip->mutex);
-       if (err < 0)
-               return err;
-       if (err > 0) /* found the matching? */
-               goto add_extra_rules;
-
        runtime->hw.formats = subs->formats;
 
        runtime->hw.rate_min = 0x7fffffff;
@@ -957,6 +981,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
 
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                  hw_rule_rate, subs,
+                                 SNDRV_PCM_HW_PARAM_RATE,
                                  SNDRV_PCM_HW_PARAM_FORMAT,
                                  SNDRV_PCM_HW_PARAM_CHANNELS,
                                  param_period_time_if_needed,
@@ -964,9 +989,9 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
        if (err < 0)
                return err;
 
-add_extra_rules:
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
                                  hw_rule_channels, subs,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS,
                                  SNDRV_PCM_HW_PARAM_FORMAT,
                                  SNDRV_PCM_HW_PARAM_RATE,
                                  param_period_time_if_needed,
@@ -975,6 +1000,7 @@ add_extra_rules:
                return err;
        err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
                                  hw_rule_format, subs,
+                                 SNDRV_PCM_HW_PARAM_FORMAT,
                                  SNDRV_PCM_HW_PARAM_RATE,
                                  SNDRV_PCM_HW_PARAM_CHANNELS,
                                  param_period_time_if_needed,
@@ -993,6 +1019,28 @@ add_extra_rules:
                        return err;
        }
 
+       /* additional hw constraints for implicit fb */
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+                                 hw_rule_format_implicit_fb, subs,
+                                 SNDRV_PCM_HW_PARAM_FORMAT, -1);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+                                 hw_rule_rate_implicit_fb, subs,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                 hw_rule_period_size_implicit_fb, subs,
+                                 SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS,
+                                 hw_rule_periods_implicit_fb, subs,
+                                 SNDRV_PCM_HW_PARAM_PERIODS, -1);
+       if (err < 0)
+               return err;
+
        return 0;
 }
 
index 0e11cb9..c8a4bdf 100644 (file)
@@ -3362,6 +3362,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                                        .altsetting = 1,
                                        .altset_idx = 1,
                                        .endpoint = 0x86,
+                                       .ep_idx = 1,
                                        .ep_attr = USB_ENDPOINT_XFER_ISOC|
                                                 USB_ENDPOINT_SYNC_ASYNC|
                                                 USB_ENDPOINT_USAGE_IMPLICIT_FB,
@@ -3450,6 +3451,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                                        .altsetting = 1,
                                        .altset_idx = 1,
                                        .endpoint = 0x82,
+                                       .ep_idx = 1,
                                        .ep_attr = USB_ENDPOINT_XFER_ISOC|
                                                USB_ENDPOINT_SYNC_ASYNC|
                                                USB_ENDPOINT_USAGE_IMPLICIT_FB,
@@ -3506,6 +3508,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                                        .altsetting = 1,
                                        .altset_idx = 1,
                                        .endpoint = 0x82,
+                                       .ep_idx = 1,
                                        .ep_attr = USB_ENDPOINT_XFER_ISOC|
                                                 USB_ENDPOINT_SYNC_ASYNC|
                                                 USB_ENDPOINT_USAGE_IMPLICIT_FB,
@@ -3562,6 +3565,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                                        .altsetting = 1,
                                        .altset_idx = 1,
                                        .endpoint = 0x82,
+                                       .ep_idx = 1,
                                        .ep_attr = USB_ENDPOINT_XFER_ISOC|
                                                 USB_ENDPOINT_SYNC_ASYNC|
                                                 USB_ENDPOINT_USAGE_IMPLICIT_FB,
@@ -3619,6 +3623,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                                        .altsetting = 1,
                                        .altset_idx = 1,
                                        .endpoint = 0x82,
+                                       .ep_idx = 1,
                                        .ep_attr = USB_ENDPOINT_XFER_ISOC|
                                                USB_ENDPOINT_SYNC_ASYNC|
                                        USB_ENDPOINT_USAGE_IMPLICIT_FB,
@@ -3679,6 +3684,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
                                        .altsetting = 1,
                                        .altset_idx = 1,
                                        .endpoint = 0x82,
+                                       .ep_idx = 1,
                                        .ep_attr = USB_ENDPOINT_XFER_ISOC|
                                            USB_ENDPOINT_SYNC_ASYNC|
                                            USB_ENDPOINT_USAGE_IMPLICIT_FB,
index e4a690b..e196e36 100644 (file)
@@ -120,6 +120,40 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
        return 0;
 }
 
+/* create the audio stream and the corresponding endpoints from the fixed
+ * audioformat object; this is used for quirks with the fixed EPs
+ */
+static int add_audio_stream_from_fixed_fmt(struct snd_usb_audio *chip,
+                                          struct audioformat *fp)
+{
+       int stream, err;
+
+       stream = (fp->endpoint & USB_DIR_IN) ?
+               SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+
+       snd_usb_audioformat_set_sync_ep(chip, fp);
+
+       err = snd_usb_add_audio_stream(chip, stream, fp);
+       if (err < 0)
+               return err;
+
+       err = snd_usb_add_endpoint(chip, fp->endpoint,
+                                  SND_USB_ENDPOINT_TYPE_DATA);
+       if (err < 0)
+               return err;
+
+       if (fp->sync_ep) {
+               err = snd_usb_add_endpoint(chip, fp->sync_ep,
+                                          fp->implicit_fb ?
+                                          SND_USB_ENDPOINT_TYPE_DATA :
+                                          SND_USB_ENDPOINT_TYPE_SYNC);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
 /*
  * create a stream for an endpoint/altsetting without proper descriptors
  */
@@ -131,8 +165,8 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
        struct audioformat *fp;
        struct usb_host_interface *alts;
        struct usb_interface_descriptor *altsd;
-       int stream, err;
        unsigned *rate_table = NULL;
+       int err;
 
        fp = kmemdup(quirk->data, sizeof(*fp), GFP_KERNEL);
        if (!fp)
@@ -153,11 +187,6 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
                fp->rate_table = rate_table;
        }
 
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = snd_usb_add_audio_stream(chip, stream, fp);
-       if (err < 0)
-               goto error;
        if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
            fp->altset_idx >= iface->num_altsetting) {
                err = -EINVAL;
@@ -165,7 +194,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
        }
        alts = &iface->altsetting[fp->altset_idx];
        altsd = get_iface_desc(alts);
-       if (altsd->bNumEndpoints < 1) {
+       if (altsd->bNumEndpoints <= fp->ep_idx) {
                err = -EINVAL;
                goto error;
        }
@@ -175,7 +204,14 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
        if (fp->datainterval == 0)
                fp->datainterval = snd_usb_parse_datainterval(chip, alts);
        if (fp->maxpacksize == 0)
-               fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
+               fp->maxpacksize = le16_to_cpu(get_endpoint(alts, fp->ep_idx)->wMaxPacketSize);
+       if (!fp->fmt_type)
+               fp->fmt_type = UAC_FORMAT_TYPE_I;
+
+       err = add_audio_stream_from_fixed_fmt(chip, fp);
+       if (err < 0)
+               goto error;
+
        usb_set_interface(chip->dev, fp->iface, 0);
        snd_usb_init_pitch(chip, fp);
        snd_usb_init_sample_rate(chip, fp, fp->rate_max);
@@ -417,7 +453,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
        struct usb_host_interface *alts;
        struct usb_interface_descriptor *altsd;
        struct audioformat *fp;
-       int stream, err;
+       int err;
 
        /* both PCM and MIDI interfaces have 2 or more altsettings */
        if (iface->num_altsetting < 2)
@@ -482,9 +518,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
                return -ENXIO;
        }
 
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = snd_usb_add_audio_stream(chip, stream, fp);
+       err = add_audio_stream_from_fixed_fmt(chip, fp);
        if (err < 0) {
                list_del(&fp->list); /* unlink for avoiding double-free */
                kfree(fp);
@@ -1436,30 +1470,6 @@ static void set_format_emu_quirk(struct snd_usb_substream *subs,
        subs->pkt_offset_adj = (emu_samplerate_id >= EMU_QUIRK_SR_176400HZ) ? 4 : 0;
 }
 
-
-/*
- * Pioneer DJ DJM-900NXS2
- * Device needs to know the sample rate each time substream is started
- */
-static int pioneer_djm_set_format_quirk(struct snd_usb_substream *subs)
-{
-       unsigned int cur_rate = subs->data_endpoint->cur_rate;
-       /* Convert sample rate value to little endian */
-       u8 sr[3];
-
-       sr[0] = cur_rate & 0xff;
-       sr[1] = (cur_rate >> 8) & 0xff;
-       sr[2] = (cur_rate >> 16) & 0xff;
-
-       /* Configure device */
-       usb_set_interface(subs->dev, 0, 1);
-       snd_usb_ctl_msg(subs->stream->chip->dev,
-               usb_rcvctrlpipe(subs->stream->chip->dev, 0),
-               0x01, 0x22, 0x0100, 0x0082, &sr, 0x0003);
-
-       return 0;
-}
-
 void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
                              const struct audioformat *fmt)
 {
@@ -1470,10 +1480,6 @@ void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
        case USB_ID(0x041e, 0x3f19): /* E-Mu 0204 USB */
                set_format_emu_quirk(subs, fmt);
                break;
-       case USB_ID(0x2b73, 0x000a): /* Pioneer DJ DJM-900NXS2 */
-       case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */
-               pioneer_djm_set_format_quirk(subs);
-               break;
        case USB_ID(0x534d, 0x2109): /* MacroSilicon MS2109 */
                subs->stream_offset_adj = 2;
                break;
index 980287a..215c177 100644 (file)
@@ -44,6 +44,7 @@ struct snd_usb_audio {
 
        struct list_head pcm_list;      /* list of pcm streams */
        struct list_head ep_list;       /* list of audio-related endpoints */
+       struct list_head iface_ref_list; /* list of interface refcounts */
        int pcm_devs;
 
        struct list_head midi_list;     /* list of midi interfaces */
index 595e164..feb30c2 100755 (executable)
@@ -152,6 +152,7 @@ setup_instance() { # [instance]
        set_array_of ${instance}.options ${instancedir}/trace_options
        set_value_of ${instance}.trace_clock ${instancedir}/trace_clock
        set_value_of ${instance}.cpumask ${instancedir}/tracing_cpumask
+       set_value_of ${instance}.tracing_on ${instancedir}/tracing_on
        set_value_of ${instance}.tracer ${instancedir}/current_tracer
        set_array_of ${instance}.ftrace.filters \
                ${instancedir}/set_ftrace_filter
index 6c0d4b6..a0c3bcc 100755 (executable)
@@ -221,6 +221,10 @@ instance_options() { # [instance-name]
        if [ `echo $val | sed -e s/f//g`x != x ]; then
                emit_kv $PREFIX.cpumask = $val
        fi
+       val=`cat $INSTANCE/tracing_on`
+       if [ `echo $val | sed -e s/f//g`x != x ]; then
+               emit_kv $PREFIX.tracing_on = $val
+       fi
 
        val=
        for i in `cat $INSTANCE/set_event`; do
index f897cb5..45ac2f9 100644 (file)
@@ -166,7 +166,7 @@ $(OUTPUT)%.bpf.o: skeleton/%.bpf.c $(OUTPUT)vmlinux.h $(LIBBPF)
                -I$(srctree)/tools/include/uapi/ \
                -I$(LIBBPF_PATH) \
                -I$(srctree)/tools/lib \
-               -g -O2 -target bpf -c $< -o $@ && $(LLVM_STRIP) -g $@
+               -g -O2 -Wall -target bpf -c $< -o $@ && $(LLVM_STRIP) -g $@
 
 $(OUTPUT)%.skel.h: $(OUTPUT)%.bpf.o $(BPFTOOL_BOOTSTRAP)
        $(QUIET_GEN)$(BPFTOOL_BOOTSTRAP) gen skeleton $< > $@
index 3fae61e..ff3aa0c 100644 (file)
@@ -11,7 +11,6 @@
 #include <bpf/bpf.h>
 #include <bpf/libbpf.h>
 #include <net/if.h>
-#include <linux/if.h>
 #include <linux/rtnetlink.h>
 #include <linux/socket.h>
 #include <linux/tc_act/tc_bpf.h>
index e3ea569..7409d78 100644 (file)
@@ -139,6 +139,8 @@ int eprintf(int level, int var, const char *fmt, ...)
 #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
 #define pr_err(fmt, ...) \
        eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt, ...) \
+       eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
 
 static bool is_btf_id(const char *name)
 {
@@ -472,7 +474,7 @@ static int symbols_resolve(struct object *obj)
        int nr_funcs    = obj->nr_funcs;
        int err, type_id;
        struct btf *btf;
-       __u32 nr;
+       __u32 nr_types;
 
        btf = btf__parse(obj->btf ?: obj->path, NULL);
        err = libbpf_get_error(btf);
@@ -483,12 +485,12 @@ static int symbols_resolve(struct object *obj)
        }
 
        err = -1;
-       nr  = btf__get_nr_types(btf);
+       nr_types = btf__get_nr_types(btf);
 
        /*
         * Iterate all the BTF types and search for collected symbol IDs.
         */
-       for (type_id = 1; type_id <= nr; type_id++) {
+       for (type_id = 1; type_id <= nr_types; type_id++) {
                const struct btf_type *type;
                struct rb_root *root;
                struct btf_id *id;
@@ -526,8 +528,13 @@ static int symbols_resolve(struct object *obj)
 
                id = btf_id__find(root, str);
                if (id) {
-                       id->id = type_id;
-                       (*nr)--;
+                       if (id->id) {
+                               pr_info("WARN: multiple IDs found for '%s': %d, %d - using %d\n",
+                                       str, id->id, type_id, id->id);
+                       } else {
+                               id->id = type_id;
+                               (*nr)--;
+                       }
                }
        }
 
index cacd66a..a2b233f 100644 (file)
@@ -107,8 +107,8 @@ int monitor_device(const char *device_name,
                        ret = -EIO;
                        break;
                }
-               fprintf(stdout, "GPIO EVENT at %llu on line %d (%d|%d) ",
-                       event.timestamp_ns, event.offset, event.line_seqno,
+               fprintf(stdout, "GPIO EVENT at %" PRIu64 " on line %d (%d|%d) ",
+                       (uint64_t)event.timestamp_ns, event.offset, event.line_seqno,
                        event.seqno);
                switch (event.id) {
                case GPIO_V2_LINE_EVENT_RISING_EDGE:
index f229ec6..41e76d2 100644 (file)
@@ -10,6 +10,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <linux/gpio.h>
 #include <poll.h>
 #include <stdbool.h>
@@ -86,8 +87,8 @@ int main(int argc, char **argv)
                                return EXIT_FAILURE;
                        }
 
-                       printf("line %u: %s at %llu\n",
-                              chg.info.offset, event, chg.timestamp_ns);
+                       printf("line %u: %s at %" PRIu64 "\n",
+                              chg.info.offset, event, (uint64_t)chg.timestamp_ns);
                }
        }
 
index ce365d2..cc7070c 100644 (file)
@@ -79,9 +79,4 @@
 #define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
 #endif // static_assert
 
-#ifdef __GENKSYMS__
-/* genksyms gets confused by _Static_assert */
-#define _Static_assert(expr, ...)
-#endif
-
 #endif /* _LINUX_BUILD_BUG_H */
index ca28b6a..736bdec 100644 (file)
                .off   = OFF,                                   \
                .imm   = 0 })
 
-/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */
+/*
+ * Atomic operations:
+ *
+ *   BPF_ADD                  *(uint *) (dst_reg + off16) += src_reg
+ *   BPF_AND                  *(uint *) (dst_reg + off16) &= src_reg
+ *   BPF_OR                   *(uint *) (dst_reg + off16) |= src_reg
+ *   BPF_XOR                  *(uint *) (dst_reg + off16) ^= src_reg
+ *   BPF_ADD | BPF_FETCH      src_reg = atomic_fetch_add(dst_reg + off16, src_reg);
+ *   BPF_AND | BPF_FETCH      src_reg = atomic_fetch_and(dst_reg + off16, src_reg);
+ *   BPF_OR | BPF_FETCH       src_reg = atomic_fetch_or(dst_reg + off16, src_reg);
+ *   BPF_XOR | BPF_FETCH      src_reg = atomic_fetch_xor(dst_reg + off16, src_reg);
+ *   BPF_XCHG                 src_reg = atomic_xchg(dst_reg + off16, src_reg)
+ *   BPF_CMPXCHG              r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg)
+ */
 
-#define BPF_STX_XADD(SIZE, DST, SRC, OFF)                      \
+#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF)                 \
        ((struct bpf_insn) {                                    \
-               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD,   \
+               .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \
                .dst_reg = DST,                                 \
                .src_reg = SRC,                                 \
                .off   = OFF,                                   \
-               .imm   = 0 })
+               .imm   = OP })
+
+/* Legacy alias */
+#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF)
 
 /* Memory store, *(uint *) (dst_reg + off16) = imm32 */
 
index 77d7c1b..c001766 100644 (file)
@@ -19,7 +19,8 @@
 
 /* ld/ldx fields */
 #define BPF_DW         0x18    /* double word (64-bit) */
-#define BPF_XADD       0xc0    /* exclusive add */
+#define BPF_ATOMIC     0xc0    /* atomic memory ops - op type in immediate */
+#define BPF_XADD       0xc0    /* exclusive add - legacy name */
 
 /* alu/jmp fields */
 #define BPF_MOV                0xb0    /* mov reg to reg */
 #define BPF_CALL       0x80    /* function call */
 #define BPF_EXIT       0x90    /* function return */
 
+/* atomic op type fields (stored in immediate) */
+#define BPF_FETCH      0x01    /* not an opcode on its own, used to build others */
+#define BPF_XCHG       (0xe0 | BPF_FETCH)      /* atomic exchange */
+#define BPF_CMPXCHG    (0xf0 | BPF_FETCH)      /* atomic compare-and-write */
+
 /* Register numbers */
 enum {
        BPF_REG_0 = 0,
@@ -2448,7 +2454,7 @@ union bpf_attr {
  *             running simultaneously.
  *
  *             A user should care about the synchronization by himself.
- *             For example, by using the **BPF_STX_XADD** instruction to alter
+ *             For example, by using the **BPF_ATOMIC** instructions to alter
  *             the shared data.
  *     Return
  *             A pointer to the local storage area.
@@ -2993,10 +2999,10 @@ union bpf_attr {
  *             string length is larger than *size*, just *size*-1 bytes are
  *             copied and the last byte is set to NUL.
  *
- *             On success, the length of the copied string is returned. This
- *             makes this helper useful in tracing programs for reading
- *             strings, and more importantly to get its length at runtime. See
- *             the following snippet:
+ *             On success, returns the number of bytes that were written,
+ *             including the terminal NUL. This makes this helper useful in
+ *             tracing programs for reading strings, and more importantly to
+ *             get its length at runtime. See the following snippet:
  *
  *             ::
  *
@@ -3024,7 +3030,7 @@ union bpf_attr {
  *             **->mm->env_start**: using this helper and the return value,
  *             one can quickly iterate at the right offset of the memory area.
  *     Return
- *             On success, the strictly positive length of the string,
+ *             On success, the strictly positive length of the output string,
  *             including the trailing NUL character. On error, a negative
  *             value.
  *
index d208b2a..28d649b 100644 (file)
@@ -617,6 +617,7 @@ enum {
        IFLA_GTP_FD1,
        IFLA_GTP_PDP_HASHSIZE,
        IFLA_GTP_ROLE,
+       IFLA_GTP_COLLECT_METADATA,
        __IFLA_GTP_MAX,
 };
 #define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
index 886802b..374c678 100644 (file)
@@ -251,6 +251,7 @@ struct kvm_hyperv_exit {
 #define KVM_EXIT_X86_RDMSR        29
 #define KVM_EXIT_X86_WRMSR        30
 #define KVM_EXIT_DIRTY_RING_FULL  31
+#define KVM_EXIT_AP_RESET_HOLD    32
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -573,6 +574,7 @@ struct kvm_vapic_addr {
 #define KVM_MP_STATE_CHECK_STOP        6
 #define KVM_MP_STATE_OPERATING         7
 #define KVM_MP_STATE_LOAD              8
+#define KVM_MP_STATE_AP_RESET_HOLD     9
 
 struct kvm_mp_state {
        __u32 mp_state;
index 0d18b1d..5c903ab 100644 (file)
@@ -414,6 +414,7 @@ enum {
        TCA_HTB_RATE64,
        TCA_HTB_CEIL64,
        TCA_HTB_PAD,
+       TCA_HTB_OFFLOAD,
        __TCA_HTB_MAX,
 };
 
index bbcefb3..53b3e19 100644 (file)
@@ -195,17 +195,22 @@ enum bpf_enum_value_kind {
  * (local) BTF, used to record relocation.
  */
 #define bpf_core_read(dst, sz, src)                                        \
-       bpf_probe_read_kernel(dst, sz,                                      \
-                             (const void *)__builtin_preserve_access_index(src))
+       bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src))
 
+/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */
+#define bpf_core_read_user(dst, sz, src)                                   \
+       bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src))
 /*
  * bpf_core_read_str() is a thin wrapper around bpf_probe_read_str()
  * additionally emitting BPF CO-RE field relocation for specified source
  * argument.
  */
 #define bpf_core_read_str(dst, sz, src)                                            \
-       bpf_probe_read_kernel_str(dst, sz,                                  \
-                                 (const void *)__builtin_preserve_access_index(src))
+       bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src))
+
+/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */
+#define bpf_core_read_user_str(dst, sz, src)                               \
+       bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src))
 
 #define ___concat(a, b) a ## b
 #define ___apply(fn, n) ___concat(fn, n)
@@ -264,30 +269,29 @@ enum bpf_enum_value_kind {
        read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor)
 
 /* "recursively" read a sequence of inner pointers using local __t var */
-#define ___rd_first(src, a) ___read(bpf_core_read, &__t, ___type(src), src, a);
-#define ___rd_last(...)                                                            \
-       ___read(bpf_core_read, &__t,                                        \
-               ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__));
-#define ___rd_p1(...) const void *__t; ___rd_first(__VA_ARGS__)
-#define ___rd_p2(...) ___rd_p1(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p3(...) ___rd_p2(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p4(...) ___rd_p3(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p5(...) ___rd_p4(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p6(...) ___rd_p5(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p7(...) ___rd_p6(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p8(...) ___rd_p7(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___rd_p9(...) ___rd_p8(___nolast(__VA_ARGS__)) ___rd_last(__VA_ARGS__)
-#define ___read_ptrs(src, ...)                                             \
-       ___apply(___rd_p, ___narg(__VA_ARGS__))(src, __VA_ARGS__)
-
-#define ___core_read0(fn, dst, src, a)                                     \
+#define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a);
+#define ___rd_last(fn, ...)                                                \
+       ___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__));
+#define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__)
+#define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
+#define ___read_ptrs(fn, src, ...)                                         \
+       ___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__)
+
+#define ___core_read0(fn, fn_ptr, dst, src, a)                             \
        ___read(fn, dst, ___type(src), src, a);
-#define ___core_readN(fn, dst, src, ...)                                   \
-       ___read_ptrs(src, ___nolast(__VA_ARGS__))                           \
+#define ___core_readN(fn, fn_ptr, dst, src, ...)                           \
+       ___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__))                   \
        ___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t,         \
                ___last(__VA_ARGS__));
-#define ___core_read(fn, dst, src, a, ...)                                 \
-       ___apply(___core_read, ___empty(__VA_ARGS__))(fn, dst,              \
+#define ___core_read(fn, fn_ptr, dst, src, a, ...)                         \
+       ___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst,      \
                                                      src, a, ##__VA_ARGS__)
 
 /*
@@ -295,20 +299,73 @@ enum bpf_enum_value_kind {
  * BPF_CORE_READ(), in which final field is read into user-provided storage.
  * See BPF_CORE_READ() below for more details on general usage.
  */
-#define BPF_CORE_READ_INTO(dst, src, a, ...)                               \
-       ({                                                                  \
-               ___core_read(bpf_core_read, dst, (src), a, ##__VA_ARGS__)   \
-       })
+#define BPF_CORE_READ_INTO(dst, src, a, ...) ({                                    \
+       ___core_read(bpf_core_read, bpf_core_read,                          \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
+
+/*
+ * Variant of BPF_CORE_READ_INTO() for reading from user-space memory.
+ *
+ * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use.
+ */
+#define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({                       \
+       ___core_read(bpf_core_read_user, bpf_core_read_user,                \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
+
+/* Non-CO-RE variant of BPF_CORE_READ_INTO() */
+#define BPF_PROBE_READ_INTO(dst, src, a, ...) ({                           \
+       ___core_read(bpf_probe_read, bpf_probe_read,                        \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
+
+/* Non-CO-RE variant of BPF_CORE_READ_USER_INTO().
+ *
+ * As no CO-RE relocations are emitted, source types can be arbitrary and are
+ * not restricted to kernel types only.
+ */
+#define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({                      \
+       ___core_read(bpf_probe_read_user, bpf_probe_read_user,              \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
 
 /*
  * BPF_CORE_READ_STR_INTO() does same "pointer chasing" as
  * BPF_CORE_READ() for intermediate pointers, but then executes (and returns
  * corresponding error code) bpf_core_read_str() for final string read.
  */
-#define BPF_CORE_READ_STR_INTO(dst, src, a, ...)                           \
-       ({                                                                  \
-               ___core_read(bpf_core_read_str, dst, (src), a, ##__VA_ARGS__)\
-       })
+#define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({                        \
+       ___core_read(bpf_core_read_str, bpf_core_read,                      \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
+
+/*
+ * Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory.
+ *
+ * NOTE: see comments for BPF_CORE_READ_USER() about the proper types use.
+ */
+#define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({                   \
+       ___core_read(bpf_core_read_user_str, bpf_core_read_user,            \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
+
+/* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */
+#define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({                       \
+       ___core_read(bpf_probe_read_str, bpf_probe_read,                    \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
+
+/*
+ * Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO().
+ *
+ * As no CO-RE relocations are emitted, source types can be arbitrary and are
+ * not restricted to kernel types only.
+ */
+#define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({                  \
+       ___core_read(bpf_probe_read_user_str, bpf_probe_read_user,          \
+                    dst, (src), a, ##__VA_ARGS__)                          \
+})
 
 /*
  * BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially
@@ -334,12 +391,46 @@ enum bpf_enum_value_kind {
  * N.B. Only up to 9 "field accessors" are supported, which should be more
  * than enough for any practical purpose.
  */
-#define BPF_CORE_READ(src, a, ...)                                         \
-       ({                                                                  \
-               ___type((src), a, ##__VA_ARGS__) __r;                       \
-               BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__);          \
-               __r;                                                        \
-       })
+#define BPF_CORE_READ(src, a, ...) ({                                      \
+       ___type((src), a, ##__VA_ARGS__) __r;                               \
+       BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__);                  \
+       __r;                                                                \
+})
+
+/*
+ * Variant of BPF_CORE_READ() for reading from user-space memory.
+ *
+ * NOTE: all the source types involved are still *kernel types* and need to
+ * exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will
+ * fail. Custom user types are not relocatable with CO-RE.
+ * The typical situation in which BPF_CORE_READ_USER() might be used is to
+ * read kernel UAPI types from the user-space memory passed in as a syscall
+ * input argument.
+ */
+#define BPF_CORE_READ_USER(src, a, ...) ({                                 \
+       ___type((src), a, ##__VA_ARGS__) __r;                               \
+       BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__);             \
+       __r;                                                                \
+})
+
+/* Non-CO-RE variant of BPF_CORE_READ() */
+#define BPF_PROBE_READ(src, a, ...) ({                                     \
+       ___type((src), a, ##__VA_ARGS__) __r;                               \
+       BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__);                 \
+       __r;                                                                \
+})
+
+/*
+ * Non-CO-RE variant of BPF_CORE_READ_USER().
+ *
+ * As no CO-RE relocations are emitted, source types can be arbitrary and are
+ * not restricted to kernel types only.
+ */
+#define BPF_PROBE_READ_USER(src, a, ...) ({                                \
+       ___type((src), a, ##__VA_ARGS__) __r;                               \
+       BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__);            \
+       __r;                                                                \
+})
 
 #endif
 
index 72b2511..ae6c975 100644 (file)
@@ -30,7 +30,7 @@
 #define SEC(NAME) __attribute__((section(NAME), used))
 
 #ifndef __always_inline
-#define __always_inline __attribute__((always_inline))
+#define __always_inline inline __attribute__((always_inline))
 #endif
 #ifndef __noinline
 #define __noinline __attribute__((noinline))
index 3c3f2bc..9970a28 100644 (file)
@@ -240,11 +240,6 @@ static int btf_parse_hdr(struct btf *btf)
        }
 
        meta_left = btf->raw_size - sizeof(*hdr);
-       if (!meta_left) {
-               pr_debug("BTF has no data\n");
-               return -EINVAL;
-       }
-
        if (meta_left < hdr->str_off + hdr->str_len) {
                pr_debug("Invalid BTF total size:%u\n", btf->raw_size);
                return -EINVAL;
index 6ae748f..2abbc38 100644 (file)
@@ -395,7 +395,8 @@ struct extern_desc {
                        unsigned long long addr;
 
                        /* target btf_id of the corresponding kernel var. */
-                       int vmlinux_btf_id;
+                       int kernel_btf_obj_fd;
+                       int kernel_btf_id;
 
                        /* local btf_id of the ksym extern's type. */
                        __u32 type_id;
@@ -6162,7 +6163,8 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
                        } else /* EXT_KSYM */ {
                                if (ext->ksym.type_id) { /* typed ksyms */
                                        insn[0].src_reg = BPF_PSEUDO_BTF_ID;
-                                       insn[0].imm = ext->ksym.vmlinux_btf_id;
+                                       insn[0].imm = ext->ksym.kernel_btf_id;
+                                       insn[1].imm = ext->ksym.kernel_btf_obj_fd;
                                } else { /* typeless ksyms */
                                        insn[0].imm = (__u32)ext->ksym.addr;
                                        insn[1].imm = ext->ksym.addr >> 32;
@@ -7319,7 +7321,8 @@ out:
 static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj)
 {
        struct extern_desc *ext;
-       int i, id;
+       struct btf *btf;
+       int i, j, id, btf_fd, err;
 
        for (i = 0; i < obj->nr_extern; i++) {
                const struct btf_type *targ_var, *targ_type;
@@ -7331,10 +7334,25 @@ static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj)
                if (ext->type != EXT_KSYM || !ext->ksym.type_id)
                        continue;
 
-               id = btf__find_by_name_kind(obj->btf_vmlinux, ext->name,
-                                           BTF_KIND_VAR);
+               btf = obj->btf_vmlinux;
+               btf_fd = 0;
+               id = btf__find_by_name_kind(btf, ext->name, BTF_KIND_VAR);
+               if (id == -ENOENT) {
+                       err = load_module_btfs(obj);
+                       if (err)
+                               return err;
+
+                       for (j = 0; j < obj->btf_module_cnt; j++) {
+                               btf = obj->btf_modules[j].btf;
+                               /* we assume module BTF FD is always >0 */
+                               btf_fd = obj->btf_modules[j].fd;
+                               id = btf__find_by_name_kind(btf, ext->name, BTF_KIND_VAR);
+                               if (id != -ENOENT)
+                                       break;
+                       }
+               }
                if (id <= 0) {
-                       pr_warn("extern (ksym) '%s': failed to find BTF ID in vmlinux BTF.\n",
+                       pr_warn("extern (ksym) '%s': failed to find BTF ID in kernel BTF(s).\n",
                                ext->name);
                        return -ESRCH;
                }
@@ -7343,24 +7361,19 @@ static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj)
                local_type_id = ext->ksym.type_id;
 
                /* find target type_id */
-               targ_var = btf__type_by_id(obj->btf_vmlinux, id);
-               targ_var_name = btf__name_by_offset(obj->btf_vmlinux,
-                                                   targ_var->name_off);
-               targ_type = skip_mods_and_typedefs(obj->btf_vmlinux,
-                                                  targ_var->type,
-                                                  &targ_type_id);
+               targ_var = btf__type_by_id(btf, id);
+               targ_var_name = btf__name_by_offset(btf, targ_var->name_off);
+               targ_type = skip_mods_and_typedefs(btf, targ_var->type, &targ_type_id);
 
                ret = bpf_core_types_are_compat(obj->btf, local_type_id,
-                                               obj->btf_vmlinux, targ_type_id);
+                                               btf, targ_type_id);
                if (ret <= 0) {
                        const struct btf_type *local_type;
                        const char *targ_name, *local_name;
 
                        local_type = btf__type_by_id(obj->btf, local_type_id);
-                       local_name = btf__name_by_offset(obj->btf,
-                                                        local_type->name_off);
-                       targ_name = btf__name_by_offset(obj->btf_vmlinux,
-                                                       targ_type->name_off);
+                       local_name = btf__name_by_offset(obj->btf, local_type->name_off);
+                       targ_name = btf__name_by_offset(btf, targ_type->name_off);
 
                        pr_warn("extern (ksym) '%s': incompatible types, expected [%d] %s %s, but kernel has [%d] %s %s\n",
                                ext->name, local_type_id,
@@ -7370,7 +7383,8 @@ static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj)
                }
 
                ext->is_set = true;
-               ext->ksym.vmlinux_btf_id = id;
+               ext->ksym.kernel_btf_obj_fd = btf_fd;
+               ext->ksym.kernel_btf_id = id;
                pr_debug("extern (ksym) '%s': resolved to [%d] %s %s\n",
                         ext->name, id, btf_kind_str(targ_var), targ_var_name);
        }
index cfcdbd7..17465d4 100644 (file)
@@ -367,21 +367,13 @@ static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, boo
        return map;
 }
 
-static void perf_evlist__set_sid_idx(struct perf_evlist *evlist,
-                                    struct perf_evsel *evsel, int idx, int cpu,
-                                    int thread)
+static void perf_evsel__set_sid_idx(struct perf_evsel *evsel, int idx, int cpu, int thread)
 {
        struct perf_sample_id *sid = SID(evsel, cpu, thread);
 
        sid->idx = idx;
-       if (evlist->cpus && cpu >= 0)
-               sid->cpu = evlist->cpus->map[cpu];
-       else
-               sid->cpu = -1;
-       if (!evsel->system_wide && evlist->threads && thread >= 0)
-               sid->tid = perf_thread_map__pid(evlist->threads, thread);
-       else
-               sid->tid = -1;
+       sid->cpu = perf_cpu_map__cpu(evsel->cpus, cpu);
+       sid->tid = perf_thread_map__pid(evsel->threads, thread);
 }
 
 static struct perf_mmap*
@@ -500,8 +492,7 @@ mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops,
                        if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread,
                                                   fd) < 0)
                                return -1;
-                       perf_evlist__set_sid_idx(evlist, evsel, idx, cpu,
-                                                thread);
+                       perf_evsel__set_sid_idx(evsel, idx, cpu, thread);
                }
        }
 
index c8d4509..c70e9e0 100644 (file)
@@ -27,5 +27,5 @@ int main(int argc, char **argv)
        perf_cpu_map__put(cpus);
 
        __T_END;
-       return 0;
+       return tests_failed == 0 ? 0 : -1;
 }
index 6d8ebe0..e2ac0b7 100644 (file)
@@ -208,13 +208,13 @@ static int test_mmap_thread(void)
        char path[PATH_MAX];
        int id, err, pid, go_pipe[2];
        union perf_event *event;
-       char bf;
        int count = 0;
 
        snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id",
                 sysfs__mountpoint());
 
        if (filename__read_int(path, &id)) {
+               tests_failed++;
                fprintf(stderr, "error: failed to get tracepoint id: %s\n", path);
                return -1;
        }
@@ -229,6 +229,7 @@ static int test_mmap_thread(void)
        pid = fork();
        if (!pid) {
                int i;
+               char bf;
 
                read(go_pipe[0], &bf, 1);
 
@@ -266,7 +267,7 @@ static int test_mmap_thread(void)
        perf_evlist__enable(evlist);
 
        /* kick the child and wait for it to finish */
-       write(go_pipe[1], &bf, 1);
+       write(go_pipe[1], "A", 1);
        waitpid(pid, NULL, 0);
 
        /*
@@ -409,5 +410,5 @@ int main(int argc, char **argv)
        test_mmap_cpus();
 
        __T_END;
-       return 0;
+       return tests_failed == 0 ? 0 : -1;
 }
index 135722a..0ad82d7 100644 (file)
@@ -131,5 +131,5 @@ int main(int argc, char **argv)
        test_stat_thread_enable();
 
        __T_END;
-       return 0;
+       return tests_failed == 0 ? 0 : -1;
 }
index 7dc4d6f..3844714 100644 (file)
@@ -27,5 +27,5 @@ int main(int argc, char **argv)
        perf_thread_map__put(threads);
 
        __T_END;
-       return 0;
+       return tests_failed == 0 ? 0 : -1;
 }
index 5f8d3ee..4bd3031 100644 (file)
@@ -2928,14 +2928,10 @@ int check(struct objtool_file *file)
        warnings += ret;
 
 out:
-       if (ret < 0) {
-               /*
-                *  Fatal error.  The binary is corrupt or otherwise broken in
-                *  some way, or objtool itself is broken.  Fail the kernel
-                *  build.
-                */
-               return ret;
-       }
-
+       /*
+        *  For now, don't fail the kernel build on fatal warnings.  These
+        *  errors are still fairly common due to the growing matrix of
+        *  supported toolchains and their recent pace of change.
+        */
        return 0;
 }
index be89c74..d8421e1 100644 (file)
@@ -380,8 +380,11 @@ static int read_symbols(struct elf *elf)
 
        symtab = find_section_by_name(elf, ".symtab");
        if (!symtab) {
-               WARN("missing symbol table");
-               return -1;
+               /*
+                * A missing symbol table is actually possible if it's an empty
+                * .o file.  This can happen for thunk_64.o.
+                */
+               return 0;
        }
 
        symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
@@ -448,6 +451,13 @@ static int read_symbols(struct elf *elf)
                list_add(&sym->list, entry);
                elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
                elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
+
+               /*
+                * Don't store empty STT_NOTYPE symbols in the rbtree.  They
+                * can exist within a function, confusing the sorting.
+                */
+               if (!sym->len)
+                       rb_erase(&sym->node, &sym->sec->symbol_tree);
        }
 
        if (stats)
index edacfa9..42dad4a 100644 (file)
@@ -186,6 +186,7 @@ struct output_option {
 
 enum {
        OUTPUT_TYPE_SYNTH = PERF_TYPE_MAX,
+       OUTPUT_TYPE_OTHER,
        OUTPUT_TYPE_MAX
 };
 
@@ -283,6 +284,18 @@ static struct {
 
                .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
        },
+
+       [OUTPUT_TYPE_OTHER] = {
+               .user_set = false,
+
+               .fields = PERF_OUTPUT_COMM | PERF_OUTPUT_TID |
+                             PERF_OUTPUT_CPU | PERF_OUTPUT_TIME |
+                             PERF_OUTPUT_EVNAME | PERF_OUTPUT_IP |
+                             PERF_OUTPUT_SYM | PERF_OUTPUT_SYMOFFSET |
+                             PERF_OUTPUT_DSO | PERF_OUTPUT_PERIOD,
+
+               .invalid_fields = PERF_OUTPUT_TRACE | PERF_OUTPUT_BPF_OUTPUT,
+       },
 };
 
 struct evsel_script {
@@ -343,8 +356,11 @@ static inline int output_type(unsigned int type)
        case PERF_TYPE_SYNTH:
                return OUTPUT_TYPE_SYNTH;
        default:
-               return type;
+               if (type < PERF_TYPE_MAX)
+                       return type;
        }
+
+       return OUTPUT_TYPE_OTHER;
 }
 
 static inline unsigned int attr_type(unsigned int type)
index 65c4ff6..e6b6181 100644 (file)
@@ -39,7 +39,7 @@
    Copyright (C) 2018 Red Hat, Inc., Arnaldo Carvalho de Melo <acme@redhat.com>
 */
 
-#include <bpf/bpf.h>
+#include <bpf.h>
 
 #define NSEC_PER_SEC   1000000000L
 
index 249dfe4..ebebd35 100755 (executable)
@@ -9,31 +9,29 @@ perf stat -a true > /dev/null 2>&1 || exit 2
 
 test_global_aggr()
 {
-       local cyc
-
        perf stat -a --no-big-num -e cycles,instructions sleep 1  2>&1 | \
        grep -e cycles -e instructions | \
        while read num evt hash ipc rest
        do
                # skip not counted events
-               if [[ $num == "<not" ]]; then
+               if [ "$num" = "<not" ]; then
                        continue
                fi
 
                # save cycles count
-               if [[ $evt == "cycles" ]]; then
+               if [ "$evt" = "cycles" ]; then
                        cyc=$num
                        continue
                fi
 
                # skip if no cycles
-               if [[ -z $cyc ]]; then
+               if [ -z "$cyc" ]; then
                        continue
                fi
 
                # use printf for rounding and a leading zero
-               local res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)`
-               if [[ $ipc != $res ]]; then
+               res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)`
+               if [ "$ipc" != "$res" ]; then
                        echo "IPC is different: $res != $ipc  ($num / $cyc)"
                        exit 1
                fi
@@ -42,32 +40,32 @@ test_global_aggr()
 
 test_no_aggr()
 {
-       declare -A results
-
        perf stat -a -A --no-big-num -e cycles,instructions sleep 1  2>&1 | \
        grep ^CPU | \
        while read cpu num evt hash ipc rest
        do
                # skip not counted events
-               if [[ $num == "<not" ]]; then
+               if [ "$num" = "<not" ]; then
                        continue
                fi
 
                # save cycles count
-               if [[ $evt == "cycles" ]]; then
-                       results[$cpu]=$num
+               if [ "$evt" = "cycles" ]; then
+                       results="$results $cpu:$num"
                        continue
                fi
 
+               cyc=${results##* $cpu:}
+               cyc=${cyc%% *}
+
                # skip if no cycles
-               local cyc=${results[$cpu]}
-               if [[ -z $cyc ]]; then
+               if [ -z "$cyc" ]; then
                        continue
                fi
 
                # use printf for rounding and a leading zero
-               local res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)`
-               if [[ $ipc != $res ]]; then
+               res=`printf "%.2f" $(echo "scale=6; $num / $cyc" | bc -q)`
+               if [ "$ipc" != "$res" ]; then
                        echo "IPC is different for $cpu: $res != $ipc  ($num / $cyc)"
                        exit 1
                fi
index 062383e..c4ed3dc 100644 (file)
@@ -3323,6 +3323,14 @@ int perf_session__write_header(struct perf_session *session,
        attr_offset = lseek(ff.fd, 0, SEEK_CUR);
 
        evlist__for_each_entry(evlist, evsel) {
+               if (evsel->core.attr.size < sizeof(evsel->core.attr)) {
+                       /*
+                        * We are likely in "perf inject" and have read
+                        * from an older file. Update attr size so that
+                        * reader gets the right offset to the ids.
+                        */
+                       evsel->core.attr.size = sizeof(evsel->core.attr);
+               }
                f_attr = (struct perf_file_attr){
                        .attr = evsel->core.attr,
                        .ids  = {
index f841f35..1e9d3f9 100644 (file)
@@ -2980,7 +2980,7 @@ int machines__for_each_thread(struct machines *machines,
 
 pid_t machine__get_current_tid(struct machine *machine, int cpu)
 {
-       int nr_cpus = min(machine->env->nr_cpus_online, MAX_NR_CPUS);
+       int nr_cpus = min(machine->env->nr_cpus_avail, MAX_NR_CPUS);
 
        if (cpu < 0 || cpu >= nr_cpus || !machine->current_tid)
                return -1;
@@ -2992,7 +2992,7 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
                             pid_t tid)
 {
        struct thread *thread;
-       int nr_cpus = min(machine->env->nr_cpus_online, MAX_NR_CPUS);
+       int nr_cpus = min(machine->env->nr_cpus_avail, MAX_NR_CPUS);
 
        if (cpu < 0)
                return -EINVAL;
index ee94d3e..e6d3452 100644 (file)
@@ -162,6 +162,14 @@ static bool contains_event(struct evsel **metric_events, int num_events,
        return false;
 }
 
+static bool evsel_same_pmu(struct evsel *ev1, struct evsel *ev2)
+{
+       if (!ev1->pmu_name || !ev2->pmu_name)
+               return false;
+
+       return !strcmp(ev1->pmu_name, ev2->pmu_name);
+}
+
 /**
  * Find a group of events in perf_evlist that correspond to those from a parsed
  * metric expression. Note, as find_evsel_group is called in the same order as
@@ -280,8 +288,7 @@ static struct evsel *find_evsel_group(struct evlist *perf_evlist,
                         */
                        if (!has_constraint &&
                            ev->leader != metric_events[i]->leader &&
-                           !strcmp(ev->leader->pmu_name,
-                                   metric_events[i]->leader->pmu_name))
+                           evsel_same_pmu(ev->leader, metric_events[i]->leader))
                                break;
                        if (!strcmp(metric_events[i]->name, ev->name)) {
                                set_bit(ev->idx, evlist_used);
@@ -766,7 +773,6 @@ int __weak arch_get_runtimeparam(struct pmu_event *pe __maybe_unused)
 struct metricgroup_add_iter_data {
        struct list_head *metric_list;
        const char *metric;
-       struct metric **m;
        struct expr_ids *ids;
        int *ret;
        bool *has_match;
@@ -1058,12 +1064,13 @@ static int metricgroup__add_metric_sys_event_iter(struct pmu_event *pe,
                                                  void *data)
 {
        struct metricgroup_add_iter_data *d = data;
+       struct metric *m = NULL;
        int ret;
 
        if (!match_pe_metric(pe, d->metric))
                return 0;
 
-       ret = add_metric(d->metric_list, pe, d->metric_no_group, d->m, NULL, d->ids);
+       ret = add_metric(d->metric_list, pe, d->metric_no_group, &m, NULL, d->ids);
        if (ret)
                return ret;
 
@@ -1114,7 +1121,6 @@ static int metricgroup__add_metric(const char *metric, bool metric_no_group,
                                .metric_list = &list,
                                .metric = metric,
                                .metric_no_group = metric_no_group,
-                               .m = &m,
                                .ids = &ids,
                                .has_match = &has_match,
                                .ret = &ret,
index 50ff979..25adbcc 100644 (file)
@@ -2404,7 +2404,7 @@ int perf_session__cpu_bitmap(struct perf_session *session,
 {
        int i, err = -1;
        struct perf_cpu_map *map;
-       int nr_cpus = min(session->header.env.nr_cpus_online, MAX_NR_CPUS);
+       int nr_cpus = min(session->header.env.nr_cpus_avail, MAX_NR_CPUS);
 
        for (i = 0; i < PERF_TYPE_MAX; ++i) {
                struct evsel *evsel;
index 9012651..12eafd1 100644 (file)
@@ -8,6 +8,7 @@
 #include "evlist.h"
 #include "expr.h"
 #include "metricgroup.h"
+#include "cgroup.h"
 #include <linux/zalloc.h>
 
 /*
@@ -28,6 +29,7 @@ struct saved_value {
        enum stat_type type;
        int ctx;
        int cpu;
+       struct cgroup *cgrp;
        struct runtime_stat *stat;
        struct stats stats;
        u64 metric_total;
@@ -57,6 +59,9 @@ static int saved_value_cmp(struct rb_node *rb_node, const void *entry)
        if (a->ctx != b->ctx)
                return a->ctx - b->ctx;
 
+       if (a->cgrp != b->cgrp)
+               return (char *)a->cgrp < (char *)b->cgrp ? -1 : +1;
+
        if (a->evsel == NULL && b->evsel == NULL) {
                if (a->stat == b->stat)
                        return 0;
@@ -100,7 +105,8 @@ static struct saved_value *saved_value_lookup(struct evsel *evsel,
                                              bool create,
                                              enum stat_type type,
                                              int ctx,
-                                             struct runtime_stat *st)
+                                             struct runtime_stat *st,
+                                             struct cgroup *cgrp)
 {
        struct rblist *rblist;
        struct rb_node *nd;
@@ -110,10 +116,15 @@ static struct saved_value *saved_value_lookup(struct evsel *evsel,
                .type = type,
                .ctx = ctx,
                .stat = st,
+               .cgrp = cgrp,
        };
 
        rblist = &st->value_list;
 
+       /* don't use context info for clock events */
+       if (type == STAT_NSECS)
+               dm.ctx = 0;
+
        nd = rblist__find(rblist, &dm);
        if (nd)
                return container_of(nd, struct saved_value, rb_node);
@@ -191,12 +202,18 @@ void perf_stat__reset_shadow_per_stat(struct runtime_stat *st)
        reset_stat(st);
 }
 
+struct runtime_stat_data {
+       int ctx;
+       struct cgroup *cgrp;
+};
+
 static void update_runtime_stat(struct runtime_stat *st,
                                enum stat_type type,
-                               int ctx, int cpu, u64 count)
+                               int cpu, u64 count,
+                               struct runtime_stat_data *rsd)
 {
-       struct saved_value *v = saved_value_lookup(NULL, cpu, true,
-                                                  type, ctx, st);
+       struct saved_value *v = saved_value_lookup(NULL, cpu, true, type,
+                                                  rsd->ctx, st, rsd->cgrp);
 
        if (v)
                update_stats(&v->stats, count);
@@ -210,82 +227,86 @@ static void update_runtime_stat(struct runtime_stat *st,
 void perf_stat__update_shadow_stats(struct evsel *counter, u64 count,
                                    int cpu, struct runtime_stat *st)
 {
-       int ctx = evsel_context(counter);
        u64 count_ns = count;
        struct saved_value *v;
+       struct runtime_stat_data rsd = {
+               .ctx = evsel_context(counter),
+               .cgrp = counter->cgrp,
+       };
 
        count *= counter->scale;
 
        if (evsel__is_clock(counter))
-               update_runtime_stat(st, STAT_NSECS, 0, cpu, count_ns);
+               update_runtime_stat(st, STAT_NSECS, cpu, count_ns, &rsd);
        else if (evsel__match(counter, HARDWARE, HW_CPU_CYCLES))
-               update_runtime_stat(st, STAT_CYCLES, ctx, cpu, count);
+               update_runtime_stat(st, STAT_CYCLES, cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, CYCLES_IN_TX))
-               update_runtime_stat(st, STAT_CYCLES_IN_TX, ctx, cpu, count);
+               update_runtime_stat(st, STAT_CYCLES_IN_TX, cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TRANSACTION_START))
-               update_runtime_stat(st, STAT_TRANSACTION, ctx, cpu, count);
+               update_runtime_stat(st, STAT_TRANSACTION, cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, ELISION_START))
-               update_runtime_stat(st, STAT_ELISION, ctx, cpu, count);
+               update_runtime_stat(st, STAT_ELISION, cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_TOTAL_SLOTS))
                update_runtime_stat(st, STAT_TOPDOWN_TOTAL_SLOTS,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_ISSUED))
                update_runtime_stat(st, STAT_TOPDOWN_SLOTS_ISSUED,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_SLOTS_RETIRED))
                update_runtime_stat(st, STAT_TOPDOWN_SLOTS_RETIRED,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_FETCH_BUBBLES))
                update_runtime_stat(st, STAT_TOPDOWN_FETCH_BUBBLES,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_RECOVERY_BUBBLES))
                update_runtime_stat(st, STAT_TOPDOWN_RECOVERY_BUBBLES,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_RETIRING))
                update_runtime_stat(st, STAT_TOPDOWN_RETIRING,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_BAD_SPEC))
                update_runtime_stat(st, STAT_TOPDOWN_BAD_SPEC,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_FE_BOUND))
                update_runtime_stat(st, STAT_TOPDOWN_FE_BOUND,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, TOPDOWN_BE_BOUND))
                update_runtime_stat(st, STAT_TOPDOWN_BE_BOUND,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
                update_runtime_stat(st, STAT_STALLED_CYCLES_FRONT,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (evsel__match(counter, HARDWARE, HW_STALLED_CYCLES_BACKEND))
                update_runtime_stat(st, STAT_STALLED_CYCLES_BACK,
-                                   ctx, cpu, count);
+                                   cpu, count, &rsd);
        else if (evsel__match(counter, HARDWARE, HW_BRANCH_INSTRUCTIONS))
-               update_runtime_stat(st, STAT_BRANCHES, ctx, cpu, count);
+               update_runtime_stat(st, STAT_BRANCHES, cpu, count, &rsd);
        else if (evsel__match(counter, HARDWARE, HW_CACHE_REFERENCES))
-               update_runtime_stat(st, STAT_CACHEREFS, ctx, cpu, count);
+               update_runtime_stat(st, STAT_CACHEREFS, cpu, count, &rsd);
        else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1D))
-               update_runtime_stat(st, STAT_L1_DCACHE, ctx, cpu, count);
+               update_runtime_stat(st, STAT_L1_DCACHE, cpu, count, &rsd);
        else if (evsel__match(counter, HW_CACHE, HW_CACHE_L1I))
-               update_runtime_stat(st, STAT_L1_ICACHE, ctx, cpu, count);
+               update_runtime_stat(st, STAT_L1_ICACHE, cpu, count, &rsd);
        else if (evsel__match(counter, HW_CACHE, HW_CACHE_LL))
-               update_runtime_stat(st, STAT_LL_CACHE, ctx, cpu, count);
+               update_runtime_stat(st, STAT_LL_CACHE, cpu, count, &rsd);
        else if (evsel__match(counter, HW_CACHE, HW_CACHE_DTLB))
-               update_runtime_stat(st, STAT_DTLB_CACHE, ctx, cpu, count);
+               update_runtime_stat(st, STAT_DTLB_CACHE, cpu, count, &rsd);
        else if (evsel__match(counter, HW_CACHE, HW_CACHE_ITLB))
-               update_runtime_stat(st, STAT_ITLB_CACHE, ctx, cpu, count);
+               update_runtime_stat(st, STAT_ITLB_CACHE, cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, SMI_NUM))
-               update_runtime_stat(st, STAT_SMI_NUM, ctx, cpu, count);
+               update_runtime_stat(st, STAT_SMI_NUM, cpu, count, &rsd);
        else if (perf_stat_evsel__is(counter, APERF))
-               update_runtime_stat(st, STAT_APERF, ctx, cpu, count);
+               update_runtime_stat(st, STAT_APERF, cpu, count, &rsd);
 
        if (counter->collect_stat) {
-               v = saved_value_lookup(counter, cpu, true, STAT_NONE, 0, st);
+               v = saved_value_lookup(counter, cpu, true, STAT_NONE, 0, st,
+                                      rsd.cgrp);
                update_stats(&v->stats, count);
                if (counter->metric_leader)
                        v->metric_total += count;
        } else if (counter->metric_leader) {
                v = saved_value_lookup(counter->metric_leader,
-                                      cpu, true, STAT_NONE, 0, st);
+                                      cpu, true, STAT_NONE, 0, st, rsd.cgrp);
                v->metric_total += count;
                v->metric_other++;
        }
@@ -422,11 +443,12 @@ void perf_stat__collect_metric_expr(struct evlist *evsel_list)
 }
 
 static double runtime_stat_avg(struct runtime_stat *st,
-                              enum stat_type type, int ctx, int cpu)
+                              enum stat_type type, int cpu,
+                              struct runtime_stat_data *rsd)
 {
        struct saved_value *v;
 
-       v = saved_value_lookup(NULL, cpu, false, type, ctx, st);
+       v = saved_value_lookup(NULL, cpu, false, type, rsd->ctx, st, rsd->cgrp);
        if (!v)
                return 0.0;
 
@@ -434,11 +456,12 @@ static double runtime_stat_avg(struct runtime_stat *st,
 }
 
 static double runtime_stat_n(struct runtime_stat *st,
-                            enum stat_type type, int ctx, int cpu)
+                            enum stat_type type, int cpu,
+                            struct runtime_stat_data *rsd)
 {
        struct saved_value *v;
 
-       v = saved_value_lookup(NULL, cpu, false, type, ctx, st);
+       v = saved_value_lookup(NULL, cpu, false, type, rsd->ctx, st, rsd->cgrp);
        if (!v)
                return 0.0;
 
@@ -446,16 +469,15 @@ static double runtime_stat_n(struct runtime_stat *st,
 }
 
 static void print_stalled_cycles_frontend(struct perf_stat_config *config,
-                                         int cpu,
-                                         struct evsel *evsel, double avg,
+                                         int cpu, double avg,
                                          struct perf_stat_output_ctx *out,
-                                         struct runtime_stat *st)
+                                         struct runtime_stat *st,
+                                         struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_CYCLES, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -470,16 +492,15 @@ static void print_stalled_cycles_frontend(struct perf_stat_config *config,
 }
 
 static void print_stalled_cycles_backend(struct perf_stat_config *config,
-                                        int cpu,
-                                        struct evsel *evsel, double avg,
+                                        int cpu, double avg,
                                         struct perf_stat_output_ctx *out,
-                                        struct runtime_stat *st)
+                                        struct runtime_stat *st,
+                                        struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_CYCLES, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -490,17 +511,15 @@ static void print_stalled_cycles_backend(struct perf_stat_config *config,
 }
 
 static void print_branch_misses(struct perf_stat_config *config,
-                               int cpu,
-                               struct evsel *evsel,
-                               double avg,
+                               int cpu, double avg,
                                struct perf_stat_output_ctx *out,
-                               struct runtime_stat *st)
+                               struct runtime_stat *st,
+                               struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_BRANCHES, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_BRANCHES, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -511,18 +530,15 @@ static void print_branch_misses(struct perf_stat_config *config,
 }
 
 static void print_l1_dcache_misses(struct perf_stat_config *config,
-                                  int cpu,
-                                  struct evsel *evsel,
-                                  double avg,
+                                  int cpu, double avg,
                                   struct perf_stat_output_ctx *out,
-                                  struct runtime_stat *st)
-
+                                  struct runtime_stat *st,
+                                  struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_L1_DCACHE, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_L1_DCACHE, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -533,18 +549,15 @@ static void print_l1_dcache_misses(struct perf_stat_config *config,
 }
 
 static void print_l1_icache_misses(struct perf_stat_config *config,
-                                  int cpu,
-                                  struct evsel *evsel,
-                                  double avg,
+                                  int cpu, double avg,
                                   struct perf_stat_output_ctx *out,
-                                  struct runtime_stat *st)
-
+                                  struct runtime_stat *st,
+                                  struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_L1_ICACHE, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_L1_ICACHE, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -554,17 +567,15 @@ static void print_l1_icache_misses(struct perf_stat_config *config,
 }
 
 static void print_dtlb_cache_misses(struct perf_stat_config *config,
-                                   int cpu,
-                                   struct evsel *evsel,
-                                   double avg,
+                                   int cpu, double avg,
                                    struct perf_stat_output_ctx *out,
-                                   struct runtime_stat *st)
+                                   struct runtime_stat *st,
+                                   struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_DTLB_CACHE, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_DTLB_CACHE, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -574,17 +585,15 @@ static void print_dtlb_cache_misses(struct perf_stat_config *config,
 }
 
 static void print_itlb_cache_misses(struct perf_stat_config *config,
-                                   int cpu,
-                                   struct evsel *evsel,
-                                   double avg,
+                                   int cpu, double avg,
                                    struct perf_stat_output_ctx *out,
-                                   struct runtime_stat *st)
+                                   struct runtime_stat *st,
+                                   struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_ITLB_CACHE, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_ITLB_CACHE, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -594,17 +603,15 @@ static void print_itlb_cache_misses(struct perf_stat_config *config,
 }
 
 static void print_ll_cache_misses(struct perf_stat_config *config,
-                                 int cpu,
-                                 struct evsel *evsel,
-                                 double avg,
+                                 int cpu, double avg,
                                  struct perf_stat_output_ctx *out,
-                                 struct runtime_stat *st)
+                                 struct runtime_stat *st,
+                                 struct runtime_stat_data *rsd)
 {
        double total, ratio = 0.0;
        const char *color;
-       int ctx = evsel_context(evsel);
 
-       total = runtime_stat_avg(st, STAT_LL_CACHE, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_LL_CACHE, cpu, rsd);
 
        if (total)
                ratio = avg / total * 100.0;
@@ -662,56 +669,61 @@ static double sanitize_val(double x)
        return x;
 }
 
-static double td_total_slots(int ctx, int cpu, struct runtime_stat *st)
+static double td_total_slots(int cpu, struct runtime_stat *st,
+                            struct runtime_stat_data *rsd)
 {
-       return runtime_stat_avg(st, STAT_TOPDOWN_TOTAL_SLOTS, ctx, cpu);
+       return runtime_stat_avg(st, STAT_TOPDOWN_TOTAL_SLOTS, cpu, rsd);
 }
 
-static double td_bad_spec(int ctx, int cpu, struct runtime_stat *st)
+static double td_bad_spec(int cpu, struct runtime_stat *st,
+                         struct runtime_stat_data *rsd)
 {
        double bad_spec = 0;
        double total_slots;
        double total;
 
-       total = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_ISSUED, ctx, cpu) -
-               runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, ctx, cpu) +
-               runtime_stat_avg(st, STAT_TOPDOWN_RECOVERY_BUBBLES, ctx, cpu);
+       total = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_ISSUED, cpu, rsd) -
+               runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED, cpu, rsd) +
+               runtime_stat_avg(st, STAT_TOPDOWN_RECOVERY_BUBBLES, cpu, rsd);
 
-       total_slots = td_total_slots(ctx, cpu, st);
+       total_slots = td_total_slots(cpu, st, rsd);
        if (total_slots)
                bad_spec = total / total_slots;
        return sanitize_val(bad_spec);
 }
 
-static double td_retiring(int ctx, int cpu, struct runtime_stat *st)
+static double td_retiring(int cpu, struct runtime_stat *st,
+                         struct runtime_stat_data *rsd)
 {
        double retiring = 0;
-       double total_slots = td_total_slots(ctx, cpu, st);
+       double total_slots = td_total_slots(cpu, st, rsd);
        double ret_slots = runtime_stat_avg(st, STAT_TOPDOWN_SLOTS_RETIRED,
-                                           ctx, cpu);
+                                           cpu, rsd);
 
        if (total_slots)
                retiring = ret_slots / total_slots;
        return retiring;
 }
 
-static double td_fe_bound(int ctx, int cpu, struct runtime_stat *st)
+static double td_fe_bound(int cpu, struct runtime_stat *st,
+                         struct runtime_stat_data *rsd)
 {
        double fe_bound = 0;
-       double total_slots = td_total_slots(ctx, cpu, st);
+       double total_slots = td_total_slots(cpu, st, rsd);
        double fetch_bub = runtime_stat_avg(st, STAT_TOPDOWN_FETCH_BUBBLES,
-                                           ctx, cpu);
+                                           cpu, rsd);
 
        if (total_slots)
                fe_bound = fetch_bub / total_slots;
        return fe_bound;
 }
 
-static double td_be_bound(int ctx, int cpu, struct runtime_stat *st)
+static double td_be_bound(int cpu, struct runtime_stat *st,
+                         struct runtime_stat_data *rsd)
 {
-       double sum = (td_fe_bound(ctx, cpu, st) +
-                     td_bad_spec(ctx, cpu, st) +
-                     td_retiring(ctx, cpu, st));
+       double sum = (td_fe_bound(cpu, st, rsd) +
+                     td_bad_spec(cpu, st, rsd) +
+                     td_retiring(cpu, st, rsd));
        if (sum == 0)
                return 0;
        return sanitize_val(1.0 - sum);
@@ -722,15 +734,15 @@ static double td_be_bound(int ctx, int cpu, struct runtime_stat *st)
  * the ratios we need to recreate the sum.
  */
 
-static double td_metric_ratio(int ctx, int cpu,
-                             enum stat_type type,
-                             struct runtime_stat *stat)
+static double td_metric_ratio(int cpu, enum stat_type type,
+                             struct runtime_stat *stat,
+                             struct runtime_stat_data *rsd)
 {
-       double sum = runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, ctx, cpu) +
-               runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, ctx, cpu) +
-               runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, ctx, cpu) +
-               runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, ctx, cpu);
-       double d = runtime_stat_avg(stat, type, ctx, cpu);
+       double sum = runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, cpu, rsd) +
+               runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, cpu, rsd) +
+               runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, cpu, rsd) +
+               runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, cpu, rsd);
+       double d = runtime_stat_avg(stat, type, cpu, rsd);
 
        if (sum)
                return d / sum;
@@ -742,34 +754,33 @@ static double td_metric_ratio(int ctx, int cpu,
  * We allow two missing.
  */
 
-static bool full_td(int ctx, int cpu,
-                   struct runtime_stat *stat)
+static bool full_td(int cpu, struct runtime_stat *stat,
+                   struct runtime_stat_data *rsd)
 {
        int c = 0;
 
-       if (runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, ctx, cpu) > 0)
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_RETIRING, cpu, rsd) > 0)
                c++;
-       if (runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, ctx, cpu) > 0)
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_BE_BOUND, cpu, rsd) > 0)
                c++;
-       if (runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, ctx, cpu) > 0)
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_FE_BOUND, cpu, rsd) > 0)
                c++;
-       if (runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, ctx, cpu) > 0)
+       if (runtime_stat_avg(stat, STAT_TOPDOWN_BAD_SPEC, cpu, rsd) > 0)
                c++;
        return c >= 2;
 }
 
-static void print_smi_cost(struct perf_stat_config *config,
-                          int cpu, struct evsel *evsel,
+static void print_smi_cost(struct perf_stat_config *config, int cpu,
                           struct perf_stat_output_ctx *out,
-                          struct runtime_stat *st)
+                          struct runtime_stat *st,
+                          struct runtime_stat_data *rsd)
 {
        double smi_num, aperf, cycles, cost = 0.0;
-       int ctx = evsel_context(evsel);
        const char *color = NULL;
 
-       smi_num = runtime_stat_avg(st, STAT_SMI_NUM, ctx, cpu);
-       aperf = runtime_stat_avg(st, STAT_APERF, ctx, cpu);
-       cycles = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
+       smi_num = runtime_stat_avg(st, STAT_SMI_NUM, cpu, rsd);
+       aperf = runtime_stat_avg(st, STAT_APERF, cpu, rsd);
+       cycles = runtime_stat_avg(st, STAT_CYCLES, cpu, rsd);
 
        if ((cycles == 0) || (aperf == 0))
                return;
@@ -804,7 +815,8 @@ static int prepare_metric(struct evsel **metric_events,
                        scale = 1e-9;
                } else {
                        v = saved_value_lookup(metric_events[i], cpu, false,
-                                              STAT_NONE, 0, st);
+                                              STAT_NONE, 0, st,
+                                              metric_events[i]->cgrp);
                        if (!v)
                                break;
                        stats = &v->stats;
@@ -930,12 +942,15 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
        print_metric_t print_metric = out->print_metric;
        double total, ratio = 0.0, total2;
        const char *color = NULL;
-       int ctx = evsel_context(evsel);
+       struct runtime_stat_data rsd = {
+               .ctx = evsel_context(evsel),
+               .cgrp = evsel->cgrp,
+       };
        struct metric_event *me;
        int num = 1;
 
        if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) {
-               total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
+               total = runtime_stat_avg(st, STAT_CYCLES, cpu, &rsd);
 
                if (total) {
                        ratio = avg / total;
@@ -945,12 +960,11 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                        print_metric(config, ctxp, NULL, NULL, "insn per cycle", 0);
                }
 
-               total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT,
-                                        ctx, cpu);
+               total = runtime_stat_avg(st, STAT_STALLED_CYCLES_FRONT, cpu, &rsd);
 
                total = max(total, runtime_stat_avg(st,
                                                    STAT_STALLED_CYCLES_BACK,
-                                                   ctx, cpu));
+                                                   cpu, &rsd));
 
                if (total && avg) {
                        out->new_line(config, ctxp);
@@ -960,8 +974,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        ratio);
                }
        } else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES)) {
-               if (runtime_stat_n(st, STAT_BRANCHES, ctx, cpu) != 0)
-                       print_branch_misses(config, cpu, evsel, avg, out, st);
+               if (runtime_stat_n(st, STAT_BRANCHES, cpu, &rsd) != 0)
+                       print_branch_misses(config, cpu, avg, out, st, &rsd);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all branches", 0);
        } else if (
@@ -970,8 +984,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
 
-               if (runtime_stat_n(st, STAT_L1_DCACHE, ctx, cpu) != 0)
-                       print_l1_dcache_misses(config, cpu, evsel, avg, out, st);
+               if (runtime_stat_n(st, STAT_L1_DCACHE, cpu, &rsd) != 0)
+                       print_l1_dcache_misses(config, cpu, avg, out, st, &rsd);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all L1-dcache accesses", 0);
        } else if (
@@ -980,8 +994,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
 
-               if (runtime_stat_n(st, STAT_L1_ICACHE, ctx, cpu) != 0)
-                       print_l1_icache_misses(config, cpu, evsel, avg, out, st);
+               if (runtime_stat_n(st, STAT_L1_ICACHE, cpu, &rsd) != 0)
+                       print_l1_icache_misses(config, cpu, avg, out, st, &rsd);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all L1-icache accesses", 0);
        } else if (
@@ -990,8 +1004,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
 
-               if (runtime_stat_n(st, STAT_DTLB_CACHE, ctx, cpu) != 0)
-                       print_dtlb_cache_misses(config, cpu, evsel, avg, out, st);
+               if (runtime_stat_n(st, STAT_DTLB_CACHE, cpu, &rsd) != 0)
+                       print_dtlb_cache_misses(config, cpu, avg, out, st, &rsd);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all dTLB cache accesses", 0);
        } else if (
@@ -1000,8 +1014,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
 
-               if (runtime_stat_n(st, STAT_ITLB_CACHE, ctx, cpu) != 0)
-                       print_itlb_cache_misses(config, cpu, evsel, avg, out, st);
+               if (runtime_stat_n(st, STAT_ITLB_CACHE, cpu, &rsd) != 0)
+                       print_itlb_cache_misses(config, cpu, avg, out, st, &rsd);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all iTLB cache accesses", 0);
        } else if (
@@ -1010,27 +1024,27 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                                        ((PERF_COUNT_HW_CACHE_OP_READ) << 8) |
                                         ((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16))) {
 
-               if (runtime_stat_n(st, STAT_LL_CACHE, ctx, cpu) != 0)
-                       print_ll_cache_misses(config, cpu, evsel, avg, out, st);
+               if (runtime_stat_n(st, STAT_LL_CACHE, cpu, &rsd) != 0)
+                       print_ll_cache_misses(config, cpu, avg, out, st, &rsd);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all LL-cache accesses", 0);
        } else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES)) {
-               total = runtime_stat_avg(st, STAT_CACHEREFS, ctx, cpu);
+               total = runtime_stat_avg(st, STAT_CACHEREFS, cpu, &rsd);
 
                if (total)
                        ratio = avg * 100 / total;
 
-               if (runtime_stat_n(st, STAT_CACHEREFS, ctx, cpu) != 0)
+               if (runtime_stat_n(st, STAT_CACHEREFS, cpu, &rsd) != 0)
                        print_metric(config, ctxp, NULL, "%8.3f %%",
                                     "of all cache refs", ratio);
                else
                        print_metric(config, ctxp, NULL, NULL, "of all cache refs", 0);
        } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND)) {
-               print_stalled_cycles_frontend(config, cpu, evsel, avg, out, st);
+               print_stalled_cycles_frontend(config, cpu, avg, out, st, &rsd);
        } else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND)) {
-               print_stalled_cycles_backend(config, cpu, evsel, avg, out, st);
+               print_stalled_cycles_backend(config, cpu, avg, out, st, &rsd);
        } else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES)) {
-               total = runtime_stat_avg(st, STAT_NSECS, 0, cpu);
+               total = runtime_stat_avg(st, STAT_NSECS, cpu, &rsd);
 
                if (total) {
                        ratio = avg / total;
@@ -1039,7 +1053,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                        print_metric(config, ctxp, NULL, NULL, "Ghz", 0);
                }
        } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX)) {
-               total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
+               total = runtime_stat_avg(st, STAT_CYCLES, cpu, &rsd);
 
                if (total)
                        print_metric(config, ctxp, NULL,
@@ -1049,8 +1063,8 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                        print_metric(config, ctxp, NULL, NULL, "transactional cycles",
                                     0);
        } else if (perf_stat_evsel__is(evsel, CYCLES_IN_TX_CP)) {
-               total = runtime_stat_avg(st, STAT_CYCLES, ctx, cpu);
-               total2 = runtime_stat_avg(st, STAT_CYCLES_IN_TX, ctx, cpu);
+               total = runtime_stat_avg(st, STAT_CYCLES, cpu, &rsd);
+               total2 = runtime_stat_avg(st, STAT_CYCLES_IN_TX, cpu, &rsd);
 
                if (total2 < avg)
                        total2 = avg;
@@ -1060,21 +1074,19 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                else
                        print_metric(config, ctxp, NULL, NULL, "aborted cycles", 0);
        } else if (perf_stat_evsel__is(evsel, TRANSACTION_START)) {
-               total = runtime_stat_avg(st, STAT_CYCLES_IN_TX,
-                                        ctx, cpu);
+               total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, cpu, &rsd);
 
                if (avg)
                        ratio = total / avg;
 
-               if (runtime_stat_n(st, STAT_CYCLES_IN_TX, ctx, cpu) != 0)
+               if (runtime_stat_n(st, STAT_CYCLES_IN_TX, cpu, &rsd) != 0)
                        print_metric(config, ctxp, NULL, "%8.0f",
                                     "cycles / transaction", ratio);
                else
                        print_metric(config, ctxp, NULL, NULL, "cycles / transaction",
                                      0);
        } else if (perf_stat_evsel__is(evsel, ELISION_START)) {
-               total = runtime_stat_avg(st, STAT_CYCLES_IN_TX,
-                                        ctx, cpu);
+               total = runtime_stat_avg(st, STAT_CYCLES_IN_TX, cpu, &rsd);
 
                if (avg)
                        ratio = total / avg;
@@ -1087,28 +1099,28 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                else
                        print_metric(config, ctxp, NULL, NULL, "CPUs utilized", 0);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_FETCH_BUBBLES)) {
-               double fe_bound = td_fe_bound(ctx, cpu, st);
+               double fe_bound = td_fe_bound(cpu, st, &rsd);
 
                if (fe_bound > 0.2)
                        color = PERF_COLOR_RED;
                print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
                                fe_bound * 100.);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_RETIRED)) {
-               double retiring = td_retiring(ctx, cpu, st);
+               double retiring = td_retiring(cpu, st, &rsd);
 
                if (retiring > 0.7)
                        color = PERF_COLOR_GREEN;
                print_metric(config, ctxp, color, "%8.1f%%", "retiring",
                                retiring * 100.);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_RECOVERY_BUBBLES)) {
-               double bad_spec = td_bad_spec(ctx, cpu, st);
+               double bad_spec = td_bad_spec(cpu, st, &rsd);
 
                if (bad_spec > 0.1)
                        color = PERF_COLOR_RED;
                print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
                                bad_spec * 100.);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_SLOTS_ISSUED)) {
-               double be_bound = td_be_bound(ctx, cpu, st);
+               double be_bound = td_be_bound(cpu, st, &rsd);
                const char *name = "backend bound";
                static int have_recovery_bubbles = -1;
 
@@ -1121,43 +1133,43 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
 
                if (be_bound > 0.2)
                        color = PERF_COLOR_RED;
-               if (td_total_slots(ctx, cpu, st) > 0)
+               if (td_total_slots(cpu, st, &rsd) > 0)
                        print_metric(config, ctxp, color, "%8.1f%%", name,
                                        be_bound * 100.);
                else
                        print_metric(config, ctxp, NULL, NULL, name, 0);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_RETIRING) &&
-                       full_td(ctx, cpu, st)) {
-               double retiring = td_metric_ratio(ctx, cpu,
-                                                 STAT_TOPDOWN_RETIRING, st);
-
+                  full_td(cpu, st, &rsd)) {
+               double retiring = td_metric_ratio(cpu,
+                                                 STAT_TOPDOWN_RETIRING, st,
+                                                 &rsd);
                if (retiring > 0.7)
                        color = PERF_COLOR_GREEN;
                print_metric(config, ctxp, color, "%8.1f%%", "retiring",
                                retiring * 100.);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_FE_BOUND) &&
-                       full_td(ctx, cpu, st)) {
-               double fe_bound = td_metric_ratio(ctx, cpu,
-                                                 STAT_TOPDOWN_FE_BOUND, st);
-
+                  full_td(cpu, st, &rsd)) {
+               double fe_bound = td_metric_ratio(cpu,
+                                                 STAT_TOPDOWN_FE_BOUND, st,
+                                                 &rsd);
                if (fe_bound > 0.2)
                        color = PERF_COLOR_RED;
                print_metric(config, ctxp, color, "%8.1f%%", "frontend bound",
                                fe_bound * 100.);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_BE_BOUND) &&
-                       full_td(ctx, cpu, st)) {
-               double be_bound = td_metric_ratio(ctx, cpu,
-                                                 STAT_TOPDOWN_BE_BOUND, st);
-
+                  full_td(cpu, st, &rsd)) {
+               double be_bound = td_metric_ratio(cpu,
+                                                 STAT_TOPDOWN_BE_BOUND, st,
+                                                 &rsd);
                if (be_bound > 0.2)
                        color = PERF_COLOR_RED;
                print_metric(config, ctxp, color, "%8.1f%%", "backend bound",
                                be_bound * 100.);
        } else if (perf_stat_evsel__is(evsel, TOPDOWN_BAD_SPEC) &&
-                       full_td(ctx, cpu, st)) {
-               double bad_spec = td_metric_ratio(ctx, cpu,
-                                                 STAT_TOPDOWN_BAD_SPEC, st);
-
+                  full_td(cpu, st, &rsd)) {
+               double bad_spec = td_metric_ratio(cpu,
+                                                 STAT_TOPDOWN_BAD_SPEC, st,
+                                                 &rsd);
                if (bad_spec > 0.1)
                        color = PERF_COLOR_RED;
                print_metric(config, ctxp, color, "%8.1f%%", "bad speculation",
@@ -1165,11 +1177,11 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
        } else if (evsel->metric_expr) {
                generic_metric(config, evsel->metric_expr, evsel->metric_events, NULL,
                                evsel->name, evsel->metric_name, NULL, 1, cpu, out, st);
-       } else if (runtime_stat_n(st, STAT_NSECS, 0, cpu) != 0) {
+       } else if (runtime_stat_n(st, STAT_NSECS, cpu, &rsd) != 0) {
                char unit = 'M';
                char unit_buf[10];
 
-               total = runtime_stat_avg(st, STAT_NSECS, 0, cpu);
+               total = runtime_stat_avg(st, STAT_NSECS, cpu, &rsd);
 
                if (total)
                        ratio = 1000.0 * avg / total;
@@ -1180,7 +1192,7 @@ void perf_stat__print_shadow_stats(struct perf_stat_config *config,
                snprintf(unit_buf, sizeof(unit_buf), "%c/sec", unit);
                print_metric(config, ctxp, NULL, "%8.3f", unit_buf, ratio);
        } else if (perf_stat_evsel__is(evsel, SMI_NUM)) {
-               print_smi_cost(config, cpu, evsel, out, st);
+               print_smi_cost(config, cpu, out, st, &rsd);
        } else {
                num = 0;
        }
index 5390158..09cb3a6 100644 (file)
@@ -1249,6 +1249,8 @@ static void dump_isst_config(int arg)
        isst_ctdp_display_information_end(outf);
 }
 
+static void adjust_scaling_max_from_base_freq(int cpu);
+
 static void set_tdp_level_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
                                  void *arg4)
 {
@@ -1267,6 +1269,9 @@ static void set_tdp_level_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
                        int pkg_id = get_physical_package_id(cpu);
                        int die_id = get_physical_die_id(cpu);
 
+                       /* Wait for updated base frequencies */
+                       usleep(2000);
+
                        fprintf(stderr, "Option is set to online/offline\n");
                        ctdp_level.core_cpumask_size =
                                alloc_cpu_set(&ctdp_level.core_cpumask);
@@ -1283,6 +1288,7 @@ static void set_tdp_level_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
                                        if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) {
                                                fprintf(stderr, "online cpu %d\n", i);
                                                set_cpu_online_offline(i, 1);
+                                               adjust_scaling_max_from_base_freq(i);
                                        } else {
                                                fprintf(stderr, "offline cpu %d\n", i);
                                                set_cpu_online_offline(i, 0);
@@ -1440,6 +1446,31 @@ static int set_cpufreq_scaling_min_max(int cpu, int max, int freq)
        return 0;
 }
 
+static int no_turbo(void)
+{
+       return parse_int_file(0, "/sys/devices/system/cpu/intel_pstate/no_turbo");
+}
+
+static void adjust_scaling_max_from_base_freq(int cpu)
+{
+       int base_freq, scaling_max_freq;
+
+       scaling_max_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu);
+       base_freq = get_cpufreq_base_freq(cpu);
+       if (scaling_max_freq < base_freq || no_turbo())
+               set_cpufreq_scaling_min_max(cpu, 1, base_freq);
+}
+
+static void adjust_scaling_min_from_base_freq(int cpu)
+{
+       int base_freq, scaling_min_freq;
+
+       scaling_min_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu);
+       base_freq = get_cpufreq_base_freq(cpu);
+       if (scaling_min_freq < base_freq)
+               set_cpufreq_scaling_min_max(cpu, 0, base_freq);
+}
+
 static int set_clx_pbf_cpufreq_scaling_min_max(int cpu)
 {
        struct isst_pkg_ctdp_level_info *ctdp_level;
@@ -1537,6 +1568,7 @@ static void set_scaling_min_to_cpuinfo_max(int cpu)
                        continue;
 
                set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0);
+               adjust_scaling_min_from_base_freq(i);
        }
 }
 
index 21516e2..e808a47 100755 (executable)
@@ -43,9 +43,9 @@ class KunitStatus(Enum):
        BUILD_FAILURE = auto()
        TEST_FAILURE = auto()
 
-def get_kernel_root_path():
-       parts = sys.argv[0] if not __file__ else __file__
-       parts = os.path.realpath(parts).split('tools/testing/kunit')
+def get_kernel_root_path() -> str:
+       path = sys.argv[0] if not __file__ else __file__
+       parts = os.path.realpath(path).split('tools/testing/kunit')
        if len(parts) != 2:
                sys.exit(1)
        return parts[0]
@@ -171,7 +171,7 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree,
                                exec_result.elapsed_time))
        return parse_result
 
-def add_common_opts(parser):
+def add_common_opts(parser) -> None:
        parser.add_argument('--build_dir',
                            help='As in the make command, it specifies the build '
                            'directory.',
@@ -183,13 +183,13 @@ def add_common_opts(parser):
                            help='Run all KUnit tests through allyesconfig',
                            action='store_true')
 
-def add_build_opts(parser):
+def add_build_opts(parser) -> None:
        parser.add_argument('--jobs',
                            help='As in the make command, "Specifies  the number of '
                            'jobs (commands) to run simultaneously."',
                            type=int, default=8, metavar='jobs')
 
-def add_exec_opts(parser):
+def add_exec_opts(parser) -> None:
        parser.add_argument('--timeout',
                            help='maximum number of seconds to allow for all tests '
                            'to run. This does not include time taken to build the '
@@ -198,7 +198,7 @@ def add_exec_opts(parser):
                            default=300,
                            metavar='timeout')
 
-def add_parse_opts(parser):
+def add_parse_opts(parser) -> None:
        parser.add_argument('--raw_output', help='don\'t format output from kernel',
                            action='store_true')
        parser.add_argument('--json',
@@ -256,10 +256,7 @@ def main(argv, linux=None):
                        os.mkdir(cli_args.build_dir)
 
                if not linux:
-                       linux = kunit_kernel.LinuxSourceTree()
-
-               linux.create_kunitconfig(cli_args.build_dir)
-               linux.read_kunitconfig(cli_args.build_dir)
+                       linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir)
 
                request = KunitRequest(cli_args.raw_output,
                                       cli_args.timeout,
@@ -277,10 +274,7 @@ def main(argv, linux=None):
                        os.mkdir(cli_args.build_dir)
 
                if not linux:
-                       linux = kunit_kernel.LinuxSourceTree()
-
-               linux.create_kunitconfig(cli_args.build_dir)
-               linux.read_kunitconfig(cli_args.build_dir)
+                       linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir)
 
                request = KunitConfigRequest(cli_args.build_dir,
                                             cli_args.make_options)
@@ -292,10 +286,7 @@ def main(argv, linux=None):
                        sys.exit(1)
        elif cli_args.subcommand == 'build':
                if not linux:
-                       linux = kunit_kernel.LinuxSourceTree()
-
-               linux.create_kunitconfig(cli_args.build_dir)
-               linux.read_kunitconfig(cli_args.build_dir)
+                       linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir)
 
                request = KunitBuildRequest(cli_args.jobs,
                                            cli_args.build_dir,
@@ -309,10 +300,7 @@ def main(argv, linux=None):
                        sys.exit(1)
        elif cli_args.subcommand == 'exec':
                if not linux:
-                       linux = kunit_kernel.LinuxSourceTree()
-
-               linux.create_kunitconfig(cli_args.build_dir)
-               linux.read_kunitconfig(cli_args.build_dir)
+                       linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir)
 
                exec_request = KunitExecRequest(cli_args.timeout,
                                                cli_args.build_dir,
index 02ffc3a..bdd6023 100644 (file)
@@ -8,6 +8,7 @@
 
 import collections
 import re
+from typing import List, Set
 
 CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$'
 CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$'
@@ -30,10 +31,10 @@ class KconfigParseError(Exception):
 class Kconfig(object):
        """Represents defconfig or .config specified using the Kconfig language."""
 
-       def __init__(self):
-               self._entries = []
+       def __init__(self) -> None:
+               self._entries = []  # type: List[KconfigEntry]
 
-       def entries(self):
+       def entries(self) -> Set[KconfigEntry]:
                return set(self._entries)
 
        def add_entry(self, entry: KconfigEntry) -> None:
index 624b31b..f5cca5c 100644 (file)
@@ -13,7 +13,7 @@ import kunit_parser
 
 from kunit_parser import TestStatus
 
-def get_json_result(test_result, def_config, build_dir, json_path):
+def get_json_result(test_result, def_config, build_dir, json_path) -> str:
        sub_groups = []
 
        # Each test suite is mapped to a KernelCI sub_group
index 57c1724..2076a5a 100644 (file)
@@ -11,6 +11,7 @@ import subprocess
 import os
 import shutil
 import signal
+from typing import Iterator
 
 from contextlib import ExitStack
 
@@ -39,7 +40,7 @@ class BuildError(Exception):
 class LinuxSourceTreeOperations(object):
        """An abstraction over command line operations performed on a source tree."""
 
-       def make_mrproper(self):
+       def make_mrproper(self) -> None:
                try:
                        subprocess.check_output(['make', 'mrproper'], stderr=subprocess.STDOUT)
                except OSError as e:
@@ -47,7 +48,7 @@ class LinuxSourceTreeOperations(object):
                except subprocess.CalledProcessError as e:
                        raise ConfigError(e.output.decode())
 
-       def make_olddefconfig(self, build_dir, make_options):
+       def make_olddefconfig(self, build_dir, make_options) -> None:
                command = ['make', 'ARCH=um', 'olddefconfig']
                if make_options:
                        command.extend(make_options)
@@ -60,7 +61,7 @@ class LinuxSourceTreeOperations(object):
                except subprocess.CalledProcessError as e:
                        raise ConfigError(e.output.decode())
 
-       def make_allyesconfig(self, build_dir, make_options):
+       def make_allyesconfig(self, build_dir, make_options) -> None:
                kunit_parser.print_with_timestamp(
                        'Enabling all CONFIGs for UML...')
                command = ['make', 'ARCH=um', 'allyesconfig']
@@ -82,7 +83,7 @@ class LinuxSourceTreeOperations(object):
                kunit_parser.print_with_timestamp(
                        'Starting Kernel with all configs takes a few minutes...')
 
-       def make(self, jobs, build_dir, make_options):
+       def make(self, jobs, build_dir, make_options) -> None:
                command = ['make', 'ARCH=um', '--jobs=' + str(jobs)]
                if make_options:
                        command.extend(make_options)
@@ -100,7 +101,7 @@ class LinuxSourceTreeOperations(object):
                if stderr:  # likely only due to build warnings
                        print(stderr.decode())
 
-       def linux_bin(self, params, timeout, build_dir):
+       def linux_bin(self, params, timeout, build_dir) -> None:
                """Runs the Linux UML binary. Must be named 'linux'."""
                linux_bin = get_file_path(build_dir, 'linux')
                outfile = get_outfile_path(build_dir)
@@ -110,41 +111,42 @@ class LinuxSourceTreeOperations(object):
                                                   stderr=subprocess.STDOUT)
                        process.wait(timeout)
 
-def get_kconfig_path(build_dir):
+def get_kconfig_path(build_dir) -> str:
        return get_file_path(build_dir, KCONFIG_PATH)
 
-def get_kunitconfig_path(build_dir):
+def get_kunitconfig_path(build_dir) -> str:
        return get_file_path(build_dir, KUNITCONFIG_PATH)
 
-def get_outfile_path(build_dir):
+def get_outfile_path(build_dir) -> str:
        return get_file_path(build_dir, OUTFILE_PATH)
 
 class LinuxSourceTree(object):
        """Represents a Linux kernel source tree with KUnit tests."""
 
-       def __init__(self):
-               self._ops = LinuxSourceTreeOperations()
+       def __init__(self, build_dir: str, load_config=True, defconfig=DEFAULT_KUNITCONFIG_PATH) -> None:
                signal.signal(signal.SIGINT, self.signal_handler)
 
-       def clean(self):
-               try:
-                       self._ops.make_mrproper()
-               except ConfigError as e:
-                       logging.error(e)
-                       return False
-               return True
+               self._ops = LinuxSourceTreeOperations()
+
+               if not load_config:
+                       return
 
-       def create_kunitconfig(self, build_dir, defconfig=DEFAULT_KUNITCONFIG_PATH):
                kunitconfig_path = get_kunitconfig_path(build_dir)
                if not os.path.exists(kunitconfig_path):
                        shutil.copyfile(defconfig, kunitconfig_path)
 
-       def read_kunitconfig(self, build_dir):
-               kunitconfig_path = get_kunitconfig_path(build_dir)
                self._kconfig = kunit_config.Kconfig()
                self._kconfig.read_from_file(kunitconfig_path)
 
-       def validate_config(self, build_dir):
+       def clean(self) -> bool:
+               try:
+                       self._ops.make_mrproper()
+               except ConfigError as e:
+                       logging.error(e)
+                       return False
+               return True
+
+       def validate_config(self, build_dir) -> bool:
                kconfig_path = get_kconfig_path(build_dir)
                validated_kconfig = kunit_config.Kconfig()
                validated_kconfig.read_from_file(kconfig_path)
@@ -158,7 +160,7 @@ class LinuxSourceTree(object):
                        return False
                return True
 
-       def build_config(self, build_dir, make_options):
+       def build_config(self, build_dir, make_options) -> bool:
                kconfig_path = get_kconfig_path(build_dir)
                if build_dir and not os.path.exists(build_dir):
                        os.mkdir(build_dir)
@@ -170,7 +172,7 @@ class LinuxSourceTree(object):
                        return False
                return self.validate_config(build_dir)
 
-       def build_reconfig(self, build_dir, make_options):
+       def build_reconfig(self, build_dir, make_options) -> bool:
                """Creates a new .config if it is not a subset of the .kunitconfig."""
                kconfig_path = get_kconfig_path(build_dir)
                if os.path.exists(kconfig_path):
@@ -186,7 +188,7 @@ class LinuxSourceTree(object):
                        print('Generating .config ...')
                        return self.build_config(build_dir, make_options)
 
-       def build_um_kernel(self, alltests, jobs, build_dir, make_options):
+       def build_um_kernel(self, alltests, jobs, build_dir, make_options) -> bool:
                try:
                        if alltests:
                                self._ops.make_allyesconfig(build_dir, make_options)
@@ -197,8 +199,8 @@ class LinuxSourceTree(object):
                        return False
                return self.validate_config(build_dir)
 
-       def run_kernel(self, args=[], build_dir='', timeout=None):
-               args.extend(['mem=1G'])
+       def run_kernel(self, args=[], build_dir='', timeout=None) -> Iterator[str]:
+               args.extend(['mem=1G', 'console=tty'])
                self._ops.linux_bin(args, timeout, build_dir)
                outfile = get_outfile_path(build_dir)
                subprocess.call(['stty', 'sane'])
@@ -206,6 +208,6 @@ class LinuxSourceTree(object):
                        for line in file:
                                yield line
 
-       def signal_handler(self, sig, frame):
+       def signal_handler(self, sig, frame) -> None:
                logging.error('Build interruption occurred. Cleaning console.')
                subprocess.call(['stty', 'sane'])
index 6614ec4..e8bcc13 100644 (file)
@@ -12,32 +12,32 @@ from collections import namedtuple
 from datetime import datetime
 from enum import Enum, auto
 from functools import reduce
-from typing import List, Optional, Tuple
+from typing import Iterable, Iterator, List, Optional, Tuple
 
 TestResult = namedtuple('TestResult', ['status','suites','log'])
 
 class TestSuite(object):
-       def __init__(self):
-               self.status = None
-               self.name = None
-               self.cases = []
+       def __init__(self) -> None:
+               self.status = TestStatus.SUCCESS
+               self.name = ''
+               self.cases = []  # type: List[TestCase]
 
-       def __str__(self):
-               return 'TestSuite(' + self.status + ',' + self.name + ',' + str(self.cases) + ')'
+       def __str__(self) -> str:
+               return 'TestSuite(' + str(self.status) + ',' + self.name + ',' + str(self.cases) + ')'
 
-       def __repr__(self):
+       def __repr__(self) -> str:
                return str(self)
 
 class TestCase(object):
-       def __init__(self):
-               self.status = None
+       def __init__(self) -> None:
+               self.status = TestStatus.SUCCESS
                self.name = ''
-               self.log = []
+               self.log = []  # type: List[str]
 
-       def __str__(self):
-               return 'TestCase(' + self.status + ',' + self.name + ',' + str(self.log) + ')'
+       def __str__(self) -> str:
+               return 'TestCase(' + str(self.status) + ',' + self.name + ',' + str(self.log) + ')'
 
-       def __repr__(self):
+       def __repr__(self) -> str:
                return str(self)
 
 class TestStatus(Enum):
@@ -51,7 +51,7 @@ kunit_start_re = re.compile(r'TAP version [0-9]+$')
 kunit_end_re = re.compile('(List of all partitions:|'
                          'Kernel panic - not syncing: VFS:)')
 
-def isolate_kunit_output(kernel_output):
+def isolate_kunit_output(kernel_output) -> Iterator[str]:
        started = False
        for line in kernel_output:
                line = line.rstrip()  # line always has a trailing \n
@@ -64,7 +64,7 @@ def isolate_kunit_output(kernel_output):
                elif started:
                        yield line[prefix_len:] if prefix_len > 0 else line
 
-def raw_output(kernel_output):
+def raw_output(kernel_output) -> None:
        for line in kernel_output:
                print(line.rstrip())
 
@@ -72,36 +72,36 @@ DIVIDER = '=' * 60
 
 RESET = '\033[0;0m'
 
-def red(text):
+def red(text) -> str:
        return '\033[1;31m' + text + RESET
 
-def yellow(text):
+def yellow(text) -> str:
        return '\033[1;33m' + text + RESET
 
-def green(text):
+def green(text) -> str:
        return '\033[1;32m' + text + RESET
 
-def print_with_timestamp(message):
+def print_with_timestamp(message) -> None:
        print('[%s] %s' % (datetime.now().strftime('%H:%M:%S'), message))
 
-def format_suite_divider(message):
+def format_suite_divider(message) -> str:
        return '======== ' + message + ' ========'
 
-def print_suite_divider(message):
+def print_suite_divider(message) -> None:
        print_with_timestamp(DIVIDER)
        print_with_timestamp(format_suite_divider(message))
 
-def print_log(log):
+def print_log(log) -> None:
        for m in log:
                print_with_timestamp(m)
 
 TAP_ENTRIES = re.compile(r'^(TAP|[\s]*ok|[\s]*not ok|[\s]*[0-9]+\.\.[0-9]+|[\s]*#).*$')
 
-def consume_non_diagnositic(lines: List[str]) -> None:
+def consume_non_diagnostic(lines: List[str]) -> None:
        while lines and not TAP_ENTRIES.match(lines[0]):
                lines.pop(0)
 
-def save_non_diagnositic(lines: List[str], test_case: TestCase) -> None:
+def save_non_diagnostic(lines: List[str], test_case: TestCase) -> None:
        while lines and not TAP_ENTRIES.match(lines[0]):
                test_case.log.append(lines[0])
                lines.pop(0)
@@ -113,7 +113,7 @@ OK_NOT_OK_SUBTEST = re.compile(r'^[\s]+(ok|not ok) [0-9]+ - (.*)$')
 OK_NOT_OK_MODULE = re.compile(r'^(ok|not ok) ([0-9]+) - (.*)$')
 
 def parse_ok_not_ok_test_case(lines: List[str], test_case: TestCase) -> bool:
-       save_non_diagnositic(lines, test_case)
+       save_non_diagnostic(lines, test_case)
        if not lines:
                test_case.status = TestStatus.TEST_CRASHED
                return True
@@ -139,7 +139,7 @@ SUBTEST_DIAGNOSTIC = re.compile(r'^[\s]+# (.*)$')
 DIAGNOSTIC_CRASH_MESSAGE = re.compile(r'^[\s]+# .*?: kunit test case crashed!$')
 
 def parse_diagnostic(lines: List[str], test_case: TestCase) -> bool:
-       save_non_diagnositic(lines, test_case)
+       save_non_diagnostic(lines, test_case)
        if not lines:
                return False
        line = lines[0]
@@ -155,7 +155,7 @@ def parse_diagnostic(lines: List[str], test_case: TestCase) -> bool:
 
 def parse_test_case(lines: List[str]) -> Optional[TestCase]:
        test_case = TestCase()
-       save_non_diagnositic(lines, test_case)
+       save_non_diagnostic(lines, test_case)
        while parse_diagnostic(lines, test_case):
                pass
        if parse_ok_not_ok_test_case(lines, test_case):
@@ -166,7 +166,7 @@ def parse_test_case(lines: List[str]) -> Optional[TestCase]:
 SUBTEST_HEADER = re.compile(r'^[\s]+# Subtest: (.*)$')
 
 def parse_subtest_header(lines: List[str]) -> Optional[str]:
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        if not lines:
                return None
        match = SUBTEST_HEADER.match(lines[0])
@@ -179,7 +179,7 @@ def parse_subtest_header(lines: List[str]) -> Optional[str]:
 SUBTEST_PLAN = re.compile(r'[\s]+[0-9]+\.\.([0-9]+)')
 
 def parse_subtest_plan(lines: List[str]) -> Optional[int]:
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        match = SUBTEST_PLAN.match(lines[0])
        if match:
                lines.pop(0)
@@ -202,7 +202,7 @@ def max_status(left: TestStatus, right: TestStatus) -> TestStatus:
 def parse_ok_not_ok_test_suite(lines: List[str],
                               test_suite: TestSuite,
                               expected_suite_index: int) -> bool:
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        if not lines:
                test_suite.status = TestStatus.TEST_CRASHED
                return False
@@ -224,18 +224,17 @@ def parse_ok_not_ok_test_suite(lines: List[str],
        else:
                return False
 
-def bubble_up_errors(to_status, status_container_list) -> TestStatus:
-       status_list = map(to_status, status_container_list)
-       return reduce(max_status, status_list, TestStatus.SUCCESS)
+def bubble_up_errors(statuses: Iterable[TestStatus]) -> TestStatus:
+       return reduce(max_status, statuses, TestStatus.SUCCESS)
 
 def bubble_up_test_case_errors(test_suite: TestSuite) -> TestStatus:
-       max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases)
+       max_test_case_status = bubble_up_errors(x.status for x in test_suite.cases)
        return max_status(max_test_case_status, test_suite.status)
 
 def parse_test_suite(lines: List[str], expected_suite_index: int) -> Optional[TestSuite]:
        if not lines:
                return None
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        test_suite = TestSuite()
        test_suite.status = TestStatus.SUCCESS
        name = parse_subtest_header(lines)
@@ -264,7 +263,7 @@ def parse_test_suite(lines: List[str], expected_suite_index: int) -> Optional[Te
 TAP_HEADER = re.compile(r'^TAP version 14$')
 
 def parse_tap_header(lines: List[str]) -> bool:
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        if TAP_HEADER.match(lines[0]):
                lines.pop(0)
                return True
@@ -274,7 +273,7 @@ def parse_tap_header(lines: List[str]) -> bool:
 TEST_PLAN = re.compile(r'[0-9]+\.\.([0-9]+)')
 
 def parse_test_plan(lines: List[str]) -> Optional[int]:
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        match = TEST_PLAN.match(lines[0])
        if match:
                lines.pop(0)
@@ -282,11 +281,11 @@ def parse_test_plan(lines: List[str]) -> Optional[int]:
        else:
                return None
 
-def bubble_up_suite_errors(test_suite_list: List[TestSuite]) -> TestStatus:
-       return bubble_up_errors(lambda x: x.status, test_suite_list)
+def bubble_up_suite_errors(test_suites: Iterable[TestSuite]) -> TestStatus:
+       return bubble_up_errors(x.status for x in test_suites)
 
 def parse_test_result(lines: List[str]) -> TestResult:
-       consume_non_diagnositic(lines)
+       consume_non_diagnostic(lines)
        if not lines or not parse_tap_header(lines):
                return TestResult(TestStatus.NO_TESTS, [], lines)
        expected_test_suite_num = parse_test_plan(lines)
index afbab4a..c42aace 100644 (file)
@@ -34,6 +34,7 @@ TARGETS += memory-hotplug
 TARGETS += mincore
 TARGETS += mount
 TARGETS += mqueue
+TARGETS += nci
 TARGETS += net
 TARGETS += net/forwarding
 TARGETS += net/mptcp
@@ -77,8 +78,10 @@ TARGETS += zram
 TARGETS_HOTPLUG = cpu-hotplug
 TARGETS_HOTPLUG += memory-hotplug
 
-# User can optionally provide a TARGETS skiplist.
-SKIP_TARGETS ?=
+# User can optionally provide a TARGETS skiplist.  By default we skip
+# BPF since it has cutting edge build time dependencies which require
+# more effort to install.
+SKIP_TARGETS ?= bpf
 ifneq ($(SKIP_TARGETS),)
        TMP := $(filter-out $(SKIP_TARGETS), $(TARGETS))
        override TARGETS := $(TMP)
index 1c5556b..0dbd594 100644 (file)
@@ -457,7 +457,7 @@ function barf
        mov     x11, x1 // actual data
        mov     x12, x2 // data size
 
-       puts    "Mistatch: PID="
+       puts    "Mismatch: PID="
        mov     x0, x20
        bl      putdec
        puts    ", iteration="
index f95074c..9210691 100644 (file)
@@ -625,7 +625,7 @@ function barf
        mov     x11, x1 // actual data
        mov     x12, x2 // data size
 
-       puts    "Mistatch: PID="
+       puts    "Mismatch: PID="
        mov     x0, x20
        bl      putdec
        puts    ", iteration="
index 8c33e99..0552b07 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 include ../../../../scripts/Kbuild.include
 include ../../../scripts/Makefile.arch
+include ../../../scripts/Makefile.include
 
 CXX ?= $(CROSS_COMPILE)g++
 
@@ -24,7 +25,7 @@ BPF_GCC               ?= $(shell command -v bpf-gcc;)
 SAN_CFLAGS     ?=
 CFLAGS += -g -rdynamic -Wall -O2 $(GENFLAGS) $(SAN_CFLAGS)             \
          -I$(CURDIR) -I$(INCLUDE_DIR) -I$(GENDIR) -I$(LIBDIR)          \
-         -I$(TOOLSINCDIR) -I$(APIDIR)                                  \
+         -I$(TOOLSINCDIR) -I$(APIDIR) -I$(OUTPUT)                      \
          -Dbpf_prog_load=bpf_prog_test_load                            \
          -Dbpf_load_program=bpf_test_load_program
 LDLIBS += -lcap -lelf -lz -lrt -lpthread
@@ -43,10 +44,10 @@ ifneq ($(BPF_GCC),)
 TEST_GEN_PROGS += test_progs-bpf_gcc
 endif
 
-TEST_GEN_FILES =
-TEST_FILES = test_lwt_ip_encap.o \
-       test_tc_edt.o \
-       xsk_prereqs.sh
+TEST_GEN_FILES = test_lwt_ip_encap.o \
+       test_tc_edt.o
+TEST_FILES = xsk_prereqs.sh \
+       $(wildcard progs/btf_dump_test_case_*.c)
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
@@ -82,7 +83,7 @@ TEST_GEN_PROGS_EXTENDED = test_sock_addr test_skb_cgroup_id_user \
        test_lirc_mode2_user xdping test_cpp runqslower bench bpf_testmod.ko \
        xdpxceiver
 
-TEST_CUSTOM_PROGS = urandom_read
+TEST_CUSTOM_PROGS = $(OUTPUT)/urandom_read
 
 # Emit succinct information message describing current building step
 # $1 - generic step name (e.g., CC, LINK, etc);
@@ -113,7 +114,15 @@ SCRATCH_DIR := $(OUTPUT)/tools
 BUILD_DIR := $(SCRATCH_DIR)/build
 INCLUDE_DIR := $(SCRATCH_DIR)/include
 BPFOBJ := $(BUILD_DIR)/libbpf/libbpf.a
-RESOLVE_BTFIDS := $(BUILD_DIR)/resolve_btfids/resolve_btfids
+ifneq ($(CROSS_COMPILE),)
+HOST_BUILD_DIR         := $(BUILD_DIR)/host
+HOST_SCRATCH_DIR       := $(OUTPUT)/host-tools
+else
+HOST_BUILD_DIR         := $(BUILD_DIR)
+HOST_SCRATCH_DIR       := $(SCRATCH_DIR)
+endif
+HOST_BPFOBJ := $(HOST_BUILD_DIR)/libbpf/libbpf.a
+RESOLVE_BTFIDS := $(HOST_BUILD_DIR)/resolve_btfids/resolve_btfids
 
 VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)                           \
                     $(if $(KBUILD_OUTPUT),$(KBUILD_OUTPUT)/vmlinux)    \
@@ -121,6 +130,9 @@ VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux)                                \
                     /sys/kernel/btf/vmlinux                            \
                     /boot/vmlinux-$(shell uname -r)
 VMLINUX_BTF ?= $(abspath $(firstword $(wildcard $(VMLINUX_BTF_PATHS))))
+ifeq ($(VMLINUX_BTF),)
+$(error Cannot find a vmlinux for VMLINUX_BTF at any of "$(VMLINUX_BTF_PATHS)")
+endif
 
 # Define simple and short `make test_progs`, `make test_sysctl`, etc targets
 # to build individual tests.
@@ -132,6 +144,14 @@ $(notdir $(TEST_GEN_PROGS)                                         \
         $(TEST_GEN_PROGS_EXTENDED)                                     \
         $(TEST_CUSTOM_PROGS)): %: $(OUTPUT)/% ;
 
+# sort removes libbpf duplicates when not cross-building
+MAKE_DIRS := $(sort $(BUILD_DIR)/libbpf $(HOST_BUILD_DIR)/libbpf              \
+              $(HOST_BUILD_DIR)/bpftool $(HOST_BUILD_DIR)/resolve_btfids      \
+              $(INCLUDE_DIR))
+$(MAKE_DIRS):
+       $(call msg,MKDIR,,$@)
+       $(Q)mkdir -p $@
+
 $(OUTPUT)/%.o: %.c
        $(call msg,CC,,$@)
        $(Q)$(CC) $(CFLAGS) -c $(filter %.c,$^) $(LDLIBS) -o $@
@@ -154,7 +174,7 @@ $(OUTPUT)/test_stub.o: test_stub.c $(BPFOBJ)
        $(call msg,CC,,$@)
        $(Q)$(CC) -c $(CFLAGS) -o $@ $<
 
-DEFAULT_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool
+DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
 
 $(OUTPUT)/runqslower: $(BPFOBJ) | $(DEFAULT_BPFTOOL)
        $(Q)$(MAKE) $(submake_extras) -C $(TOOLSDIR)/bpf/runqslower     \
@@ -179,10 +199,11 @@ $(OUTPUT)/test_sysctl: cgroup_helpers.c
 
 BPFTOOL ?= $(DEFAULT_BPFTOOL)
 $(DEFAULT_BPFTOOL): $(wildcard $(BPFTOOLDIR)/*.[ch] $(BPFTOOLDIR)/Makefile)    \
-                   $(BPFOBJ) | $(BUILD_DIR)/bpftool
+                   $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/bpftool
        $(Q)$(MAKE) $(submake_extras)  -C $(BPFTOOLDIR)                        \
-                   OUTPUT=$(BUILD_DIR)/bpftool/                               \
-                   prefix= DESTDIR=$(SCRATCH_DIR)/ install
+                   CC=$(HOSTCC) LD=$(HOSTLD)                                  \
+                   OUTPUT=$(HOST_BUILD_DIR)/bpftool/                          \
+                   prefix= DESTDIR=$(HOST_SCRATCH_DIR)/ install
        $(Q)mkdir -p $(BUILD_DIR)/bpftool/Documentation
        $(Q)RST2MAN_OPTS="--exit-status=1" $(MAKE) $(submake_extras)           \
                    -C $(BPFTOOLDIR)/Documentation                             \
@@ -195,9 +216,14 @@ $(BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile)                \
        $(Q)$(MAKE) $(submake_extras) -C $(BPFDIR) OUTPUT=$(BUILD_DIR)/libbpf/ \
                    DESTDIR=$(SCRATCH_DIR) prefix= all install_headers
 
-$(BUILD_DIR)/libbpf $(BUILD_DIR)/bpftool $(BUILD_DIR)/resolve_btfids $(INCLUDE_DIR):
-       $(call msg,MKDIR,,$@)
-       $(Q)mkdir -p $@
+ifneq ($(BPFOBJ),$(HOST_BPFOBJ))
+$(HOST_BPFOBJ): $(wildcard $(BPFDIR)/*.[ch] $(BPFDIR)/Makefile)                \
+          ../../../include/uapi/linux/bpf.h                                   \
+          | $(INCLUDE_DIR) $(HOST_BUILD_DIR)/libbpf
+       $(Q)$(MAKE) $(submake_extras) -C $(BPFDIR)                             \
+               OUTPUT=$(HOST_BUILD_DIR)/libbpf/ CC=$(HOSTCC) LD=$(HOSTLD)     \
+                   DESTDIR=$(HOST_SCRATCH_DIR)/ prefix= all install_headers
+endif
 
 $(INCLUDE_DIR)/vmlinux.h: $(VMLINUX_BTF) | $(BPFTOOL) $(INCLUDE_DIR)
 ifeq ($(VMLINUX_H),)
@@ -208,7 +234,7 @@ else
        $(Q)cp "$(VMLINUX_H)" $@
 endif
 
-$(RESOLVE_BTFIDS): $(BPFOBJ) | $(BUILD_DIR)/resolve_btfids     \
+$(RESOLVE_BTFIDS): $(HOST_BPFOBJ) | $(HOST_BUILD_DIR)/resolve_btfids   \
                       $(TOOLSDIR)/bpf/resolve_btfids/main.c    \
                       $(TOOLSDIR)/lib/rbtree.c                 \
                       $(TOOLSDIR)/lib/zalloc.c                 \
@@ -216,7 +242,8 @@ $(RESOLVE_BTFIDS): $(BPFOBJ) | $(BUILD_DIR)/resolve_btfids  \
                       $(TOOLSDIR)/lib/ctype.c                  \
                       $(TOOLSDIR)/lib/str_error_r.c
        $(Q)$(MAKE) $(submake_extras) -C $(TOOLSDIR)/bpf/resolve_btfids \
-               OUTPUT=$(BUILD_DIR)/resolve_btfids/ BPFOBJ=$(BPFOBJ)
+               CC=$(HOSTCC) LD=$(HOSTLD) AR=$(HOSTAR) \
+               OUTPUT=$(HOST_BUILD_DIR)/resolve_btfids/ BPFOBJ=$(HOST_BPFOBJ)
 
 # Get Clang's default includes on this system, as opposed to those seen by
 # '-target bpf'. This fixes "missing" files on some architectures/distros,
@@ -387,10 +414,12 @@ TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko    \
                       $(wildcard progs/btf_dump_test_case_*.c)
 TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
 TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS)
+TRUNNER_BPF_CFLAGS += -DENABLE_ATOMICS_TESTS
 $(eval $(call DEFINE_TEST_RUNNER,test_progs))
 
 # Define test_progs-no_alu32 test runner.
 TRUNNER_BPF_BUILD_RULE := CLANG_NOALU32_BPF_BUILD_RULE
+TRUNNER_BPF_CFLAGS := $(BPF_CFLAGS) $(CLANG_CFLAGS)
 $(eval $(call DEFINE_TEST_RUNNER,test_progs,no_alu32))
 
 # Define test_progs BPF-GCC-flavored test runner.
@@ -447,7 +476,7 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o $(OUTPUT)/testing_helpers.o \
        $(call msg,BINARY,,$@)
        $(Q)$(CC) $(LDFLAGS) -o $@ $(filter %.a %.o,$^) $(LDLIBS)
 
-EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR)                     \
+EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(SCRATCH_DIR) $(HOST_SCRATCH_DIR) \
        prog_tests/tests.h map_tests/tests.h verifier/tests.h           \
        feature                                                         \
        $(addprefix $(OUTPUT)/,*.o *.skel.h no_alu32 bpf_gcc bpf_testmod.ko)
index 2df19d7..0b991e1 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/error-injection.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/percpu-defs.h>
 #include <linux/sysfs.h>
 #include <linux/tracepoint.h>
 #include "bpf_testmod.h"
@@ -10,6 +11,8 @@
 #define CREATE_TRACE_POINTS
 #include "bpf_testmod-events.h"
 
+DEFINE_PER_CPU(int, bpf_testmod_ksym_percpu) = 123;
+
 noinline ssize_t
 bpf_testmod_test_read(struct file *file, struct kobject *kobj,
                      struct bin_attribute *bin_attr,
diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c b/tools/testing/selftests/bpf/prog_tests/atomics.c
new file mode 100644 (file)
index 0000000..21efe7b
--- /dev/null
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+
+#include "atomics.skel.h"
+
+static void test_add(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.add);
+       if (CHECK(IS_ERR(link), "attach(add)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.add);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run add",
+                 "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->add64_value, 3, "add64_value");
+       ASSERT_EQ(skel->bss->add64_result, 1, "add64_result");
+
+       ASSERT_EQ(skel->data->add32_value, 3, "add32_value");
+       ASSERT_EQ(skel->bss->add32_result, 1, "add32_result");
+
+       ASSERT_EQ(skel->bss->add_stack_value_copy, 3, "add_stack_value");
+       ASSERT_EQ(skel->bss->add_stack_result, 1, "add_stack_result");
+
+       ASSERT_EQ(skel->data->add_noreturn_value, 3, "add_noreturn_value");
+
+cleanup:
+       bpf_link__destroy(link);
+}
+
+static void test_sub(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.sub);
+       if (CHECK(IS_ERR(link), "attach(sub)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.sub);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run sub",
+                 "err %d errno %d retval %d duration %d\n",
+                 err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->sub64_value, -1, "sub64_value");
+       ASSERT_EQ(skel->bss->sub64_result, 1, "sub64_result");
+
+       ASSERT_EQ(skel->data->sub32_value, -1, "sub32_value");
+       ASSERT_EQ(skel->bss->sub32_result, 1, "sub32_result");
+
+       ASSERT_EQ(skel->bss->sub_stack_value_copy, -1, "sub_stack_value");
+       ASSERT_EQ(skel->bss->sub_stack_result, 1, "sub_stack_result");
+
+       ASSERT_EQ(skel->data->sub_noreturn_value, -1, "sub_noreturn_value");
+
+cleanup:
+       bpf_link__destroy(link);
+}
+
+static void test_and(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.and);
+       if (CHECK(IS_ERR(link), "attach(and)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.and);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run and",
+                 "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->and64_value, 0x010ull << 32, "and64_value");
+       ASSERT_EQ(skel->bss->and64_result, 0x110ull << 32, "and64_result");
+
+       ASSERT_EQ(skel->data->and32_value, 0x010, "and32_value");
+       ASSERT_EQ(skel->bss->and32_result, 0x110, "and32_result");
+
+       ASSERT_EQ(skel->data->and_noreturn_value, 0x010ull << 32, "and_noreturn_value");
+cleanup:
+       bpf_link__destroy(link);
+}
+
+static void test_or(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.or);
+       if (CHECK(IS_ERR(link), "attach(or)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.or);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run or",
+                 "err %d errno %d retval %d duration %d\n",
+                 err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->or64_value, 0x111ull << 32, "or64_value");
+       ASSERT_EQ(skel->bss->or64_result, 0x110ull << 32, "or64_result");
+
+       ASSERT_EQ(skel->data->or32_value, 0x111, "or32_value");
+       ASSERT_EQ(skel->bss->or32_result, 0x110, "or32_result");
+
+       ASSERT_EQ(skel->data->or_noreturn_value, 0x111ull << 32, "or_noreturn_value");
+cleanup:
+       bpf_link__destroy(link);
+}
+
+static void test_xor(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.xor);
+       if (CHECK(IS_ERR(link), "attach(xor)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.xor);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run xor",
+                 "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->xor64_value, 0x101ull << 32, "xor64_value");
+       ASSERT_EQ(skel->bss->xor64_result, 0x110ull << 32, "xor64_result");
+
+       ASSERT_EQ(skel->data->xor32_value, 0x101, "xor32_value");
+       ASSERT_EQ(skel->bss->xor32_result, 0x110, "xor32_result");
+
+       ASSERT_EQ(skel->data->xor_noreturn_value, 0x101ull << 32, "xor_nxoreturn_value");
+cleanup:
+       bpf_link__destroy(link);
+}
+
+static void test_cmpxchg(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.cmpxchg);
+       if (CHECK(IS_ERR(link), "attach(cmpxchg)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.cmpxchg);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run add",
+                 "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->cmpxchg64_value, 2, "cmpxchg64_value");
+       ASSERT_EQ(skel->bss->cmpxchg64_result_fail, 1, "cmpxchg_result_fail");
+       ASSERT_EQ(skel->bss->cmpxchg64_result_succeed, 1, "cmpxchg_result_succeed");
+
+       ASSERT_EQ(skel->data->cmpxchg32_value, 2, "lcmpxchg32_value");
+       ASSERT_EQ(skel->bss->cmpxchg32_result_fail, 1, "cmpxchg_result_fail");
+       ASSERT_EQ(skel->bss->cmpxchg32_result_succeed, 1, "cmpxchg_result_succeed");
+
+cleanup:
+       bpf_link__destroy(link);
+}
+
+static void test_xchg(struct atomics *skel)
+{
+       int err, prog_fd;
+       __u32 duration = 0, retval;
+       struct bpf_link *link;
+
+       link = bpf_program__attach(skel->progs.xchg);
+       if (CHECK(IS_ERR(link), "attach(xchg)", "err: %ld\n", PTR_ERR(link)))
+               return;
+
+       prog_fd = bpf_program__fd(skel->progs.xchg);
+       err = bpf_prog_test_run(prog_fd, 1, NULL, 0,
+                               NULL, NULL, &retval, &duration);
+       if (CHECK(err || retval, "test_run add",
+                 "err %d errno %d retval %d duration %d\n", err, errno, retval, duration))
+               goto cleanup;
+
+       ASSERT_EQ(skel->data->xchg64_value, 2, "xchg64_value");
+       ASSERT_EQ(skel->bss->xchg64_result, 1, "xchg64_result");
+
+       ASSERT_EQ(skel->data->xchg32_value, 2, "xchg32_value");
+       ASSERT_EQ(skel->bss->xchg32_result, 1, "xchg32_result");
+
+cleanup:
+       bpf_link__destroy(link);
+}
+
+void test_atomics(void)
+{
+       struct atomics *skel;
+       __u32 duration = 0;
+
+       skel = atomics__open_and_load();
+       if (CHECK(!skel, "skel_load", "atomics skeleton failed\n"))
+               return;
+
+       if (skel->data->skip_tests) {
+               printf("%s:SKIP:no ENABLE_ATOMICS_TESTS (missing Clang BPF atomics support)",
+                      __func__);
+               test__skip();
+               goto cleanup;
+       }
+
+       if (test__start_subtest("add"))
+               test_add(skel);
+       if (test__start_subtest("sub"))
+               test_sub(skel);
+       if (test__start_subtest("and"))
+               test_and(skel);
+       if (test__start_subtest("or"))
+               test_or(skel);
+       if (test__start_subtest("xor"))
+               test_xor(skel);
+       if (test__start_subtest("cmpxchg"))
+               test_cmpxchg(skel);
+       if (test__start_subtest("xchg"))
+               test_xchg(skel);
+
+cleanup:
+       atomics__destroy(skel);
+}
index 76ebe4c..eb90a6b 100644 (file)
@@ -20,39 +20,6 @@ static __u32 bpf_map_id(struct bpf_map *map)
        return info.id;
 }
 
-/*
- * Trigger synchronize_rcu() in kernel.
- *
- * ARRAY_OF_MAPS/HASH_OF_MAPS lookup/update operations trigger synchronize_rcu()
- * if looking up an existing non-NULL element or updating the map with a valid
- * inner map FD. Use this fact to trigger synchronize_rcu(): create map-in-map,
- * create a trivial ARRAY map, update map-in-map with ARRAY inner map. Then
- * cleanup. At the end, at least one synchronize_rcu() would be called.
- */
-static int kern_sync_rcu(void)
-{
-       int inner_map_fd, outer_map_fd, err, zero = 0;
-
-       inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 4, 1, 0);
-       if (CHECK(inner_map_fd < 0, "inner_map_create", "failed %d\n", -errno))
-               return -1;
-
-       outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS, NULL,
-                                            sizeof(int), inner_map_fd, 1, 0);
-       if (CHECK(outer_map_fd < 0, "outer_map_create", "failed %d\n", -errno)) {
-               close(inner_map_fd);
-               return -1;
-       }
-
-       err = bpf_map_update_elem(outer_map_fd, &zero, &inner_map_fd, 0);
-       if (err)
-               err = -errno;
-       CHECK(err, "outer_map_update", "failed %d\n", err);
-       close(inner_map_fd);
-       close(outer_map_fd);
-       return err;
-}
-
 static void test_lookup_update(void)
 {
        int map1_fd, map2_fd, map3_fd, map4_fd, map5_fd, map1_id, map2_id;
index b549fcf..0a1fc98 100644 (file)
@@ -45,13 +45,13 @@ static int prog_load_cnt(int verdict, int val)
                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
                BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
                BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */
-               BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
 
                BPF_LD_MAP_FD(BPF_REG_1, cgroup_storage_fd),
                BPF_MOV64_IMM(BPF_REG_2, 0),
                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_local_storage),
                BPF_MOV64_IMM(BPF_REG_1, val),
-               BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_0, BPF_REG_1, 0, 0),
+               BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
 
                BPF_LD_MAP_FD(BPF_REG_1, percpu_cgroup_storage_fd),
                BPF_MOV64_IMM(BPF_REG_2, 0),
diff --git a/tools/testing/selftests/bpf/prog_tests/core_read_macros.c b/tools/testing/selftests/bpf/prog_tests/core_read_macros.c
new file mode 100644 (file)
index 0000000..96f5cf3
--- /dev/null
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020 Facebook */
+
+#include <test_progs.h>
+
+struct callback_head {
+       struct callback_head *next;
+       void (*func)(struct callback_head *head);
+};
+
+/* ___shuffled flavor is just an illusion for BPF code, it doesn't really
+ * exist and user-space needs to provide data in the memory layout that
+ * matches callback_head. We just defined ___shuffled flavor to make it easier
+ * to work with the skeleton
+ */
+struct callback_head___shuffled {
+       struct callback_head___shuffled *next;
+       void (*func)(struct callback_head *head);
+};
+
+#include "test_core_read_macros.skel.h"
+
+void test_core_read_macros(void)
+{
+       int duration = 0, err;
+       struct test_core_read_macros* skel;
+       struct test_core_read_macros__bss *bss;
+       struct callback_head u_probe_in;
+       struct callback_head___shuffled u_core_in;
+
+       skel = test_core_read_macros__open_and_load();
+       if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
+               return;
+       bss = skel->bss;
+       bss->my_pid = getpid();
+
+       /* next pointers have to be set from the kernel side */
+       bss->k_probe_in.func = (void *)(long)0x1234;
+       bss->k_core_in.func = (void *)(long)0xabcd;
+
+       u_probe_in.next = &u_probe_in;
+       u_probe_in.func = (void *)(long)0x5678;
+       bss->u_probe_in = &u_probe_in;
+
+       u_core_in.next = &u_core_in;
+       u_core_in.func = (void *)(long)0xdbca;
+       bss->u_core_in = &u_core_in;
+
+       err = test_core_read_macros__attach(skel);
+       if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
+               goto cleanup;
+
+       /* trigger tracepoint */
+       usleep(1);
+
+       ASSERT_EQ(bss->k_probe_out, 0x1234, "k_probe_out");
+       ASSERT_EQ(bss->k_core_out, 0xabcd, "k_core_out");
+
+       ASSERT_EQ(bss->u_probe_out, 0x5678, "u_probe_out");
+       ASSERT_EQ(bss->u_core_out, 0xdbca, "u_core_out");
+
+cleanup:
+       test_core_read_macros__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms_module.c b/tools/testing/selftests/bpf/prog_tests/ksyms_module.c
new file mode 100644 (file)
index 0000000..4c232b4
--- /dev/null
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include <test_progs.h>
+#include <bpf/libbpf.h>
+#include <bpf/btf.h>
+#include "test_ksyms_module.skel.h"
+
+static int duration;
+
+void test_ksyms_module(void)
+{
+       struct test_ksyms_module* skel;
+       int err;
+
+       skel = test_ksyms_module__open_and_load();
+       if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
+               return;
+
+       err = test_ksyms_module__attach(skel);
+       if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
+               goto cleanup;
+
+       usleep(1);
+
+       ASSERT_EQ(skel->bss->triggered, true, "triggered");
+       ASSERT_EQ(skel->bss->out_mod_ksym_global, 123, "global_ksym_val");
+
+cleanup:
+       test_ksyms_module__destroy(skel);
+}
index c0fe73a..3bfcf00 100644 (file)
@@ -34,61 +34,6 @@ struct storage {
        struct bpf_spin_lock lock;
 };
 
-/* Copies an rm binary to a temp file. dest is a mkstemp template */
-static int copy_rm(char *dest)
-{
-       int fd_in, fd_out = -1, ret = 0;
-       struct stat stat;
-       char *buf = NULL;
-
-       fd_in = open("/bin/rm", O_RDONLY);
-       if (fd_in < 0)
-               return -errno;
-
-       fd_out = mkstemp(dest);
-       if (fd_out < 0) {
-               ret = -errno;
-               goto out;
-       }
-
-       ret = fstat(fd_in, &stat);
-       if (ret == -1) {
-               ret = -errno;
-               goto out;
-       }
-
-       buf = malloc(stat.st_blksize);
-       if (!buf) {
-               ret = -errno;
-               goto out;
-       }
-
-       while (ret = read(fd_in, buf, stat.st_blksize), ret > 0) {
-               ret = write(fd_out, buf, ret);
-               if (ret < 0) {
-                       ret = -errno;
-                       goto out;
-
-               }
-       }
-       if (ret < 0) {
-               ret = -errno;
-               goto out;
-
-       }
-
-       /* Set executable permission on the copied file */
-       ret = chmod(dest, 0100);
-       if (ret == -1)
-               ret = -errno;
-
-out:
-       free(buf);
-       close(fd_in);
-       close(fd_out);
-       return ret;
-}
-
 /* Fork and exec the provided rm binary and return the exit code of the
  * forked process and its pid.
  */
@@ -168,9 +113,11 @@ static bool check_syscall_operations(int map_fd, int obj_fd)
 
 void test_test_local_storage(void)
 {
-       char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXXXXXX";
+       char tmp_dir_path[64] = "/tmp/local_storageXXXXXX";
        int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
        struct local_storage *skel = NULL;
+       char tmp_exec_path[64];
+       char cmd[256];
 
        skel = local_storage__open_and_load();
        if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
@@ -189,18 +136,24 @@ void test_test_local_storage(void)
                                      task_fd))
                goto close_prog;
 
-       err = copy_rm(tmp_exec_path);
-       if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
+       if (CHECK(!mkdtemp(tmp_dir_path), "mkdtemp",
+                 "unable to create tmpdir: %d\n", errno))
                goto close_prog;
 
+       snprintf(tmp_exec_path, sizeof(tmp_exec_path), "%s/copy_of_rm",
+                tmp_dir_path);
+       snprintf(cmd, sizeof(cmd), "cp /bin/rm %s", tmp_exec_path);
+       if (CHECK_FAIL(system(cmd)))
+               goto close_prog_rmdir;
+
        rm_fd = open(tmp_exec_path, O_RDONLY);
        if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
                  tmp_exec_path, rm_fd, errno))
-               goto close_prog;
+               goto close_prog_rmdir;
 
        if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
                                      rm_fd))
-               goto close_prog;
+               goto close_prog_rmdir;
 
        /* Sets skel->bss->monitored_pid to the pid of the forked child
         * forks a child process that executes tmp_exec_path and tries to
@@ -209,33 +162,36 @@ void test_test_local_storage(void)
         */
        err = run_self_unlink(&skel->bss->monitored_pid, tmp_exec_path);
        if (CHECK(err != EPERM, "run_self_unlink", "err %d want EPERM\n", err))
-               goto close_prog_unlink;
+               goto close_prog_rmdir;
 
        /* Set the process being monitored to be the current process */
        skel->bss->monitored_pid = getpid();
 
-       /* Remove the temporary created executable */
-       err = unlink(tmp_exec_path);
-       if (CHECK(err != 0, "unlink", "unable to unlink %s: %d", tmp_exec_path,
-                 errno))
-               goto close_prog_unlink;
+       /* Move copy_of_rm to a new location so that it triggers the
+        * inode_rename LSM hook with a new_dentry that has a NULL inode ptr.
+        */
+       snprintf(cmd, sizeof(cmd), "mv %s/copy_of_rm %s/check_null_ptr",
+                tmp_dir_path, tmp_dir_path);
+       if (CHECK_FAIL(system(cmd)))
+               goto close_prog_rmdir;
 
        CHECK(skel->data->inode_storage_result != 0, "inode_storage_result",
              "inode_local_storage not set\n");
 
        serv_sk = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
        if (CHECK(serv_sk < 0, "start_server", "failed to start server\n"))
-               goto close_prog;
+               goto close_prog_rmdir;
 
        CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
              "sk_local_storage not set\n");
 
        if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
                                      serv_sk))
-               goto close_prog;
+               goto close_prog_rmdir;
 
-close_prog_unlink:
-       unlink(tmp_exec_path);
+close_prog_rmdir:
+       snprintf(cmd, sizeof(cmd), "rm -rf %s", tmp_dir_path);
+       system(cmd);
 close_prog:
        close(serv_sk);
        close(rm_fd);
index 6ab2922..2755e4f 100644 (file)
@@ -10,7 +10,6 @@
 #include <unistd.h>
 #include <malloc.h>
 #include <stdlib.h>
-#include <unistd.h>
 
 #include "lsm.skel.h"
 
diff --git a/tools/testing/selftests/bpf/progs/atomics.c b/tools/testing/selftests/bpf/progs/atomics.c
new file mode 100644 (file)
index 0000000..c245345
--- /dev/null
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <stdbool.h>
+
+#ifdef ENABLE_ATOMICS_TESTS
+bool skip_tests __attribute((__section__(".data"))) = false;
+#else
+bool skip_tests = true;
+#endif
+
+__u64 add64_value = 1;
+__u64 add64_result = 0;
+__u32 add32_value = 1;
+__u32 add32_result = 0;
+__u64 add_stack_value_copy = 0;
+__u64 add_stack_result = 0;
+__u64 add_noreturn_value = 1;
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(add, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+       __u64 add_stack_value = 1;
+
+       add64_result = __sync_fetch_and_add(&add64_value, 2);
+       add32_result = __sync_fetch_and_add(&add32_value, 2);
+       add_stack_result = __sync_fetch_and_add(&add_stack_value, 2);
+       add_stack_value_copy = add_stack_value;
+       __sync_fetch_and_add(&add_noreturn_value, 2);
+#endif
+
+       return 0;
+}
+
+__s64 sub64_value = 1;
+__s64 sub64_result = 0;
+__s32 sub32_value = 1;
+__s32 sub32_result = 0;
+__s64 sub_stack_value_copy = 0;
+__s64 sub_stack_result = 0;
+__s64 sub_noreturn_value = 1;
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(sub, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+       __u64 sub_stack_value = 1;
+
+       sub64_result = __sync_fetch_and_sub(&sub64_value, 2);
+       sub32_result = __sync_fetch_and_sub(&sub32_value, 2);
+       sub_stack_result = __sync_fetch_and_sub(&sub_stack_value, 2);
+       sub_stack_value_copy = sub_stack_value;
+       __sync_fetch_and_sub(&sub_noreturn_value, 2);
+#endif
+
+       return 0;
+}
+
+__u64 and64_value = (0x110ull << 32);
+__u64 and64_result = 0;
+__u32 and32_value = 0x110;
+__u32 and32_result = 0;
+__u64 and_noreturn_value = (0x110ull << 32);
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(and, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+
+       and64_result = __sync_fetch_and_and(&and64_value, 0x011ull << 32);
+       and32_result = __sync_fetch_and_and(&and32_value, 0x011);
+       __sync_fetch_and_and(&and_noreturn_value, 0x011ull << 32);
+#endif
+
+       return 0;
+}
+
+__u64 or64_value = (0x110ull << 32);
+__u64 or64_result = 0;
+__u32 or32_value = 0x110;
+__u32 or32_result = 0;
+__u64 or_noreturn_value = (0x110ull << 32);
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(or, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+       or64_result = __sync_fetch_and_or(&or64_value, 0x011ull << 32);
+       or32_result = __sync_fetch_and_or(&or32_value, 0x011);
+       __sync_fetch_and_or(&or_noreturn_value, 0x011ull << 32);
+#endif
+
+       return 0;
+}
+
+__u64 xor64_value = (0x110ull << 32);
+__u64 xor64_result = 0;
+__u32 xor32_value = 0x110;
+__u32 xor32_result = 0;
+__u64 xor_noreturn_value = (0x110ull << 32);
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(xor, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+       xor64_result = __sync_fetch_and_xor(&xor64_value, 0x011ull << 32);
+       xor32_result = __sync_fetch_and_xor(&xor32_value, 0x011);
+       __sync_fetch_and_xor(&xor_noreturn_value, 0x011ull << 32);
+#endif
+
+       return 0;
+}
+
+__u64 cmpxchg64_value = 1;
+__u64 cmpxchg64_result_fail = 0;
+__u64 cmpxchg64_result_succeed = 0;
+__u32 cmpxchg32_value = 1;
+__u32 cmpxchg32_result_fail = 0;
+__u32 cmpxchg32_result_succeed = 0;
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(cmpxchg, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+       cmpxchg64_result_fail = __sync_val_compare_and_swap(&cmpxchg64_value, 0, 3);
+       cmpxchg64_result_succeed = __sync_val_compare_and_swap(&cmpxchg64_value, 1, 2);
+
+       cmpxchg32_result_fail = __sync_val_compare_and_swap(&cmpxchg32_value, 0, 3);
+       cmpxchg32_result_succeed = __sync_val_compare_and_swap(&cmpxchg32_value, 1, 2);
+#endif
+
+       return 0;
+}
+
+__u64 xchg64_value = 1;
+__u64 xchg64_result = 0;
+__u32 xchg32_value = 1;
+__u32 xchg32_result = 0;
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(xchg, int a)
+{
+#ifdef ENABLE_ATOMICS_TESTS
+       __u64 val64 = 2;
+       __u32 val32 = 2;
+
+       xchg64_result = __sync_lock_test_and_set(&xchg64_value, val64);
+       xchg32_result = __sync_lock_test_and_set(&xchg32_value, val32);
+#endif
+
+       return 0;
+}
index c6520f2..115a3b0 100644 (file)
@@ -29,18 +29,48 @@ static __inline int bind_to_device(struct bpf_sock_addr *ctx)
        char veth2[IFNAMSIZ] = "test_sock_addr2";
        char missing[IFNAMSIZ] = "nonexistent_dev";
        char del_bind[IFNAMSIZ] = "";
+       int veth1_idx, veth2_idx;
 
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &veth1, sizeof(veth1)))
+                          &veth1, sizeof(veth1)))
+               return 1;
+       if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
+                          &veth1_idx, sizeof(veth1_idx)) || !veth1_idx)
                return 1;
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &veth2, sizeof(veth2)))
+                          &veth2, sizeof(veth2)))
+               return 1;
+       if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
+                          &veth2_idx, sizeof(veth2_idx)) || !veth2_idx ||
+           veth1_idx == veth2_idx)
                return 1;
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &missing, sizeof(missing)) != -ENODEV)
+                          &missing, sizeof(missing)) != -ENODEV)
+               return 1;
+       if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
+                          &veth1_idx, sizeof(veth1_idx)))
                return 1;
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &del_bind, sizeof(del_bind)))
+                          &del_bind, sizeof(del_bind)))
+               return 1;
+
+       return 0;
+}
+
+static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
+{
+       int old, tmp, new = 0xeb9f;
+
+       /* Socket in test case has guarantee that old never equals to new. */
+       if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
+           old == new)
+               return 1;
+       if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
+               return 1;
+       if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
+           tmp != new)
+               return 1;
+       if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
                return 1;
 
        return 0;
@@ -93,6 +123,10 @@ int bind_v4_prog(struct bpf_sock_addr *ctx)
        if (bind_to_device(ctx))
                return 0;
 
+       /* Test for misc socket options. */
+       if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
+               return 0;
+
        ctx->user_ip4 = bpf_htonl(SERV4_REWRITE_IP);
        ctx->user_port = bpf_htons(SERV4_REWRITE_PORT);
 
index 4358e44..4c0d348 100644 (file)
@@ -35,18 +35,48 @@ static __inline int bind_to_device(struct bpf_sock_addr *ctx)
        char veth2[IFNAMSIZ] = "test_sock_addr2";
        char missing[IFNAMSIZ] = "nonexistent_dev";
        char del_bind[IFNAMSIZ] = "";
+       int veth1_idx, veth2_idx;
 
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &veth1, sizeof(veth1)))
+                          &veth1, sizeof(veth1)))
+               return 1;
+       if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
+                          &veth1_idx, sizeof(veth1_idx)) || !veth1_idx)
                return 1;
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &veth2, sizeof(veth2)))
+                          &veth2, sizeof(veth2)))
+               return 1;
+       if (bpf_getsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
+                          &veth2_idx, sizeof(veth2_idx)) || !veth2_idx ||
+           veth1_idx == veth2_idx)
                return 1;
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &missing, sizeof(missing)) != -ENODEV)
+                          &missing, sizeof(missing)) != -ENODEV)
+               return 1;
+       if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTOIFINDEX,
+                          &veth1_idx, sizeof(veth1_idx)))
                return 1;
        if (bpf_setsockopt(ctx, SOL_SOCKET, SO_BINDTODEVICE,
-                               &del_bind, sizeof(del_bind)))
+                          &del_bind, sizeof(del_bind)))
+               return 1;
+
+       return 0;
+}
+
+static __inline int misc_opts(struct bpf_sock_addr *ctx, int opt)
+{
+       int old, tmp, new = 0xeb9f;
+
+       /* Socket in test case has guarantee that old never equals to new. */
+       if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)) ||
+           old == new)
+               return 1;
+       if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &new, sizeof(new)))
+               return 1;
+       if (bpf_getsockopt(ctx, SOL_SOCKET, opt, &tmp, sizeof(tmp)) ||
+           tmp != new)
+               return 1;
+       if (bpf_setsockopt(ctx, SOL_SOCKET, opt, &old, sizeof(old)))
                return 1;
 
        return 0;
@@ -107,6 +137,10 @@ int bind_v6_prog(struct bpf_sock_addr *ctx)
        if (bind_to_device(ctx))
                return 0;
 
+       /* Test for misc socket options. */
+       if (misc_opts(ctx, SO_MARK) || misc_opts(ctx, SO_PRIORITY))
+               return 0;
+
        ctx->user_ip6[0] = bpf_htonl(SERV6_REWRITE_IP_0);
        ctx->user_ip6[1] = bpf_htonl(SERV6_REWRITE_IP_1);
        ctx->user_ip6[2] = bpf_htonl(SERV6_REWRITE_IP_2);
index 5bfef28..418d9c6 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright 2020 Google LLC.
  */
 
-#include "vmlinux.h"
+#include <linux/bpf.h>
 #include <errno.h>
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_tracing.h>
index 3e3de13..95868bc 100644 (file)
@@ -50,7 +50,6 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
        __u32 pid = bpf_get_current_pid_tgid() >> 32;
        struct local_storage *storage;
        bool is_self_unlink;
-       int err;
 
        if (pid != monitored_pid)
                return 0;
@@ -66,8 +65,27 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
                        return -EPERM;
        }
 
-       storage = bpf_inode_storage_get(&inode_storage_map, victim->d_inode, 0,
-                                       BPF_LOCAL_STORAGE_GET_F_CREATE);
+       return 0;
+}
+
+SEC("lsm/inode_rename")
+int BPF_PROG(inode_rename, struct inode *old_dir, struct dentry *old_dentry,
+            struct inode *new_dir, struct dentry *new_dentry,
+            unsigned int flags)
+{
+       __u32 pid = bpf_get_current_pid_tgid() >> 32;
+       struct local_storage *storage;
+       int err;
+
+       /* new_dentry->d_inode can be NULL when the inode is renamed to a file
+        * that did not exist before. The helper should be able to handle this
+        * NULL pointer.
+        */
+       bpf_inode_storage_get(&inode_storage_map, new_dentry->d_inode, 0,
+                             BPF_LOCAL_STORAGE_GET_F_CREATE);
+
+       storage = bpf_inode_storage_get(&inode_storage_map, old_dentry->d_inode,
+                                       0, 0);
        if (!storage)
                return 0;
 
@@ -76,7 +94,7 @@ int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
                inode_storage_result = -1;
        bpf_spin_unlock(&storage->lock);
 
-       err = bpf_inode_storage_delete(&inode_storage_map, victim->d_inode);
+       err = bpf_inode_storage_delete(&inode_storage_map, old_dentry->d_inode);
        if (!err)
                inode_storage_result = err;
 
@@ -133,37 +151,18 @@ int BPF_PROG(socket_post_create, struct socket *sock, int family, int type,
        return 0;
 }
 
-SEC("lsm/file_open")
-int BPF_PROG(file_open, struct file *file)
-{
-       __u32 pid = bpf_get_current_pid_tgid() >> 32;
-       struct local_storage *storage;
-
-       if (pid != monitored_pid)
-               return 0;
-
-       if (!file->f_inode)
-               return 0;
-
-       storage = bpf_inode_storage_get(&inode_storage_map, file->f_inode, 0,
-                                       BPF_LOCAL_STORAGE_GET_F_CREATE);
-       if (!storage)
-               return 0;
-
-       bpf_spin_lock(&storage->lock);
-       storage->value = DUMMY_STORAGE_VALUE;
-       bpf_spin_unlock(&storage->lock);
-       return 0;
-}
-
 /* This uses the local storage to remember the inode of the binary that a
  * process was originally executing.
  */
 SEC("lsm/bprm_committed_creds")
 void BPF_PROG(exec, struct linux_binprm *bprm)
 {
+       __u32 pid = bpf_get_current_pid_tgid() >> 32;
        struct local_storage *storage;
 
+       if (pid != monitored_pid)
+               return;
+
        storage = bpf_task_storage_get(&task_storage_map,
                                       bpf_get_current_task_btf(), 0,
                                       BPF_LOCAL_STORAGE_GET_F_CREATE);
@@ -172,4 +171,13 @@ void BPF_PROG(exec, struct linux_binprm *bprm)
                storage->exec_inode = bprm->file->f_inode;
                bpf_spin_unlock(&storage->lock);
        }
+
+       storage = bpf_inode_storage_get(&inode_storage_map, bprm->file->f_inode,
+                                       0, BPF_LOCAL_STORAGE_GET_F_CREATE);
+       if (!storage)
+               return;
+
+       bpf_spin_lock(&storage->lock);
+       storage->value = DUMMY_STORAGE_VALUE;
+       bpf_spin_unlock(&storage->lock);
 }
diff --git a/tools/testing/selftests/bpf/progs/test_core_read_macros.c b/tools/testing/selftests/bpf/progs/test_core_read_macros.c
new file mode 100644 (file)
index 0000000..fd54caa
--- /dev/null
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 Facebook
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_core_read.h>
+
+char _license[] SEC("license") = "GPL";
+
+/* shuffled layout for relocatable (CO-RE) reads */
+struct callback_head___shuffled {
+       void (*func)(struct callback_head___shuffled *head);
+       struct callback_head___shuffled *next;
+};
+
+struct callback_head k_probe_in = {};
+struct callback_head___shuffled k_core_in = {};
+
+struct callback_head *u_probe_in = 0;
+struct callback_head___shuffled *u_core_in = 0;
+
+long k_probe_out = 0;
+long u_probe_out = 0;
+
+long k_core_out = 0;
+long u_core_out = 0;
+
+int my_pid = 0;
+
+SEC("raw_tracepoint/sys_enter")
+int handler(void *ctx)
+{
+       int pid = bpf_get_current_pid_tgid() >> 32;
+
+       if (my_pid != pid)
+               return 0;
+
+       /* next pointers for kernel address space have to be initialized from
+        * BPF side, user-space mmaped addresses are stil user-space addresses
+        */
+       k_probe_in.next = &k_probe_in;
+       __builtin_preserve_access_index(({k_core_in.next = &k_core_in;}));
+
+       k_probe_out = (long)BPF_PROBE_READ(&k_probe_in, next, next, func);
+       k_core_out = (long)BPF_CORE_READ(&k_core_in, next, next, func);
+       u_probe_out = (long)BPF_PROBE_READ_USER(u_probe_in, next, next, func);
+       u_core_out = (long)BPF_CORE_READ_USER(u_core_in, next, next, func);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/bpf/progs/test_ksyms_module.c b/tools/testing/selftests/bpf/progs/test_ksyms_module.c
new file mode 100644 (file)
index 0000000..d6a0b30
--- /dev/null
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+
+extern const int bpf_testmod_ksym_percpu __ksym;
+
+int out_mod_ksym_global = 0;
+bool triggered = false;
+
+SEC("raw_tp/sys_enter")
+int handler(const void *ctx)
+{
+       int *val;
+       __u32 cpu;
+
+       val = (int *)bpf_this_cpu_ptr(&bpf_testmod_ksym_percpu);
+       out_mod_ksym_global = *val;
+       triggered = true;
+
+       return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
index d946252..0cda61d 100644 (file)
@@ -29,7 +29,7 @@ int main(int argc, char **argv)
                BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
                             BPF_FUNC_get_local_storage),
                BPF_MOV64_IMM(BPF_REG_1, 1),
-               BPF_STX_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
                BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),
                BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0x1),
                BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
index 0ad3e63..51adc42 100644 (file)
@@ -1312,22 +1312,58 @@ static void test_map_stress(void)
 #define DO_UPDATE 1
 #define DO_DELETE 0
 
+#define MAP_RETRIES 20
+
+static int map_update_retriable(int map_fd, const void *key, const void *value,
+                               int flags, int attempts)
+{
+       while (bpf_map_update_elem(map_fd, key, value, flags)) {
+               if (!attempts || (errno != EAGAIN && errno != EBUSY))
+                       return -errno;
+
+               usleep(1);
+               attempts--;
+       }
+
+       return 0;
+}
+
+static int map_delete_retriable(int map_fd, const void *key, int attempts)
+{
+       while (bpf_map_delete_elem(map_fd, key)) {
+               if (!attempts || (errno != EAGAIN && errno != EBUSY))
+                       return -errno;
+
+               usleep(1);
+               attempts--;
+       }
+
+       return 0;
+}
+
 static void test_update_delete(unsigned int fn, void *data)
 {
        int do_update = ((int *)data)[1];
        int fd = ((int *)data)[0];
-       int i, key, value;
+       int i, key, value, err;
 
        for (i = fn; i < MAP_SIZE; i += TASKS) {
                key = value = i;
 
                if (do_update) {
-                       assert(bpf_map_update_elem(fd, &key, &value,
-                                                  BPF_NOEXIST) == 0);
-                       assert(bpf_map_update_elem(fd, &key, &value,
-                                                  BPF_EXIST) == 0);
+                       err = map_update_retriable(fd, &key, &value, BPF_NOEXIST, MAP_RETRIES);
+                       if (err)
+                               printf("error %d %d\n", err, errno);
+                       assert(err == 0);
+                       err = map_update_retriable(fd, &key, &value, BPF_EXIST, MAP_RETRIES);
+                       if (err)
+                               printf("error %d %d\n", err, errno);
+                       assert(err == 0);
                } else {
-                       assert(bpf_map_delete_elem(fd, &key) == 0);
+                       err = map_delete_retriable(fd, &key, MAP_RETRIES);
+                       if (err)
+                               printf("error %d %d\n", err, errno);
+                       assert(err == 0);
                }
        }
 }
index 7d077d4..213628e 100644 (file)
@@ -11,6 +11,7 @@
 #include <signal.h>
 #include <string.h>
 #include <execinfo.h> /* backtrace */
+#include <linux/membarrier.h>
 
 #define EXIT_NO_TEST           2
 #define EXIT_ERR_SETUP_INFRA   3
@@ -370,8 +371,18 @@ static int delete_module(const char *name, int flags)
        return syscall(__NR_delete_module, name, flags);
 }
 
+/*
+ * Trigger synchronize_rcu() in kernel.
+ */
+int kern_sync_rcu(void)
+{
+       return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0);
+}
+
 static void unload_bpf_testmod(void)
 {
+       if (kern_sync_rcu())
+               fprintf(env.stderr, "Failed to trigger kernel-side RCU sync!\n");
        if (delete_module("bpf_testmod", 0)) {
                if (errno == ENOENT) {
                        if (env.verbosity > VERBOSE_NONE)
index 1159532..e49e2fd 100644 (file)
@@ -219,6 +219,7 @@ int bpf_find_map(const char *test, struct bpf_object *obj, const char *name);
 int compare_map_keys(int map1_fd, int map2_fd);
 int compare_stack_ips(int smap_fd, int amap_fd, int stack_trace_len);
 int extract_build_id(char *build_id, size_t size);
+int kern_sync_rcu(void);
 
 #ifdef __x86_64__
 #define SYS_NANOSLEEP_KPROBE_NAME "__x64_sys_nanosleep"
index 777a814..f8569f0 100644 (file)
@@ -50,7 +50,7 @@
 #define MAX_INSNS      BPF_MAXINSNS
 #define MAX_TEST_INSNS 1000000
 #define MAX_FIXUPS     8
-#define MAX_NR_MAPS    20
+#define MAX_NR_MAPS    21
 #define MAX_TEST_RUNS  8
 #define POINTER_VALUE  0xcafe4all
 #define TEST_DATA_LEN  64
@@ -87,6 +87,7 @@ struct bpf_test {
        int fixup_sk_storage_map[MAX_FIXUPS];
        int fixup_map_event_output[MAX_FIXUPS];
        int fixup_map_reuseport_array[MAX_FIXUPS];
+       int fixup_map_ringbuf[MAX_FIXUPS];
        const char *errstr;
        const char *errstr_unpriv;
        uint32_t insn_processed;
@@ -640,6 +641,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
        int *fixup_sk_storage_map = test->fixup_sk_storage_map;
        int *fixup_map_event_output = test->fixup_map_event_output;
        int *fixup_map_reuseport_array = test->fixup_map_reuseport_array;
+       int *fixup_map_ringbuf = test->fixup_map_ringbuf;
 
        if (test->fill_helper) {
                test->fill_insns = calloc(MAX_TEST_INSNS, sizeof(struct bpf_insn));
@@ -817,6 +819,14 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
                        fixup_map_reuseport_array++;
                } while (*fixup_map_reuseport_array);
        }
+       if (*fixup_map_ringbuf) {
+               map_fds[20] = create_map(BPF_MAP_TYPE_RINGBUF, 0,
+                                          0, 4096);
+               do {
+                       prog[*fixup_map_ringbuf].imm = map_fds[20];
+                       fixup_map_ringbuf++;
+               } while (*fixup_map_ringbuf);
+       }
 }
 
 struct libcap {
diff --git a/tools/testing/selftests/bpf/verifier/atomic_and.c b/tools/testing/selftests/bpf/verifier/atomic_and.c
new file mode 100644 (file)
index 0000000..600bc5e
--- /dev/null
@@ -0,0 +1,77 @@
+{
+       "BPF_ATOMIC_AND without fetch",
+       .insns = {
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0x110),
+               /* atomic_and(&val, 0x011); */
+               BPF_MOV64_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_DW, BPF_AND, BPF_REG_10, BPF_REG_1, -8),
+               /* if (val != 0x010) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0x010, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* r1 should not be clobbered, no BPF_FETCH flag */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x011, 1),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC_AND with fetch",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 123),
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0x110),
+               /* old = atomic_fetch_and(&val, 0x011); */
+               BPF_MOV64_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_DW, BPF_AND | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8),
+               /* if (old != 0x110) exit(3); */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x110, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* if (val != 0x010) exit(2); */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x010, 2),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_EXIT_INSN(),
+               /* Check R0 wasn't clobbered (for fear of x86 JIT bug) */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 123, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC_AND with fetch 32bit",
+       .insns = {
+               /* r0 = (s64) -1 */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, 1),
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0x110),
+               /* old = atomic_fetch_and(&val, 0x011); */
+               BPF_MOV32_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_W, BPF_AND | BPF_FETCH, BPF_REG_10, BPF_REG_1, -4),
+               /* if (old != 0x110) exit(3); */
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 0x110, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* if (val != 0x010) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, -4),
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 0x010, 2),
+               BPF_MOV32_IMM(BPF_REG_1, 2),
+               BPF_EXIT_INSN(),
+               /* Check R0 wasn't clobbered (for fear of x86 JIT bug)
+                * It should be -1 so add 1 to get exit code.
+                */
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
diff --git a/tools/testing/selftests/bpf/verifier/atomic_cmpxchg.c b/tools/testing/selftests/bpf/verifier/atomic_cmpxchg.c
new file mode 100644 (file)
index 0000000..2efd8bc
--- /dev/null
@@ -0,0 +1,96 @@
+{
+       "atomic compare-and-exchange smoketest - 64bit",
+       .insns = {
+               /* val = 3; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 3),
+               /* old = atomic_cmpxchg(&val, 2, 4); */
+               BPF_MOV64_IMM(BPF_REG_1, 4),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -8),
+               /* if (old != 3) exit(2); */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 3, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* if (val != 3) exit(3); */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 3, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* old = atomic_cmpxchg(&val, 3, 4); */
+               BPF_MOV64_IMM(BPF_REG_1, 4),
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -8),
+               /* if (old != 3) exit(4); */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 3, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 4),
+               BPF_EXIT_INSN(),
+               /* if (val != 4) exit(5); */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 4, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 5),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "atomic compare-and-exchange smoketest - 32bit",
+       .insns = {
+               /* val = 3; */
+               BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 3),
+               /* old = atomic_cmpxchg(&val, 2, 4); */
+               BPF_MOV32_IMM(BPF_REG_1, 4),
+               BPF_MOV32_IMM(BPF_REG_0, 2),
+               BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -4),
+               /* if (old != 3) exit(2); */
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 3, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* if (val != 3) exit(3); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -4),
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 3, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* old = atomic_cmpxchg(&val, 3, 4); */
+               BPF_MOV32_IMM(BPF_REG_1, 4),
+               BPF_MOV32_IMM(BPF_REG_0, 3),
+               BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -4),
+               /* if (old != 3) exit(4); */
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 3, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 4),
+               BPF_EXIT_INSN(),
+               /* if (val != 4) exit(5); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -4),
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 4, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 5),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV32_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "Can't use cmpxchg on uninit src reg",
+       .insns = {
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 3),
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8),
+               BPF_EXIT_INSN(),
+       },
+       .result = REJECT,
+       .errstr = "!read_ok",
+},
+{
+       "Can't use cmpxchg on uninit memory",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_MOV64_IMM(BPF_REG_2, 4),
+               BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_10, BPF_REG_2, -8),
+               BPF_EXIT_INSN(),
+       },
+       .result = REJECT,
+       .errstr = "invalid read from stack",
+},
diff --git a/tools/testing/selftests/bpf/verifier/atomic_fetch_add.c b/tools/testing/selftests/bpf/verifier/atomic_fetch_add.c
new file mode 100644 (file)
index 0000000..a91de8c
--- /dev/null
@@ -0,0 +1,106 @@
+{
+       "BPF_ATOMIC_FETCH_ADD smoketest - 64bit",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               /* Write 3 to stack */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 3),
+               /* Put a 1 in R1, add it to the 3 on the stack, and load the value back into R1 */
+               BPF_MOV64_IMM(BPF_REG_1, 1),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8),
+               /* Check the value we loaded back was 3 */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* Load value from stack */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -8),
+               /* Check value loaded from stack was 4 */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC_FETCH_ADD smoketest - 32bit",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               /* Write 3 to stack */
+               BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 3),
+               /* Put a 1 in R1, add it to the 3 on the stack, and load the value back into R1 */
+               BPF_MOV32_IMM(BPF_REG_1, 1),
+               BPF_ATOMIC_OP(BPF_W, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_1, -4),
+               /* Check the value we loaded back was 3 */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* Load value from stack */
+               BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, -4),
+               /* Check value loaded from stack was 4 */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "Can't use ATM_FETCH_ADD on frame pointer",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 3),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_10, -8),
+               BPF_EXIT_INSN(),
+       },
+       .result = REJECT,
+       .errstr_unpriv = "R10 leaks addr into mem",
+       .errstr = "frame pointer is read only",
+},
+{
+       "Can't use ATM_FETCH_ADD on uninit src reg",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 3),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_2, -8),
+               BPF_EXIT_INSN(),
+       },
+       .result = REJECT,
+       /* It happens that the address leak check is first, but it would also be
+        * complain about the fact that we're trying to modify R10.
+        */
+       .errstr = "!read_ok",
+},
+{
+       "Can't use ATM_FETCH_ADD on uninit dst reg",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_2, BPF_REG_0, -8),
+               BPF_EXIT_INSN(),
+       },
+       .result = REJECT,
+       /* It happens that the address leak check is first, but it would also be
+        * complain about the fact that we're trying to modify R10.
+        */
+       .errstr = "!read_ok",
+},
+{
+       "Can't use ATM_FETCH_ADD on kernel memory",
+       .insns = {
+               /* This is an fentry prog, context is array of the args of the
+                * kernel function being called. Load first arg into R2.
+                */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, 0),
+               /* First arg of bpf_fentry_test7 is a pointer to a struct.
+                * Attempt to modify that struct. Verifier shouldn't let us
+                * because it's kernel memory.
+                */
+               BPF_MOV64_IMM(BPF_REG_3, 1),
+               BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH, BPF_REG_2, BPF_REG_3, 0),
+               /* Done */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .prog_type = BPF_PROG_TYPE_TRACING,
+       .expected_attach_type = BPF_TRACE_FENTRY,
+       .kfunc = "bpf_fentry_test7",
+       .result = REJECT,
+       .errstr = "only read is supported",
+},
diff --git a/tools/testing/selftests/bpf/verifier/atomic_or.c b/tools/testing/selftests/bpf/verifier/atomic_or.c
new file mode 100644 (file)
index 0000000..ebe6e51
--- /dev/null
@@ -0,0 +1,77 @@
+{
+       "BPF_ATOMIC OR without fetch",
+       .insns = {
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0x110),
+               /* atomic_or(&val, 0x011); */
+               BPF_MOV64_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_DW, BPF_OR, BPF_REG_10, BPF_REG_1, -8),
+               /* if (val != 0x111) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0x111, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* r1 should not be clobbered, no BPF_FETCH flag */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x011, 1),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC OR with fetch",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 123),
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0x110),
+               /* old = atomic_fetch_or(&val, 0x011); */
+               BPF_MOV64_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8),
+               /* if (old != 0x110) exit(3); */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x110, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* if (val != 0x111) exit(2); */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x111, 2),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_EXIT_INSN(),
+               /* Check R0 wasn't clobbered (for fear of x86 JIT bug) */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 123, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC OR with fetch 32bit",
+       .insns = {
+               /* r0 = (s64) -1 */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, 1),
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0x110),
+               /* old = atomic_fetch_or(&val, 0x011); */
+               BPF_MOV32_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_W, BPF_OR | BPF_FETCH, BPF_REG_10, BPF_REG_1, -4),
+               /* if (old != 0x110) exit(3); */
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 0x110, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* if (val != 0x111) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, -4),
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 0x111, 2),
+               BPF_MOV32_IMM(BPF_REG_1, 2),
+               BPF_EXIT_INSN(),
+               /* Check R0 wasn't clobbered (for fear of x86 JIT bug)
+                * It should be -1 so add 1 to get exit code.
+                */
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
diff --git a/tools/testing/selftests/bpf/verifier/atomic_xchg.c b/tools/testing/selftests/bpf/verifier/atomic_xchg.c
new file mode 100644 (file)
index 0000000..33e2d6c
--- /dev/null
@@ -0,0 +1,46 @@
+{
+       "atomic exchange smoketest - 64bit",
+       .insns = {
+               /* val = 3; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 3),
+               /* old = atomic_xchg(&val, 4); */
+               BPF_MOV64_IMM(BPF_REG_1, 4),
+               BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_10, BPF_REG_1, -8),
+               /* if (old != 3) exit(1); */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* if (val != 4) exit(2); */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 4, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "atomic exchange smoketest - 32bit",
+       .insns = {
+               /* val = 3; */
+               BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 3),
+               /* old = atomic_xchg(&val, 4); */
+               BPF_MOV32_IMM(BPF_REG_1, 4),
+               BPF_ATOMIC_OP(BPF_W, BPF_XCHG, BPF_REG_10, BPF_REG_1, -4),
+               /* if (old != 3) exit(1); */
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* if (val != 4) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -4),
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_0, 4, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV32_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
diff --git a/tools/testing/selftests/bpf/verifier/atomic_xor.c b/tools/testing/selftests/bpf/verifier/atomic_xor.c
new file mode 100644 (file)
index 0000000..eb791e5
--- /dev/null
@@ -0,0 +1,77 @@
+{
+       "BPF_ATOMIC XOR without fetch",
+       .insns = {
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0x110),
+               /* atomic_xor(&val, 0x011); */
+               BPF_MOV64_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_DW, BPF_XOR, BPF_REG_10, BPF_REG_1, -8),
+               /* if (val != 0x101) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0x101, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 2),
+               BPF_EXIT_INSN(),
+               /* r1 should not be clobbered, no BPF_FETCH flag */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x011, 1),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC XOR with fetch",
+       .insns = {
+               BPF_MOV64_IMM(BPF_REG_0, 123),
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0x110),
+               /* old = atomic_fetch_xor(&val, 0x011); */
+               BPF_MOV64_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_DW, BPF_XOR | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8),
+               /* if (old != 0x110) exit(3); */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x110, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* if (val != 0x101) exit(2); */
+               BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -8),
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0x101, 2),
+               BPF_MOV64_IMM(BPF_REG_1, 2),
+               BPF_EXIT_INSN(),
+               /* Check R0 wasn't clobbered (fxor fear of x86 JIT bug) */
+               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 123, 2),
+               BPF_MOV64_IMM(BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+               /* exit(0); */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
+{
+       "BPF_ATOMIC XOR with fetch 32bit",
+       .insns = {
+               /* r0 = (s64) -1 */
+               BPF_MOV64_IMM(BPF_REG_0, 0),
+               BPF_ALU64_IMM(BPF_SUB, BPF_REG_0, 1),
+               /* val = 0x110; */
+               BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0x110),
+               /* old = atomic_fetch_xor(&val, 0x011); */
+               BPF_MOV32_IMM(BPF_REG_1, 0x011),
+               BPF_ATOMIC_OP(BPF_W, BPF_XOR | BPF_FETCH, BPF_REG_10, BPF_REG_1, -4),
+               /* if (old != 0x110) exit(3); */
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 0x110, 2),
+               BPF_MOV32_IMM(BPF_REG_0, 3),
+               BPF_EXIT_INSN(),
+               /* if (val != 0x101) exit(2); */
+               BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_10, -4),
+               BPF_JMP32_IMM(BPF_JEQ, BPF_REG_1, 0x101, 2),
+               BPF_MOV32_IMM(BPF_REG_1, 2),
+               BPF_EXIT_INSN(),
+               /* Check R0 wasn't clobbered (fxor fear of x86 JIT bug)
+                * It should be -1 so add 1 to get exit code.
+                */
+               BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),
+               BPF_EXIT_INSN(),
+       },
+       .result = ACCEPT,
+},
index 93d6b16..2308086 100644 (file)
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
 },
 {
-       "context stores via XADD",
+       "context stores via BPF_ATOMIC",
        .insns = {
        BPF_MOV64_IMM(BPF_REG_0, 0),
-       BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_W, BPF_REG_1,
-                    BPF_REG_0, offsetof(struct __sk_buff, mark), 0),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_1, BPF_REG_0, offsetof(struct __sk_buff, mark)),
        BPF_EXIT_INSN(),
        },
-       .errstr = "BPF_XADD stores into R1 ctx is not allowed",
+       .errstr = "BPF_ATOMIC stores into R1 ctx is not allowed",
        .result = REJECT,
        .prog_type = BPF_PROG_TYPE_SCHED_CLS,
 },
index ae72536..ac1e19d 100644 (file)
        BPF_MOV64_REG(BPF_REG_4, BPF_REG_10),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, -8),
        BPF_STX_MEM(BPF_DW, BPF_REG_4, BPF_REG_2, 0),
-       BPF_STX_XADD(BPF_DW, BPF_REG_4, BPF_REG_5, 0),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_4, BPF_REG_5, 0),
        BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_4, 0),
        BPF_STX_MEM(BPF_W, BPF_REG_2, BPF_REG_5, 0),
        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 11),
        BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8),
        BPF_MOV64_IMM(BPF_REG_4, 0xffffffff),
-       BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_4, -8),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_10, BPF_REG_4, -8),
        BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8),
        BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 49),
        BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),
index d6eec17..73f0dea 100644 (file)
@@ -5,7 +5,7 @@
        BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
                    offsetof(struct __sk_buff, cb[0])),
        BPF_LD_MAP_FD(BPF_REG_2, 0),
-       BPF_STX_XADD(BPF_DW, BPF_REG_1, BPF_REG_2,
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_1, BPF_REG_2,
                      offsetof(struct __sk_buff, cb[0])),
        BPF_EXIT_INSN(),
        },
@@ -13,7 +13,7 @@
        .errstr_unpriv = "R2 leaks addr into mem",
        .result_unpriv = REJECT,
        .result = REJECT,
-       .errstr = "BPF_XADD stores into R1 ctx is not allowed",
+       .errstr = "BPF_ATOMIC stores into R1 ctx is not allowed",
 },
 {
        "leak pointer into ctx 2",
        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
                    offsetof(struct __sk_buff, cb[0])),
-       BPF_STX_XADD(BPF_DW, BPF_REG_1, BPF_REG_10,
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_1, BPF_REG_10,
                      offsetof(struct __sk_buff, cb[0])),
        BPF_EXIT_INSN(),
        },
        .errstr_unpriv = "R10 leaks addr into mem",
        .result_unpriv = REJECT,
        .result = REJECT,
-       .errstr = "BPF_XADD stores into R1 ctx is not allowed",
+       .errstr = "BPF_ATOMIC stores into R1 ctx is not allowed",
 },
 {
        "leak pointer into ctx 3",
@@ -56,7 +56,7 @@
        BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3),
        BPF_MOV64_IMM(BPF_REG_3, 0),
        BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_3, 0),
-       BPF_STX_XADD(BPF_DW, BPF_REG_0, BPF_REG_6, 0),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_6, 0),
        BPF_MOV64_IMM(BPF_REG_0, 0),
        BPF_EXIT_INSN(),
        },
index 205292b..b45e8af 100644 (file)
        BPF_MOV64_IMM(BPF_REG_5, 42),
        BPF_MOV64_IMM(BPF_REG_6, 24),
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_5, -8),
-       BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_10, BPF_REG_6, -8),
        BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_10, -8),
        BPF_JMP_IMM(BPF_JGT, BPF_REG_5, 100, 6),
        BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_5),
        BPF_MOV64_IMM(BPF_REG_5, 42),
        BPF_MOV64_IMM(BPF_REG_6, 24),
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_5, -8),
-       BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_10, BPF_REG_6, -8),
        BPF_LDX_MEM(BPF_DW, BPF_REG_5, BPF_REG_10, -8),
        BPF_JMP_IMM(BPF_JGT, BPF_REG_5, 100, 6),
        BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_5),
index 45d43bf..0b94389 100644 (file)
        .result = ACCEPT,
        .result_unpriv = ACCEPT,
 },
+{
+       "check valid spill/fill, ptr to mem",
+       .insns = {
+       /* reserve 8 byte ringbuf memory */
+       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+       BPF_LD_MAP_FD(BPF_REG_1, 0),
+       BPF_MOV64_IMM(BPF_REG_2, 8),
+       BPF_MOV64_IMM(BPF_REG_3, 0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_reserve),
+       /* store a pointer to the reserved memory in R6 */
+       BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+       /* check whether the reservation was successful */
+       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
+       /* spill R6(mem) into the stack */
+       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
+       /* fill it back in R7 */
+       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_10, -8),
+       /* should be able to access *(R7) = 0 */
+       BPF_ST_MEM(BPF_DW, BPF_REG_7, 0, 0),
+       /* submit the reserved ringbuf memory */
+       BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
+       BPF_MOV64_IMM(BPF_REG_2, 0),
+       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_ringbuf_submit),
+       BPF_MOV64_IMM(BPF_REG_0, 0),
+       BPF_EXIT_INSN(),
+       },
+       .fixup_map_ringbuf = { 1 },
+       .result = ACCEPT,
+       .result_unpriv = ACCEPT,
+},
 {
        "check corrupted spill/fill",
        .insns = {
index a3fe0fb..ee29862 100644 (file)
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, -8),
        BPF_STX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, 0),
        BPF_MOV64_IMM(BPF_REG_0, 1),
-       BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_10, BPF_REG_0, -8, 0),
+       BPF_RAW_INSN(BPF_STX | BPF_ATOMIC | BPF_DW,
+                    BPF_REG_10, BPF_REG_0, -8, BPF_ADD),
        BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_6, 0),
        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_hash_recalc),
        BPF_EXIT_INSN(),
index ed1c2ce..4890628 100644 (file)
@@ -82,7 +82,7 @@
        BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
        BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
        BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0),
-       BPF_STX_XADD(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_2, BPF_REG_3, 0),
        BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 0),
        BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 22),
        BPF_EXIT_INSN(),
index c5de2e6..b96ef35 100644 (file)
@@ -3,7 +3,7 @@
        .insns = {
        BPF_MOV64_IMM(BPF_REG_0, 1),
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
-       BPF_STX_XADD(BPF_W, BPF_REG_10, BPF_REG_0, -7),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_10, BPF_REG_0, -7),
        BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
        BPF_EXIT_INSN(),
        },
@@ -22,7 +22,7 @@
        BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
        BPF_EXIT_INSN(),
        BPF_MOV64_IMM(BPF_REG_1, 1),
-       BPF_STX_XADD(BPF_W, BPF_REG_0, BPF_REG_1, 3),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_0, BPF_REG_1, 3),
        BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, 3),
        BPF_EXIT_INSN(),
        },
        BPF_MOV64_IMM(BPF_REG_0, 1),
        BPF_ST_MEM(BPF_W, BPF_REG_2, 0, 0),
        BPF_ST_MEM(BPF_W, BPF_REG_2, 3, 0),
-       BPF_STX_XADD(BPF_W, BPF_REG_2, BPF_REG_0, 1),
-       BPF_STX_XADD(BPF_W, BPF_REG_2, BPF_REG_0, 2),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_2, BPF_REG_0, 1),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_2, BPF_REG_0, 2),
        BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 1),
        BPF_EXIT_INSN(),
        },
        .result = REJECT,
-       .errstr = "BPF_XADD stores into R2 pkt is not allowed",
+       .errstr = "BPF_ATOMIC stores into R2 pkt is not allowed",
        .prog_type = BPF_PROG_TYPE_XDP,
        .flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,
 },
@@ -62,8 +62,8 @@
        BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
        BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
-       BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
-       BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_0, -8),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_10, BPF_REG_0, -8),
+       BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_10, BPF_REG_0, -8),
        BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_0, 3),
        BPF_JMP_REG(BPF_JNE, BPF_REG_7, BPF_REG_10, 2),
        BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
@@ -82,8 +82,8 @@
        BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
        BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
        BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -8),
-       BPF_STX_XADD(BPF_W, BPF_REG_10, BPF_REG_0, -8),
-       BPF_STX_XADD(BPF_W, BPF_REG_10, BPF_REG_0, -8),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_10, BPF_REG_0, -8),
+       BPF_ATOMIC_OP(BPF_W, BPF_ADD, BPF_REG_10, BPF_REG_0, -8),
        BPF_JMP_REG(BPF_JNE, BPF_REG_6, BPF_REG_0, 3),
        BPF_JMP_REG(BPF_JNE, BPF_REG_7, BPF_REG_10, 2),
        BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8),
index 014deda..1e722ee 100644 (file)
@@ -715,7 +715,7 @@ static void worker_pkt_dump(void)
                int payload = *((uint32_t *)(pkt_buf[iter]->payload + PKT_HDR_SIZE));
 
                if (payload == EOT) {
-                       ksft_print_msg("End-of-tranmission frame received\n");
+                       ksft_print_msg("End-of-transmission frame received\n");
                        fprintf(stdout, "---------------------------------------\n");
                        break;
                }
@@ -747,7 +747,7 @@ static void worker_pkt_validate(void)
                        }
 
                        if (payloadseqnum == EOT) {
-                               ksft_print_msg("End-of-tranmission frame received: PASS\n");
+                               ksft_print_msg("End-of-transmission frame received: PASS\n");
                                sigvar = 1;
                                break;
                        }
diff --git a/tools/testing/selftests/drivers/net/mlxsw/port_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/port_scale.sh
new file mode 100644 (file)
index 0000000..f813ffe
--- /dev/null
@@ -0,0 +1,64 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Test for physical ports resource. The test splits each splittable port
+# to its width and checks that eventually the number of physical ports equals
+# the maximum number of physical ports.
+
+PORT_NUM_NETIFS=0
+
+port_setup_prepare()
+{
+       :
+}
+
+port_cleanup()
+{
+       pre_cleanup
+
+       for port in "${unsplit[@]}"; do
+               devlink port unsplit $port
+               check_err $? "Did not unsplit $netdev"
+       done
+}
+
+split_all_ports()
+{
+       local should_fail=$1; shift
+       local -a unsplit
+
+       # Loop over the splittable netdevs and create tuples of netdev along
+       # with its width. For example:
+       # '$netdev1 $count1 $netdev2 $count2...', when:
+       # $netdev1-2 are splittable netdevs in the device, and
+       # $count1-2 are the netdevs width respectively.
+       while read netdev count <<<$(
+               devlink -j port show |
+               jq -r '.[][] | select(.splittable==true) | "\(.netdev) \(.lanes)"'
+               )
+               [[ ! -z $netdev ]]
+       do
+               devlink port split $netdev count $count
+               check_err $? "Did not split $netdev into $count"
+               unsplit+=( "${netdev}s0" )
+       done
+}
+
+port_test()
+{
+       local max_ports=$1; shift
+       local should_fail=$1; shift
+
+       split_all_ports $should_fail
+
+       occ=$(devlink -j resource show $DEVLINK_DEV \
+             | jq '.[][][] | select(.name=="physical_ports") |.["occ"]')
+
+       [[ $occ -eq $max_ports ]]
+       if [[ $should_fail -eq 0 ]]; then
+               check_err $? "Mismatch ports number: Expected $max_ports, got $occ."
+       else
+               check_err_fail $should_fail $? "Reached more ports than expected"
+       fi
+
+}
index 4d900bc..5c77002 100755 (executable)
@@ -230,7 +230,7 @@ switch_create()
        __mlnx_qos -i $swp4 --pfc=0,1,0,0,0,0,0,0 >/dev/null
        # PG0 will get autoconfigured to Xoff, give PG1 arbitrarily 100K, which
        # is (-2*MTU) about 80K of delay provision.
-       __mlnx_qos -i $swp3 --buffer_size=0,$_100KB,0,0,0,0,0,0 >/dev/null
+       __mlnx_qos -i $swp4 --buffer_size=0,$_100KB,0,0,0,0,0,0 >/dev/null
 
        # bridges
        # -------
diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/port_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum-2/port_scale.sh
new file mode 100644 (file)
index 0000000..0b71dfb
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+source ../port_scale.sh
+
+port_get_target()
+{
+       local should_fail=$1
+       local target
+
+       target=$(devlink_resource_size_get physical_ports)
+
+       if ((! should_fail)); then
+               echo $target
+       else
+               echo $((target + 1))
+       fi
+}
index d7cf33a..4a1c932 100755 (executable)
@@ -28,7 +28,7 @@ cleanup()
 
 trap cleanup EXIT
 
-ALL_TESTS="router tc_flower mirror_gre tc_police"
+ALL_TESTS="router tc_flower mirror_gre tc_police port"
 for current_test in ${TESTS:-$ALL_TESTS}; do
        source ${current_test}_scale.sh
 
diff --git a/tools/testing/selftests/drivers/net/mlxsw/spectrum/port_scale.sh b/tools/testing/selftests/drivers/net/mlxsw/spectrum/port_scale.sh
new file mode 100644 (file)
index 0000000..0b71dfb
--- /dev/null
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+source ../port_scale.sh
+
+port_get_target()
+{
+       local should_fail=$1
+       local target
+
+       target=$(devlink_resource_size_get physical_ports)
+
+       if ((! should_fail)); then
+               echo $target
+       else
+               echo $((target + 1))
+       fi
+}
index 43f6624..087a884 100755 (executable)
@@ -22,7 +22,7 @@ cleanup()
 devlink_sp_read_kvd_defaults
 trap cleanup EXIT
 
-ALL_TESTS="router tc_flower mirror_gre tc_police"
+ALL_TESTS="router tc_flower mirror_gre tc_police port"
 for current_test in ${TESTS:-$ALL_TESTS}; do
        source ${current_test}_scale.sh
 
index c7ca4fa..fe41c6a 100644 (file)
@@ -33,7 +33,7 @@ ifeq ($(ARCH),s390)
        UNAME_M := s390x
 endif
 
-LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c
+LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
 LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
 LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c
 LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
index 3d96a7b..cdad1ec 100644 (file)
@@ -7,23 +7,20 @@
  * Copyright (C) 2019, Google, Inc.
  */
 
-#define _GNU_SOURCE /* for program_invocation_name */
+#define _GNU_SOURCE /* for pipe2 */
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-#include <asm/unistd.h>
 #include <time.h>
 #include <poll.h>
 #include <pthread.h>
-#include <linux/bitmap.h>
-#include <linux/bitops.h>
 #include <linux/userfaultfd.h>
+#include <sys/syscall.h>
 
-#include "perf_test_util.h"
-#include "processor.h"
+#include "kvm_util.h"
 #include "test_util.h"
+#include "perf_test_util.h"
+#include "guest_modes.h"
 
 #ifdef __NR_userfaultfd
 
 #define PER_VCPU_DEBUG(...) _no_printf(__VA_ARGS__)
 #endif
 
+static int nr_vcpus = 1;
+static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
 static char *guest_data_prototype;
 
 static void *vcpu_worker(void *data)
 {
        int ret;
-       struct vcpu_args *vcpu_args = (struct vcpu_args *)data;
+       struct perf_test_vcpu_args *vcpu_args = (struct perf_test_vcpu_args *)data;
        int vcpu_id = vcpu_args->vcpu_id;
        struct kvm_vm *vm = perf_test_args.vm;
        struct kvm_run *run;
@@ -248,9 +247,14 @@ static int setup_demand_paging(struct kvm_vm *vm,
        return 0;
 }
 
-static void run_test(enum vm_guest_mode mode, bool use_uffd,
-                    useconds_t uffd_delay)
+struct test_params {
+       bool use_uffd;
+       useconds_t uffd_delay;
+};
+
+static void run_test(enum vm_guest_mode mode, void *arg)
 {
+       struct test_params *p = arg;
        pthread_t *vcpu_threads;
        pthread_t *uffd_handler_threads = NULL;
        struct uffd_handler_args *uffd_args = NULL;
@@ -261,7 +265,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
        int vcpu_id;
        int r;
 
-       vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size);
+       vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size);
 
        perf_test_args.wr_fract = 1;
 
@@ -273,9 +277,9 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
        vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
        TEST_ASSERT(vcpu_threads, "Memory allocation failed");
 
-       add_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
+       perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
 
-       if (use_uffd) {
+       if (p->use_uffd) {
                uffd_handler_threads =
                        malloc(nr_vcpus * sizeof(*uffd_handler_threads));
                TEST_ASSERT(uffd_handler_threads, "Memory allocation failed");
@@ -308,7 +312,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
                        r = setup_demand_paging(vm,
                                                &uffd_handler_threads[vcpu_id],
                                                pipefds[vcpu_id * 2],
-                                               uffd_delay, &uffd_args[vcpu_id],
+                                               p->uffd_delay, &uffd_args[vcpu_id],
                                                vcpu_hva, guest_percpu_mem_size);
                        if (r < 0)
                                exit(-r);
@@ -339,7 +343,7 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
 
        pr_info("All vCPU threads joined\n");
 
-       if (use_uffd) {
+       if (p->use_uffd) {
                char c;
 
                /* Tell the user fault fd handler threads to quit */
@@ -357,43 +361,23 @@ static void run_test(enum vm_guest_mode mode, bool use_uffd,
                perf_test_args.vcpu_args[0].pages * nr_vcpus /
                ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
 
-       ucall_uninit(vm);
-       kvm_vm_free(vm);
+       perf_test_destroy_vm(vm);
 
        free(guest_data_prototype);
        free(vcpu_threads);
-       if (use_uffd) {
+       if (p->use_uffd) {
                free(uffd_handler_threads);
                free(uffd_args);
                free(pipefds);
        }
 }
 
-struct guest_mode {
-       bool supported;
-       bool enabled;
-};
-static struct guest_mode guest_modes[NUM_VM_MODES];
-
-#define guest_mode_init(mode, supported, enabled) ({ \
-       guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
-})
-
 static void help(char *name)
 {
-       int i;
-
        puts("");
        printf("usage: %s [-h] [-m mode] [-u] [-d uffd_delay_usec]\n"
               "          [-b memory] [-v vcpus]\n", name);
-       printf(" -m: specify the guest mode ID to test\n"
-              "     (default: test all supported modes)\n"
-              "     This option may be used multiple times.\n"
-              "     Guest mode IDs:\n");
-       for (i = 0; i < NUM_VM_MODES; ++i) {
-               printf("         %d:    %s%s\n", i, vm_guest_mode_string(i),
-                      guest_modes[i].supported ? " (supported)" : "");
-       }
+       guest_modes_help();
        printf(" -u: use User Fault FD to handle vCPU page\n"
               "     faults.\n");
        printf(" -d: add a delay in usec to the User Fault\n"
@@ -410,53 +394,22 @@ static void help(char *name)
 int main(int argc, char *argv[])
 {
        int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
-       bool mode_selected = false;
-       unsigned int mode;
-       int opt, i;
-       bool use_uffd = false;
-       useconds_t uffd_delay = 0;
-
-#ifdef __x86_64__
-       guest_mode_init(VM_MODE_PXXV48_4K, true, true);
-#endif
-#ifdef __aarch64__
-       guest_mode_init(VM_MODE_P40V48_4K, true, true);
-       guest_mode_init(VM_MODE_P40V48_64K, true, true);
-       {
-               unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
-
-               if (limit >= 52)
-                       guest_mode_init(VM_MODE_P52V48_64K, true, true);
-               if (limit >= 48) {
-                       guest_mode_init(VM_MODE_P48V48_4K, true, true);
-                       guest_mode_init(VM_MODE_P48V48_64K, true, true);
-               }
-       }
-#endif
-#ifdef __s390x__
-       guest_mode_init(VM_MODE_P40V48_4K, true, true);
-#endif
+       struct test_params p = {};
+       int opt;
+
+       guest_modes_append_default();
 
        while ((opt = getopt(argc, argv, "hm:ud:b:v:")) != -1) {
                switch (opt) {
                case 'm':
-                       if (!mode_selected) {
-                               for (i = 0; i < NUM_VM_MODES; ++i)
-                                       guest_modes[i].enabled = false;
-                               mode_selected = true;
-                       }
-                       mode = strtoul(optarg, NULL, 10);
-                       TEST_ASSERT(mode < NUM_VM_MODES,
-                                   "Guest mode ID %d too big", mode);
-                       guest_modes[mode].enabled = true;
+                       guest_modes_cmdline(optarg);
                        break;
                case 'u':
-                       use_uffd = true;
+                       p.use_uffd = true;
                        break;
                case 'd':
-                       uffd_delay = strtoul(optarg, NULL, 0);
-                       TEST_ASSERT(uffd_delay >= 0,
-                                   "A negative UFFD delay is not supported.");
+                       p.uffd_delay = strtoul(optarg, NULL, 0);
+                       TEST_ASSERT(p.uffd_delay >= 0, "A negative UFFD delay is not supported.");
                        break;
                case 'b':
                        guest_percpu_mem_size = parse_size(optarg);
@@ -473,14 +426,7 @@ int main(int argc, char *argv[])
                }
        }
 
-       for (i = 0; i < NUM_VM_MODES; ++i) {
-               if (!guest_modes[i].enabled)
-                       continue;
-               TEST_ASSERT(guest_modes[i].supported,
-                           "Guest mode ID %d (%s) not supported.",
-                           i, vm_guest_mode_string(i));
-               run_test(i, use_uffd, uffd_delay);
-       }
+       for_each_guest_mode(run_test, &p);
 
        return 0;
 }
index 9c6a7be..2283a0e 100644 (file)
@@ -8,29 +8,28 @@
  * Copyright (C) 2020, Google, Inc.
  */
 
-#define _GNU_SOURCE /* for program_invocation_name */
-
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <time.h>
 #include <pthread.h>
 #include <linux/bitmap.h>
-#include <linux/bitops.h>
 
 #include "kvm_util.h"
-#include "perf_test_util.h"
-#include "processor.h"
 #include "test_util.h"
+#include "perf_test_util.h"
+#include "guest_modes.h"
 
 /* How many host loops to run by default (one KVM_GET_DIRTY_LOG for each loop)*/
 #define TEST_HOST_LOOP_N               2UL
 
+static int nr_vcpus = 1;
+static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
+
 /* Host variables */
 static u64 dirty_log_manual_caps;
 static bool host_quit;
 static uint64_t iteration;
-static uint64_t vcpu_last_completed_iteration[MAX_VCPUS];
+static uint64_t vcpu_last_completed_iteration[KVM_MAX_VCPUS];
 
 static void *vcpu_worker(void *data)
 {
@@ -42,7 +41,7 @@ static void *vcpu_worker(void *data)
        struct timespec ts_diff;
        struct timespec total = (struct timespec){0};
        struct timespec avg;
-       struct vcpu_args *vcpu_args = (struct vcpu_args *)data;
+       struct perf_test_vcpu_args *vcpu_args = (struct perf_test_vcpu_args *)data;
        int vcpu_id = vcpu_args->vcpu_id;
 
        vcpu_args_set(vm, vcpu_id, 1, vcpu_id);
@@ -89,9 +88,15 @@ static void *vcpu_worker(void *data)
        return NULL;
 }
 
-static void run_test(enum vm_guest_mode mode, unsigned long iterations,
-                    uint64_t phys_offset, int wr_fract)
+struct test_params {
+       unsigned long iterations;
+       uint64_t phys_offset;
+       int wr_fract;
+};
+
+static void run_test(enum vm_guest_mode mode, void *arg)
 {
+       struct test_params *p = arg;
        pthread_t *vcpu_threads;
        struct kvm_vm *vm;
        unsigned long *bmap;
@@ -106,9 +111,9 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
        struct kvm_enable_cap cap = {};
        struct timespec clear_dirty_log_total = (struct timespec){0};
 
-       vm = create_vm(mode, nr_vcpus, guest_percpu_mem_size);
+       vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size);
 
-       perf_test_args.wr_fract = wr_fract;
+       perf_test_args.wr_fract = p->wr_fract;
 
        guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm_get_page_shift(vm);
        guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
@@ -124,7 +129,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
        vcpu_threads = malloc(nr_vcpus * sizeof(*vcpu_threads));
        TEST_ASSERT(vcpu_threads, "Memory allocation failed");
 
-       add_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
+       perf_test_setup_vcpus(vm, nr_vcpus, guest_percpu_mem_size);
 
        sync_global_to_guest(vm, perf_test_args);
 
@@ -150,13 +155,13 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
 
        /* Enable dirty logging */
        clock_gettime(CLOCK_MONOTONIC, &start);
-       vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX,
+       vm_mem_region_set_flags(vm, PERF_TEST_MEM_SLOT_INDEX,
                                KVM_MEM_LOG_DIRTY_PAGES);
        ts_diff = timespec_diff_now(start);
        pr_info("Enabling dirty logging time: %ld.%.9lds\n\n",
                ts_diff.tv_sec, ts_diff.tv_nsec);
 
-       while (iteration < iterations) {
+       while (iteration < p->iterations) {
                /*
                 * Incrementing the iteration number will start the vCPUs
                 * dirtying memory again.
@@ -177,7 +182,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
                        iteration, ts_diff.tv_sec, ts_diff.tv_nsec);
 
                clock_gettime(CLOCK_MONOTONIC, &start);
-               kvm_vm_get_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap);
+               kvm_vm_get_dirty_log(vm, PERF_TEST_MEM_SLOT_INDEX, bmap);
 
                ts_diff = timespec_diff_now(start);
                get_dirty_log_total = timespec_add(get_dirty_log_total,
@@ -187,7 +192,7 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
 
                if (dirty_log_manual_caps) {
                        clock_gettime(CLOCK_MONOTONIC, &start);
-                       kvm_vm_clear_dirty_log(vm, TEST_MEM_SLOT_INDEX, bmap, 0,
+                       kvm_vm_clear_dirty_log(vm, PERF_TEST_MEM_SLOT_INDEX, bmap, 0,
                                               host_num_pages);
 
                        ts_diff = timespec_diff_now(start);
@@ -205,43 +210,30 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
 
        /* Disable dirty logging */
        clock_gettime(CLOCK_MONOTONIC, &start);
-       vm_mem_region_set_flags(vm, TEST_MEM_SLOT_INDEX, 0);
+       vm_mem_region_set_flags(vm, PERF_TEST_MEM_SLOT_INDEX, 0);
        ts_diff = timespec_diff_now(start);
        pr_info("Disabling dirty logging time: %ld.%.9lds\n",
                ts_diff.tv_sec, ts_diff.tv_nsec);
 
-       avg = timespec_div(get_dirty_log_total, iterations);
+       avg = timespec_div(get_dirty_log_total, p->iterations);
        pr_info("Get dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
-               iterations, get_dirty_log_total.tv_sec,
+               p->iterations, get_dirty_log_total.tv_sec,
                get_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
 
        if (dirty_log_manual_caps) {
-               avg = timespec_div(clear_dirty_log_total, iterations);
+               avg = timespec_div(clear_dirty_log_total, p->iterations);
                pr_info("Clear dirty log over %lu iterations took %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n",
-                       iterations, clear_dirty_log_total.tv_sec,
+                       p->iterations, clear_dirty_log_total.tv_sec,
                        clear_dirty_log_total.tv_nsec, avg.tv_sec, avg.tv_nsec);
        }
 
        free(bmap);
        free(vcpu_threads);
-       ucall_uninit(vm);
-       kvm_vm_free(vm);
+       perf_test_destroy_vm(vm);
 }
 
-struct guest_mode {
-       bool supported;
-       bool enabled;
-};
-static struct guest_mode guest_modes[NUM_VM_MODES];
-
-#define guest_mode_init(mode, supported, enabled) ({ \
-       guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
-})
-
 static void help(char *name)
 {
-       int i;
-
        puts("");
        printf("usage: %s [-h] [-i iterations] [-p offset] "
               "[-m mode] [-b vcpu bytes] [-v vcpus]\n", name);
@@ -250,14 +242,7 @@ static void help(char *name)
               TEST_HOST_LOOP_N);
        printf(" -p: specify guest physical test memory offset\n"
               "     Warning: a low offset can conflict with the loaded test code.\n");
-       printf(" -m: specify the guest mode ID to test "
-              "(default: test all supported modes)\n"
-              "     This option may be used multiple times.\n"
-              "     Guest mode IDs:\n");
-       for (i = 0; i < NUM_VM_MODES; ++i) {
-               printf("         %d:    %s%s\n", i, vm_guest_mode_string(i),
-                      guest_modes[i].supported ? " (supported)" : "");
-       }
+       guest_modes_help();
        printf(" -b: specify the size of the memory region which should be\n"
               "     dirtied by each vCPU. e.g. 10M or 3G.\n"
               "     (default: 1G)\n");
@@ -272,74 +257,43 @@ static void help(char *name)
 
 int main(int argc, char *argv[])
 {
-       unsigned long iterations = TEST_HOST_LOOP_N;
-       bool mode_selected = false;
-       uint64_t phys_offset = 0;
-       unsigned int mode;
-       int opt, i;
-       int wr_fract = 1;
+       int max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
+       struct test_params p = {
+               .iterations = TEST_HOST_LOOP_N,
+               .wr_fract = 1,
+       };
+       int opt;
 
        dirty_log_manual_caps =
                kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2);
        dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE |
                                  KVM_DIRTY_LOG_INITIALLY_SET);
 
-#ifdef __x86_64__
-       guest_mode_init(VM_MODE_PXXV48_4K, true, true);
-#endif
-#ifdef __aarch64__
-       guest_mode_init(VM_MODE_P40V48_4K, true, true);
-       guest_mode_init(VM_MODE_P40V48_64K, true, true);
-
-       {
-               unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
-
-               if (limit >= 52)
-                       guest_mode_init(VM_MODE_P52V48_64K, true, true);
-               if (limit >= 48) {
-                       guest_mode_init(VM_MODE_P48V48_4K, true, true);
-                       guest_mode_init(VM_MODE_P48V48_64K, true, true);
-               }
-       }
-#endif
-#ifdef __s390x__
-       guest_mode_init(VM_MODE_P40V48_4K, true, true);
-#endif
+       guest_modes_append_default();
 
        while ((opt = getopt(argc, argv, "hi:p:m:b:f:v:")) != -1) {
                switch (opt) {
                case 'i':
-                       iterations = strtol(optarg, NULL, 10);
+                       p.iterations = strtol(optarg, NULL, 10);
                        break;
                case 'p':
-                       phys_offset = strtoull(optarg, NULL, 0);
+                       p.phys_offset = strtoull(optarg, NULL, 0);
                        break;
                case 'm':
-                       if (!mode_selected) {
-                               for (i = 0; i < NUM_VM_MODES; ++i)
-                                       guest_modes[i].enabled = false;
-                               mode_selected = true;
-                       }
-                       mode = strtoul(optarg, NULL, 10);
-                       TEST_ASSERT(mode < NUM_VM_MODES,
-                                   "Guest mode ID %d too big", mode);
-                       guest_modes[mode].enabled = true;
+                       guest_modes_cmdline(optarg);
                        break;
                case 'b':
                        guest_percpu_mem_size = parse_size(optarg);
                        break;
                case 'f':
-                       wr_fract = atoi(optarg);
-                       TEST_ASSERT(wr_fract >= 1,
+                       p.wr_fract = atoi(optarg);
+                       TEST_ASSERT(p.wr_fract >= 1,
                                    "Write fraction cannot be less than one");
                        break;
                case 'v':
                        nr_vcpus = atoi(optarg);
-                       TEST_ASSERT(nr_vcpus > 0,
-                                   "Must have a positive number of vCPUs");
-                       TEST_ASSERT(nr_vcpus <= MAX_VCPUS,
-                                   "This test does not currently support\n"
-                                   "more than %d vCPUs.", MAX_VCPUS);
+                       TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
+                                   "Invalid number of vcpus, must be between 1 and %d", max_vcpus);
                        break;
                case 'h':
                default:
@@ -348,18 +302,11 @@ int main(int argc, char *argv[])
                }
        }
 
-       TEST_ASSERT(iterations >= 2, "The test should have at least two iterations");
+       TEST_ASSERT(p.iterations >= 2, "The test should have at least two iterations");
 
-       pr_info("Test iterations: %"PRIu64"\n", iterations);
+       pr_info("Test iterations: %"PRIu64"\n", p.iterations);
 
-       for (i = 0; i < NUM_VM_MODES; ++i) {
-               if (!guest_modes[i].enabled)
-                       continue;
-               TEST_ASSERT(guest_modes[i].supported,
-                           "Guest mode ID %d (%s) not supported.",
-                           i, vm_guest_mode_string(i));
-               run_test(i, iterations, phys_offset, wr_fract);
-       }
+       for_each_guest_mode(run_test, &p);
 
        return 0;
 }
index 471baec..bb2752d 100644 (file)
@@ -9,8 +9,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
-#include <time.h>
 #include <pthread.h>
 #include <semaphore.h>
 #include <sys/types.h>
@@ -20,8 +18,9 @@
 #include <linux/bitops.h>
 #include <asm/barrier.h>
 
-#include "test_util.h"
 #include "kvm_util.h"
+#include "test_util.h"
+#include "guest_modes.h"
 #include "processor.h"
 
 #define VCPU_ID                                1
@@ -673,9 +672,15 @@ static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid,
 #define DIRTY_MEM_BITS 30 /* 1G */
 #define PAGE_SHIFT_4K  12
 
-static void run_test(enum vm_guest_mode mode, unsigned long iterations,
-                    unsigned long interval, uint64_t phys_offset)
+struct test_params {
+       unsigned long iterations;
+       unsigned long interval;
+       uint64_t phys_offset;
+};
+
+static void run_test(enum vm_guest_mode mode, void *arg)
 {
+       struct test_params *p = arg;
        struct kvm_vm *vm;
        unsigned long *bmap;
 
@@ -709,12 +714,12 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
        host_page_size = getpagesize();
        host_num_pages = vm_num_host_pages(mode, guest_num_pages);
 
-       if (!phys_offset) {
+       if (!p->phys_offset) {
                guest_test_phys_mem = (vm_get_max_gfn(vm) -
                                       guest_num_pages) * guest_page_size;
                guest_test_phys_mem &= ~(host_page_size - 1);
        } else {
-               guest_test_phys_mem = phys_offset;
+               guest_test_phys_mem = p->phys_offset;
        }
 
 #ifdef __s390x__
@@ -758,9 +763,9 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
 
        pthread_create(&vcpu_thread, NULL, vcpu_worker, vm);
 
-       while (iteration < iterations) {
+       while (iteration < p->iterations) {
                /* Give the vcpu thread some time to dirty some pages */
-               usleep(interval * 1000);
+               usleep(p->interval * 1000);
                log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX,
                                             bmap, host_num_pages);
                vm_dirty_log_verify(mode, bmap);
@@ -783,20 +788,8 @@ static void run_test(enum vm_guest_mode mode, unsigned long iterations,
        kvm_vm_free(vm);
 }
 
-struct guest_mode {
-       bool supported;
-       bool enabled;
-};
-static struct guest_mode guest_modes[NUM_VM_MODES];
-
-#define guest_mode_init(mode, supported, enabled) ({ \
-       guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
-})
-
 static void help(char *name)
 {
-       int i;
-
        puts("");
        printf("usage: %s [-h] [-i iterations] [-I interval] "
               "[-p offset] [-m mode]\n", name);
@@ -813,51 +806,23 @@ static void help(char *name)
        printf(" -M: specify the host logging mode "
               "(default: run all log modes).  Supported modes: \n\t");
        log_modes_dump();
-       printf(" -m: specify the guest mode ID to test "
-              "(default: test all supported modes)\n"
-              "     This option may be used multiple times.\n"
-              "     Guest mode IDs:\n");
-       for (i = 0; i < NUM_VM_MODES; ++i) {
-               printf("         %d:    %s%s\n", i, vm_guest_mode_string(i),
-                      guest_modes[i].supported ? " (supported)" : "");
-       }
+       guest_modes_help();
        puts("");
        exit(0);
 }
 
 int main(int argc, char *argv[])
 {
-       unsigned long iterations = TEST_HOST_LOOP_N;
-       unsigned long interval = TEST_HOST_LOOP_INTERVAL;
-       bool mode_selected = false;
-       uint64_t phys_offset = 0;
-       unsigned int mode;
-       int opt, i, j;
+       struct test_params p = {
+               .iterations = TEST_HOST_LOOP_N,
+               .interval = TEST_HOST_LOOP_INTERVAL,
+       };
+       int opt, i;
 
        sem_init(&dirty_ring_vcpu_stop, 0, 0);
        sem_init(&dirty_ring_vcpu_cont, 0, 0);
 
-#ifdef __x86_64__
-       guest_mode_init(VM_MODE_PXXV48_4K, true, true);
-#endif
-#ifdef __aarch64__
-       guest_mode_init(VM_MODE_P40V48_4K, true, true);
-       guest_mode_init(VM_MODE_P40V48_64K, true, true);
-
-       {
-               unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
-
-               if (limit >= 52)
-                       guest_mode_init(VM_MODE_P52V48_64K, true, true);
-               if (limit >= 48) {
-                       guest_mode_init(VM_MODE_P48V48_4K, true, true);
-                       guest_mode_init(VM_MODE_P48V48_64K, true, true);
-               }
-       }
-#endif
-#ifdef __s390x__
-       guest_mode_init(VM_MODE_P40V48_4K, true, true);
-#endif
+       guest_modes_append_default();
 
        while ((opt = getopt(argc, argv, "c:hi:I:p:m:M:")) != -1) {
                switch (opt) {
@@ -865,24 +830,16 @@ int main(int argc, char *argv[])
                        test_dirty_ring_count = strtol(optarg, NULL, 10);
                        break;
                case 'i':
-                       iterations = strtol(optarg, NULL, 10);
+                       p.iterations = strtol(optarg, NULL, 10);
                        break;
                case 'I':
-                       interval = strtol(optarg, NULL, 10);
+                       p.interval = strtol(optarg, NULL, 10);
                        break;
                case 'p':
-                       phys_offset = strtoull(optarg, NULL, 0);
+                       p.phys_offset = strtoull(optarg, NULL, 0);
                        break;
                case 'm':
-                       if (!mode_selected) {
-                               for (i = 0; i < NUM_VM_MODES; ++i)
-                                       guest_modes[i].enabled = false;
-                               mode_selected = true;
-                       }
-                       mode = strtoul(optarg, NULL, 10);
-                       TEST_ASSERT(mode < NUM_VM_MODES,
-                                   "Guest mode ID %d too big", mode);
-                       guest_modes[mode].enabled = true;
+                       guest_modes_cmdline(optarg);
                        break;
                case 'M':
                        if (!strcmp(optarg, "all")) {
@@ -911,32 +868,24 @@ int main(int argc, char *argv[])
                }
        }
 
-       TEST_ASSERT(iterations > 2, "Iterations must be greater than two");
-       TEST_ASSERT(interval > 0, "Interval must be greater than zero");
+       TEST_ASSERT(p.iterations > 2, "Iterations must be greater than two");
+       TEST_ASSERT(p.interval > 0, "Interval must be greater than zero");
 
        pr_info("Test iterations: %"PRIu64", interval: %"PRIu64" (ms)\n",
-               iterations, interval);
+               p.iterations, p.interval);
 
        srandom(time(0));
 
-       for (i = 0; i < NUM_VM_MODES; ++i) {
-               if (!guest_modes[i].enabled)
-                       continue;
-               TEST_ASSERT(guest_modes[i].supported,
-                           "Guest mode ID %d (%s) not supported.",
-                           i, vm_guest_mode_string(i));
-               if (host_log_mode_option == LOG_MODE_ALL) {
-                       /* Run each log mode */
-                       for (j = 0; j < LOG_MODE_NUM; j++) {
-                               pr_info("Testing Log Mode '%s'\n",
-                                       log_modes[j].name);
-                               host_log_mode = j;
-                               run_test(i, iterations, interval, phys_offset);
-                       }
-               } else {
-                       host_log_mode = host_log_mode_option;
-                       run_test(i, iterations, interval, phys_offset);
+       if (host_log_mode_option == LOG_MODE_ALL) {
+               /* Run each log mode */
+               for (i = 0; i < LOG_MODE_NUM; i++) {
+                       pr_info("Testing Log Mode '%s'\n", log_modes[i].name);
+                       host_log_mode = i;
+                       for_each_guest_mode(run_test, &p);
                }
+       } else {
+               host_log_mode = host_log_mode_option;
+               for_each_guest_mode(run_test, &p);
        }
 
        return 0;
diff --git a/tools/testing/selftests/kvm/include/guest_modes.h b/tools/testing/selftests/kvm/include/guest_modes.h
new file mode 100644 (file)
index 0000000..b691df3
--- /dev/null
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020, Red Hat, Inc.
+ */
+#include "kvm_util.h"
+
+struct guest_mode {
+       bool supported;
+       bool enabled;
+};
+
+extern struct guest_mode guest_modes[NUM_VM_MODES];
+
+#define guest_mode_append(mode, supported, enabled) ({ \
+       guest_modes[mode] = (struct guest_mode){ supported, enabled }; \
+})
+
+void guest_modes_append_default(void);
+void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg);
+void guest_modes_help(void);
+void guest_modes_cmdline(const char *arg);
index dfa9d36..5cbb861 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "sparsebit.h"
 
+#define KVM_MAX_VCPUS 512
 
 /*
  * Callers of kvm_util only have an incomplete/opaque description of the
@@ -70,6 +71,14 @@ enum vm_guest_mode {
 #define vm_guest_mode_string(m) vm_guest_mode_string[m]
 extern const char * const vm_guest_mode_string[];
 
+struct vm_guest_mode_params {
+       unsigned int pa_bits;
+       unsigned int va_bits;
+       unsigned int page_size;
+       unsigned int page_shift;
+};
+extern const struct vm_guest_mode_params vm_guest_mode_params[];
+
 enum vm_mem_backing_src_type {
        VM_MEM_SRC_ANONYMOUS,
        VM_MEM_SRC_ANONYMOUS_THP,
index 239421e..b118882 100644 (file)
@@ -9,38 +9,15 @@
 #define SELFTEST_KVM_PERF_TEST_UTIL_H
 
 #include "kvm_util.h"
-#include "processor.h"
-
-#define MAX_VCPUS 512
-
-#define PAGE_SHIFT_4K  12
-#define PTES_PER_4K_PT 512
-
-#define TEST_MEM_SLOT_INDEX            1
 
 /* Default guest test virtual memory offset */
 #define DEFAULT_GUEST_TEST_MEM         0xc0000000
 
 #define DEFAULT_PER_VCPU_MEM_SIZE      (1 << 30) /* 1G */
 
-/*
- * Guest physical memory offset of the testing memory slot.
- * This will be set to the topmost valid physical address minus
- * the test memory size.
- */
-static uint64_t guest_test_phys_mem;
-
-/*
- * Guest virtual memory offset of the testing memory slot.
- * Must not conflict with identity mapped test code.
- */
-static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
-static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE;
-
-/* Number of VCPUs for the test */
-static int nr_vcpus = 1;
+#define PERF_TEST_MEM_SLOT_INDEX       1
 
-struct vcpu_args {
+struct perf_test_vcpu_args {
        uint64_t gva;
        uint64_t pages;
 
@@ -54,141 +31,21 @@ struct perf_test_args {
        uint64_t guest_page_size;
        int wr_fract;
 
-       struct vcpu_args vcpu_args[MAX_VCPUS];
+       struct perf_test_vcpu_args vcpu_args[KVM_MAX_VCPUS];
 };
 
-static struct perf_test_args perf_test_args;
+extern struct perf_test_args perf_test_args;
 
 /*
- * Continuously write to the first 8 bytes of each page in the
- * specified region.
+ * Guest physical memory offset of the testing memory slot.
+ * This will be set to the topmost valid physical address minus
+ * the test memory size.
  */
-static void guest_code(uint32_t vcpu_id)
-{
-       struct vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
-       uint64_t gva;
-       uint64_t pages;
-       int i;
-
-       /* Make sure vCPU args data structure is not corrupt. */
-       GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id);
-
-       gva = vcpu_args->gva;
-       pages = vcpu_args->pages;
-
-       while (true) {
-               for (i = 0; i < pages; i++) {
-                       uint64_t addr = gva + (i * perf_test_args.guest_page_size);
-
-                       if (i % perf_test_args.wr_fract == 0)
-                               *(uint64_t *)addr = 0x0123456789ABCDEF;
-                       else
-                               READ_ONCE(*(uint64_t *)addr);
-               }
-
-               GUEST_SYNC(1);
-       }
-}
-
-static struct kvm_vm *create_vm(enum vm_guest_mode mode, int vcpus,
-                               uint64_t vcpu_memory_bytes)
-{
-       struct kvm_vm *vm;
-       uint64_t pages = DEFAULT_GUEST_PHY_PAGES;
-       uint64_t guest_num_pages;
-
-       /* Account for a few pages per-vCPU for stacks */
-       pages += DEFAULT_STACK_PGS * vcpus;
-
-       /*
-        * Reserve twice the ammount of memory needed to map the test region and
-        * the page table / stacks region, at 4k, for page tables. Do the
-        * calculation with 4K page size: the smallest of all archs. (e.g., 64K
-        * page size guest will need even less memory for page tables).
-        */
-       pages += (2 * pages) / PTES_PER_4K_PT;
-       pages += ((2 * vcpus * vcpu_memory_bytes) >> PAGE_SHIFT_4K) /
-                PTES_PER_4K_PT;
-       pages = vm_adjust_num_guest_pages(mode, pages);
-
-       pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
-
-       vm = vm_create(mode, pages, O_RDWR);
-       kvm_vm_elf_load(vm, program_invocation_name, 0, 0);
-#ifdef __x86_64__
-       vm_create_irqchip(vm);
-#endif
-
-       perf_test_args.vm = vm;
-       perf_test_args.guest_page_size = vm_get_page_size(vm);
-       perf_test_args.host_page_size = getpagesize();
-
-       TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0,
-                   "Guest memory size is not guest page size aligned.");
-
-       guest_num_pages = (vcpus * vcpu_memory_bytes) /
-                         perf_test_args.guest_page_size;
-       guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages);
-
-       /*
-        * If there should be more memory in the guest test region than there
-        * can be pages in the guest, it will definitely cause problems.
-        */
-       TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
-                   "Requested more guest memory than address space allows.\n"
-                   "    guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
-                   guest_num_pages, vm_get_max_gfn(vm), vcpus,
-                   vcpu_memory_bytes);
-
-       TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0,
-                   "Guest memory size is not host page size aligned.");
-
-       guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) *
-                             perf_test_args.guest_page_size;
-       guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1);
-
-#ifdef __s390x__
-       /* Align to 1M (segment size) */
-       guest_test_phys_mem &= ~((1 << 20) - 1);
-#endif
-
-       pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
-
-       /* Add an extra memory slot for testing */
-       vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
-                                   guest_test_phys_mem,
-                                   TEST_MEM_SLOT_INDEX,
-                                   guest_num_pages, 0);
-
-       /* Do mapping for the demand paging memory slot */
-       virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0);
-
-       ucall_init(vm, NULL);
-
-       return vm;
-}
-
-static void add_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes)
-{
-       vm_paddr_t vcpu_gpa;
-       struct vcpu_args *vcpu_args;
-       int vcpu_id;
-
-       for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
-               vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
-
-               vm_vcpu_add_default(vm, vcpu_id, guest_code);
-
-               vcpu_args->vcpu_id = vcpu_id;
-               vcpu_args->gva = guest_test_virt_mem +
-                                (vcpu_id * vcpu_memory_bytes);
-               vcpu_args->pages = vcpu_memory_bytes /
-                                  perf_test_args.guest_page_size;
+extern uint64_t guest_test_phys_mem;
 
-               vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes);
-               pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
-                        vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes);
-       }
-}
+struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
+                               uint64_t vcpu_memory_bytes);
+void perf_test_destroy_vm(struct kvm_vm *vm);
+void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes);
 
 #endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */
diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c
new file mode 100644 (file)
index 0000000..25bff30
--- /dev/null
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Red Hat, Inc.
+ */
+#include "guest_modes.h"
+
+struct guest_mode guest_modes[NUM_VM_MODES];
+
+void guest_modes_append_default(void)
+{
+       guest_mode_append(VM_MODE_DEFAULT, true, true);
+
+#ifdef __aarch64__
+       guest_mode_append(VM_MODE_P40V48_64K, true, true);
+       {
+               unsigned int limit = kvm_check_cap(KVM_CAP_ARM_VM_IPA_SIZE);
+               if (limit >= 52)
+                       guest_mode_append(VM_MODE_P52V48_64K, true, true);
+               if (limit >= 48) {
+                       guest_mode_append(VM_MODE_P48V48_4K, true, true);
+                       guest_mode_append(VM_MODE_P48V48_64K, true, true);
+               }
+       }
+#endif
+}
+
+void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
+{
+       int i;
+
+       for (i = 0; i < NUM_VM_MODES; ++i) {
+               if (!guest_modes[i].enabled)
+                       continue;
+               TEST_ASSERT(guest_modes[i].supported,
+                           "Guest mode ID %d (%s) not supported.",
+                           i, vm_guest_mode_string(i));
+               func(i, arg);
+       }
+}
+
+void guest_modes_help(void)
+{
+       int i;
+
+       printf(" -m: specify the guest mode ID to test\n"
+              "     (default: test all supported modes)\n"
+              "     This option may be used multiple times.\n"
+              "     Guest mode IDs:\n");
+       for (i = 0; i < NUM_VM_MODES; ++i) {
+               printf("         %d:    %s%s\n", i, vm_guest_mode_string(i),
+                      guest_modes[i].supported ? " (supported)" : "");
+       }
+}
+
+void guest_modes_cmdline(const char *arg)
+{
+       static bool mode_selected;
+       unsigned int mode;
+       int i;
+
+       if (!mode_selected) {
+               for (i = 0; i < NUM_VM_MODES; ++i)
+                       guest_modes[i].enabled = false;
+               mode_selected = true;
+       }
+
+       mode = strtoul(optarg, NULL, 10);
+       TEST_ASSERT(mode < NUM_VM_MODES, "Guest mode ID %d too big", mode);
+       guest_modes[mode].enabled = true;
+}
index 88ef706..fa5a90e 100644 (file)
@@ -153,14 +153,7 @@ const char * const vm_guest_mode_string[] = {
 _Static_assert(sizeof(vm_guest_mode_string)/sizeof(char *) == NUM_VM_MODES,
               "Missing new mode strings?");
 
-struct vm_guest_mode_params {
-       unsigned int pa_bits;
-       unsigned int va_bits;
-       unsigned int page_size;
-       unsigned int page_shift;
-};
-
-static const struct vm_guest_mode_params vm_guest_mode_params[] = {
+const struct vm_guest_mode_params vm_guest_mode_params[] = {
        { 52, 48,  0x1000, 12 },
        { 52, 48, 0x10000, 16 },
        { 48, 48,  0x1000, 12 },
diff --git a/tools/testing/selftests/kvm/lib/perf_test_util.c b/tools/testing/selftests/kvm/lib/perf_test_util.c
new file mode 100644 (file)
index 0000000..9be1944
--- /dev/null
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Google LLC.
+ */
+
+#include "kvm_util.h"
+#include "perf_test_util.h"
+#include "processor.h"
+
+struct perf_test_args perf_test_args;
+
+uint64_t guest_test_phys_mem;
+
+/*
+ * Guest virtual memory offset of the testing memory slot.
+ * Must not conflict with identity mapped test code.
+ */
+static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM;
+
+/*
+ * Continuously write to the first 8 bytes of each page in the
+ * specified region.
+ */
+static void guest_code(uint32_t vcpu_id)
+{
+       struct perf_test_vcpu_args *vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
+       uint64_t gva;
+       uint64_t pages;
+       int i;
+
+       /* Make sure vCPU args data structure is not corrupt. */
+       GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id);
+
+       gva = vcpu_args->gva;
+       pages = vcpu_args->pages;
+
+       while (true) {
+               for (i = 0; i < pages; i++) {
+                       uint64_t addr = gva + (i * perf_test_args.guest_page_size);
+
+                       if (i % perf_test_args.wr_fract == 0)
+                               *(uint64_t *)addr = 0x0123456789ABCDEF;
+                       else
+                               READ_ONCE(*(uint64_t *)addr);
+               }
+
+               GUEST_SYNC(1);
+       }
+}
+
+struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus,
+                                  uint64_t vcpu_memory_bytes)
+{
+       struct kvm_vm *vm;
+       uint64_t guest_num_pages;
+
+       pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode));
+
+       perf_test_args.host_page_size = getpagesize();
+       perf_test_args.guest_page_size = vm_guest_mode_params[mode].page_size;
+
+       guest_num_pages = vm_adjust_num_guest_pages(mode,
+                               (vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size);
+
+       TEST_ASSERT(vcpu_memory_bytes % perf_test_args.host_page_size == 0,
+                   "Guest memory size is not host page size aligned.");
+       TEST_ASSERT(vcpu_memory_bytes % perf_test_args.guest_page_size == 0,
+                   "Guest memory size is not guest page size aligned.");
+
+       vm = vm_create_with_vcpus(mode, vcpus,
+                                 (vcpus * vcpu_memory_bytes) / perf_test_args.guest_page_size,
+                                 0, guest_code, NULL);
+
+       perf_test_args.vm = vm;
+
+       /*
+        * If there should be more memory in the guest test region than there
+        * can be pages in the guest, it will definitely cause problems.
+        */
+       TEST_ASSERT(guest_num_pages < vm_get_max_gfn(vm),
+                   "Requested more guest memory than address space allows.\n"
+                   "    guest pages: %lx max gfn: %x vcpus: %d wss: %lx]\n",
+                   guest_num_pages, vm_get_max_gfn(vm), vcpus,
+                   vcpu_memory_bytes);
+
+       guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) *
+                             perf_test_args.guest_page_size;
+       guest_test_phys_mem &= ~(perf_test_args.host_page_size - 1);
+#ifdef __s390x__
+       /* Align to 1M (segment size) */
+       guest_test_phys_mem &= ~((1 << 20) - 1);
+#endif
+       pr_info("guest physical test memory offset: 0x%lx\n", guest_test_phys_mem);
+
+       /* Add an extra memory slot for testing */
+       vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+                                   guest_test_phys_mem,
+                                   PERF_TEST_MEM_SLOT_INDEX,
+                                   guest_num_pages, 0);
+
+       /* Do mapping for the demand paging memory slot */
+       virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, guest_num_pages, 0);
+
+       ucall_init(vm, NULL);
+
+       return vm;
+}
+
+void perf_test_destroy_vm(struct kvm_vm *vm)
+{
+       ucall_uninit(vm);
+       kvm_vm_free(vm);
+}
+
+void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, uint64_t vcpu_memory_bytes)
+{
+       vm_paddr_t vcpu_gpa;
+       struct perf_test_vcpu_args *vcpu_args;
+       int vcpu_id;
+
+       for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) {
+               vcpu_args = &perf_test_args.vcpu_args[vcpu_id];
+
+               vcpu_args->vcpu_id = vcpu_id;
+               vcpu_args->gva = guest_test_virt_mem +
+                                (vcpu_id * vcpu_memory_bytes);
+               vcpu_args->pages = vcpu_memory_bytes /
+                                  perf_test_args.guest_page_size;
+
+               vcpu_gpa = guest_test_phys_mem + (vcpu_id * vcpu_memory_bytes);
+               pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n",
+                        vcpu_id, vcpu_gpa, vcpu_gpa + vcpu_memory_bytes);
+       }
+}
diff --git a/tools/testing/selftests/nci/Makefile b/tools/testing/selftests/nci/Makefile
new file mode 100644 (file)
index 0000000..47669a1
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+CFLAGS += -Wl,-no-as-needed -Wall
+LDFLAGS += -lpthread
+
+TEST_GEN_PROGS := nci_dev
+include ../lib.mk
diff --git a/tools/testing/selftests/nci/config b/tools/testing/selftests/nci/config
new file mode 100644 (file)
index 0000000..b084e78
--- /dev/null
@@ -0,0 +1,3 @@
+CONFIG_NFC=y
+CONFIG_NFC_NCI=y
+CONFIG_NFC_VIRTUAL_NCI=y
diff --git a/tools/testing/selftests/nci/nci_dev.c b/tools/testing/selftests/nci/nci_dev.c
new file mode 100644 (file)
index 0000000..57b505c
--- /dev/null
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Samsung Electrnoics
+ * Bongsu Jeon <bongsu.jeon@samsung.com>
+ *
+ * Test code for nci
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <linux/genetlink.h>
+#include <sys/socket.h>
+#include <linux/nfc.h>
+
+#include "../kselftest_harness.h"
+
+#define GENLMSG_DATA(glh)      ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
+#define GENLMSG_PAYLOAD(glh)   (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
+#define NLA_DATA(na)           ((void *)((char *)(na) + NLA_HDRLEN))
+#define NLA_PAYLOAD(len)       ((len) - NLA_HDRLEN)
+
+#define MAX_MSG_SIZE   1024
+
+#define IOCTL_GET_NCIDEV_IDX   0
+#define VIRTUAL_NFC_PROTOCOLS  (NFC_PROTO_JEWEL_MASK | \
+                                NFC_PROTO_MIFARE_MASK | \
+                                NFC_PROTO_FELICA_MASK | \
+                                NFC_PROTO_ISO14443_MASK | \
+                                NFC_PROTO_ISO14443_B_MASK | \
+                                NFC_PROTO_ISO15693_MASK)
+
+const __u8 nci_reset_cmd[] = {0x20, 0x00, 0x01, 0x01};
+const __u8 nci_init_cmd[] = {0x20, 0x01, 0x00};
+const __u8 nci_rf_discovery_cmd[] = {0x21, 0x03, 0x09, 0x04, 0x00, 0x01,
+                                     0x01, 0x01, 0x02, 0x01, 0x06, 0x01};
+const __u8 nci_init_cmd_v2[] = {0x20, 0x01, 0x02, 0x00, 0x00};
+const __u8 nci_rf_disc_map_cmd[] = {0x21, 0x00, 0x07, 0x02, 0x04, 0x03,
+                                    0x02, 0x05, 0x03, 0x03};
+const __u8 nci_rf_deact_cmd[] = {0x21, 0x06, 0x01, 0x00};
+const __u8 nci_reset_rsp[] = {0x40, 0x00, 0x03, 0x00, 0x10, 0x01};
+const __u8 nci_reset_rsp_v2[] = {0x40, 0x00, 0x01, 0x00};
+const __u8 nci_reset_ntf[] = {0x60, 0x00, 0x09, 0x02, 0x01, 0x20, 0x0e,
+                              0x04, 0x61, 0x00, 0x04, 0x02};
+const __u8 nci_init_rsp[] = {0x40, 0x01, 0x14, 0x00, 0x02, 0x0e, 0x02,
+                             0x00, 0x03, 0x01, 0x02, 0x03, 0x02, 0xc8,
+                             0x00, 0xff, 0x10, 0x00, 0x0e, 0x12, 0x00,
+                             0x00, 0x04};
+const __u8 nci_init_rsp_v2[] = {0x40, 0x01, 0x1c, 0x00, 0x1a, 0x7e, 0x06,
+                                0x00, 0x02, 0x92, 0x04, 0xff, 0xff, 0x01,
+                                0x00, 0x40, 0x06, 0x00, 0x00, 0x01, 0x01,
+                                0x00, 0x02, 0x00, 0x03, 0x01, 0x01, 0x06,
+                                0x00, 0x80, 0x00};
+const __u8 nci_rf_disc_map_rsp[] = {0x41, 0x00, 0x01, 0x00};
+const __u8 nci_rf_disc_rsp[] = {0x41, 0x03, 0x01, 0x00};
+const __u8 nci_rf_deact_rsp[] = {0x41, 0x06, 0x01, 0x00};
+
+struct msgtemplate {
+       struct nlmsghdr n;
+       struct genlmsghdr g;
+       char buf[MAX_MSG_SIZE];
+};
+
+static int create_nl_socket(void)
+{
+       int fd;
+       struct sockaddr_nl local;
+
+       fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+       if (fd < 0)
+               return -1;
+
+       memset(&local, 0, sizeof(local));
+       local.nl_family = AF_NETLINK;
+
+       if (bind(fd, (struct sockaddr *)&local, sizeof(local)) < 0)
+               goto error;
+
+       return fd;
+error:
+       close(fd);
+       return -1;
+}
+
+static int send_cmd_mt_nla(int sd, __u16 nlmsg_type, __u32 nlmsg_pid,
+                          __u8 genl_cmd, int nla_num, __u16 nla_type[],
+                          void *nla_data[], int nla_len[])
+{
+       struct sockaddr_nl nladdr;
+       struct msgtemplate msg;
+       struct nlattr *na;
+       int cnt, prv_len;
+       int r, buflen;
+       char *buf;
+
+       msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+       msg.n.nlmsg_type = nlmsg_type;
+       msg.n.nlmsg_flags = NLM_F_REQUEST;
+       msg.n.nlmsg_seq = 0;
+       msg.n.nlmsg_pid = nlmsg_pid;
+       msg.g.cmd = genl_cmd;
+       msg.g.version = 0x1;
+
+       prv_len = 0;
+       for (cnt = 0; cnt < nla_num; cnt++) {
+               na = (struct nlattr *)(GENLMSG_DATA(&msg) + prv_len);
+               na->nla_type = nla_type[cnt];
+               na->nla_len = nla_len[cnt] + NLA_HDRLEN;
+
+               if (nla_len > 0)
+                       memcpy(NLA_DATA(na), nla_data[cnt], nla_len[cnt]);
+
+               msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
+               prv_len = na->nla_len;
+       }
+
+       buf = (char *)&msg;
+       buflen = msg.n.nlmsg_len;
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+
+       while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *)&nladdr,
+                          sizeof(nladdr))) < buflen) {
+               if (r > 0) {
+                       buf += r;
+                       buflen -= r;
+               } else if (errno != EAGAIN) {
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static int send_get_nfc_family(int sd, __u32 pid)
+{
+       __u16 nla_get_family_type = CTRL_ATTR_FAMILY_NAME;
+       void *nla_get_family_data;
+       int nla_get_family_len;
+       char family_name[100];
+
+       nla_get_family_len = strlen(NFC_GENL_NAME) + 1;
+       strcpy(family_name, NFC_GENL_NAME);
+       nla_get_family_data = family_name;
+
+       return send_cmd_mt_nla(sd, GENL_ID_CTRL, pid, CTRL_CMD_GETFAMILY,
+                               1, &nla_get_family_type,
+                               &nla_get_family_data, &nla_get_family_len);
+}
+
+static int get_family_id(int sd, __u32 pid)
+{
+       struct {
+               struct nlmsghdr n;
+               struct genlmsghdr g;
+               char buf[512];
+       } ans;
+       struct nlattr *na;
+       int rep_len;
+       __u16 id;
+       int rc;
+
+       rc = send_get_nfc_family(sd, pid);
+
+       if (rc < 0)
+               return 0;
+
+       rep_len = recv(sd, &ans, sizeof(ans), 0);
+
+       if (ans.n.nlmsg_type == NLMSG_ERROR || rep_len < 0 ||
+           !NLMSG_OK(&ans.n, rep_len))
+               return 0;
+
+       na = (struct nlattr *)GENLMSG_DATA(&ans);
+       na = (struct nlattr *)((char *)na + NLA_ALIGN(na->nla_len));
+       if (na->nla_type == CTRL_ATTR_FAMILY_ID)
+               id = *(__u16 *)NLA_DATA(na);
+
+       return id;
+}
+
+static int send_cmd_with_idx(int sd, __u16 nlmsg_type, __u32 nlmsg_pid,
+                            __u8 genl_cmd, int dev_id)
+{
+       __u16 nla_type = NFC_ATTR_DEVICE_INDEX;
+       void *nla_data = &dev_id;
+       int nla_len = 4;
+
+       return send_cmd_mt_nla(sd, nlmsg_type, nlmsg_pid, genl_cmd, 1,
+                               &nla_type, &nla_data, &nla_len);
+}
+
+static int get_nci_devid(int sd, __u16 fid, __u32 pid, int dev_id, struct msgtemplate *msg)
+{
+       int rc, rep_len;
+
+       rc = send_cmd_with_idx(sd, fid, pid, NFC_CMD_GET_DEVICE, dev_id);
+       if (rc < 0) {
+               rc = -1;
+               goto error;
+       }
+
+       rep_len = recv(sd, msg, sizeof(*msg), 0);
+       if (rep_len < 0) {
+               rc = -2;
+               goto error;
+       }
+
+       if (msg->n.nlmsg_type == NLMSG_ERROR ||
+           !NLMSG_OK(&msg->n, rep_len)) {
+               rc = -3;
+               goto error;
+       }
+
+       return 0;
+error:
+       return rc;
+}
+
+static __u8 get_dev_enable_state(struct msgtemplate *msg)
+{
+       struct nlattr *na;
+       int rep_len;
+       int len;
+
+       rep_len = GENLMSG_PAYLOAD(&msg->n);
+       na = (struct nlattr *)GENLMSG_DATA(msg);
+       len = 0;
+
+       while (len < rep_len) {
+               len += NLA_ALIGN(na->nla_len);
+               if (na->nla_type == NFC_ATTR_DEVICE_POWERED)
+                       return *(char *)NLA_DATA(na);
+               na = (struct nlattr *)(GENLMSG_DATA(msg) + len);
+       }
+
+       return rep_len;
+}
+
+FIXTURE(NCI) {
+       int virtual_nci_fd;
+       bool open_state;
+       int dev_idex;
+       bool isNCI2;
+       int proto;
+       __u32 pid;
+       __u16 fid;
+       int sd;
+};
+
+FIXTURE_VARIANT(NCI) {
+       bool isNCI2;
+};
+
+FIXTURE_VARIANT_ADD(NCI, NCI1_0) {
+       .isNCI2 = false,
+};
+
+FIXTURE_VARIANT_ADD(NCI, NCI2_0) {
+       .isNCI2 = true,
+};
+
+static void *virtual_dev_open(void *data)
+{
+       char buf[258];
+       int dev_fd;
+       int len;
+
+       dev_fd = *(int *)data;
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_reset_cmd))
+               goto error;
+       if (memcmp(nci_reset_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_reset_rsp, sizeof(nci_reset_rsp));
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_init_cmd))
+               goto error;
+       if (memcmp(nci_init_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_init_rsp, sizeof(nci_init_rsp));
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_rf_disc_map_cmd))
+               goto error;
+       if (memcmp(nci_rf_disc_map_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_rf_disc_map_rsp, sizeof(nci_rf_disc_map_rsp));
+
+       return (void *)0;
+error:
+       return (void *)-1;
+}
+
+static void *virtual_dev_open_v2(void *data)
+{
+       char buf[258];
+       int dev_fd;
+       int len;
+
+       dev_fd = *(int *)data;
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_reset_cmd))
+               goto error;
+       if (memcmp(nci_reset_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_reset_rsp_v2, sizeof(nci_reset_rsp_v2));
+       write(dev_fd, nci_reset_ntf, sizeof(nci_reset_ntf));
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_init_cmd_v2))
+               goto error;
+       if (memcmp(nci_init_cmd_v2, buf, len))
+               goto error;
+       write(dev_fd, nci_init_rsp_v2, sizeof(nci_init_rsp_v2));
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_rf_disc_map_cmd))
+               goto error;
+       if (memcmp(nci_rf_disc_map_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_rf_disc_map_rsp, sizeof(nci_rf_disc_map_rsp));
+
+       return (void *)0;
+error:
+       return (void *)-1;
+}
+
+FIXTURE_SETUP(NCI)
+{
+       struct msgtemplate msg;
+       pthread_t thread_t;
+       int status;
+       int rc;
+
+       self->open_state = false;
+       self->proto = VIRTUAL_NFC_PROTOCOLS;
+       self->isNCI2 = variant->isNCI2;
+
+       self->sd = create_nl_socket();
+       ASSERT_NE(self->sd, -1);
+
+       self->pid = getpid();
+       self->fid = get_family_id(self->sd, self->pid);
+       ASSERT_NE(self->fid, -1);
+
+       self->virtual_nci_fd = open("/dev/virtual_nci", O_RDWR);
+       ASSERT_GT(self->virtual_nci_fd, -1);
+
+       rc = ioctl(self->virtual_nci_fd, IOCTL_GET_NCIDEV_IDX, &self->dev_idex);
+       ASSERT_EQ(rc, 0);
+
+       rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex, &msg);
+       ASSERT_EQ(rc, 0);
+       EXPECT_EQ(get_dev_enable_state(&msg), 0);
+
+       if (self->isNCI2)
+               rc = pthread_create(&thread_t, NULL, virtual_dev_open_v2,
+                                   (void *)&self->virtual_nci_fd);
+       else
+               rc = pthread_create(&thread_t, NULL, virtual_dev_open,
+                                   (void *)&self->virtual_nci_fd);
+       ASSERT_GT(rc, -1);
+
+       rc = send_cmd_with_idx(self->sd, self->fid, self->pid,
+                              NFC_CMD_DEV_UP, self->dev_idex);
+       EXPECT_EQ(rc, 0);
+
+       pthread_join(thread_t, (void **)&status);
+       ASSERT_EQ(status, 0);
+       self->open_state = true;
+}
+
+static void *virtual_deinit(void *data)
+{
+       char buf[258];
+       int dev_fd;
+       int len;
+
+       dev_fd = *(int *)data;
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_reset_cmd))
+               goto error;
+       if (memcmp(nci_reset_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_reset_rsp, sizeof(nci_reset_rsp));
+
+       return (void *)0;
+error:
+       return (void *)-1;
+}
+
+static void *virtual_deinit_v2(void *data)
+{
+       char buf[258];
+       int dev_fd;
+       int len;
+
+       dev_fd = *(int *)data;
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_reset_cmd))
+               goto error;
+       if (memcmp(nci_reset_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_reset_rsp_v2, sizeof(nci_reset_rsp_v2));
+       write(dev_fd, nci_reset_ntf, sizeof(nci_reset_ntf));
+
+       return (void *)0;
+error:
+       return (void *)-1;
+}
+
+FIXTURE_TEARDOWN(NCI)
+{
+       pthread_t thread_t;
+       int status;
+       int rc;
+
+       if (self->open_state) {
+               if (self->isNCI2)
+                       rc = pthread_create(&thread_t, NULL,
+                                           virtual_deinit_v2,
+                                           (void *)&self->virtual_nci_fd);
+               else
+                       rc = pthread_create(&thread_t, NULL, virtual_deinit,
+                                           (void *)&self->virtual_nci_fd);
+
+               ASSERT_GT(rc, -1);
+               rc = send_cmd_with_idx(self->sd, self->fid, self->pid,
+                                      NFC_CMD_DEV_DOWN, self->dev_idex);
+               EXPECT_EQ(rc, 0);
+
+               pthread_join(thread_t, (void **)&status);
+               ASSERT_EQ(status, 0);
+       }
+
+       close(self->sd);
+       close(self->virtual_nci_fd);
+       self->open_state = false;
+}
+
+TEST_F(NCI, init)
+{
+       struct msgtemplate msg;
+       int rc;
+
+       rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex,
+                          &msg);
+       ASSERT_EQ(rc, 0);
+       EXPECT_EQ(get_dev_enable_state(&msg), 1);
+}
+
+static void *virtual_poll_start(void *data)
+{
+       char buf[258];
+       int dev_fd;
+       int len;
+
+       dev_fd = *(int *)data;
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_rf_discovery_cmd))
+               goto error;
+       if (memcmp(nci_rf_discovery_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_rf_disc_rsp, sizeof(nci_rf_disc_rsp))
+               ;
+
+       return (void *)0;
+error:
+       return (void *)-1;
+}
+
+static void *virtual_poll_stop(void *data)
+{
+       char buf[258];
+       int dev_fd;
+       int len;
+
+       dev_fd = *(int *)data;
+
+       while ((len = read(dev_fd, buf, 258)) == 0)
+               ;
+       if (len <= 0)
+               goto error;
+       if (len != sizeof(nci_rf_deact_cmd))
+               goto error;
+       if (memcmp(nci_rf_deact_cmd, buf, len))
+               goto error;
+       write(dev_fd, nci_rf_deact_rsp, sizeof(nci_rf_deact_rsp));
+
+       return (void *)0;
+error:
+       return (void *)-1;
+}
+
+TEST_F(NCI, start_poll)
+{
+       __u16 nla_start_poll_type[2] = {NFC_ATTR_DEVICE_INDEX,
+                                        NFC_ATTR_PROTOCOLS};
+       void *nla_start_poll_data[2] = {&self->dev_idex, &self->proto};
+       int nla_start_poll_len[2] = {4, 4};
+       pthread_t thread_t;
+       int status;
+       int rc;
+
+       rc = pthread_create(&thread_t, NULL, virtual_poll_start,
+                           (void *)&self->virtual_nci_fd);
+       ASSERT_GT(rc, -1);
+
+       rc = send_cmd_mt_nla(self->sd, self->fid, self->pid,
+                            NFC_CMD_START_POLL, 2, nla_start_poll_type,
+                            nla_start_poll_data, nla_start_poll_len);
+       EXPECT_EQ(rc, 0);
+
+       pthread_join(thread_t, (void **)&status);
+       ASSERT_EQ(status, 0);
+
+       rc = pthread_create(&thread_t, NULL, virtual_poll_stop,
+                           (void *)&self->virtual_nci_fd);
+       ASSERT_GT(rc, -1);
+
+       rc = send_cmd_with_idx(self->sd, self->fid, self->pid,
+                              NFC_CMD_STOP_POLL, self->dev_idex);
+       EXPECT_EQ(rc, 0);
+
+       pthread_join(thread_t, (void **)&status);
+       ASSERT_EQ(status, 0);
+}
+
+TEST_F(NCI, deinit)
+{
+       struct msgtemplate msg;
+       pthread_t thread_t;
+       int status;
+       int rc;
+
+       rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex,
+                          &msg);
+       ASSERT_EQ(rc, 0);
+       EXPECT_EQ(get_dev_enable_state(&msg), 1);
+
+       if (self->isNCI2)
+               rc = pthread_create(&thread_t, NULL, virtual_deinit_v2,
+                                   (void *)&self->virtual_nci_fd);
+       else
+               rc = pthread_create(&thread_t, NULL, virtual_deinit,
+                                   (void *)&self->virtual_nci_fd);
+       ASSERT_GT(rc, -1);
+
+       rc = send_cmd_with_idx(self->sd, self->fid, self->pid,
+                              NFC_CMD_DEV_DOWN, self->dev_idex);
+       EXPECT_EQ(rc, 0);
+
+       pthread_join(thread_t, (void **)&status);
+       self->open_state = 0;
+       ASSERT_EQ(status, 0);
+
+       rc = get_nci_devid(self->sd, self->fid, self->pid, self->dev_idex,
+                          &msg);
+       ASSERT_EQ(rc, 0);
+       EXPECT_EQ(get_dev_enable_state(&msg), 0);
+}
+
+TEST_HARNESS_MAIN
index fa5fa42..25f198b 100644 (file)
@@ -22,6 +22,7 @@ TEST_PROGS += devlink_port_split.py
 TEST_PROGS += drop_monitor_tests.sh
 TEST_PROGS += vrf_route_leaking.sh
 TEST_PROGS += bareudp.sh
+TEST_PROGS += unicast_extensions.sh
 TEST_PROGS_EXTENDED := in_netns.sh
 TEST_GEN_FILES =  socket nettest
 TEST_GEN_FILES += psock_fanout psock_tpacket msg_zerocopy reuseport_addr_any
index 02b0b9e..a8ad928 100755 (executable)
@@ -801,9 +801,9 @@ ipv4_tcp_md5_novrf()
 
        # basic use case
        log_start
-       run_cmd nettest -s -M ${MD5_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -M ${MD5_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 0 "MD5: Single address config"
 
        # client sends MD5, server not configured
@@ -811,23 +811,23 @@ ipv4_tcp_md5_novrf()
        show_hint "Should timeout due to MD5 mismatch"
        run_cmd nettest -s &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: Server no config, client uses password"
 
        # wrong password
        log_start
        show_hint "Should timeout since client uses wrong password"
-       run_cmd nettest -s -M ${MD5_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -M ${MD5_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: Client uses wrong password"
 
        # client from different address
        log_start
        show_hint "Should timeout due to MD5 mismatch"
-       run_cmd nettest -s -M ${MD5_PW} -r ${NSB_LO_IP} &
+       run_cmd nettest -s -M ${MD5_PW} -m ${NSB_LO_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: Client address does not match address configured with password"
 
        #
@@ -838,7 +838,7 @@ ipv4_tcp_md5_novrf()
        log_start
        run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest  -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest  -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 0 "MD5: Prefix config"
 
        # client in prefix, wrong password
@@ -846,7 +846,7 @@ ipv4_tcp_md5_novrf()
        show_hint "Should timeout since client uses wrong password"
        run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: Prefix config, client uses wrong password"
 
        # client outside of prefix
@@ -854,7 +854,7 @@ ipv4_tcp_md5_novrf()
        show_hint "Should timeout due to MD5 mismatch"
        run_cmd nettest -s -M ${MD5_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest -l ${NSB_LO_IP} -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: Prefix config, client address not in configured prefix"
 }
 
@@ -869,33 +869,33 @@ ipv4_tcp_md5()
 
        # basic use case
        log_start
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Single address config"
 
        # client sends MD5, server not configured
        log_start
        show_hint "Should timeout since server does not have MD5 auth"
-       run_cmd nettest -s -d ${VRF} &
+       run_cmd nettest -s -I ${VRF} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Server no config, client uses password"
 
        # wrong password
        log_start
        show_hint "Should timeout since client uses wrong password"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Client uses wrong password"
 
        # client from different address
        log_start
        show_hint "Should timeout since server config differs from client"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_LO_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_LO_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Client address does not match address configured with password"
 
        #
@@ -904,25 +904,25 @@ ipv4_tcp_md5()
 
        # client in prefix
        log_start
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest  -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest  -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Prefix config"
 
        # client in prefix, wrong password
        log_start
        show_hint "Should timeout since client uses wrong password"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Prefix config, client uses wrong password"
 
        # client outside of prefix
        log_start
        show_hint "Should timeout since client address is outside of prefix"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest -l ${NSB_LO_IP} -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest -c ${NSB_LO_IP} -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Prefix config, client address not in configured prefix"
 
        #
@@ -930,74 +930,74 @@ ipv4_tcp_md5()
        #
 
        log_start
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
-       run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} &
+       run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsb nettest  -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest  -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF"
 
        log_start
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
-       run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} &
+       run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsc nettest  -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsc nettest  -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF"
 
        log_start
        show_hint "Should timeout since client in default VRF uses VRF password"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
-       run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} &
+       run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsc nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF with VRF pw"
 
        log_start
        show_hint "Should timeout since client in VRF uses default VRF password"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP} &
-       run_cmd nettest -s -M ${MD5_WRONG_PW} -r ${NSB_IP} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP} &
+       run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NSB_IP} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF with default VRF pw"
 
        log_start
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest  -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsb nettest  -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF"
 
        log_start
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsc nettest  -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsc nettest  -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF"
 
        log_start
        show_hint "Should timeout since client in default VRF uses VRF password"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsc nettest -r ${NSA_IP} -M ${MD5_PW}
+       run_cmd_nsc nettest -r ${NSA_IP} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF with VRF pw"
 
        log_start
        show_hint "Should timeout since client in VRF uses default VRF password"
-       run_cmd nettest -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET} &
+       run_cmd nettest -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET} &
        run_cmd nettest -s -M ${MD5_WRONG_PW} -m ${NS_NET} &
        sleep 1
-       run_cmd_nsb nettest -r ${NSA_IP} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -r ${NSA_IP} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF with default VRF pw"
 
        #
        # negative tests
        #
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} -M ${MD5_PW} -r ${NSB_IP}
+       run_cmd nettest -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NSB_IP}
        log_test $? 1 "MD5: VRF: Device must be a VRF - single address"
 
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET}
+       run_cmd nettest -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET}
        log_test $? 1 "MD5: VRF: Device must be a VRF - prefix"
 
 }
@@ -1020,7 +1020,7 @@ ipv4_tcp_novrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} &
+       run_cmd nettest -s -I ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -r ${a}
        log_test_addr ${a} $? 0 "Device server"
@@ -1076,7 +1076,7 @@ ipv4_tcp_novrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} &
+       run_cmd nettest -s -I ${NSA_DEV} &
        sleep 1
        run_cmd nettest -r ${a} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
@@ -1085,7 +1085,7 @@ ipv4_tcp_novrf()
        do
                log_start
                show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
-               run_cmd nettest -s -d ${NSA_DEV} &
+               run_cmd nettest -s -I ${NSA_DEV} &
                sleep 1
                run_cmd nettest -r ${a}
                log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
@@ -1110,7 +1110,7 @@ ipv4_tcp_novrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest  -d ${NSA_DEV} -r ${a} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, device client, local connection"
@@ -1145,13 +1145,13 @@ ipv4_tcp_vrf()
                log_test_addr ${a} $? 1 "Global server"
 
                log_start
-               run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -s -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd_nsb nettest -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
 
                log_start
-               run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+               run_cmd nettest -s -I ${NSA_DEV} -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -r ${a}
                log_test_addr ${a} $? 0 "Device server"
@@ -1186,14 +1186,14 @@ ipv4_tcp_vrf()
        do
                log_start
                show_hint "client socket should be bound to VRF"
-               run_cmd nettest -s -2 ${VRF} &
+               run_cmd nettest -s -3 ${VRF} &
                sleep 1
                run_cmd_nsb nettest -r ${a}
                log_test_addr ${a} $? 0 "Global server"
 
                log_start
                show_hint "client socket should be bound to VRF"
-               run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -s -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd_nsb nettest -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
@@ -1208,7 +1208,7 @@ ipv4_tcp_vrf()
        a=${NSA_IP}
        log_start
        show_hint "client socket should be bound to device"
-       run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -r ${a}
        log_test_addr ${a} $? 0 "Device server"
@@ -1218,7 +1218,7 @@ ipv4_tcp_vrf()
        do
                log_start
                show_hint "Should fail 'Connection refused' since client is not bound to VRF"
-               run_cmd nettest -s -d ${VRF} &
+               run_cmd nettest -s -I ${VRF} &
                sleep 1
                run_cmd nettest -r ${a}
                log_test_addr ${a} $? 1 "Global server, local connection"
@@ -1255,7 +1255,7 @@ ipv4_tcp_vrf()
        for a in ${NSA_IP} ${VRF_IP} 127.0.0.1
        do
                log_start
-               run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -s -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd nettest -r ${a} -d ${VRF} -0 ${a}
                log_test_addr ${a} $? 0 "VRF server, VRF client, local connection"
@@ -1263,26 +1263,26 @@ ipv4_tcp_vrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -d ${VRF} -2 ${VRF} &
+       run_cmd nettest -s -I ${VRF} -3 ${VRF} &
        sleep 1
        run_cmd nettest -r ${a} -d ${NSA_DEV} -0 ${a}
        log_test_addr ${a} $? 0 "VRF server, device client, local connection"
 
        log_start
        show_hint "Should fail 'No route to host' since client is out of VRF scope"
-       run_cmd nettest -s -d ${VRF} &
+       run_cmd nettest -s -I ${VRF} &
        sleep 1
        run_cmd nettest -r ${a}
        log_test_addr ${a} $? 1 "VRF server, unbound client, local connection"
 
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -r ${a} -d ${VRF} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, VRF client, local connection"
 
        log_start
-       run_cmd nettest -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -r ${a} -d ${NSA_DEV} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, device client, local connection"
@@ -1321,7 +1321,7 @@ ipv4_udp_novrf()
        for a in ${NSA_IP} ${NSA_LO_IP}
        do
                log_start
-               run_cmd nettest -D -s -2 ${NSA_DEV} &
+               run_cmd nettest -D -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -D -r ${a}
                log_test_addr ${a} $? 0 "Global server"
@@ -1334,7 +1334,7 @@ ipv4_udp_novrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+       run_cmd nettest -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -D -r ${a}
        log_test_addr ${a} $? 0 "Device server"
@@ -1393,7 +1393,7 @@ ipv4_udp_novrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -r ${a}
        log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
@@ -1402,7 +1402,7 @@ ipv4_udp_novrf()
        do
                log_start
                show_hint "Should fail 'Connection refused' since address is out of device scope"
-               run_cmd nettest -s -D -d ${NSA_DEV} &
+               run_cmd nettest -s -D -I ${NSA_DEV} &
                sleep 1
                run_cmd nettest -D -r ${a}
                log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
@@ -1456,7 +1456,7 @@ ipv4_udp_novrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -D -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -D -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${NSA_DEV} -r ${a} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, device client, local conn"
@@ -1487,13 +1487,13 @@ ipv4_udp_vrf()
                log_test_addr ${a} $? 1 "Global server"
 
                log_start
-               run_cmd nettest -D -d ${VRF} -s -2 ${NSA_DEV} &
+               run_cmd nettest -D -I ${VRF} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -D -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
 
                log_start
-               run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+               run_cmd nettest -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -D -r ${a}
                log_test_addr ${a} $? 0 "Enslaved device server"
@@ -1513,26 +1513,26 @@ ipv4_udp_vrf()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${VRF} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${VRF} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, enslaved device client, local connection"
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
@@ -1547,19 +1547,19 @@ ipv4_udp_vrf()
        for a in ${NSA_IP} ${VRF_IP}
        do
                log_start
-               run_cmd nettest -D -s -2 ${NSA_DEV} &
+               run_cmd nettest -D -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -D -r ${a}
                log_test_addr ${a} $? 0 "Global server"
 
                log_start
-               run_cmd nettest -D -d ${VRF} -s -2 ${NSA_DEV} &
+               run_cmd nettest -D -I ${VRF} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -D -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
 
                log_start
-               run_cmd nettest -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+               run_cmd nettest -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -D -r ${a}
                log_test_addr ${a} $? 0 "Enslaved device server"
@@ -1601,31 +1601,31 @@ ipv4_udp_vrf()
        #
        a=${NSA_IP}
        log_start
-       run_cmd nettest -D -s -2 ${NSA_DEV} &
+       run_cmd nettest -D -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${VRF} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -s -D -d ${VRF} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${VRF} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, device client, local conn"
 
        log_start
-       run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -s -D -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
@@ -1633,7 +1633,7 @@ ipv4_udp_vrf()
        for a in ${VRF_IP} 127.0.0.1
        do
                log_start
-               run_cmd nettest -D -s -2 ${VRF} &
+               run_cmd nettest -D -s -3 ${VRF} &
                sleep 1
                run_cmd nettest -D -d ${VRF} -r ${a}
                log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
@@ -1642,7 +1642,7 @@ ipv4_udp_vrf()
        for a in ${VRF_IP} 127.0.0.1
        do
                log_start
-               run_cmd nettest -s -D -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -s -D -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd nettest -D -d ${VRF} -r ${a}
                log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
@@ -1697,7 +1697,7 @@ ipv4_addr_bind_novrf()
                log_test_addr ${a} $? 0 "Raw socket bind to local address"
 
                log_start
-               run_cmd nettest -s -R -P icmp -l ${a} -d ${NSA_DEV} -b
+               run_cmd nettest -s -R -P icmp -l ${a} -I ${NSA_DEV} -b
                log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
        done
 
@@ -1706,11 +1706,11 @@ ipv4_addr_bind_novrf()
        #
        a=${NSA_IP}
        log_start
-       run_cmd nettest -l ${a} -r ${NSB_IP} -t1 -b
+       run_cmd nettest -c ${a} -r ${NSB_IP} -t1 -b
        log_test_addr ${a} $? 0 "TCP socket bind to local address"
 
        log_start
-       run_cmd nettest -l ${a} -r ${NSB_IP} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -c ${a} -r ${NSB_IP} -d ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
 
        # Sadly, the kernel allows binding a socket to a device and then
@@ -1720,7 +1720,7 @@ ipv4_addr_bind_novrf()
        #a=${NSA_LO_IP}
        #log_start
        #show_hint "Should fail with 'Cannot assign requested address'"
-       #run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+       #run_cmd nettest -s -l ${a} -I ${NSA_DEV} -t1 -b
        #log_test_addr ${a} $? 1 "TCP socket bind to out of scope local address"
 }
 
@@ -1736,17 +1736,17 @@ ipv4_addr_bind_vrf()
                log_test_addr ${a} $? 0 "Raw socket bind to local address"
 
                log_start
-               run_cmd nettest -s -R -P icmp -l ${a} -d ${NSA_DEV} -b
+               run_cmd nettest -s -R -P icmp -l ${a} -I ${NSA_DEV} -b
                log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
                log_start
-               run_cmd nettest -s -R -P icmp -l ${a} -d ${VRF} -b
+               run_cmd nettest -s -R -P icmp -l ${a} -I ${VRF} -b
                log_test_addr ${a} $? 0 "Raw socket bind to local address after VRF bind"
        done
 
        a=${NSA_LO_IP}
        log_start
        show_hint "Address on loopback is out of VRF scope"
-       run_cmd nettest -s -R -P icmp -l ${a} -d ${VRF} -b
+       run_cmd nettest -s -R -P icmp -l ${a} -I ${VRF} -b
        log_test_addr ${a} $? 1 "Raw socket bind to out of scope address after VRF bind"
 
        #
@@ -1755,23 +1755,23 @@ ipv4_addr_bind_vrf()
        for a in ${NSA_IP} ${VRF_IP}
        do
                log_start
-               run_cmd nettest -s -l ${a} -d ${VRF} -t1 -b
+               run_cmd nettest -s -l ${a} -I ${VRF} -t1 -b
                log_test_addr ${a} $? 0 "TCP socket bind to local address"
 
                log_start
-               run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+               run_cmd nettest -s -l ${a} -I ${NSA_DEV} -t1 -b
                log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
        done
 
        a=${NSA_LO_IP}
        log_start
        show_hint "Address on loopback out of scope for VRF"
-       run_cmd nettest -s -l ${a} -d ${VRF} -t1 -b
+       run_cmd nettest -s -l ${a} -I ${VRF} -t1 -b
        log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for VRF"
 
        log_start
        show_hint "Address on loopback out of scope for device in VRF"
-       run_cmd nettest -s -l ${a} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -s -l ${a} -I ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for device bind"
 }
 
@@ -1818,7 +1818,7 @@ ipv4_rt()
        for a in ${NSA_IP} ${VRF_IP}
        do
                log_start
-               run_cmd nettest ${varg} -s -d ${VRF} &
+               run_cmd nettest ${varg} -s -I ${VRF} &
                sleep 1
                run_cmd_nsb nettest ${varg} -r ${a} &
                sleep 3
@@ -1831,7 +1831,7 @@ ipv4_rt()
 
        a=${NSA_IP}
        log_start
-       run_cmd nettest ${varg} -s -d ${NSA_DEV} &
+       run_cmd nettest ${varg} -s -I ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest ${varg} -r ${a} &
        sleep 3
@@ -1886,7 +1886,7 @@ ipv4_rt()
        for a in ${NSA_IP} ${VRF_IP}
        do
                log_start
-               run_cmd nettest ${varg} -d ${VRF} -s &
+               run_cmd nettest ${varg} -I ${VRF} -s &
                sleep 1
                run_cmd nettest ${varg} -d ${VRF} -r ${a} &
                sleep 3
@@ -1910,7 +1910,7 @@ ipv4_rt()
        setup ${with_vrf}
 
        log_start
-       run_cmd nettest ${varg} -d ${VRF} -s &
+       run_cmd nettest ${varg} -I ${VRF} -s &
        sleep 1
        run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
        sleep 3
@@ -1921,7 +1921,7 @@ ipv4_rt()
        setup ${with_vrf}
 
        log_start
-       run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+       run_cmd nettest ${varg} -I ${NSA_DEV} -s &
        sleep 1
        run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
        sleep 3
@@ -2265,9 +2265,9 @@ ipv6_tcp_md5_novrf()
 
        # basic use case
        log_start
-       run_cmd nettest -6 -s -M ${MD5_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 0 "MD5: Single address config"
 
        # client sends MD5, server not configured
@@ -2275,23 +2275,23 @@ ipv6_tcp_md5_novrf()
        show_hint "Should timeout due to MD5 mismatch"
        run_cmd nettest -6 -s &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: Server no config, client uses password"
 
        # wrong password
        log_start
        show_hint "Should timeout since client uses wrong password"
-       run_cmd nettest -6 -s -M ${MD5_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: Client uses wrong password"
 
        # client from different address
        log_start
        show_hint "Should timeout due to MD5 mismatch"
-       run_cmd nettest -6 -s -M ${MD5_PW} -r ${NSB_LO_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_PW} -m ${NSB_LO_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: Client address does not match address configured with password"
 
        #
@@ -2302,7 +2302,7 @@ ipv6_tcp_md5_novrf()
        log_start
        run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6  -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 0 "MD5: Prefix config"
 
        # client in prefix, wrong password
@@ -2310,7 +2310,7 @@ ipv6_tcp_md5_novrf()
        show_hint "Should timeout since client uses wrong password"
        run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: Prefix config, client uses wrong password"
 
        # client outside of prefix
@@ -2318,7 +2318,7 @@ ipv6_tcp_md5_novrf()
        show_hint "Should timeout due to MD5 mismatch"
        run_cmd nettest -6 -s -M ${MD5_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6 -l ${NSB_LO_IP6} -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: Prefix config, client address not in configured prefix"
 }
 
@@ -2333,33 +2333,33 @@ ipv6_tcp_md5()
 
        # basic use case
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Single address config"
 
        # client sends MD5, server not configured
        log_start
        show_hint "Should timeout since server does not have MD5 auth"
-       run_cmd nettest -6 -s -d ${VRF} &
+       run_cmd nettest -6 -s -I ${VRF} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Server no config, client uses password"
 
        # wrong password
        log_start
        show_hint "Should timeout since client uses wrong password"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Client uses wrong password"
 
        # client from different address
        log_start
        show_hint "Should timeout since server config differs from client"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_LO_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_LO_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Client address does not match address configured with password"
 
        #
@@ -2368,25 +2368,25 @@ ipv6_tcp_md5()
 
        # client in prefix
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6  -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Prefix config"
 
        # client in prefix, wrong password
        log_start
        show_hint "Should timeout since client uses wrong password"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Prefix config, client uses wrong password"
 
        # client outside of prefix
        log_start
        show_hint "Should timeout since client address is outside of prefix"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6 -l ${NSB_LO_IP6} -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -c ${NSB_LO_IP6} -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Prefix config, client address not in configured prefix"
 
        #
@@ -2394,74 +2394,74 @@ ipv6_tcp_md5()
        #
 
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
-       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6  -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF"
 
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
-       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsc nettest -6  -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 0 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF"
 
        log_start
        show_hint "Should timeout since client in default VRF uses VRF password"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
-       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsc nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in default VRF with VRF pw"
 
        log_start
        show_hint "Should timeout since client in VRF uses default VRF password"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -r ${NSB_IP6} &
-       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -r ${NSB_IP6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NSB_IP6} &
+       run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NSB_IP6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Single address config in default VRF and VRF, conn in VRF with default VRF pw"
 
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6  -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF"
 
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsc nettest -6  -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 0 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF"
 
        log_start
        show_hint "Should timeout since client in default VRF uses VRF password"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsc nettest -6 -r ${NSA_IP6} -M ${MD5_PW}
+       run_cmd_nsc nettest -6 -r ${NSA_IP6} -X ${MD5_PW}
        log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in default VRF with VRF pw"
 
        log_start
        show_hint "Should timeout since client in VRF uses default VRF password"
-       run_cmd nettest -6 -s -d ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
+       run_cmd nettest -6 -s -I ${VRF} -M ${MD5_PW} -m ${NS_NET6} &
        run_cmd nettest -6 -s -M ${MD5_WRONG_PW} -m ${NS_NET6} &
        sleep 1
-       run_cmd_nsb nettest -6 -r ${NSA_IP6} -M ${MD5_WRONG_PW}
+       run_cmd_nsb nettest -6 -r ${NSA_IP6} -X ${MD5_WRONG_PW}
        log_test $? 2 "MD5: VRF: Prefix config in default VRF and VRF, conn in VRF with default VRF pw"
 
        #
        # negative tests
        #
        log_start
-       run_cmd nettest -6 -s -d ${NSA_DEV} -M ${MD5_PW} -r ${NSB_IP6}
+       run_cmd nettest -6 -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NSB_IP6}
        log_test $? 1 "MD5: VRF: Device must be a VRF - single address"
 
        log_start
-       run_cmd nettest -6 -s -d ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET6}
+       run_cmd nettest -6 -s -I ${NSA_DEV} -M ${MD5_PW} -m ${NS_NET6}
        log_test $? 1 "MD5: VRF: Device must be a VRF - prefix"
 
 }
@@ -2534,7 +2534,7 @@ ipv6_tcp_novrf()
 
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -6 -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -r ${a} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
@@ -2543,7 +2543,7 @@ ipv6_tcp_novrf()
        do
                log_start
                show_hint "Should fail 'Connection refused' since addresses on loopback are out of device scope"
-               run_cmd nettest -6 -s -d ${NSA_DEV} &
+               run_cmd nettest -6 -s -I ${NSA_DEV} &
                sleep 1
                run_cmd nettest -6 -r ${a}
                log_test_addr ${a} $? 1 "Device server, unbound client, local connection"
@@ -2569,7 +2569,7 @@ ipv6_tcp_novrf()
        for a in ${NSA_IP6} ${NSA_LINKIP6}
        do
                log_start
-               run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+               run_cmd nettest -6 -s -I ${NSA_DEV} -3 ${NSA_DEV} &
                sleep 1
                run_cmd nettest -6  -d ${NSA_DEV} -r ${a}
                log_test_addr ${a} $? 0 "Device server, device client, local conn"
@@ -2611,7 +2611,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -6 -s -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd_nsb nettest -6 -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
@@ -2620,7 +2620,7 @@ ipv6_tcp_vrf()
        # link local is always bound to ingress device
        a=${NSA_LINKIP6}%${NSB_DEV}
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -2 ${NSA_DEV} &
+       run_cmd nettest -6 -s -I ${VRF} -3 ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -6 -r ${a}
        log_test_addr ${a} $? 0 "VRF server"
@@ -2628,7 +2628,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6} ${NSA_LINKIP6}%${NSB_DEV}
        do
                log_start
-               run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+               run_cmd nettest -6 -s -I ${NSA_DEV} -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -r ${a}
                log_test_addr ${a} $? 0 "Device server"
@@ -2664,7 +2664,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -s -2 ${VRF} &
+               run_cmd nettest -6 -s -3 ${VRF} &
                sleep 1
                run_cmd_nsb nettest -6 -r ${a}
                log_test_addr ${a} $? 0 "Global server"
@@ -2673,7 +2673,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -6 -s -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd_nsb nettest -6 -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
@@ -2682,13 +2682,13 @@ ipv6_tcp_vrf()
        # For LLA, child socket is bound to device
        a=${NSA_LINKIP6}%${NSB_DEV}
        log_start
-       run_cmd nettest -6 -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -6 -r ${a}
        log_test_addr ${a} $? 0 "Global server"
 
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -2 ${NSA_DEV} &
+       run_cmd nettest -6 -s -I ${VRF} -3 ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -6 -r ${a}
        log_test_addr ${a} $? 0 "VRF server"
@@ -2696,7 +2696,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSB_DEV}
        do
                log_start
-               run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+               run_cmd nettest -6 -s -I ${NSA_DEV} -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -r ${a}
                log_test_addr ${a} $? 0 "Device server"
@@ -2716,7 +2716,7 @@ ipv6_tcp_vrf()
        do
                log_start
                show_hint "Fails 'Connection refused' since client is not in VRF"
-               run_cmd nettest -6 -s -d ${VRF} &
+               run_cmd nettest -6 -s -I ${VRF} &
                sleep 1
                run_cmd nettest -6 -r ${a}
                log_test_addr ${a} $? 1 "Global server, local connection"
@@ -2771,7 +2771,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6} ::1
        do
                log_start
-               run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+               run_cmd nettest -6 -s -I ${VRF} -3 ${VRF} &
                sleep 1
                run_cmd nettest -6 -r ${a} -d ${VRF} -0 ${a}
                log_test_addr ${a} $? 0 "VRF server, VRF client, local connection"
@@ -2779,7 +2779,7 @@ ipv6_tcp_vrf()
 
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -s -d ${VRF} -2 ${VRF} &
+       run_cmd nettest -6 -s -I ${VRF} -3 ${VRF} &
        sleep 1
        run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
        log_test_addr ${a} $? 0 "VRF server, device client, local connection"
@@ -2787,13 +2787,13 @@ ipv6_tcp_vrf()
        a=${NSA_IP6}
        log_start
        show_hint "Should fail since unbound client is out of VRF scope"
-       run_cmd nettest -6 -s -d ${VRF} &
+       run_cmd nettest -6 -s -I ${VRF} &
        sleep 1
        run_cmd nettest -6 -r ${a}
        log_test_addr ${a} $? 1 "VRF server, unbound client, local connection"
 
        log_start
-       run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -6 -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -r ${a} -d ${VRF} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, VRF client, local connection"
@@ -2801,7 +2801,7 @@ ipv6_tcp_vrf()
        for a in ${NSA_IP6} ${NSA_LINKIP6}
        do
                log_start
-               run_cmd nettest -6 -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+               run_cmd nettest -6 -s -I ${NSA_DEV} -3 ${NSA_DEV} &
                sleep 1
                run_cmd nettest -6 -r ${a} -d ${NSA_DEV} -0 ${a}
                log_test_addr ${a} $? 0 "Device server, device client, local connection"
@@ -2841,13 +2841,13 @@ ipv6_udp_novrf()
        for a in ${NSA_IP6} ${NSA_LINKIP6}%${NSB_DEV}
        do
                log_start
-               run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "Global server"
 
                log_start
-               run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "Device server"
@@ -2855,7 +2855,7 @@ ipv6_udp_novrf()
 
        a=${NSA_LO_IP6}
        log_start
-       run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd_nsb nettest -6 -D -r ${a}
        log_test_addr ${a} $? 0 "Global server"
@@ -2865,7 +2865,7 @@ ipv6_udp_novrf()
        # behavior.
        #log_start
        #show_hint "Should fail since loopback address is out of scope"
-       #run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+       #run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
        #sleep 1
        #run_cmd_nsb nettest -6 -D -r ${a}
        #log_test_addr ${a} $? 1 "Device server"
@@ -2933,7 +2933,7 @@ ipv6_udp_novrf()
 
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -s -D -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -6 -s -D -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -r ${a}
        log_test_addr ${a} $? 0 "Device server, unbound client, local connection"
@@ -2942,7 +2942,7 @@ ipv6_udp_novrf()
        do
                log_start
                show_hint "Should fail 'Connection refused' since address is out of device scope"
-               run_cmd nettest -6 -s -D -d ${NSA_DEV} &
+               run_cmd nettest -6 -s -D -I ${NSA_DEV} &
                sleep 1
                run_cmd nettest -6 -D -r ${a}
                log_test_addr ${a} $? 1 "Device server, local connection"
@@ -2993,7 +2993,7 @@ ipv6_udp_novrf()
 
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -D -s -d ${NSA_DEV} -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -s -I ${NSA_DEV} -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a} -0 ${a}
        log_test_addr ${a} $? 0 "Device server, device client, local conn"
@@ -3040,7 +3040,7 @@ ipv6_udp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -I ${VRF} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
@@ -3049,7 +3049,7 @@ ipv6_udp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "Enslaved device server"
@@ -3080,7 +3080,7 @@ ipv6_udp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -D -d ${VRF} -s &
+               run_cmd nettest -6 -D -I ${VRF} -s &
                sleep 1
                run_cmd nettest -6 -D -d ${VRF} -r ${a}
                log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
@@ -3095,19 +3095,19 @@ ipv6_udp_vrf()
        log_test_addr ${a} $? 1 "Global server, device client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${VRF} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, device client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Enslaved device server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "Enslaved device server, device client, local conn"
@@ -3122,7 +3122,7 @@ ipv6_udp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "Global server"
@@ -3131,7 +3131,7 @@ ipv6_udp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -I ${VRF} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "VRF server"
@@ -3140,7 +3140,7 @@ ipv6_udp_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+               run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
                sleep 1
                run_cmd_nsb nettest -6 -D -r ${a}
                log_test_addr ${a} $? 0 "Enslaved device server"
@@ -3184,13 +3184,13 @@ ipv6_udp_vrf()
        #
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
 
        #log_start
-       run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${VRF} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
@@ -3198,13 +3198,13 @@ ipv6_udp_vrf()
 
        a=${VRF_IP6}
        log_start
-       run_cmd nettest -6 -D -s -2 ${VRF} &
+       run_cmd nettest -6 -D -s -3 ${VRF} &
        sleep 1
        run_cmd nettest -6 -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Global server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${VRF} -s -2 ${VRF} &
+       run_cmd nettest -6 -D -I ${VRF} -s -3 ${VRF} &
        sleep 1
        run_cmd nettest -6 -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, VRF client, local conn"
@@ -3220,25 +3220,25 @@ ipv6_udp_vrf()
        # device to global IP
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -D -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "Global server, device client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${VRF} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${VRF} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "VRF server, device client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${VRF} -r ${a}
        log_test_addr ${a} $? 0 "Device server, VRF client, local conn"
 
        log_start
-       run_cmd nettest -6 -D -d ${NSA_DEV} -s -2 ${NSA_DEV} &
+       run_cmd nettest -6 -D -I ${NSA_DEV} -s -3 ${NSA_DEV} &
        sleep 1
        run_cmd nettest -6 -D -d ${NSA_DEV} -r ${a}
        log_test_addr ${a} $? 0 "Device server, device client, local conn"
@@ -3332,7 +3332,7 @@ ipv6_addr_bind_novrf()
                log_test_addr ${a} $? 0 "Raw socket bind to local address"
 
                log_start
-               run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${NSA_DEV} -b
+               run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -I ${NSA_DEV} -b
                log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
        done
 
@@ -3345,13 +3345,13 @@ ipv6_addr_bind_novrf()
        log_test_addr ${a} $? 0 "TCP socket bind to local address"
 
        log_start
-       run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -6 -s -l ${a} -I ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 0 "TCP socket bind to local address after device bind"
 
        a=${NSA_LO_IP6}
        log_start
        show_hint "Should fail with 'Cannot assign requested address'"
-       run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -6 -s -l ${a} -I ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 1 "TCP socket bind to out of scope local address"
 }
 
@@ -3363,18 +3363,18 @@ ipv6_addr_bind_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${VRF} -b
+               run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -I ${VRF} -b
                log_test_addr ${a} $? 0 "Raw socket bind to local address after vrf bind"
 
                log_start
-               run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${NSA_DEV} -b
+               run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -I ${NSA_DEV} -b
                log_test_addr ${a} $? 0 "Raw socket bind to local address after device bind"
        done
 
        a=${NSA_LO_IP6}
        log_start
        show_hint "Address on loopback is out of VRF scope"
-       run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -d ${VRF} -b
+       run_cmd nettest -6 -s -R -P ipv6-icmp -l ${a} -I ${VRF} -b
        log_test_addr ${a} $? 1 "Raw socket bind to invalid local address after vrf bind"
 
        #
@@ -3384,29 +3384,29 @@ ipv6_addr_bind_vrf()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest -6 -s -l ${a} -d ${VRF} -t1 -b
+               run_cmd nettest -6 -s -l ${a} -I ${VRF} -t1 -b
                log_test_addr ${a} $? 0 "TCP socket bind to local address with VRF bind"
        done
 
        a=${NSA_IP6}
        log_start
-       run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -6 -s -l ${a} -I ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 0 "TCP socket bind to local address with device bind"
 
        a=${VRF_IP6}
        log_start
-       run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -6 -s -l ${a} -I ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 1 "TCP socket bind to VRF address with device bind"
 
        a=${NSA_LO_IP6}
        log_start
        show_hint "Address on loopback out of scope for VRF"
-       run_cmd nettest -6 -s -l ${a} -d ${VRF} -t1 -b
+       run_cmd nettest -6 -s -l ${a} -I ${VRF} -t1 -b
        log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for VRF"
 
        log_start
        show_hint "Address on loopback out of scope for device in VRF"
-       run_cmd nettest -6 -s -l ${a} -d ${NSA_DEV} -t1 -b
+       run_cmd nettest -6 -s -l ${a} -I ${NSA_DEV} -t1 -b
        log_test_addr ${a} $? 1 "TCP socket bind to invalid local address for device bind"
 
 }
@@ -3454,7 +3454,7 @@ ipv6_rt()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest ${varg} -d ${VRF} -s &
+               run_cmd nettest ${varg} -I ${VRF} -s &
                sleep 1
                run_cmd_nsb nettest ${varg} -r ${a} &
                sleep 3
@@ -3468,7 +3468,7 @@ ipv6_rt()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+               run_cmd nettest ${varg} -I ${NSA_DEV} -s &
                sleep 1
                run_cmd_nsb nettest ${varg} -r ${a} &
                sleep 3
@@ -3525,7 +3525,7 @@ ipv6_rt()
        for a in ${NSA_IP6} ${VRF_IP6}
        do
                log_start
-               run_cmd nettest ${varg} -d ${VRF} -s &
+               run_cmd nettest ${varg} -I ${VRF} -s &
                sleep 1
                run_cmd nettest ${varg} -d ${VRF} -r ${a} &
                sleep 3
@@ -3549,7 +3549,7 @@ ipv6_rt()
        setup ${with_vrf}
 
        log_start
-       run_cmd nettest ${varg} -d ${VRF} -s &
+       run_cmd nettest ${varg} -I ${VRF} -s &
        sleep 1
        run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
        sleep 3
@@ -3560,7 +3560,7 @@ ipv6_rt()
        setup ${with_vrf}
 
        log_start
-       run_cmd nettest ${varg} -d ${NSA_DEV} -s &
+       run_cmd nettest ${varg} -I ${NSA_DEV} -s &
        sleep 1
        run_cmd nettest ${varg} -d ${NSA_DEV} -r ${a} &
        sleep 3
index eb693a3..4c7d336 100755 (executable)
@@ -869,7 +869,7 @@ ipv6_torture()
        pid3=$!
        ip netns exec me ping -f 2001:db8:101::2 >/dev/null 2>&1 &
        pid4=$!
-       ip netns exec me mausezahn veth1 -B 2001:db8:101::2 -A 2001:db8:91::1 -c 0 -t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1 &
+       ip netns exec me mausezahn -6 veth1 -B 2001:db8:101::2 -A 2001:db8:91::1 -c 0 -t tcp "dp=1-1023, flags=syn" >/dev/null 2>&1 &
        pid5=$!
 
        sleep 300
index 84205c3..2b57077 100755 (executable)
@@ -1055,7 +1055,6 @@ ipv6_addr_metric_test()
 
        check_route6 "2001:db8:104::1 dev dummy2 proto kernel metric 260"
        log_test $? 0 "Set metric with peer route on local side"
-       log_test $? 0 "User specified metric on local address"
        check_route6 "2001:db8:104::2 dev dummy2 proto kernel metric 260"
        log_test $? 0 "Set metric with peer route on peer side"
 
index 388e449..76efb1f 100755 (executable)
@@ -203,7 +203,7 @@ multipath4_test()
        t0_rp12=$(link_stats_tx_packets_get $rp12)
        t0_rp13=$(link_stats_tx_packets_get $rp13)
 
-       ip vrf exec vrf-h1 $MZ -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
+       ip vrf exec vrf-h1 $MZ $h1 -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
                -d 1msec -t udp "sp=1024,dp=0-32768"
 
        t1_rp12=$(link_stats_tx_packets_get $rp12)
index 79a2099..464821c 100755 (executable)
@@ -178,7 +178,7 @@ multipath4_test()
        t0_rp12=$(link_stats_tx_packets_get $rp12)
        t0_rp13=$(link_stats_tx_packets_get $rp13)
 
-       ip vrf exec vrf-h1 $MZ -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
+       ip vrf exec vrf-h1 $MZ $h1 -q -p 64 -A 192.0.2.2 -B 198.51.100.2 \
               -d 1msec -t udp "sp=1024,dp=0-32768"
 
        t1_rp12=$(link_stats_tx_packets_get $rp12)
index 2934fb5..b95de04 100755 (executable)
@@ -136,7 +136,7 @@ template_filter_fits()
 
        tc filter add dev $h2 ingress protocol ip pref 1 handle 1102 \
                flower src_mac $h2mac action drop &> /dev/null
-       check_fail $? "Incorrectly succeded to insert filter which does not template"
+       check_fail $? "Incorrectly succeeded to insert filter which does not template"
 
        tc filter add dev $h2 ingress chain 1 protocol ip pref 1 handle 1101 \
                flower src_mac $h2mac action drop
@@ -144,7 +144,7 @@ template_filter_fits()
 
        tc filter add dev $h2 ingress chain 1 protocol ip pref 1 handle 1102 \
                flower dst_mac $h2mac action drop &> /dev/null
-       check_fail $? "Incorrectly succeded to insert filter which does not template"
+       check_fail $? "Incorrectly succeeded to insert filter which does not template"
 
        tc filter del dev $h2 ingress chain 1 protocol ip pref 1 handle 1102 \
                flower &> /dev/null
index 9aa9624..be34b9c 100755 (executable)
@@ -212,6 +212,7 @@ do_transfer()
        rm_nr_ns1="$7"
        rm_nr_ns2="$8"
        speed="$9"
+       bkup="${10}"
 
        port=$((10000+$TEST_COUNT))
        TEST_COUNT=$((TEST_COUNT+1))
@@ -297,6 +298,18 @@ do_transfer()
                fi
        fi
 
+       if [ ! -z $bkup ]; then
+               sleep 1
+               for netns in "$ns1" "$ns2"; do
+                       dump=(`ip netns exec $netns ./pm_nl_ctl dump`)
+                       if [ ${#dump[@]} -gt 0 ]; then
+                               addr=${dump[${#dump[@]} - 1]}
+                               backup="ip netns exec $netns ./pm_nl_ctl set $addr flags $bkup"
+                               $backup
+                       fi
+               done
+       fi
+
        wait $cpid
        retc=$?
        wait $spid
@@ -358,6 +371,7 @@ run_tests()
        rm_nr_ns1="${5:-0}"
        rm_nr_ns2="${6:-0}"
        speed="${7:-fast}"
+       bkup="${8:-""}"
        lret=0
        oldin=""
 
@@ -372,7 +386,7 @@ run_tests()
        fi
 
        do_transfer ${listener_ns} ${connector_ns} MPTCP MPTCP ${connect_addr} \
-               ${test_linkfail} ${rm_nr_ns1} ${rm_nr_ns2} ${speed}
+               ${test_linkfail} ${rm_nr_ns1} ${rm_nr_ns2} ${speed} ${bkup}
        lret=$?
 
        if [ "$test_linkfail" -eq 1 ];then
@@ -509,6 +523,43 @@ chk_rm_nr()
        fi
 }
 
+chk_prio_nr()
+{
+       local mp_prio_nr_tx=$1
+       local mp_prio_nr_rx=$2
+       local count
+       local dump_stats
+
+       printf "%-39s %s" " " "ptx"
+       count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioTx | awk '{print $2}'`
+       [ -z "$count" ] && count=0
+       if [ "$count" != "$mp_prio_nr_tx" ]; then
+               echo "[fail] got $count MP_PRIO[s] TX expected $mp_prio_nr_tx"
+               ret=1
+               dump_stats=1
+       else
+               echo -n "[ ok ]"
+       fi
+
+       echo -n " - prx   "
+       count=`ip netns exec $ns1 nstat -as | grep MPTcpExtMPPrioRx | awk '{print $2}'`
+       [ -z "$count" ] && count=0
+       if [ "$count" != "$mp_prio_nr_rx" ]; then
+               echo "[fail] got $count MP_PRIO[s] RX expected $mp_prio_nr_rx"
+               ret=1
+               dump_stats=1
+       else
+               echo "[ ok ]"
+       fi
+
+       if [ "${dump_stats}" = 1 ]; then
+               echo Server ns stats
+               ip netns exec $ns1 nstat -as | grep MPTcp
+               echo Client ns stats
+               ip netns exec $ns2 nstat -as | grep MPTcp
+       fi
+}
+
 sin=$(mktemp)
 sout=$(mktemp)
 cin=$(mktemp)
@@ -739,6 +790,100 @@ chk_join_nr "remove subflow and signal IPv6" 2 2 2
 chk_add_nr 1 1
 chk_rm_nr 1 1
 
+# subflow IPv4-mapped to IPv4-mapped
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add "::ffff:10.0.3.2" flags subflow
+run_tests $ns1 $ns2 "::ffff:10.0.1.1"
+chk_join_nr "single subflow IPv4-mapped" 1 1 1
+
+# signal address IPv4-mapped with IPv4-mapped sk
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 1 1
+ip netns exec $ns1 ./pm_nl_ctl add "::ffff:10.0.2.1" flags signal
+run_tests $ns1 $ns2 "::ffff:10.0.1.1"
+chk_join_nr "signal address IPv4-mapped" 1 1 1
+chk_add_nr 1 1
+
+# subflow v4-map-v6
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
+run_tests $ns1 $ns2 "::ffff:10.0.1.1"
+chk_join_nr "single subflow v4-map-v6" 1 1 1
+
+# signal address v4-map-v6
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 1 1
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
+run_tests $ns1 $ns2 "::ffff:10.0.1.1"
+chk_join_nr "signal address v4-map-v6" 1 1 1
+chk_add_nr 1 1
+
+# subflow v6-map-v4
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add "::ffff:10.0.3.2" flags subflow
+run_tests $ns1 $ns2 10.0.1.1
+chk_join_nr "single subflow v6-map-v4" 1 1 1
+
+# signal address v6-map-v4
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 1 1
+ip netns exec $ns1 ./pm_nl_ctl add "::ffff:10.0.2.1" flags signal
+run_tests $ns1 $ns2 10.0.1.1
+chk_join_nr "signal address v6-map-v4" 1 1 1
+chk_add_nr 1 1
+
+# no subflow IPv6 to v4 address
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add dead:beef:2::2 flags subflow
+run_tests $ns1 $ns2 10.0.1.1
+chk_join_nr "no JOIN with diff families v4-v6" 0 0 0
+
+# no subflow IPv6 to v4 address even if v6 has a valid v4 at the end
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add dead:beef:2::10.0.3.2 flags subflow
+run_tests $ns1 $ns2 10.0.1.1
+chk_join_nr "no JOIN with diff families v4-v6-2" 0 0 0
+
+# no subflow IPv4 to v6 address, no need to slow down too then
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow
+run_tests $ns1 $ns2 dead:beef:1::1
+chk_join_nr "no JOIN with diff families v6-v4" 0 0 0
+
+# single subflow, backup
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl limits 0 1
+ip netns exec $ns2 ./pm_nl_ctl add 10.0.3.2 flags subflow,backup
+run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow nobackup
+chk_join_nr "single subflow, backup" 1 1 1
+chk_prio_nr 0 1
+
+# single address, backup
+reset
+ip netns exec $ns1 ./pm_nl_ctl limits 0 1
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.2.1 flags signal
+ip netns exec $ns2 ./pm_nl_ctl limits 1 1
+run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow backup
+chk_join_nr "single address, backup" 1 1 1
+chk_add_nr 1 1
+chk_prio_nr 1 0
+
 # single subflow, syncookies
 reset_with_cookies
 ip netns exec $ns1 ./pm_nl_ctl limits 0 1
index 15f4f46..a617e29 100755 (executable)
@@ -91,7 +91,7 @@ id 3 flags signal,backup 10.0.1.3" "dump addrs after del"
 ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3
 check "ip netns exec $ns1 ./pm_nl_ctl get 4" "" "duplicate addr"
 
-ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4 id 10 flags signal
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4 flags signal
 check "ip netns exec $ns1 ./pm_nl_ctl get 4" "id 4 flags signal 10.0.1.4" "id addr increment"
 
 for i in `seq 5 9`; do
@@ -102,9 +102,10 @@ check "ip netns exec $ns1 ./pm_nl_ctl get 10" "" "above hard addr limit"
 
 for i in `seq 9 256`; do
        ip netns exec $ns1 ./pm_nl_ctl del $i
-       ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.9
+       ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.9 id $((i+1))
 done
 check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags  10.0.1.1
+id 2 flags  10.0.0.9
 id 3 flags signal,backup 10.0.1.3
 id 4 flags signal 10.0.1.4
 id 5 flags signal 10.0.1.5
@@ -127,4 +128,40 @@ ip netns exec $ns1 ./pm_nl_ctl limits 8 8
 check "ip netns exec $ns1 ./pm_nl_ctl limits" "accept 8
 subflows 8" "set limits"
 
+ip netns exec $ns1 ./pm_nl_ctl flush
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.1
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.2
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.3 id 100
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.4
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.5 id 254
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.6
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.7
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.1.8
+check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags  10.0.1.1
+id 2 flags  10.0.1.2
+id 3 flags  10.0.1.7
+id 4 flags  10.0.1.8
+id 100 flags  10.0.1.3
+id 101 flags  10.0.1.4
+id 254 flags  10.0.1.5
+id 255 flags  10.0.1.6" "set ids"
+
+ip netns exec $ns1 ./pm_nl_ctl flush
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.1
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.2 id 254
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.3
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.4
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.5 id 253
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.6
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.7
+ip netns exec $ns1 ./pm_nl_ctl add 10.0.0.8
+check "ip netns exec $ns1 ./pm_nl_ctl dump" "id 1 flags  10.0.0.1
+id 2 flags  10.0.0.4
+id 3 flags  10.0.0.6
+id 4 flags  10.0.0.7
+id 5 flags  10.0.0.8
+id 253 flags  10.0.0.5
+id 254 flags  10.0.0.2
+id 255 flags  10.0.0.3" "wrap-around ids"
+
 exit $ret
index b24a2f1..abc269e 100644 (file)
 
 static void syntax(char *argv[])
 {
-       fprintf(stderr, "%s add|get|del|flush|dump|accept [<args>]\n", argv[0]);
+       fprintf(stderr, "%s add|get|set|del|flush|dump|accept [<args>]\n", argv[0]);
        fprintf(stderr, "\tadd [flags signal|subflow|backup] [id <nr>] [dev <name>] <ip>\n");
        fprintf(stderr, "\tdel <id>\n");
        fprintf(stderr, "\tget <id>\n");
+       fprintf(stderr, "\tset <ip> [flags backup|nobackup]\n");
        fprintf(stderr, "\tflush\n");
        fprintf(stderr, "\tdump\n");
        fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n");
@@ -584,6 +585,88 @@ int get_set_limits(int fd, int pm_family, int argc, char *argv[])
        return 0;
 }
 
+int set_flags(int fd, int pm_family, int argc, char *argv[])
+{
+       char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) +
+                 NLMSG_ALIGN(sizeof(struct genlmsghdr)) +
+                 1024];
+       struct rtattr *rta, *nest;
+       struct nlmsghdr *nh;
+       u_int32_t flags = 0;
+       u_int16_t family;
+       int nest_start;
+       int off = 0;
+       int arg;
+
+       memset(data, 0, sizeof(data));
+       nh = (void *)data;
+       off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SET_FLAGS,
+                           MPTCP_PM_VER);
+
+       if (argc < 3)
+               syntax(argv);
+
+       nest_start = off;
+       nest = (void *)(data + off);
+       nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR;
+       nest->rta_len = RTA_LENGTH(0);
+       off += NLMSG_ALIGN(nest->rta_len);
+
+       /* addr data */
+       rta = (void *)(data + off);
+       if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) {
+               family = AF_INET;
+               rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4;
+               rta->rta_len = RTA_LENGTH(4);
+       } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) {
+               family = AF_INET6;
+               rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6;
+               rta->rta_len = RTA_LENGTH(16);
+       } else {
+               error(1, errno, "can't parse ip %s", argv[2]);
+       }
+       off += NLMSG_ALIGN(rta->rta_len);
+
+       /* family */
+       rta = (void *)(data + off);
+       rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY;
+       rta->rta_len = RTA_LENGTH(2);
+       memcpy(RTA_DATA(rta), &family, 2);
+       off += NLMSG_ALIGN(rta->rta_len);
+
+       for (arg = 3; arg < argc; arg++) {
+               if (!strcmp(argv[arg], "flags")) {
+                       char *tok, *str;
+
+                       /* flags */
+                       if (++arg >= argc)
+                               error(1, 0, " missing flags value");
+
+                       /* do not support flag list yet */
+                       for (str = argv[arg]; (tok = strtok(str, ","));
+                            str = NULL) {
+                               if (!strcmp(tok, "backup"))
+                                       flags |= MPTCP_PM_ADDR_FLAG_BACKUP;
+                               else if (strcmp(tok, "nobackup"))
+                                       error(1, errno,
+                                             "unknown flag %s", argv[arg]);
+                       }
+
+                       rta = (void *)(data + off);
+                       rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS;
+                       rta->rta_len = RTA_LENGTH(4);
+                       memcpy(RTA_DATA(rta), &flags, 4);
+                       off += NLMSG_ALIGN(rta->rta_len);
+               } else {
+                       error(1, 0, "unknown keyword %s", argv[arg]);
+               }
+       }
+       nest->rta_len = off - nest_start;
+
+       do_nl_req(fd, nh, off, 0);
+       return 0;
+}
+
 int main(int argc, char *argv[])
 {
        int fd, pm_family;
@@ -609,6 +692,8 @@ int main(int argc, char *argv[])
                return dump_addrs(fd, pm_family, argc, argv);
        else if (!strcmp(argv[1], "limits"))
                return get_set_limits(fd, pm_family, argc, argv);
+       else if (!strcmp(argv[1], "set"))
+               return set_flags(fd, pm_family, argc, argv);
 
        fprintf(stderr, "unknown sub-command: %s", argv[1]);
        syntax(argv);
index f75c53c..6365c7f 100644 (file)
@@ -9,6 +9,7 @@
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
+#include <sys/wait.h>
 #include <linux/tcp.h>
 #include <arpa/inet.h>
 #include <net/if.h>
@@ -17,6 +18,7 @@
 #include <fcntl.h>
 #include <libgen.h>
 #include <limits.h>
+#include <sched.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -34,6 +36,8 @@
 
 #define DEFAULT_PORT 12345
 
+#define NS_PREFIX "/run/netns/"
+
 #ifndef MAX
 #define MAX(a, b)  ((a) > (b) ? (a) : (b))
 #endif
 
 struct sock_args {
        /* local address */
+       const char *local_addr_str;
+       const char *client_local_addr_str;
        union {
                struct in_addr  in;
                struct in6_addr in6;
        } local_addr;
 
        /* remote address */
+       const char *remote_addr_str;
        union {
                struct in_addr  in;
                struct in6_addr in6;
@@ -73,10 +80,16 @@ struct sock_args {
        int use_setsockopt;
        int use_cmsg;
        const char *dev;
+       const char *server_dev;
        int ifindex;
 
+       const char *clientns;
+       const char *serverns;
+
        const char *password;
+       const char *client_pw;
        /* prefix for MD5 password */
+       const char *md5_prefix_str;
        union {
                struct sockaddr_in v4;
                struct sockaddr_in6 v6;
@@ -84,15 +97,19 @@ struct sock_args {
        unsigned int prefix_len;
 
        /* expected addresses and device index for connection */
+       const char *expected_dev;
+       const char *expected_server_dev;
        int expected_ifindex;
 
        /* local address */
+       const char *expected_laddr_str;
        union {
                struct in_addr  in;
                struct in6_addr in6;
        } expected_laddr;
 
        /* remote address */
+       const char *expected_raddr_str;
        union {
                struct in_addr  in;
                struct in6_addr in6;
@@ -186,7 +203,7 @@ static void log_address(const char *desc, struct sockaddr *sa)
        if (sa->sa_family == AF_INET) {
                struct sockaddr_in *s = (struct sockaddr_in *) sa;
 
-               log_msg("%s %s:%d",
+               log_msg("%s %s:%d\n",
                        desc,
                        inet_ntop(AF_INET, &s->sin_addr, addrstr,
                                  sizeof(addrstr)),
@@ -195,18 +212,37 @@ static void log_address(const char *desc, struct sockaddr *sa)
        } else if (sa->sa_family == AF_INET6) {
                struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) sa;
 
-               log_msg("%s [%s]:%d",
+               log_msg("%s [%s]:%d\n",
                        desc,
                        inet_ntop(AF_INET6, &s6->sin6_addr, addrstr,
                                  sizeof(addrstr)),
                        ntohs(s6->sin6_port));
        }
 
-       printf("\n");
-
        fflush(stdout);
 }
 
+static int switch_ns(const char *ns)
+{
+       char path[PATH_MAX];
+       int fd, ret;
+
+       if (geteuid())
+               log_error("warning: likely need root to set netns %s!\n", ns);
+
+       snprintf(path, sizeof(path), "%s%s", NS_PREFIX, ns);
+       fd = open(path, 0);
+       if (fd < 0) {
+               log_err_errno("Failed to open netns path; can not switch netns");
+               return 1;
+       }
+
+       ret = setns(fd, CLONE_NEWNET);
+       close(fd);
+
+       return ret;
+}
+
 static int tcp_md5sig(int sd, void *addr, socklen_t alen, struct sock_args *args)
 {
        int keylen = strlen(args->password);
@@ -259,13 +295,13 @@ static int tcp_md5_remote(int sd, struct sock_args *args)
        switch (args->version) {
        case AF_INET:
                sin.sin_port = htons(args->port);
-               sin.sin_addr = args->remote_addr.in;
+               sin.sin_addr = args->md5_prefix.v4.sin_addr;
                addr = &sin;
                alen = sizeof(sin);
                break;
        case AF_INET6:
                sin6.sin6_port = htons(args->port);
-               sin6.sin6_addr = args->remote_addr.in6;
+               sin6.sin6_addr = args->md5_prefix.v6.sin6_addr;
                addr = &sin6;
                alen = sizeof(sin6);
                break;
@@ -522,6 +558,33 @@ static int str_to_uint(const char *str, int min, int max, unsigned int *value)
        return -1;
 }
 
+static int resolve_devices(struct sock_args *args)
+{
+       if (args->dev) {
+               args->ifindex = get_ifidx(args->dev);
+               if (args->ifindex < 0) {
+                       log_error("Invalid device name\n");
+                       return 1;
+               }
+       }
+
+       if (args->expected_dev) {
+               unsigned int tmp;
+
+               if (str_to_uint(args->expected_dev, 0, INT_MAX, &tmp) == 0) {
+                       args->expected_ifindex = (int)tmp;
+               } else {
+                       args->expected_ifindex = get_ifidx(args->expected_dev);
+                       if (args->expected_ifindex < 0) {
+                               fprintf(stderr, "Invalid expected device\n");
+                               return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int expected_addr_match(struct sockaddr *sa, void *expected,
                               const char *desc)
 {
@@ -533,7 +596,7 @@ static int expected_addr_match(struct sockaddr *sa, void *expected,
                struct in_addr *exp_in = (struct in_addr *) expected;
 
                if (s->sin_addr.s_addr != exp_in->s_addr) {
-                       log_error("%s address does not match expected %s",
+                       log_error("%s address does not match expected %s\n",
                                  desc,
                                  inet_ntop(AF_INET, exp_in,
                                            addrstr, sizeof(addrstr)));
@@ -544,14 +607,14 @@ static int expected_addr_match(struct sockaddr *sa, void *expected,
                struct in6_addr *exp_in = (struct in6_addr *) expected;
 
                if (memcmp(&s6->sin6_addr, exp_in, sizeof(*exp_in))) {
-                       log_error("%s address does not match expected %s",
+                       log_error("%s address does not match expected %s\n",
                                  desc,
                                  inet_ntop(AF_INET6, exp_in,
                                            addrstr, sizeof(addrstr)));
                        rc = 1;
                }
        } else {
-               log_error("%s address does not match expected - unknown family",
+               log_error("%s address does not match expected - unknown family\n",
                          desc);
                rc = 1;
        }
@@ -599,6 +662,160 @@ static int show_sockstat(int sd, struct sock_args *args)
        return rc;
 }
 
+enum addr_type {
+       ADDR_TYPE_LOCAL,
+       ADDR_TYPE_REMOTE,
+       ADDR_TYPE_MCAST,
+       ADDR_TYPE_EXPECTED_LOCAL,
+       ADDR_TYPE_EXPECTED_REMOTE,
+       ADDR_TYPE_MD5_PREFIX,
+};
+
+static int convert_addr(struct sock_args *args, const char *_str,
+                       enum addr_type atype)
+{
+       int pfx_len_max = args->version == AF_INET6 ? 128 : 32;
+       int family = args->version;
+       char *str, *dev, *sep;
+       struct in6_addr *in6;
+       struct in_addr  *in;
+       const char *desc;
+       void *addr;
+       int rc = 0;
+
+       str = strdup(_str);
+       if (!str)
+               return -ENOMEM;
+
+       switch (atype) {
+       case ADDR_TYPE_LOCAL:
+               desc = "local";
+               addr = &args->local_addr;
+               break;
+       case ADDR_TYPE_REMOTE:
+               desc = "remote";
+               addr = &args->remote_addr;
+               break;
+       case ADDR_TYPE_MCAST:
+               desc = "mcast grp";
+               addr = &args->grp;
+               break;
+       case ADDR_TYPE_EXPECTED_LOCAL:
+               desc = "expected local";
+               addr = &args->expected_laddr;
+               break;
+       case ADDR_TYPE_EXPECTED_REMOTE:
+               desc = "expected remote";
+               addr = &args->expected_raddr;
+               break;
+       case ADDR_TYPE_MD5_PREFIX:
+               desc = "md5 prefix";
+               if (family == AF_INET) {
+                       args->md5_prefix.v4.sin_family = AF_INET;
+                       addr = &args->md5_prefix.v4.sin_addr;
+               } else if (family == AF_INET6) {
+                       args->md5_prefix.v6.sin6_family = AF_INET6;
+                       addr = &args->md5_prefix.v6.sin6_addr;
+               } else
+                       return 1;
+
+               sep = strchr(str, '/');
+               if (sep) {
+                       *sep = '\0';
+                       sep++;
+                       if (str_to_uint(sep, 1, pfx_len_max,
+                                       &args->prefix_len) != 0) {
+                               fprintf(stderr, "Invalid port\n");
+                               return 1;
+                       }
+               } else {
+                       args->prefix_len = 0;
+               }
+               break;
+       default:
+               log_error("unknown address type\n");
+               exit(1);
+       }
+
+       switch (family) {
+       case AF_INET:
+               in  = (struct in_addr *) addr;
+               if (str) {
+                       if (inet_pton(AF_INET, str, in) == 0) {
+                               log_error("Invalid %s IP address\n", desc);
+                               rc = -1;
+                               goto out;
+                       }
+               } else {
+                       in->s_addr = htonl(INADDR_ANY);
+               }
+               break;
+
+       case AF_INET6:
+               dev = strchr(str, '%');
+               if (dev) {
+                       *dev = '\0';
+                       dev++;
+               }
+
+               in6 = (struct in6_addr *) addr;
+               if (str) {
+                       if (inet_pton(AF_INET6, str, in6) == 0) {
+                               log_error("Invalid %s IPv6 address\n", desc);
+                               rc = -1;
+                               goto out;
+                       }
+               } else {
+                       *in6 = in6addr_any;
+               }
+               if (dev) {
+                       args->scope_id = get_ifidx(dev);
+                       if (args->scope_id < 0) {
+                               log_error("Invalid scope on %s IPv6 address\n",
+                                         desc);
+                               rc = -1;
+                               goto out;
+                       }
+               }
+               break;
+
+       default:
+               log_error("Invalid address family\n");
+       }
+
+out:
+       free(str);
+       return rc;
+}
+
+static int validate_addresses(struct sock_args *args)
+{
+       if (args->local_addr_str &&
+           convert_addr(args, args->local_addr_str, ADDR_TYPE_LOCAL) < 0)
+               return 1;
+
+       if (args->remote_addr_str &&
+           convert_addr(args, args->remote_addr_str, ADDR_TYPE_REMOTE) < 0)
+               return 1;
+
+       if (args->md5_prefix_str &&
+           convert_addr(args, args->md5_prefix_str,
+                        ADDR_TYPE_MD5_PREFIX) < 0)
+               return 1;
+
+       if (args->expected_laddr_str &&
+           convert_addr(args, args->expected_laddr_str,
+                        ADDR_TYPE_EXPECTED_LOCAL))
+               return 1;
+
+       if (args->expected_raddr_str &&
+           convert_addr(args, args->expected_raddr_str,
+                        ADDR_TYPE_EXPECTED_REMOTE))
+               return 1;
+
+       return 0;
+}
+
 static int get_index_from_cmsg(struct msghdr *m)
 {
        struct cmsghdr *cm;
@@ -1180,8 +1397,19 @@ err:
        return -1;
 }
 
-static int do_server(struct sock_args *args)
+static void ipc_write(int fd, int message)
+{
+       /* Not in both_mode, so there's no process to signal */
+       if (fd < 0)
+               return;
+
+       if (write(fd, &message, sizeof(message)) < 0)
+               log_err_errno("Failed to send client status");
+}
+
+static int do_server(struct sock_args *args, int ipc_fd)
 {
+       /* ipc_fd = -1 if no parent process to signal */
        struct timeval timeout = { .tv_sec = prog_timeout }, *ptval = NULL;
        unsigned char addr[sizeof(struct sockaddr_in6)] = {};
        socklen_t alen = sizeof(addr);
@@ -1190,6 +1418,20 @@ static int do_server(struct sock_args *args)
        fd_set rfds;
        int rc;
 
+       if (args->serverns) {
+               if (switch_ns(args->serverns)) {
+                       log_error("Could not set server netns to %s\n",
+                                 args->serverns);
+                       goto err_exit;
+               }
+               log_msg("Switched server netns\n");
+       }
+
+       args->dev = args->server_dev;
+       args->expected_dev = args->expected_server_dev;
+       if (resolve_devices(args) || validate_addresses(args))
+               goto err_exit;
+
        if (prog_timeout)
                ptval = &timeout;
 
@@ -1199,14 +1441,16 @@ static int do_server(struct sock_args *args)
                lsd = lsock_init(args);
 
        if (lsd < 0)
-               return 1;
+               goto err_exit;
 
        if (args->bind_test_only) {
                close(lsd);
+               ipc_write(ipc_fd, 1);
                return 0;
        }
 
        if (args->type != SOCK_STREAM) {
+               ipc_write(ipc_fd, 1);
                rc = msg_loop(0, lsd, (void *) addr, alen, args);
                close(lsd);
                return rc;
@@ -1214,11 +1458,11 @@ static int do_server(struct sock_args *args)
 
        if (args->password && tcp_md5_remote(lsd, args)) {
                close(lsd);
-               return 1;
+               goto err_exit;
        }
 
+       ipc_write(ipc_fd, 1);
        while (1) {
-               log_msg("\n");
                log_msg("waiting for client connection.\n");
                FD_ZERO(&rfds);
                FD_SET(lsd, &rfds);
@@ -1264,6 +1508,9 @@ static int do_server(struct sock_args *args)
        close(lsd);
 
        return rc;
+err_exit:
+       ipc_write(ipc_fd, 0);
+       return 1;
 }
 
 static int wait_for_connect(int sd)
@@ -1375,6 +1622,26 @@ static int do_client(struct sock_args *args)
                return 1;
        }
 
+       if (args->clientns) {
+               if (switch_ns(args->clientns)) {
+                       log_error("Could not set client netns to %s\n",
+                                 args->clientns);
+                       return 1;
+               }
+               log_msg("Switched client netns\n");
+       }
+
+       args->local_addr_str = args->client_local_addr_str;
+       if (resolve_devices(args) || validate_addresses(args))
+               return 1;
+
+       if ((args->use_setsockopt || args->use_cmsg) && !args->ifindex) {
+               fprintf(stderr, "Device binding not specified\n");
+               return 1;
+       }
+       if (args->use_setsockopt || args->use_cmsg)
+               args->dev = NULL;
+
        switch (args->version) {
        case AF_INET:
                sin.sin_port = htons(args->port);
@@ -1394,6 +1661,8 @@ static int do_client(struct sock_args *args)
                break;
        }
 
+       args->password = args->client_pw;
+
        if (args->has_grp)
                sd = msock_client(args);
        else
@@ -1419,132 +1688,6 @@ out:
        return rc;
 }
 
-enum addr_type {
-       ADDR_TYPE_LOCAL,
-       ADDR_TYPE_REMOTE,
-       ADDR_TYPE_MCAST,
-       ADDR_TYPE_EXPECTED_LOCAL,
-       ADDR_TYPE_EXPECTED_REMOTE,
-       ADDR_TYPE_MD5_PREFIX,
-};
-
-static int convert_addr(struct sock_args *args, const char *_str,
-                       enum addr_type atype)
-{
-       int pfx_len_max = args->version == AF_INET6 ? 128 : 32;
-       int family = args->version;
-       char *str, *dev, *sep;
-       struct in6_addr *in6;
-       struct in_addr  *in;
-       const char *desc;
-       void *addr;
-       int rc = 0;
-
-       str = strdup(_str);
-       if (!str)
-               return -ENOMEM;
-
-       switch (atype) {
-       case ADDR_TYPE_LOCAL:
-               desc = "local";
-               addr = &args->local_addr;
-               break;
-       case ADDR_TYPE_REMOTE:
-               desc = "remote";
-               addr = &args->remote_addr;
-               break;
-       case ADDR_TYPE_MCAST:
-               desc = "mcast grp";
-               addr = &args->grp;
-               break;
-       case ADDR_TYPE_EXPECTED_LOCAL:
-               desc = "expected local";
-               addr = &args->expected_laddr;
-               break;
-       case ADDR_TYPE_EXPECTED_REMOTE:
-               desc = "expected remote";
-               addr = &args->expected_raddr;
-               break;
-       case ADDR_TYPE_MD5_PREFIX:
-               desc = "md5 prefix";
-               if (family == AF_INET) {
-                       args->md5_prefix.v4.sin_family = AF_INET;
-                       addr = &args->md5_prefix.v4.sin_addr;
-               } else if (family == AF_INET6) {
-                       args->md5_prefix.v6.sin6_family = AF_INET6;
-                       addr = &args->md5_prefix.v6.sin6_addr;
-               } else
-                       return 1;
-
-               sep = strchr(str, '/');
-               if (sep) {
-                       *sep = '\0';
-                       sep++;
-                       if (str_to_uint(sep, 1, pfx_len_max,
-                                       &args->prefix_len) != 0) {
-                               fprintf(stderr, "Invalid port\n");
-                               return 1;
-                       }
-               } else {
-                       args->prefix_len = pfx_len_max;
-               }
-               break;
-       default:
-               log_error("unknown address type");
-               exit(1);
-       }
-
-       switch (family) {
-       case AF_INET:
-               in  = (struct in_addr *) addr;
-               if (str) {
-                       if (inet_pton(AF_INET, str, in) == 0) {
-                               log_error("Invalid %s IP address\n", desc);
-                               rc = -1;
-                               goto out;
-                       }
-               } else {
-                       in->s_addr = htonl(INADDR_ANY);
-               }
-               break;
-
-       case AF_INET6:
-               dev = strchr(str, '%');
-               if (dev) {
-                       *dev = '\0';
-                       dev++;
-               }
-
-               in6 = (struct in6_addr *) addr;
-               if (str) {
-                       if (inet_pton(AF_INET6, str, in6) == 0) {
-                               log_error("Invalid %s IPv6 address\n", desc);
-                               rc = -1;
-                               goto out;
-                       }
-               } else {
-                       *in6 = in6addr_any;
-               }
-               if (dev) {
-                       args->scope_id = get_ifidx(dev);
-                       if (args->scope_id < 0) {
-                               log_error("Invalid scope on %s IPv6 address\n",
-                                         desc);
-                               rc = -1;
-                               goto out;
-                       }
-               }
-               break;
-
-       default:
-               log_error("Invalid address family\n");
-       }
-
-out:
-       free(str);
-       return rc;
-}
-
 static char *random_msg(int len)
 {
        int i, n = 0, olen = len + 1;
@@ -1568,7 +1711,68 @@ static char *random_msg(int len)
        return m;
 }
 
-#define GETOPT_STR  "sr:l:p:t:g:P:DRn:M:m:d:SCi6L:0:1:2:Fbq"
+static int ipc_child(int fd, struct sock_args *args)
+{
+       char *outbuf, *errbuf;
+       int rc = 1;
+
+       outbuf = malloc(4096);
+       errbuf = malloc(4096);
+       if (!outbuf || !errbuf) {
+               fprintf(stderr, "server: Failed to allocate buffers for stdout and stderr\n");
+               goto out;
+       }
+
+       setbuffer(stdout, outbuf, 4096);
+       setbuffer(stderr, errbuf, 4096);
+
+       server_mode = 1; /* to tell log_msg in case we are in both_mode */
+
+       /* when running in both mode, address validation applies
+        * solely to client side
+        */
+       args->has_expected_laddr = 0;
+       args->has_expected_raddr = 0;
+
+       rc = do_server(args, fd);
+
+out:
+       free(outbuf);
+       free(errbuf);
+
+       return rc;
+}
+
+static int ipc_parent(int cpid, int fd, struct sock_args *args)
+{
+       int client_status;
+       int status;
+       int buf;
+
+       /* do the client-side function here in the parent process,
+        * waiting to be told when to continue
+        */
+       if (read(fd, &buf, sizeof(buf)) <= 0) {
+               log_err_errno("Failed to read IPC status from status");
+               return 1;
+       }
+       if (!buf) {
+               log_error("Server failed; can not continue\n");
+               return 1;
+       }
+       log_msg("Server is ready\n");
+
+       client_status = do_client(args);
+       log_msg("parent is done!\n");
+
+       if (kill(cpid, 0) == 0)
+               kill(cpid, SIGKILL);
+
+       wait(&status);
+       return client_status;
+}
+
+#define GETOPT_STR  "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SCi6L:0:1:2:3:Fbq"
 
 static void print_usage(char *prog)
 {
@@ -1582,13 +1786,18 @@ static void print_usage(char *prog)
        "    -t            timeout seconds (default: none)\n"
        "\n"
        "Optional:\n"
+       "    -B            do both client and server via fork and IPC\n"
+       "    -N ns         set client to network namespace ns (requires root)\n"
+       "    -O ns         set server to network namespace ns (requires root)\n"
        "    -F            Restart server loop\n"
        "    -6            IPv6 (default is IPv4)\n"
        "    -P proto      protocol for socket: icmp, ospf (default: none)\n"
        "    -D|R          datagram (D) / raw (R) socket (default stream)\n"
-       "    -l addr       local address to bind to\n"
+       "    -l addr       local address to bind to in server mode\n"
+       "    -c addr       local address to bind to in client mode\n"
        "\n"
        "    -d dev        bind socket to given device name\n"
+       "    -I dev        bind socket to given device name - server mode\n"
        "    -S            use setsockopt (IP_UNICAST_IF or IP_MULTICAST_IF)\n"
        "                  to set device binding\n"
        "    -C            use cmsg and IP_PKTINFO to specify device binding\n"
@@ -1597,6 +1806,7 @@ static void print_usage(char *prog)
        "    -n num        number of times to send message\n"
        "\n"
        "    -M password   use MD5 sum protection\n"
+       "    -X password   MD5 password for client mode\n"
        "    -m prefix/len prefix and length to use for MD5 key\n"
        "    -g grp        multicast group (e.g., 239.1.1.1)\n"
        "    -i            interactive mode (default is echo and terminate)\n"
@@ -1604,6 +1814,7 @@ static void print_usage(char *prog)
        "    -0 addr       Expected local address\n"
        "    -1 addr       Expected remote address\n"
        "    -2 dev        Expected device name (or index) to receive packet\n"
+       "    -3 dev        Expected device name (or index) to receive packets - server mode\n"
        "\n"
        "    -b            Bind test only.\n"
        "    -q            Be quiet. Run test without printing anything.\n"
@@ -1618,8 +1829,11 @@ int main(int argc, char *argv[])
                .port    = DEFAULT_PORT,
        };
        struct protoent *pe;
+       int both_mode = 0;
        unsigned int tmp;
        int forever = 0;
+       int fd[2];
+       int cpid;
 
        /* process inputs */
        extern char *optarg;
@@ -1631,6 +1845,9 @@ int main(int argc, char *argv[])
 
        while ((rc = getopt(argc, argv, GETOPT_STR)) != -1) {
                switch (rc) {
+               case 'B':
+                       both_mode = 1;
+                       break;
                case 's':
                        server_mode = 1;
                        break;
@@ -1639,13 +1856,15 @@ int main(int argc, char *argv[])
                        break;
                case 'l':
                        args.has_local_ip = 1;
-                       if (convert_addr(&args, optarg, ADDR_TYPE_LOCAL) < 0)
-                               return 1;
+                       args.local_addr_str = optarg;
                        break;
                case 'r':
                        args.has_remote_ip = 1;
-                       if (convert_addr(&args, optarg, ADDR_TYPE_REMOTE) < 0)
-                               return 1;
+                       args.remote_addr_str = optarg;
+                       break;
+               case 'c':
+                       args.has_local_ip = 1;
+                       args.client_local_addr_str = optarg;
                        break;
                case 'p':
                        if (str_to_uint(optarg, 1, 65535, &tmp) != 0) {
@@ -1685,15 +1904,23 @@ int main(int argc, char *argv[])
                case 'n':
                        iter = atoi(optarg);
                        break;
+               case 'N':
+                       args.clientns = optarg;
+                       break;
+               case 'O':
+                       args.serverns = optarg;
+                       break;
                case 'L':
                        msg = random_msg(atoi(optarg));
                        break;
                case 'M':
                        args.password = optarg;
                        break;
+               case 'X':
+                       args.client_pw = optarg;
+                       break;
                case 'm':
-                       if (convert_addr(&args, optarg, ADDR_TYPE_MD5_PREFIX) < 0)
-                               return 1;
+                       args.md5_prefix_str = optarg;
                        break;
                case 'S':
                        args.use_setsockopt = 1;
@@ -1703,11 +1930,9 @@ int main(int argc, char *argv[])
                        break;
                case 'd':
                        args.dev = optarg;
-                       args.ifindex = get_ifidx(optarg);
-                       if (args.ifindex < 0) {
-                               fprintf(stderr, "Invalid device name\n");
-                               return 1;
-                       }
+                       break;
+               case 'I':
+                       args.server_dev = optarg;
                        break;
                case 'i':
                        interactive = 1;
@@ -1726,28 +1951,17 @@ int main(int argc, char *argv[])
                        break;
                case '0':
                        args.has_expected_laddr = 1;
-                       if (convert_addr(&args, optarg,
-                                        ADDR_TYPE_EXPECTED_LOCAL))
-                               return 1;
+                       args.expected_laddr_str = optarg;
                        break;
                case '1':
                        args.has_expected_raddr = 1;
-                       if (convert_addr(&args, optarg,
-                                        ADDR_TYPE_EXPECTED_REMOTE))
-                               return 1;
-
+                       args.expected_raddr_str = optarg;
                        break;
                case '2':
-                       if (str_to_uint(optarg, 0, INT_MAX, &tmp) == 0) {
-                               args.expected_ifindex = (int)tmp;
-                       } else {
-                               args.expected_ifindex = get_ifidx(optarg);
-                               if (args.expected_ifindex < 0) {
-                                       fprintf(stderr,
-                                               "Invalid expected device\n");
-                                       return 1;
-                               }
-                       }
+                       args.expected_dev = optarg;
+                       break;
+               case '3':
+                       args.expected_server_dev = optarg;
                        break;
                case 'q':
                        quiet = 1;
@@ -1759,23 +1973,17 @@ int main(int argc, char *argv[])
        }
 
        if (args.password &&
-           ((!args.has_remote_ip && !args.prefix_len) || args.type != SOCK_STREAM)) {
+           ((!args.has_remote_ip && !args.md5_prefix_str) ||
+             args.type != SOCK_STREAM)) {
                log_error("MD5 passwords apply to TCP only and require a remote ip for the password\n");
                return 1;
        }
 
-       if (args.prefix_len && !args.password) {
+       if (args.md5_prefix_str && !args.password) {
                log_error("Prefix range for MD5 protection specified without a password\n");
                return 1;
        }
 
-       if ((args.use_setsockopt || args.use_cmsg) && !args.ifindex) {
-               fprintf(stderr, "Device binding not specified\n");
-               return 1;
-       }
-       if (args.use_setsockopt || args.use_cmsg)
-               args.dev = NULL;
-
        if (iter == 0) {
                fprintf(stderr, "Invalid number of messages to send\n");
                return 1;
@@ -1792,7 +2000,7 @@ int main(int argc, char *argv[])
                return 1;
        }
 
-       if (!server_mode && !args.has_grp &&
+       if ((both_mode || !server_mode) && !args.has_grp &&
            !args.has_remote_ip && !args.has_local_ip) {
                fprintf(stderr,
                        "Local (server mode) or remote IP (client IP) required\n");
@@ -1804,9 +2012,26 @@ int main(int argc, char *argv[])
                msg = NULL;
        }
 
+       if (both_mode) {
+               if (pipe(fd) < 0) {
+                       perror("pipe");
+                       exit(1);
+               }
+
+               cpid = fork();
+               if (cpid < 0) {
+                       perror("fork");
+                       exit(1);
+               }
+               if (cpid)
+                       return ipc_parent(cpid, fd[0], &args);
+
+               return ipc_child(fd[1], &args);
+       }
+
        if (server_mode) {
                do {
-                       rc = do_server(&args);
+                       rc = do_server(&args, -1);
                } while (forever);
 
                return rc;
index 464e31e..64cd2e2 100755 (executable)
 # - list_flush_ipv6_exception
 #      Using the same topology as in pmtu_ipv6, create exceptions, and check
 #      they are shown when listing exception caches, gone after flushing them
-
+#
+# - pmtu_ipv4_route_change
+#      Use the same topology as in pmtu_ipv4, but issue a route replacement
+#      command and delete the corresponding device afterward. This tests for
+#      proper cleanup of the PMTU exceptions by the route replacement path.
+#      Device unregistration should complete successfully
+#
+# - pmtu_ipv6_route_change
+#      Same as above but with IPv6
 
 # Kselftest framework requirement - SKIP code is 4.
 ksft_skip=4
@@ -224,7 +232,9 @@ tests="
        cleanup_ipv4_exception          ipv4: cleanup of cached exceptions      1
        cleanup_ipv6_exception          ipv6: cleanup of cached exceptions      1
        list_flush_ipv4_exception       ipv4: list and flush cached exceptions  1
-       list_flush_ipv6_exception       ipv6: list and flush cached exceptions  1"
+       list_flush_ipv6_exception       ipv6: list and flush cached exceptions  1
+       pmtu_ipv4_route_change          ipv4: PMTU exception w/route replace    1
+       pmtu_ipv6_route_change          ipv6: PMTU exception w/route replace    1"
 
 NS_A="ns-A"
 NS_B="ns-B"
@@ -1782,6 +1792,63 @@ test_list_flush_ipv6_exception() {
        return ${fail}
 }
 
+test_pmtu_ipvX_route_change() {
+       family=${1}
+
+       setup namespaces routing || return 2
+       trace "${ns_a}"  veth_A-R1    "${ns_r1}" veth_R1-A \
+             "${ns_r1}" veth_R1-B    "${ns_b}"  veth_B-R1 \
+             "${ns_a}"  veth_A-R2    "${ns_r2}" veth_R2-A \
+             "${ns_r2}" veth_R2-B    "${ns_b}"  veth_B-R2
+
+       if [ ${family} -eq 4 ]; then
+               ping=ping
+               dst1="${prefix4}.${b_r1}.1"
+               dst2="${prefix4}.${b_r2}.1"
+               gw="${prefix4}.${a_r1}.2"
+       else
+               ping=${ping6}
+               dst1="${prefix6}:${b_r1}::1"
+               dst2="${prefix6}:${b_r2}::1"
+               gw="${prefix6}:${a_r1}::2"
+       fi
+
+       # Set up initial MTU values
+       mtu "${ns_a}"  veth_A-R1 2000
+       mtu "${ns_r1}" veth_R1-A 2000
+       mtu "${ns_r1}" veth_R1-B 1400
+       mtu "${ns_b}"  veth_B-R1 1400
+
+       mtu "${ns_a}"  veth_A-R2 2000
+       mtu "${ns_r2}" veth_R2-A 2000
+       mtu "${ns_r2}" veth_R2-B 1500
+       mtu "${ns_b}"  veth_B-R2 1500
+
+       # Create route exceptions
+       run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s 1800 ${dst1}
+       run_cmd ${ns_a} ${ping} -q -M want -i 0.1 -w 1 -s 1800 ${dst2}
+
+       # Check that exceptions have been created with the correct PMTU
+       pmtu_1="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst1})"
+       check_pmtu_value "1400" "${pmtu_1}" "exceeding MTU" || return 1
+       pmtu_2="$(route_get_dst_pmtu_from_exception "${ns_a}" ${dst2})"
+       check_pmtu_value "1500" "${pmtu_2}" "exceeding MTU" || return 1
+
+       # Replace the route from A to R1
+       run_cmd ${ns_a} ip route change default via ${gw}
+
+       # Delete the device in A
+       run_cmd ${ns_a} ip link del "veth_A-R1"
+}
+
+test_pmtu_ipv4_route_change() {
+       test_pmtu_ipvX_route_change 4
+}
+
+test_pmtu_ipv6_route_change() {
+       test_pmtu_ipvX_route_change 6
+}
+
 usage() {
        echo
        echo "$0 [OPTIONS] [TEST]..."
index cb0d189..e0088c2 100644 (file)
@@ -103,8 +103,8 @@ FIXTURE(tls)
 
 FIXTURE_VARIANT(tls)
 {
-       u16 tls_version;
-       u16 cipher_type;
+       uint16_t tls_version;
+       uint16_t cipher_type;
 };
 
 FIXTURE_VARIANT_ADD(tls, 12_gcm)
index ac2a30b..f8a19f5 100755 (executable)
@@ -5,6 +5,14 @@
 
 readonly PEER_NS="ns-peer-$(mktemp -u XXXXXX)"
 
+# set global exit status, but never reset nonzero one.
+check_err()
+{
+       if [ $ret -eq 0 ]; then
+               ret=$1
+       fi
+}
+
 cleanup() {
        local -r jobs="$(jobs -p)"
        local -r ns="$(ip netns list|grep $PEER_NS)"
@@ -44,7 +52,9 @@ run_one() {
        # Hack: let bg programs complete the startup
        sleep 0.1
        ./udpgso_bench_tx ${tx_args}
+       ret=$?
        wait $(jobs -p)
+       return $ret
 }
 
 run_test() {
@@ -87,8 +97,10 @@ run_one_nat() {
 
        sleep 0.1
        ./udpgso_bench_tx ${tx_args}
+       ret=$?
        kill -INT $pid
        wait $(jobs -p)
+       return $ret
 }
 
 run_one_2sock() {
@@ -110,7 +122,9 @@ run_one_2sock() {
        sleep 0.1
        # first UDP GSO socket should be closed at this point
        ./udpgso_bench_tx ${tx_args}
+       ret=$?
        wait $(jobs -p)
+       return $ret
 }
 
 run_nat_test() {
@@ -131,36 +145,54 @@ run_all() {
        local -r core_args="-l 4"
        local -r ipv4_args="${core_args} -4 -D 192.168.1.1"
        local -r ipv6_args="${core_args} -6 -D 2001:db8::1"
+       ret=0
 
        echo "ipv4"
        run_test "no GRO" "${ipv4_args} -M 10 -s 1400" "-4 -n 10 -l 1400"
+       check_err $?
 
        # explicitly check we are not receiving UDP_SEGMENT cmsg (-S -1)
        # when GRO does not take place
        run_test "no GRO chk cmsg" "${ipv4_args} -M 10 -s 1400" "-4 -n 10 -l 1400 -S -1"
+       check_err $?
 
        # the GSO packets are aggregated because:
        # * veth schedule napi after each xmit
        # * segmentation happens in BH context, veth napi poll is delayed after
        #   the transmission of the last segment
        run_test "GRO" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720"
+       check_err $?
        run_test "GRO chk cmsg" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
+       check_err $?
        run_test "GRO with custom segment size" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720"
+       check_err $?
        run_test "GRO with custom segment size cmsg" "${ipv4_args} -M 1 -s 14720 -S 500 " "-4 -n 1 -l 14720 -S 500"
+       check_err $?
 
        run_nat_test "bad GRO lookup" "${ipv4_args} -M 1 -s 14720 -S 0" "-n 10 -l 1472"
+       check_err $?
        run_2sock_test "multiple GRO socks" "${ipv4_args} -M 1 -s 14720 -S 0 " "-4 -n 1 -l 14720 -S 1472"
+       check_err $?
 
        echo "ipv6"
        run_test "no GRO" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400"
+       check_err $?
        run_test "no GRO chk cmsg" "${ipv6_args} -M 10 -s 1400" "-n 10 -l 1400 -S -1"
+       check_err $?
        run_test "GRO" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 1 -l 14520"
+       check_err $?
        run_test "GRO chk cmsg" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 1 -l 14520 -S 1452"
+       check_err $?
        run_test "GRO with custom segment size" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520"
+       check_err $?
        run_test "GRO with custom segment size cmsg" "${ipv6_args} -M 1 -s 14520 -S 500" "-n 1 -l 14520 -S 500"
+       check_err $?
 
        run_nat_test "bad GRO lookup" "${ipv6_args} -M 1 -s 14520 -S 0" "-n 10 -l 1452"
+       check_err $?
        run_2sock_test "multiple GRO socks" "${ipv6_args} -M 1 -s 14520 -S 0 " "-n 1 -l 14520 -S 1452"
+       check_err $?
+       return $ret
 }
 
 if [ ! -f ../bpf/xdp_dummy.o ]; then
@@ -180,3 +212,5 @@ elif [[ $1 == "__subprocess_2sock" ]]; then
        shift
        run_one_2sock $@
 fi
+
+exit $?
diff --git a/tools/testing/selftests/net/unicast_extensions.sh b/tools/testing/selftests/net/unicast_extensions.sh
new file mode 100755 (executable)
index 0000000..dbf0421
--- /dev/null
@@ -0,0 +1,228 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# By Seth Schoen (c) 2021, for the IPv4 Unicast Extensions Project
+# Thanks to David Ahern for help and advice on nettest modifications.
+#
+# Self-tests for IPv4 address extensions: the kernel's ability to accept
+# certain traditionally unused or unallocated IPv4 addresses. For each kind
+# of address, we test for interface assignment, ping, TCP, and forwarding.
+# Must be run as root (to manipulate network namespaces and virtual
+# interfaces).
+#
+# Things we test for here:
+#
+# * Currently the kernel accepts addresses in 0/8 and 240/4 as valid.
+#
+# * Notwithstanding that, 0.0.0.0 and 255.255.255.255 cannot be assigned.
+#
+# * Currently the kernel DOES NOT accept unicast use of the lowest
+#   address in an IPv4 subnet (e.g. 192.168.100.0/32 in 192.168.100.0/24).
+#   This is treated as a second broadcast address, for compatibility
+#   with 4.2BSD (!).
+#
+# * Currently the kernel DOES NOT accept unicast use of any of 127/8.
+#
+# * Currently the kernel DOES NOT accept unicast use of any of 224/4.
+#
+# These tests provide an easy way to flip the expected result of any
+# of these behaviors for testing kernel patches that change them.
+
+# nettest can be run from PATH or from same directory as this selftest
+if ! which nettest >/dev/null; then
+       PATH=$PWD:$PATH
+       if ! which nettest >/dev/null; then
+               echo "'nettest' command not found; skipping tests"
+               exit 0
+       fi
+fi
+
+result=0
+
+hide_output(){ exec 3>&1 4>&2 >/dev/null 2>/dev/null; }
+show_output(){ exec >&3 2>&4; }
+
+show_result(){
+       if [ $1 -eq 0 ]; then
+               printf "TEST: %-60s  [ OK ]\n" "${2}"
+       else
+               printf "TEST: %-60s  [FAIL]\n" "${2}"
+               result=1
+       fi
+}
+
+_do_segmenttest(){
+       # Perform a simple set of link tests between a pair of
+       # IP addresses on a shared (virtual) segment, using
+       # ping and nettest.
+       # foo --- bar
+       # Arguments: ip_a ip_b prefix_length test_description
+       #
+       # Caller must set up foo-ns and bar-ns namespaces
+       # containing linked veth devices foo and bar,
+       # respectively.
+
+       ip -n foo-ns address add $1/$3 dev foo || return 1
+       ip -n foo-ns link set foo up || return 1
+       ip -n bar-ns address add $2/$3 dev bar || return 1
+       ip -n bar-ns link set bar up || return 1
+
+       ip netns exec foo-ns timeout 2 ping -c 1 $2 || return 1
+       ip netns exec bar-ns timeout 2 ping -c 1 $1 || return 1
+
+       nettest -B -N bar-ns -O foo-ns -r $1 || return 1
+       nettest -B -N foo-ns -O bar-ns -r $2 || return 1
+
+       return 0
+}
+
+_do_route_test(){
+       # Perform a simple set of gateway tests.
+       #
+       # [foo] <---> [foo1]-[bar1] <---> [bar]   /prefix
+       #  host          gateway          host
+       #
+       # Arguments: foo_ip foo1_ip bar1_ip bar_ip prefix_len test_description
+       # Displays test result and returns success or failure.
+
+       # Caller must set up foo-ns, bar-ns, and router-ns
+       # containing linked veth devices foo-foo1, bar1-bar
+       # (foo in foo-ns, foo1 and bar1 in router-ns, and
+       # bar in bar-ns).
+
+       ip -n foo-ns address add $1/$5 dev foo || return 1
+       ip -n foo-ns link set foo up || return 1
+       ip -n foo-ns route add default via $2 || return 1
+       ip -n bar-ns address add $4/$5 dev bar || return 1
+       ip -n bar-ns link set bar up || return 1
+       ip -n bar-ns route add default via $3 || return 1
+       ip -n router-ns address add $2/$5 dev foo1 || return 1
+       ip -n router-ns link set foo1 up || return 1
+       ip -n router-ns address add $3/$5 dev bar1 || return 1
+       ip -n router-ns link set bar1 up || return 1
+
+       echo 1 | ip netns exec router-ns tee /proc/sys/net/ipv4/ip_forward
+
+       ip netns exec foo-ns timeout 2 ping -c 1 $2 || return 1
+       ip netns exec foo-ns timeout 2 ping -c 1 $4 || return 1
+       ip netns exec bar-ns timeout 2 ping -c 1 $3 || return 1
+       ip netns exec bar-ns timeout 2 ping -c 1 $1 || return 1
+
+       nettest -B -N bar-ns -O foo-ns -r $1 || return 1
+       nettest -B -N foo-ns -O bar-ns -r $4 || return 1
+
+       return 0
+}
+
+segmenttest(){
+       # Sets up veth link and tries to connect over it.
+       # Arguments: ip_a ip_b prefix_len test_description
+       hide_output
+       ip netns add foo-ns
+       ip netns add bar-ns
+       ip link add foo netns foo-ns type veth peer name bar netns bar-ns
+
+       test_result=0
+       _do_segmenttest "$@" || test_result=1
+
+       ip netns pids foo-ns | xargs -r kill -9
+       ip netns pids bar-ns | xargs -r kill -9
+       ip netns del foo-ns
+       ip netns del bar-ns
+       show_output
+
+       # inverted tests will expect failure instead of success
+       [ -n "$expect_failure" ] && test_result=`expr 1 - $test_result`
+
+       show_result $test_result "$4"
+}
+
+route_test(){
+       # Sets up a simple gateway and tries to connect through it.
+       # [foo] <---> [foo1]-[bar1] <---> [bar]   /prefix
+       # Arguments: foo_ip foo1_ip bar1_ip bar_ip prefix_len test_description
+       # Returns success or failure.
+
+       hide_output
+       ip netns add foo-ns
+       ip netns add bar-ns
+       ip netns add router-ns
+       ip link add foo netns foo-ns type veth peer name foo1 netns router-ns
+       ip link add bar netns bar-ns type veth peer name bar1 netns router-ns
+
+       test_result=0
+       _do_route_test "$@" || test_result=1
+
+       ip netns pids foo-ns | xargs -r kill -9
+       ip netns pids bar-ns | xargs -r kill -9
+       ip netns pids router-ns | xargs -r kill -9
+       ip netns del foo-ns
+       ip netns del bar-ns
+       ip netns del router-ns
+
+       show_output
+
+       # inverted tests will expect failure instead of success
+       [ -n "$expect_failure" ] && test_result=`expr 1 - $test_result`
+       show_result $test_result "$6"
+}
+
+echo "###########################################################################"
+echo "Unicast address extensions tests (behavior of reserved IPv4 addresses)"
+echo "###########################################################################"
+#
+# Test support for 240/4
+segmenttest 240.1.2.1   240.1.2.4    24 "assign and ping within 240/4 (1 of 2) (is allowed)"
+segmenttest 250.100.2.1 250.100.30.4 16 "assign and ping within 240/4 (2 of 2) (is allowed)"
+#
+# Test support for 0/8
+segmenttest 0.1.2.17    0.1.2.23  24 "assign and ping within 0/8 (1 of 2) (is allowed)"
+segmenttest 0.77.240.17 0.77.2.23 16 "assign and ping within 0/8 (2 of 2) (is allowed)"
+#
+# Even 255.255/16 is OK!
+segmenttest 255.255.3.1 255.255.50.77 16 "assign and ping inside 255.255/16 (is allowed)"
+#
+# Or 255.255.255/24
+segmenttest 255.255.255.1 255.255.255.254 24 "assign and ping inside 255.255.255/24 (is allowed)"
+#
+# Routing between different networks
+route_test 240.5.6.7 240.5.6.1  255.1.2.1    255.1.2.3      24 "route between 240.5.6/24 and 255.1.2/24 (is allowed)"
+route_test 0.200.6.7 0.200.38.1 245.99.101.1 245.99.200.111 16 "route between 0.200/16 and 245.99/16 (is allowed)"
+#
+# ==============================================
+# ==== TESTS THAT CURRENTLY EXPECT FAILURE =====
+# ==============================================
+expect_failure=true
+# It should still not be possible to use 0.0.0.0 or 255.255.255.255
+# as a unicast address.  Thus, these tests expect failure.
+segmenttest 0.0.1.5       0.0.0.0         16 "assigning 0.0.0.0 (is forbidden)"
+segmenttest 255.255.255.1 255.255.255.255 16 "assigning 255.255.255.255 (is forbidden)"
+#
+# Test support for not having all of 127 be loopback
+# Currently Linux does not allow this, so this should fail too
+segmenttest 127.99.4.5 127.99.4.6 16 "assign and ping inside 127/8 (is forbidden)"
+#
+# Test support for lowest address
+# Currently Linux does not allow this, so this should fail too
+segmenttest 5.10.15.20 5.10.15.0 24 "assign and ping lowest address (is forbidden)"
+#
+# Routing using lowest address as a gateway/endpoint
+# Currently Linux does not allow this, so this should fail too
+route_test 192.168.42.1 192.168.42.0 9.8.7.6 9.8.7.0 24 "routing using lowest address (is forbidden)"
+#
+# Test support for unicast use of class D
+# Currently Linux does not allow this, so this should fail too
+segmenttest 225.1.2.3 225.1.2.200 24 "assign and ping class D address (is forbidden)"
+#
+# Routing using class D as a gateway
+route_test 225.1.42.1 225.1.42.2 9.8.7.6 9.8.7.1 24 "routing using class D (is forbidden)"
+#
+# Routing using 127/8
+# Currently Linux does not allow this, so this should fail too
+route_test 127.99.2.3 127.99.2.4 200.1.2.3 200.1.2.4 24 "routing using 127/8 (is forbidden)"
+#
+unset expect_failure
+# =====================================================
+# ==== END OF TESTS THAT CURRENTLY EXPECT FAILURE =====
+# =====================================================
+exit ${result}
index 7a1bf94..bdf450e 100755 (executable)
@@ -202,7 +202,7 @@ check_xfrm() {
        # 1: iptables -m policy rule count != 0
        rval=$1
        ip=$2
-       lret=0
+       local lret=0
 
        ip netns exec ns1 ping -q -c 1 10.0.2.$ip > /dev/null
 
@@ -287,6 +287,47 @@ check_hthresh_repeat()
        return 0
 }
 
+# insert non-overlapping policies in a random order and check that
+# all of them can be fetched using the traffic selectors.
+check_random_order()
+{
+       local ns=$1
+       local log=$2
+
+       for i in $(seq 100); do
+               ip -net $ns xfrm policy flush
+               for j in $(seq 0 16 255 | sort -R); do
+                       ip -net $ns xfrm policy add dst $j.0.0.0/24 dir out priority 10 action allow
+               done
+               for j in $(seq 0 16 255); do
+                       if ! ip -net $ns xfrm policy get dst $j.0.0.0/24 dir out > /dev/null; then
+                               echo "FAIL: $log" 1>&2
+                               return 1
+                       fi
+               done
+       done
+
+       for i in $(seq 100); do
+               ip -net $ns xfrm policy flush
+               for j in $(seq 0 16 255 | sort -R); do
+                       local addr=$(printf "e000:0000:%02x00::/56" $j)
+                       ip -net $ns xfrm policy add dst $addr dir out priority 10 action allow
+               done
+               for j in $(seq 0 16 255); do
+                       local addr=$(printf "e000:0000:%02x00::/56" $j)
+                       if ! ip -net $ns xfrm policy get dst $addr dir out > /dev/null; then
+                               echo "FAIL: $log" 1>&2
+                               return 1
+                       fi
+               done
+       done
+
+       ip -net $ns xfrm policy flush
+
+       echo "PASS: $log"
+       return 0
+}
+
 #check for needed privileges
 if [ "$(id -u)" -ne 0 ];then
        echo "SKIP: Need root privileges"
@@ -438,6 +479,8 @@ check_exceptions "exceptions and block policies after htresh change to normal"
 
 check_hthresh_repeat "policies with repeated htresh change"
 
+check_random_order ns3 "policies inserted in random order"
+
 for i in 1 2 3 4;do ip netns del ns$i;done
 
 exit $ret
index a374e10..3006a8e 100644 (file)
@@ -4,7 +4,8 @@
 TEST_PROGS := nft_trans_stress.sh nft_nat.sh bridge_brouter.sh \
        conntrack_icmp_related.sh nft_flowtable.sh ipvs.sh \
        nft_concat_range.sh nft_conntrack_helper.sh \
-       nft_queue.sh nft_meta.sh
+       nft_queue.sh nft_meta.sh \
+       ipip-conntrack-mtu.sh
 
 LDLIBS = -lmnl
 TEST_GEN_FILES =  nf-queue
diff --git a/tools/testing/selftests/netfilter/ipip-conntrack-mtu.sh b/tools/testing/selftests/netfilter/ipip-conntrack-mtu.sh
new file mode 100755 (executable)
index 0000000..4a6f5c3
--- /dev/null
@@ -0,0 +1,206 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+# Conntrack needs to reassemble fragments in order to have complete
+# packets for rule matching.  Reassembly can lead to packet loss.
+
+# Consider the following setup:
+#            +--------+       +---------+       +--------+
+#            |Router A|-------|Wanrouter|-------|Router B|
+#            |        |.IPIP..|         |..IPIP.|        |
+#            +--------+       +---------+       +--------+
+#           /                  mtu 1400                   \
+#          /                                               \
+#+--------+                                                 +--------+
+#|Client A|                                                 |Client B|
+#|        |                                                 |        |
+#+--------+                                                 +--------+
+
+# Router A and Router B use IPIP tunnel interfaces to tunnel traffic
+# between Client A and Client B over WAN. Wanrouter has MTU 1400 set
+# on its interfaces.
+
+rnd=$(mktemp -u XXXXXXXX)
+rx=$(mktemp)
+
+r_a="ns-ra-$rnd"
+r_b="ns-rb-$rnd"
+r_w="ns-rw-$rnd"
+c_a="ns-ca-$rnd"
+c_b="ns-cb-$rnd"
+
+checktool (){
+       if ! $1 > /dev/null 2>&1; then
+               echo "SKIP: Could not $2"
+               exit $ksft_skip
+       fi
+}
+
+checktool "iptables --version" "run test without iptables"
+checktool "ip -Version" "run test without ip tool"
+checktool "which nc" "run test without nc (netcat)"
+checktool "ip netns add ${r_a}" "create net namespace"
+
+for n in ${r_b} ${r_w} ${c_a} ${c_b};do
+       ip netns add ${n}
+done
+
+cleanup() {
+       for n in ${r_a} ${r_b} ${r_w} ${c_a} ${c_b};do
+               ip netns del ${n}
+       done
+       rm -f ${rx}
+}
+
+trap cleanup EXIT
+
+test_path() {
+       msg="$1"
+
+       ip netns exec ${c_b} nc -n -w 3 -q 3 -u -l -p 5000 > ${rx} < /dev/null &
+
+       sleep 1
+       for i in 1 2 3; do
+               head -c1400 /dev/zero | tr "\000" "a" | ip netns exec ${c_a} nc -n -w 1 -u 192.168.20.2 5000
+       done
+
+       wait
+
+       bytes=$(wc -c < ${rx})
+
+       if [ $bytes -eq 1400 ];then
+               echo "OK: PMTU $msg connection tracking"
+       else
+               echo "FAIL: PMTU $msg connection tracking: got $bytes, expected 1400"
+               exit 1
+       fi
+}
+
+# Detailed setup for Router A
+# ---------------------------
+# Interfaces:
+# eth0: 10.2.2.1/24
+# eth1: 192.168.10.1/24
+# ipip0: No IP address, local 10.2.2.1 remote 10.4.4.1
+# Routes:
+# 192.168.20.0/24 dev ipip0    (192.168.20.0/24 is subnet of Client B)
+# 10.4.4.1 via 10.2.2.254      (Router B via Wanrouter)
+# No iptables rules at all.
+
+ip link add veth0 netns ${r_a} type veth peer name veth0 netns ${r_w}
+ip link add veth1 netns ${r_a} type veth peer name veth0 netns ${c_a}
+
+l_addr="10.2.2.1"
+r_addr="10.4.4.1"
+ip netns exec ${r_a} ip link add ipip0 type ipip local ${l_addr} remote ${r_addr} mode ipip || exit $ksft_skip
+
+for dev in lo veth0 veth1 ipip0; do
+    ip -net ${r_a} link set $dev up
+done
+
+ip -net ${r_a} addr add 10.2.2.1/24 dev veth0
+ip -net ${r_a} addr add 192.168.10.1/24 dev veth1
+
+ip -net ${r_a} route add 192.168.20.0/24 dev ipip0
+ip -net ${r_a} route add 10.4.4.0/24 via 10.2.2.254
+
+ip netns exec ${r_a} sysctl -q net.ipv4.conf.all.forwarding=1 > /dev/null
+
+# Detailed setup for Router B
+# ---------------------------
+# Interfaces:
+# eth0: 10.4.4.1/24
+# eth1: 192.168.20.1/24
+# ipip0: No IP address, local 10.4.4.1 remote 10.2.2.1
+# Routes:
+# 192.168.10.0/24 dev ipip0    (192.168.10.0/24 is subnet of Client A)
+# 10.2.2.1 via 10.4.4.254      (Router A via Wanrouter)
+# No iptables rules at all.
+
+ip link add veth0 netns ${r_b} type veth peer name veth1 netns ${r_w}
+ip link add veth1 netns ${r_b} type veth peer name veth0 netns ${c_b}
+
+l_addr="10.4.4.1"
+r_addr="10.2.2.1"
+
+ip netns exec ${r_b} ip link add ipip0 type ipip local ${l_addr} remote ${r_addr} mode ipip || exit $ksft_skip
+
+for dev in lo veth0 veth1 ipip0; do
+       ip -net ${r_b} link set $dev up
+done
+
+ip -net ${r_b} addr add 10.4.4.1/24 dev veth0
+ip -net ${r_b} addr add 192.168.20.1/24 dev veth1
+
+ip -net ${r_b} route add 192.168.10.0/24 dev ipip0
+ip -net ${r_b} route add 10.2.2.0/24 via 10.4.4.254
+ip netns exec ${r_b} sysctl -q net.ipv4.conf.all.forwarding=1 > /dev/null
+
+# Client A
+ip -net ${c_a} addr add 192.168.10.2/24 dev veth0
+ip -net ${c_a} link set dev lo up
+ip -net ${c_a} link set dev veth0 up
+ip -net ${c_a} route add default via 192.168.10.1
+
+# Client A
+ip -net ${c_b} addr add 192.168.20.2/24 dev veth0
+ip -net ${c_b} link set dev veth0 up
+ip -net ${c_b} link set dev lo up
+ip -net ${c_b} route add default via 192.168.20.1
+
+# Wan
+ip -net ${r_w} addr add 10.2.2.254/24 dev veth0
+ip -net ${r_w} addr add 10.4.4.254/24 dev veth1
+
+ip -net ${r_w} link set dev lo up
+ip -net ${r_w} link set dev veth0 up mtu 1400
+ip -net ${r_w} link set dev veth1 up mtu 1400
+
+ip -net ${r_a} link set dev veth0 mtu 1400
+ip -net ${r_b} link set dev veth0 mtu 1400
+
+ip netns exec ${r_w} sysctl -q net.ipv4.conf.all.forwarding=1 > /dev/null
+
+# Path MTU discovery
+# ------------------
+# Running tracepath from Client A to Client B shows PMTU discovery is working
+# as expected:
+#
+# clienta:~# tracepath 192.168.20.2
+# 1?: [LOCALHOST]                      pmtu 1500
+# 1:  192.168.10.1                                          0.867ms
+# 1:  192.168.10.1                                          0.302ms
+# 2:  192.168.10.1                                          0.312ms pmtu 1480
+# 2:  no reply
+# 3:  192.168.10.1                                          0.510ms pmtu 1380
+# 3:  192.168.20.2                                          2.320ms reached
+# Resume: pmtu 1380 hops 3 back 3
+
+# ip netns exec ${c_a} traceroute --mtu 192.168.20.2
+
+# Router A has learned PMTU (1400) to Router B from Wanrouter.
+# Client A has learned PMTU (1400 - IPIP overhead = 1380) to Client B
+# from Router A.
+
+#Send large UDP packet
+#---------------------
+#Now we send a 1400 bytes UDP packet from Client A to Client B:
+
+# clienta:~# head -c1400 /dev/zero | tr "\000" "a" | nc -u 192.168.20.2 5000
+test_path "without"
+
+# The IPv4 stack on Client A already knows the PMTU to Client B, so the
+# UDP packet is sent as two fragments (1380 + 20). Router A forwards the
+# fragments between eth1 and ipip0. The fragments fit into the tunnel and
+# reach their destination.
+
+#When sending the large UDP packet again, Router A now reassembles the
+#fragments before routing the packet over ipip0. The resulting IPIP
+#packet is too big (1400) for the tunnel PMTU (1380) to Router B, it is
+#dropped on Router A before sending.
+
+ip netns exec ${r_a} iptables -A FORWARD -m conntrack --ctstate NEW
+test_path "with"
index edf0a48..bf6b962 100755 (executable)
@@ -94,7 +94,13 @@ check_for_helper()
        local message=$2
        local port=$3
 
-       ip netns exec ${netns} conntrack -L -p tcp --dport $port 2> /dev/null |grep -q 'helper=ftp'
+       if echo $message |grep -q 'ipv6';then
+               local family="ipv6"
+       else
+               local family="ipv4"
+       fi
+
+       ip netns exec ${netns} conntrack -L -f $family -p tcp --dport $port 2> /dev/null |grep -q 'helper=ftp'
        if [ $? -ne 0 ] ; then
                echo "FAIL: ${netns} did not show attached helper $message" 1>&2
                ret=1
@@ -111,8 +117,8 @@ test_helper()
 
        sleep 3 | ip netns exec ${ns2} nc -w 2 -l -p $port > /dev/null &
 
-       sleep 1
        sleep 1 | ip netns exec ${ns1} nc -w 2 10.0.1.2 $port > /dev/null &
+       sleep 1
 
        check_for_helper "$ns1" "ip $msg" $port
        check_for_helper "$ns2" "ip $msg" $port
@@ -128,8 +134,8 @@ test_helper()
 
        sleep 3 | ip netns exec ${ns2} nc -w 2 -6 -l -p $port > /dev/null &
 
-       sleep 1
        sleep 1 | ip netns exec ${ns1} nc -w 2 -6 dead:1::2 $port > /dev/null &
+       sleep 1
 
        check_for_helper "$ns1" "ipv6 $msg" $port
        check_for_helper "$ns2" "ipv6 $msg" $port
index cb53a8b..c25cf7c 100644 (file)
@@ -443,7 +443,6 @@ int test_alignment_handler_integer(void)
        LOAD_DFORM_TEST(ldu);
        LOAD_XFORM_TEST(ldx);
        LOAD_XFORM_TEST(ldux);
-       LOAD_DFORM_TEST(lmw);
        STORE_DFORM_TEST(stb);
        STORE_XFORM_TEST(stbx);
        STORE_DFORM_TEST(stbu);
@@ -462,7 +461,11 @@ int test_alignment_handler_integer(void)
        STORE_XFORM_TEST(stdx);
        STORE_DFORM_TEST(stdu);
        STORE_XFORM_TEST(stdux);
+
+#ifdef __BIG_ENDIAN__
+       LOAD_DFORM_TEST(lmw);
        STORE_DFORM_TEST(stmw);
+#endif
 
        return rc;
 }
index 9e5c7f3..0af4f02 100644 (file)
@@ -290,5 +290,5 @@ static int test(void)
 
 int main(void)
 {
-       test_harness(test, "pkey_exec_prot");
+       return test_harness(test, "pkey_exec_prot");
 }
index 4f815d7..2db76e5 100644 (file)
@@ -329,5 +329,5 @@ static int test(void)
 
 int main(void)
 {
-       test_harness(test, "pkey_siginfo");
+       return test_harness(test, "pkey_siginfo");
 }
index 5eb64d4..a8dc51a 100644 (file)
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-only
 vdso_test
+vdso_test_abi
+vdso_test_clock_getres
+vdso_test_correctness
 vdso_test_gettimeofday
 vdso_test_getcpu
 vdso_standalone_test_x86
index 5029ef9..c4aea79 100644 (file)
@@ -349,7 +349,7 @@ static void test_one_clock_gettime64(int clock, const char *name)
                return;
        }
 
-       printf("\t%llu.%09ld %llu.%09ld %llu.%09ld\n",
+       printf("\t%llu.%09lld %llu.%09lld %llu.%09lld\n",
               (unsigned long long)start.tv_sec, start.tv_nsec,
               (unsigned long long)vdso.tv_sec, vdso.tv_nsec,
               (unsigned long long)end.tv_sec, end.tv_nsec);
index 9a25307..d42115e 100644 (file)
@@ -4,7 +4,7 @@
 include local_config.mk
 
 uname_M := $(shell uname -m 2>/dev/null || echo not)
-MACHINE ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/')
+MACHINE ?= $(shell echo $(uname_M) | sed -e 's/aarch64.*/arm64/' -e 's/ppc64.*/ppc64/')
 
 # Without this, failed build products remain, with up-to-date timestamps,
 # thus tricking Make (and you!) into believing that All Is Well, in subsequent
@@ -43,7 +43,7 @@ TEST_GEN_FILES += thuge-gen
 TEST_GEN_FILES += transhuge-stress
 TEST_GEN_FILES += userfaultfd
 
-ifeq ($(ARCH),x86_64)
+ifeq ($(MACHINE),x86_64)
 CAN_BUILD_I386 := $(shell ./../x86/check_cc.sh $(CC) ../x86/trivial_32bit_program.c -m32)
 CAN_BUILD_X86_64 := $(shell ./../x86/check_cc.sh $(CC) ../x86/trivial_64bit_program.c)
 CAN_BUILD_WITH_NOPIE := $(shell ./../x86/check_cc.sh $(CC) ../x86/trivial_program.c -no-pie)
@@ -65,13 +65,13 @@ TEST_GEN_FILES += $(BINARIES_64)
 endif
 else
 
-ifneq (,$(findstring $(ARCH),powerpc))
+ifneq (,$(findstring $(MACHINE),ppc64))
 TEST_GEN_FILES += protection_keys
 endif
 
 endif
 
-ifneq (,$(filter $(MACHINE),arm64 ia64 mips64 parisc64 ppc64 ppc64le riscv64 s390x sh64 sparc64 x86_64))
+ifneq (,$(filter $(MACHINE),arm64 ia64 mips64 parisc64 ppc64 riscv64 s390x sh64 sparc64 x86_64))
 TEST_GEN_FILES += va_128TBswitch
 TEST_GEN_FILES += virtual_address_range
 TEST_GEN_FILES += write_to_hugetlbfs
@@ -84,7 +84,7 @@ TEST_FILES := test_vmalloc.sh
 KSFT_KHDR_INSTALL := 1
 include ../lib.mk
 
-ifeq ($(ARCH),x86_64)
+ifeq ($(MACHINE),x86_64)
 BINARIES_32 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_32))
 BINARIES_64 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_64))
 
index b50c208..fe07d97 100644 (file)
@@ -1,5 +1,4 @@
 CONFIG_LOCALVERSION="-debug"
-CONFIG_ENABLE_MUST_CHECK=y
 CONFIG_FRAME_POINTER=y
 CONFIG_STACK_VALIDATION=y
 CONFIG_DEBUG_KERNEL=y
index 5f26048..8367d88 100644 (file)
@@ -485,9 +485,8 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
        kvm->mmu_notifier_count++;
        need_tlb_flush = kvm_unmap_hva_range(kvm, range->start, range->end,
                                             range->flags);
-       need_tlb_flush |= kvm->tlbs_dirty;
        /* we've to flush the tlb before the pages can be freed */
-       if (need_tlb_flush)
+       if (need_tlb_flush || kvm->tlbs_dirty)
                kvm_flush_remote_tlbs(kvm);
 
        spin_unlock(&kvm->mmu_lock);
@@ -1293,6 +1292,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
                return -EINVAL;
        /* We can read the guest memory with __xxx_user() later on. */
        if ((mem->userspace_addr & (PAGE_SIZE - 1)) ||
+           (mem->userspace_addr != untagged_addr(mem->userspace_addr)) ||
             !access_ok((void __user *)(unsigned long)mem->userspace_addr,
                        mem->memory_size))
                return -EINVAL;